Move to runtime generated SDF fonts
GlyphCache is introduced as a layer that handles the bin packing with a shelf heuristic. SDF rendering is handled in the fixed-function pipeline using a simple alpha test. The shader version also applies a little smoothing. k3Batch had to be updated to add a minimum alpha parameter.
This commit is contained in:
parent
8207743308
commit
a571d2d999
@ -95,6 +95,7 @@ typedef struct GlyphCache {
|
||||
GlyphCacheRow *rows;
|
||||
|
||||
size_t item_count;
|
||||
size_t item_capacity;
|
||||
GlyphCacheGlyph *items;
|
||||
|
||||
void *userdata;
|
||||
@ -117,6 +118,13 @@ void glca_set_time(GlyphCache *gc, uint64_t time);
|
||||
#endif
|
||||
|
||||
#ifdef GLCA_IMPLEMENTATION
|
||||
static uint32_t glca_hash(uint32_t x) {
|
||||
x = ((x >> 16) ^ x) * 0x45d9f3bu;
|
||||
x = ((x >> 16) ^ x) * 0x45d9f3bu;
|
||||
x = (x >> 16) ^ x;
|
||||
return x;
|
||||
}
|
||||
|
||||
int glca_init(GlyphCache *gc, FT_Face face, size_t total_width, size_t total_height, void *userdata, GlyphCacheSetImage set_image, GlyphCacheGetImage get_image, GlyphCacheFillCustomData fill_custom_data) {
|
||||
memset(gc, 0, sizeof(*gc));
|
||||
gc->face = face;
|
||||
@ -126,6 +134,8 @@ int glca_init(GlyphCache *gc, FT_Face face, size_t total_width, size_t total_hei
|
||||
gc->set_image = set_image;
|
||||
gc->get_image = get_image;
|
||||
gc->fill_custom_data = fill_custom_data;
|
||||
gc->item_capacity = 128;
|
||||
gc->items = calloc(gc->item_capacity, sizeof(*gc->items));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -185,18 +195,22 @@ make_new:
|
||||
return row;
|
||||
}
|
||||
|
||||
static int comparator(const void *A, const void *B) {
|
||||
uint32_t cpa = *(uint32_t*) A;
|
||||
uint32_t cpb = *(uint32_t*) B;
|
||||
|
||||
if(cpa == cpb) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return cpa > cpb ? 1 : -1;
|
||||
}
|
||||
GlyphCacheGlyph *glca_get_noupdate(GlyphCache *gc, uint32_t codepoint) {
|
||||
return bsearch(&codepoint, gc->items, gc->item_count, sizeof(*gc->items), comparator);
|
||||
uint32_t i = glca_hash(codepoint);
|
||||
for(size_t probe = 0; probe < 5; probe++) {
|
||||
i &= gc->item_capacity - 1;
|
||||
|
||||
if(gc->items[i].codepoint == 0 && gc->items[i].w == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(gc->items[i].codepoint == codepoint) {
|
||||
return &gc->items[i];
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
GlyphCacheGlyph *glca_get(GlyphCache *gc, uint32_t codepoint) {
|
||||
GlyphCacheGlyph *glyph = glca_get_noupdate(gc, codepoint);
|
||||
@ -209,6 +223,10 @@ GlyphCacheGlyph *glca_get(GlyphCache *gc, uint32_t codepoint) {
|
||||
void glca_try_evict(GlyphCache *gc) {
|
||||
uint64_t min_time = gc->current_time, max_time = 0;
|
||||
for(size_t i = 0; i < gc->item_count; i++) {
|
||||
if(gc->items[i].codepoint == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(min_time > gc->items[i].last_use) {
|
||||
min_time = gc->items[i].last_use;
|
||||
}
|
||||
@ -246,13 +264,51 @@ void glca_try_evict(GlyphCache *gc) {
|
||||
gcr->width -= glyph->w;
|
||||
gcr->entry_count--;
|
||||
|
||||
size_t glyph_index = glyph - gc->items;
|
||||
memmove(glyph, glyph + 1, sizeof(*glyph) * (gc->item_count - glyph_index - 1));
|
||||
gc->item_count--;
|
||||
glyph = NULL;
|
||||
glyph->codepoint = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void glca_expand(GlyphCache *gc, size_t shift) {
|
||||
size_t new_capacity = gc->item_capacity << shift;
|
||||
GlyphCacheGlyph *newarray = calloc(new_capacity, sizeof(*newarray));
|
||||
|
||||
for(size_t idx = 0; idx < gc->item_capacity; idx++) {
|
||||
|
||||
if(gc->items[idx].codepoint == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t i = glca_hash(gc->items[idx].codepoint);
|
||||
size_t probe;
|
||||
for(probe = 0; probe < 5; probe++) {
|
||||
i &= new_capacity - 1;
|
||||
|
||||
if(newarray[i].codepoint == 0) {
|
||||
newarray[i] = gc->items[idx];
|
||||
break;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if(probe == 5) {
|
||||
goto failed_to_expand;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
free(gc->items);
|
||||
|
||||
gc->item_capacity = new_capacity;
|
||||
gc->items = newarray;
|
||||
|
||||
return;
|
||||
failed_to_expand:
|
||||
free(newarray);
|
||||
glca_expand(gc, shift + 1);
|
||||
}
|
||||
|
||||
GlyphCacheGlyph *glca_request(GlyphCache *gc, uint32_t codepoint) {
|
||||
GlyphCacheGlyph *glyph = glca_get(gc, codepoint);
|
||||
if(glyph) {
|
||||
@ -282,8 +338,24 @@ GlyphCacheGlyph *glca_request(GlyphCache *gc, uint32_t codepoint) {
|
||||
gcr->entries = realloc(gcr->entries, sizeof(*gcr->entries) * (gcr->entry_count + 1));
|
||||
gcr->entries[gcr->entry_count++] = codepoint;
|
||||
|
||||
gc->items = realloc(gc->items, sizeof(*gc->items) * (gc->item_count + 1));
|
||||
gc->items[gc->item_count++] = (GlyphCacheGlyph) {
|
||||
glyph = NULL;
|
||||
while(1) {
|
||||
uint32_t i = glca_hash(codepoint);
|
||||
for(size_t probe = 0; probe < 5; probe++) {
|
||||
i &= gc->item_capacity - 1;
|
||||
if(gc->items[i].codepoint == 0) {
|
||||
glyph = &gc->items[i];
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if(glyph) {
|
||||
break;
|
||||
}
|
||||
glca_expand(gc, 1);
|
||||
}
|
||||
|
||||
*glyph = (GlyphCacheGlyph) {
|
||||
.codepoint = codepoint,
|
||||
.x = x,
|
||||
.y = y,
|
||||
@ -292,10 +364,9 @@ GlyphCacheGlyph *glca_request(GlyphCache *gc, uint32_t codepoint) {
|
||||
};
|
||||
#ifdef GLCA_CUSTOM_GLYPH_DATA
|
||||
if(gc->fill_custom_data) {
|
||||
gc->fill_custom_data(gc, gc->face->glyph, &gc->items[gc->item_count - 1]);
|
||||
gc->fill_custom_data(gc, gc->face->glyph, glyph);
|
||||
}
|
||||
#endif
|
||||
qsort(gc->items, gc->item_count, sizeof(*gc->items), comparator);
|
||||
|
||||
uint8_t *buf = malloc(w * h);
|
||||
for(int y = 0; y < h; y++) {
|
||||
|
||||
@ -10,7 +10,7 @@ static inline void immdraw_fill_rect(int16_t x, int16_t y, int16_t w, int16_t h,
|
||||
k3BatchAdd(NULL, (struct k3RectF) {0, 0, 1, 1}, (struct k3RectF) {
|
||||
x, GameWndH - y - h,
|
||||
w, h
|
||||
}, 0, (vec4) {r, g, b, a}, borderRadius);
|
||||
}, 0, (vec4) {r, g, b, a}, borderRadius, 0);
|
||||
}
|
||||
|
||||
static inline void immdraw_font_draw(struct k3Font *font, int16_t x, int16_t y, int16_t w, float sz, size_t len, const char *txt, int alignment, float r, float g, float b, float a) {
|
||||
@ -26,7 +26,7 @@ static inline void immdraw_font_size(struct k3Font *font, float sz, const char *
|
||||
|
||||
static inline void immdraw_image_draw(k3MImageData *data, int16_t x, int16_t y, int16_t w, int16_t h, float r, float g, float b, float a) {
|
||||
struct k3Tex *tex = *data;
|
||||
k3BatchAdd(tex, (struct k3RectF) {0, 0, 1, 1}, (struct k3RectF) {x, GameWndH - y - h, w, h}, 0, (vec4) {r, g, b, a}, 0);
|
||||
k3BatchAdd(tex, (struct k3RectF) {0, 0, 1, 1}, (struct k3RectF) {x, GameWndH - y - h, w, h}, 0, (vec4) {r, g, b, a}, 0, 0);
|
||||
}
|
||||
|
||||
static int16_t crop_aabb[4] = {-1, -1, -1, -1};
|
||||
|
||||
31
src/k3.c
31
src/k3.c
@ -624,6 +624,8 @@ void k3TexUpdate(struct k3Tex *tex, enum k3TexType type, int index, uint16_t wid
|
||||
}
|
||||
|
||||
if(compressed && TextureOfflineCompressor) {
|
||||
k3Log(k3_DEBUG, "Compressing texture...");
|
||||
|
||||
size_t len;
|
||||
data = TextureOfflineCompressor(data, width, height, externalFmt, intype, &len);
|
||||
|
||||
@ -643,6 +645,8 @@ void k3TexUpdate(struct k3Tex *tex, enum k3TexType type, int index, uint16_t wid
|
||||
tex->szZ = 0;
|
||||
|
||||
tex->glInternalFormat = internalFmt;
|
||||
tex->glExternalFormat = externalFmt;
|
||||
tex->glInType = intype;
|
||||
}
|
||||
uint32_t k3TexSzX(struct k3Tex *this) {
|
||||
return this->szX;
|
||||
@ -1132,8 +1136,8 @@ static void disable_glsl_bones(struct k3Mdl *mdl, struct k3GLSLP *p) {
|
||||
GLint a0;
|
||||
GLint a1;
|
||||
|
||||
a0 = glGetAttribLocationARB(p->handle, "a_boneids");
|
||||
a1 = glGetAttribLocationARB(p->handle, "a_boneweights");
|
||||
a0 = (glGetAttribLocationARB ? glGetAttribLocationARB : glGetAttribLocation)(p->handle, "a_boneids");
|
||||
a1 = (glGetAttribLocationARB ? glGetAttribLocationARB : glGetAttribLocation)(p->handle, "a_boneweights");
|
||||
|
||||
if(a0 != -1) {
|
||||
if(!k3IsCore) {
|
||||
@ -1419,6 +1423,7 @@ static void forward_subpass(mat4 projection, mat4 view, int transparent, int lig
|
||||
struct k3Mat *lastMaterial = NULL;
|
||||
int lastAdditive = -1;
|
||||
int lastDepthwrite = -1;
|
||||
int lastNocull = -1;
|
||||
|
||||
for(size_t rble = rbleStart; rble < rbleEnd; rble++) {
|
||||
struct k3Mdl *mdl = renderQueue[rble].mdl;
|
||||
@ -1478,6 +1483,15 @@ static void forward_subpass(mat4 projection, mat4 view, int transparent, int lig
|
||||
}
|
||||
}
|
||||
|
||||
if(lastNocull != mat->passes[0].nocull) {
|
||||
lastNocull = mat->passes[0].nocull;
|
||||
if(lastNocull) {
|
||||
glDisable(GL_CULL_FACE);
|
||||
} else {
|
||||
glEnable(GL_CULL_FACE);
|
||||
}
|
||||
}
|
||||
|
||||
if(glslp) {
|
||||
if(lastGLSLP != glslp) {
|
||||
if(k3IsCore)
|
||||
@ -1498,10 +1512,8 @@ static void forward_subpass(mat4 projection, mat4 view, int transparent, int lig
|
||||
|
||||
setup_glsl_model_uniforms(glslp, modelmat);
|
||||
|
||||
if(mat != lastMaterial) {
|
||||
bind_mat_textures(mat, 0);
|
||||
bind_shadow_texture(mat->passes[0].unitsUsed);
|
||||
}
|
||||
bind_mat_textures(mat, 0);
|
||||
bind_shadow_texture(mat->passes[0].unitsUsed);
|
||||
} else if(!k3IsCore) {
|
||||
if(lastGLSLP && GLAD_GL_ARB_shading_language_100) {
|
||||
glUseProgramObjectARB(0);
|
||||
@ -1557,12 +1569,6 @@ static void forward_subpass(mat4 projection, mat4 view, int transparent, int lig
|
||||
}
|
||||
|
||||
lastMaterial = mat;
|
||||
|
||||
if(mat->passes[0].nocull) {
|
||||
glDisable(GL_CULL_FACE);
|
||||
} else {
|
||||
glEnable(GL_CULL_FACE);
|
||||
}
|
||||
if(!k3IsCore) {
|
||||
if(mat->passes[0].alphatest) {
|
||||
glEnable(GL_ALPHA_TEST);
|
||||
@ -1634,7 +1640,6 @@ void k3PassForward(mat4 projection, mat4 cam) {
|
||||
glEnable(GL_BLEND);
|
||||
glEnable(GL_MULTISAMPLE);
|
||||
|
||||
glEnable(GL_CULL_FACE);
|
||||
glFrontFace(GL_CCW);
|
||||
|
||||
if(!k3IsCore) {
|
||||
|
||||
@ -14,6 +14,7 @@ struct S {
|
||||
|
||||
static struct k3Tex *activeTex;
|
||||
static float activeBorderRadius;
|
||||
static float activeMinAlpha;
|
||||
|
||||
static size_t SCount, SCapacity;
|
||||
static struct S *S;
|
||||
@ -48,6 +49,7 @@ void k3BatchInit() {
|
||||
"uniform sampler2D u_tex;\n"
|
||||
"uniform float u_texuse;\n"
|
||||
"uniform float u_borderradius;\n"
|
||||
"uniform float u_minalpha;\n"
|
||||
"in vec2 v_uv;\n"
|
||||
"in vec4 v_color;\n"
|
||||
"in vec2 v_size;\n"
|
||||
@ -60,7 +62,11 @@ void k3BatchInit() {
|
||||
" if(length(c) > u_borderradius) {\n"
|
||||
" discard;\n"
|
||||
" }\n"
|
||||
" fragcolor = mix(vec4(1, 1, 1, 1), texture2D(u_tex, v_uv), u_texuse) * v_color;\n"
|
||||
" vec4 col = texture2D(u_tex, v_uv);\n"
|
||||
" if(u_minalpha > 0.0) {\n"
|
||||
" col.a = clamp((col.a - u_minalpha + 0.1) / 0.1, 0.0, 1.0);\n"
|
||||
" }\n"
|
||||
" fragcolor = mix(vec4(1, 1, 1, 1), col, u_texuse) * v_color;\n"
|
||||
"}\n"
|
||||
, NULL), NULL);
|
||||
|
||||
@ -70,11 +76,12 @@ void k3BatchInit() {
|
||||
}
|
||||
}
|
||||
|
||||
void k3BatchAdd(struct k3Tex *tex, struct k3RectF src, struct k3RectF dst, float rot, vec4 color, float borderRadius) {
|
||||
if(activeTex != tex || borderRadius != activeBorderRadius) {
|
||||
void k3BatchAdd(struct k3Tex *tex, struct k3RectF src, struct k3RectF dst, float rot, vec4 color, float borderRadius, float minAlpha) {
|
||||
if(activeTex != tex || borderRadius != activeBorderRadius || minAlpha != activeMinAlpha) {
|
||||
k3BatchFlush();
|
||||
activeTex = tex;
|
||||
activeBorderRadius = borderRadius;
|
||||
activeMinAlpha = minAlpha;
|
||||
}
|
||||
|
||||
if(SCount == SCapacity) {
|
||||
@ -118,9 +125,6 @@ void k3BatchFlush() {
|
||||
glDisable(GL_FRAGMENT_PROGRAM_ARB);
|
||||
}
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
if(activeTex) {
|
||||
@ -132,6 +136,9 @@ void k3BatchFlush() {
|
||||
}
|
||||
|
||||
if(k3IsCore) {
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
GLuint handle = GL_FROM_K3GLSL(coreProg);
|
||||
|
||||
glUseProgram(handle);
|
||||
@ -214,8 +221,8 @@ void k3BatchFlush() {
|
||||
glBufferDataARB(GL_ARRAY_BUFFER_ARB, SCount * 60 * sizeof(*farr), farr, GL_DYNAMIC_DRAW);
|
||||
|
||||
glUniform1f(k3ProgramGetUId(coreProg, "u_texuse"), !!activeTex);
|
||||
|
||||
glUniform1f(k3ProgramGetUId(coreProg, "u_borderradius"), activeBorderRadius);
|
||||
glUniform1f(k3ProgramGetUId(coreProg, "u_minalpha"), activeMinAlpha);
|
||||
|
||||
GLint aPos = glGetAttribLocation(handle, "a_pos");
|
||||
GLint aUv = glGetAttribLocation(handle, "a_uv");
|
||||
@ -243,6 +250,16 @@ void k3BatchFlush() {
|
||||
glUseProgramObjectARB(0);
|
||||
}
|
||||
|
||||
if(activeMinAlpha) {
|
||||
glDisable(GL_BLEND);
|
||||
} else {
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
|
||||
glEnable(GL_ALPHA_TEST);
|
||||
glAlphaFunc(GL_GREATER, activeMinAlpha);
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
struct S *s = S;
|
||||
for(size_t i = 0; i < SCount; i++) {
|
||||
@ -264,6 +281,8 @@ void k3BatchFlush() {
|
||||
}
|
||||
glEnd();
|
||||
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
||||
}
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ struct k3RectF {
|
||||
float h;
|
||||
};
|
||||
|
||||
void k3BatchAdd(struct k3Tex *tex, struct k3RectF src, struct k3RectF dst, float rot, vec4 color, float borderRadius);
|
||||
void k3BatchAdd(struct k3Tex *tex, struct k3RectF src, struct k3RectF dst, float rot, vec4 color, float borderRadius, float minAlpha);
|
||||
void k3BatchFlush();
|
||||
|
||||
void k3BatchSetResolution(float w, float h);
|
||||
|
||||
136
src/k3font.c
136
src/k3font.c
@ -3,72 +3,60 @@
|
||||
#include"k3batch.h"
|
||||
#include<string.h>
|
||||
#include"gl.h"
|
||||
#include<freetype/freetype.h>
|
||||
|
||||
#define GLCA_IMPLEMENTATION
|
||||
#include"glyphcache/glca.h"
|
||||
|
||||
#include"k3_internal.h"
|
||||
|
||||
static _Thread_local FT_Library ftlib;
|
||||
|
||||
struct k3Font *k3FontCreate() {
|
||||
if(!ftlib) {
|
||||
assert(FT_Init_FreeType(&ftlib) == 0);
|
||||
FT_Property_Set(ftlib, "sdf", "spread", &(int) {64});
|
||||
}
|
||||
|
||||
struct k3Font *ret = calloc(sizeof(*ret), 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cmpglyph(const void *a, const void *b) {
|
||||
return *(const uint32_t*) a - *(const uint32_t*) b;
|
||||
void my_set_image(struct GlyphCache *gc, int x, int y, int w, int h, const void *buf) {
|
||||
struct k3Font *this = gc->userdata;
|
||||
|
||||
for(size_t row = 0; row < h; row++) {
|
||||
memcpy(&this->atlasCPU[this->atlasSize * (y + row) + x], buf + w * row, w);
|
||||
}
|
||||
|
||||
//k3TexUpdateSub(this->atlas, 0, x, y, w, h, buf);
|
||||
k3TexUpdate(this->atlas, k3_ALPHA, 0, this->atlasSize, this->atlasSize, this->atlasCPU);
|
||||
}
|
||||
|
||||
int k3FontLoad(struct k3Font *this, const uint8_t *buf, size_t len, k3FontTexLoader texldr) {
|
||||
if(*(uint32_t*) buf != 0x03464D42) {
|
||||
return 0;
|
||||
void my_get_image(struct GlyphCache *gc, int x, int y, int w, int h, void *buf) {
|
||||
struct k3Font *this = gc->userdata;
|
||||
|
||||
for(size_t row = 0; row < h; row++) {
|
||||
memcpy(buf + w * row, &this->atlasCPU[this->atlasSize * (y + row)], w);
|
||||
}
|
||||
}
|
||||
void my_fill_custom_data(struct GlyphCache *gc, FT_GlyphSlot slot, GlyphCacheGlyph *glyph) {
|
||||
glyph->data.xAdvance = slot->metrics.horiAdvance / 64.f;
|
||||
glyph->data.xOffset = slot->metrics.horiBearingX / 64.f;
|
||||
glyph->data.yOffset = slot->metrics.horiBearingY / 64.f;
|
||||
}
|
||||
int k3FontLoad(struct k3Font *this, const char *fn) {
|
||||
this->atlasSize = k3TexSzMax() / 4;
|
||||
this->atlas = k3TexCreate(k3_ALPHA);
|
||||
this->atlasCPU = malloc(this->atlasSize * this->atlasSize);
|
||||
k3TexUpdate(this->atlas, k3_ALPHA, 0, this->atlasSize, this->atlasSize, NULL);
|
||||
|
||||
const uint8_t *end = buf + len;
|
||||
this->fontPixelSize = this->atlasSize / 16;
|
||||
|
||||
buf += 4;
|
||||
assert(FT_New_Face(ftlib, fn, 0, &this->ftface) == 0);
|
||||
FT_Select_Charmap(this->ftface, FT_ENCODING_UNICODE);
|
||||
FT_Set_Pixel_Sizes(this->ftface, this->fontPixelSize, 0);
|
||||
|
||||
uint16_t pages = 0;
|
||||
|
||||
while(buf + 5 < end) {
|
||||
uint8_t blockType = *buf;
|
||||
uint32_t blockSize = *(uint32_t*) (buf + 1);
|
||||
buf += 5;
|
||||
|
||||
if(blockType == 1) { //Info block
|
||||
buf += 14;
|
||||
while(*buf) buf++;
|
||||
buf++;
|
||||
} else if(blockType == 2) { //Common block
|
||||
this->lineScale = 1.f / *(uint16_t*) buf;
|
||||
buf += 2;
|
||||
this->baseline = *(uint16_t*) buf;
|
||||
buf += 2;
|
||||
this->texW = *(uint16_t*) buf;
|
||||
buf += 2;
|
||||
this->texH = *(uint16_t*) buf;
|
||||
buf += 2;
|
||||
pages = *(uint16_t*) buf;
|
||||
buf += 7;
|
||||
} else if(blockType == 3) { //Pages block
|
||||
if(pages == 0) return 0;
|
||||
|
||||
this->pageCount = pages;
|
||||
this->pages = malloc(sizeof(*this->pages) * this->pageCount);
|
||||
|
||||
for(size_t i = 0; i < this->pageCount; i++) {
|
||||
this->pages[i] = texldr(this, buf);
|
||||
buf += strlen(buf) + 1;
|
||||
}
|
||||
} else if(blockType == 4) { //Chars block
|
||||
size_t num = blockSize / 20;
|
||||
|
||||
this->glyphs = calloc(sizeof(*this->glyphs), this->glyphCount = num);
|
||||
|
||||
memcpy(this->glyphs, buf, num * 20);
|
||||
|
||||
qsort(this->glyphs, num, sizeof(*this->glyphs), cmpglyph);
|
||||
|
||||
buf += blockSize;
|
||||
} else if(blockType == 5) { //Kerning block
|
||||
// Ignore kerning for now
|
||||
buf += blockSize;
|
||||
}
|
||||
}
|
||||
glca_init(&this->glca, this->ftface, this->atlasSize, this->atlasSize, this, my_set_image, my_get_image, my_fill_custom_data);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -117,16 +105,16 @@ void k3FontSz(struct k3Font *this, float sz, const char *txt, float wall, struct
|
||||
y += sz;
|
||||
}
|
||||
|
||||
struct k3FontGlyph *g = k3FontGetGlyph(this, cp);
|
||||
struct GlyphCacheGlyph *g = glca_request(&this->glca, cp);
|
||||
|
||||
if(!g) continue;
|
||||
|
||||
if(x + g->width * this->lineScale * sz > wall) {
|
||||
if(x + sz * g->w / this->fontPixelSize > wall) {
|
||||
x = 0;
|
||||
y += sz;
|
||||
}
|
||||
|
||||
x += g->xadvance * this->lineScale * sz;
|
||||
x += sz * g->data.xAdvance / this->fontPixelSize;
|
||||
maxX = fmaxf(maxX, x);
|
||||
}
|
||||
|
||||
@ -153,16 +141,16 @@ void k3FontDraw(struct k3Font *this, float xStart, float yStart, float sz, const
|
||||
break;
|
||||
}
|
||||
|
||||
struct k3FontGlyph *g = k3FontGetGlyph(this, cp2);
|
||||
struct GlyphCacheGlyph *g = glca_request(&this->glca, cp2);
|
||||
if(g) {
|
||||
if(lineWidth + g->width * this->lineScale * sz > wall) {
|
||||
if(lineWidth + sz * g->w / this->fontPixelSize > wall) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lineLength++;
|
||||
if(g) {
|
||||
lineWidth += g->xadvance * this->lineScale * sz;
|
||||
lineWidth += sz * g->data.xAdvance / this->fontPixelSize;
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,24 +170,24 @@ void k3FontDraw(struct k3Font *this, float xStart, float yStart, float sz, const
|
||||
for(size_t i = 0; i < lineLength; i++) {
|
||||
uint32_t cp = read_utf8(&txt);
|
||||
|
||||
struct k3FontGlyph *g = k3FontGetGlyph(this, cp);
|
||||
struct GlyphCacheGlyph *g = glca_request(&this->glca, cp);
|
||||
|
||||
if(!g) continue;
|
||||
|
||||
struct k3Tex *tex = this->pages[g->page];
|
||||
size_t texW = this->texW;
|
||||
size_t texH = this->texH;
|
||||
struct k3Tex *tex = this->atlas;
|
||||
size_t texW = k3TexSzX(tex);
|
||||
size_t texH = k3TexSzY(tex);
|
||||
|
||||
k3BatchAdd(tex,
|
||||
(struct k3RectF) {(float) (g->x + 0.5) / texW, (float) (g->y + 0.5) / texH, (float) (g->width - 1) / texW, (float) (g->height - 1) / texH},
|
||||
(struct k3RectF) {(float) (g->x + 0.5) / texW, (float) (g->y - 0.5) / texH, (float) (g->w - 1) / texW, (float) (g->h - 1) / texH},
|
||||
(struct k3RectF) {
|
||||
x + g->xoffset * this->lineScale * sz,
|
||||
y + ((-g->height - g->yoffset) * this->lineScale + 1) * sz,
|
||||
g->width * this->lineScale * sz,
|
||||
g->height * this->lineScale * sz
|
||||
}, 0, color, 0);
|
||||
x + sz * g->data.xOffset / this->fontPixelSize,
|
||||
y - sz * ((g->h - g->data.yOffset) / (float) this->fontPixelSize - 0.5f),
|
||||
sz * g->w / this->fontPixelSize,
|
||||
sz * g->h / this->fontPixelSize
|
||||
}, 0, color, 0, k3IsCore ? 0.5 : 0.45);
|
||||
|
||||
x += g->xadvance * this->lineScale * sz;
|
||||
x += sz * g->data.xAdvance / this->fontPixelSize;
|
||||
}
|
||||
|
||||
// If the line break was caused directly by a LF, skip over it for next line
|
||||
@ -211,7 +199,3 @@ void k3FontDraw(struct k3Font *this, float xStart, float yStart, float sz, const
|
||||
}
|
||||
k3BatchFlush();
|
||||
}
|
||||
|
||||
struct k3FontGlyph *k3FontGetGlyph(struct k3Font *this, uint32_t cp) {
|
||||
return bsearch(&cp, this->glyphs, this->glyphCount, sizeof(*this->glyphs), cmpglyph);
|
||||
}
|
||||
|
||||
45
src/k3font.h
45
src/k3font.h
@ -4,49 +4,32 @@
|
||||
#include"k3batch.h"
|
||||
#include<string.h>
|
||||
|
||||
#define k3_FONT_ALIGN_LEFT 0
|
||||
#define k3_FONT_ALIGN_CENTER 1
|
||||
#define k3_FONT_ALIGN_RIGHT 2
|
||||
|
||||
struct k3FontGlyph {
|
||||
uint32_t cp;
|
||||
uint16_t x;
|
||||
uint16_t y;
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
int16_t xoffset;
|
||||
int16_t yoffset;
|
||||
uint16_t xadvance;
|
||||
uint8_t page;
|
||||
uint8_t chnl;
|
||||
};
|
||||
#define GLCA_CUSTOM_GLYPH_DATA
|
||||
typedef struct GlyphCacheGlyphData {
|
||||
float xAdvance;
|
||||
float xOffset;
|
||||
float yOffset;
|
||||
} GlyphCacheGlyphData;
|
||||
#include"glyphcache/glca.h"
|
||||
|
||||
struct k3Font {
|
||||
void *ud;
|
||||
|
||||
float lineScale;
|
||||
int atlasSize;
|
||||
uint8_t *atlasCPU;
|
||||
struct k3Tex *atlas;
|
||||
|
||||
uint16_t baseline;
|
||||
|
||||
uint16_t texW, texH;
|
||||
|
||||
size_t glyphCount;
|
||||
struct k3FontGlyph *glyphs;
|
||||
|
||||
size_t pageCount;
|
||||
struct k3Tex **pages;
|
||||
int fontPixelSize;
|
||||
FT_Face ftface;
|
||||
GlyphCache glca;
|
||||
};
|
||||
|
||||
typedef struct k3Tex*(*k3FontTexLoader)(struct k3Font*, const char *name);
|
||||
|
||||
struct k3Font *k3FontCreate();
|
||||
int k3FontLoad(struct k3Font*, const uint8_t *buf, size_t len, k3FontTexLoader);
|
||||
int k3FontLoad(struct k3Font*, const char *fn);
|
||||
|
||||
void k3FontSz(struct k3Font*, float sz, const char *txt, float wall, struct k3RectF *ret);
|
||||
void k3FontDraw(struct k3Font*, float x, float y, float sz, const char *txt, float wall, int alignment, vec4 color);
|
||||
|
||||
struct k3FontGlyph *k3FontGetGlyph(struct k3Font*, uint32_t cp);
|
||||
|
||||
static inline int k3UTF8Encode(uint32_t cp, uint8_t ret[static 4]) {
|
||||
if(cp < 0x80) {
|
||||
ret[0] = cp;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user