make font rendering unicode aware

This commit is contained in:
jacob 2024-04-04 14:53:00 -05:00
parent e4ecc217e3
commit 1cbdc27a4c
7 changed files with 66 additions and 33 deletions

View File

@ -275,24 +275,18 @@ void draw_text_ex(struct renderer_canvas *canvas, struct font *font, struct v2 p
struct v2 draw_pos = pos; struct v2 draw_pos = pos;
draw_pos.y += font->point_size * scale; draw_pos.y += font->point_size * scale;
for (u64 i = 0; i < str.len; ++i) { struct string_codepoint_iter iter = string_codepoint_iter_begin(str);
u8 c = str.text[i]; while (string_codepoint_iter_next(&iter)) {
u32 codepoint = iter.codepoint;
/* TODO: Remove this (placeholder \n) */ /* TODO: Remove this (placeholder \n) */
if (c == '\n') { if (codepoint == '\n') {
draw_pos.x = pos.x; draw_pos.x = pos.x;
draw_pos.y += (font->point_size * 1.5f) * scale; draw_pos.y += (font->point_size * 1.5f) * scale;
continue; continue;
} }
struct font_glyph *glyph = font_get_glyph(font, c); struct font_glyph *glyph = font_get_glyph(font, codepoint);
if (!glyph) {
glyph = font_get_glyph(font, (u8)'?');
if (!glyph) {
continue;
}
}
f32 x = draw_pos.x + glyph->off_x * scale; f32 x = draw_pos.x + glyph->off_x * scale;
f32 y = draw_pos.y + glyph->off_y * scale; f32 y = draw_pos.y + glyph->off_y * scale;
f32 width = glyph->width * scale; f32 width = glyph->width * scale;
@ -315,4 +309,5 @@ void draw_text_ex(struct renderer_canvas *canvas, struct font *font, struct v2 p
draw_pos.x += glyph->advance * scale; draw_pos.x += glyph->advance * scale;
} }
string_codepoint_iter_end(&iter);
} }

View File

@ -9,9 +9,12 @@
#include "string.h" #include "string.h"
#include "renderer.h" #include "renderer.h"
#define FONT_CHARS " !\"#$%&\'()-.,*/0123456789:;<=+>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
#define LOOKUP_TABLE_SIZE (256) #define LOOKUP_TABLE_SIZE (256)
GLOBAL u32 g_font_codes[] = {
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F,0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF
};
struct font_task_params { struct font_task_params {
struct font_task_params *next_free; struct font_task_params *next_free;
@ -111,11 +114,11 @@ INTERNAL WORK_TASK_FUNC_DEF(font_load_asset_task, vparams)
FMT_STR(path))); FMT_STR(path)));
} }
struct string font_chars = STR(FONT_CHARS); ASSERT(ARRAY_COUNT(g_font_codes) < LOOKUP_TABLE_SIZE);
/* Decode */ /* Decode */
struct resource res = resource_open(path); struct resource res = resource_open(path);
struct ttf_decode_result result = ttf_decode(scratch.arena, res.bytes, point_size, font_chars); struct ttf_decode_result result = ttf_decode(scratch.arena, res.bytes, point_size, g_font_codes, ARRAY_COUNT(g_font_codes));
resource_close(res); resource_close(res);
/* Send texture to GPU */ /* Send texture to GPU */
@ -150,9 +153,9 @@ INTERNAL WORK_TASK_FUNC_DEF(font_load_asset_task, vparams)
MEMCPY(font->glyphs, result.glyphs, sizeof(*font->glyphs) * result.glyphs_count); MEMCPY(font->glyphs, result.glyphs, sizeof(*font->glyphs) * result.glyphs_count);
/* Build lookup table */ /* Build lookup table */
for (u64 i = 0; i < font_chars.len; ++i) { for (u64 i = 0; i < ARRAY_COUNT(g_font_codes); ++i) {
u8 c = font_chars.text[i]; u32 codepoint = g_font_codes[i];
font->lookup[c] = result.cache_indices[i]; font->lookup[codepoint] = result.cache_indices[i];
} }
font_task_params_release(params); font_task_params_release(params);
@ -227,14 +230,13 @@ struct font *font_load(struct string path, f32 point_size)
* Other * Other
* ========================== */ * ========================== */
struct font_glyph *font_get_glyph(struct font *font, u8 c) struct font_glyph *font_get_glyph(struct font *font, u32 codepoint)
{ {
/* Need to add a check here for c < LOOKUP_TABLE_SIZE if range is ever increased */ if (codepoint < LOOKUP_TABLE_SIZE) {
CT_ASSERT(sizeof(c) == 1 && LOOKUP_TABLE_SIZE >= 256); u16 index = font->lookup[codepoint];
u16 index = font->lookup[c];
if (index < font->glyphs_count) { if (index < font->glyphs_count) {
return &font->glyphs[index]; return &font->glyphs[index];
} }
}
return &font->glyphs[0]; return &font->glyphs[0];
} }

View File

@ -40,6 +40,6 @@ struct asset *font_load_asset(struct string path, f32 point_size, b32 help);
struct font *font_load_async(struct string path, f32 point_size); struct font *font_load_async(struct string path, f32 point_size);
struct font *font_load(struct string path, f32 point_size); struct font *font_load(struct string path, f32 point_size);
struct font_glyph *font_get_glyph(struct font *font, u8 c); struct font_glyph *font_get_glyph(struct font *font, u32 codepoint);
#endif #endif

View File

@ -467,6 +467,32 @@ struct string _string_format(struct arena *arena, struct string fmt, ...)
* Unicode * Unicode
* ========================== */ * ========================== */
struct string_codepoint_iter string_codepoint_iter_begin(struct string str)
{
return (struct string_codepoint_iter) {
.src = str
};
}
b32 string_codepoint_iter_next(struct string_codepoint_iter *iter)
{
if (iter->pos < iter->src.len) {
struct string str_remaining = { .len = (iter->src.len - iter->pos), .text = iter->src.text + iter->pos };
struct utf8_decode_result decoded = utf8_decode(str_remaining);
iter->pos += decoded.advance8;
iter->codepoint = decoded.codepoint;
return true;
} else {
return false;
}
}
void string_codepoint_iter_end(struct string_codepoint_iter *iter)
{
/* Do nothing */
(UNUSED)iter;
}
/* utf8 <- utf16 */ /* utf8 <- utf16 */
struct string string_from_string16(struct arena *arena, struct string16 str16) struct string string_from_string16(struct arena *arena, struct string16 str16)
{ {

View File

@ -84,8 +84,21 @@ struct string string_formatv(struct arena *arena, struct string fmt, va_list arg
* Unicode * Unicode
* ========================== */ * ========================== */
struct string_codepoint_iter {
u32 codepoint;
/* Internal */
struct string src;
u64 pos;
};
struct string_codepoint_iter string_codepoint_iter_begin(struct string str);
b32 string_codepoint_iter_next(struct string_codepoint_iter *iter);
void string_codepoint_iter_end(struct string_codepoint_iter *iter);
struct string string_from_string16(struct arena *arena, struct string16 str16); struct string string_from_string16(struct arena *arena, struct string16 str16);
struct string string_from_string32(struct arena *arena, struct string32 str32); struct string string_from_string32(struct arena *arena, struct string32 str32);
struct string16 string16_from_string(struct arena *arena, struct string str8); struct string16 string16_from_string(struct arena *arena, struct string str8);
struct string32 string32_from_string(struct arena *arena, struct string str8); struct string32 string32_from_string(struct arena *arena, struct string str8);

View File

@ -14,6 +14,6 @@ struct ttf_decode_result {
struct ttf_startup_receipt { i32 _; }; struct ttf_startup_receipt { i32 _; };
struct ttf_startup_receipt ttf_startup(void); struct ttf_startup_receipt ttf_startup(void);
struct ttf_decode_result ttf_decode(struct arena *arena, struct buffer encoded, f32 point_size, struct string cache_chars); struct ttf_decode_result ttf_decode(struct arena *arena, struct buffer encoded, f32 point_size, u32 *cache_codes, u32 cache_codes_count);
#endif #endif

View File

@ -68,7 +68,7 @@ struct ttf_startup_receipt ttf_startup(void)
return (struct ttf_startup_receipt) { 0 }; return (struct ttf_startup_receipt) { 0 };
} }
struct ttf_decode_result ttf_decode(struct arena *arena, struct buffer encoded, f32 point_size, struct string cache_chars) struct ttf_decode_result ttf_decode(struct arena *arena, struct buffer encoded, f32 point_size, u32 *cache_codes, u32 cache_codes_count)
{ {
COLORREF bg_color = RGB(0,0,0); COLORREF bg_color = RGB(0,0,0);
COLORREF fg_color = RGB(255,255,255); COLORREF fg_color = RGB(255,255,255);
@ -274,12 +274,9 @@ struct ttf_decode_result ttf_decode(struct arena *arena, struct buffer encoded,
/* Construct indices array */ /* Construct indices array */
u16 *cache_indices = NULL; u16 *cache_indices = NULL;
if (cache_chars.len > 0) { if (cache_codes_count > 0) {
struct temp_arena scratch = scratch_begin(arena); cache_indices = arena_push_array_zero(arena, u16, cache_codes_count);
cache_indices = arena_push_array_zero(arena, u16, cache_chars.len); font_face->GetGlyphIndices(cache_codes, cache_codes_count, cache_indices);
struct string32 str32 = string32_from_string(scratch.arena, cache_chars);
font_face->GetGlyphIndicesA(str32.text, (u32)str32.len, cache_indices);
scratch_end(scratch);
} }
/* Release */ /* Release */