From e16f3acfa16b4d778c274a83d16eb7d75cc543c9 Mon Sep 17 00:00:00 2001 From: Lephe Date: Tue, 14 Jul 2020 15:28:46 +0200 Subject: [PATCH] topti: support Unicode fonts This change adds UTF-8 decoding to dtext() to fully use Unicode fonts provided by fxconv. --- src/render-cg/topti.c | 15 ++++++---- src/render-fx/topti.c | 11 ++++++-- src/render/render.h | 5 ++++ src/render/topti.c | 64 +++++++++++++++++++++++++++++++++++++++---- 4 files changed, 80 insertions(+), 15 deletions(-) diff --git a/src/render-cg/topti.c b/src/render-cg/topti.c index 2d34d2c..ec0a6fe 100644 --- a/src/render-cg/topti.c +++ b/src/render-cg/topti.c @@ -42,9 +42,11 @@ static void topti_glyph(uint16_t *vram, uint32_t const * data, int left, dataw - width, index); } -void topti_render(int x, int y, char const *str, size_t size, font_t const *f, +static void topti_render(int x, int y, char const *str_char, font_t const *f, int fg, int bg) { + uint8_t const *str = (void *)str_char; + /* Raw glyph data */ uint32_t const *data = f->data; @@ -64,15 +66,16 @@ void topti_render(int x, int y, char const *str, size_t size, font_t const *f, int active_space = 0; /* Read each character from the input string */ - while(size--) + while(1) { - int c = *str++; + uint32_t code_point = topti_utf8_next(&str); + if(!code_point) break; - int glyph = topti_glyph_index(f, c); + int glyph = topti_glyph_index(f, code_point); if(glyph < 0) continue; /* Draw the space if background is opaque */ - int prop_space = (c == ' ' && f->prop) ? 5 : 0; + int prop_space = (code_point == ' ' && f->prop) ? 5 : 0; if(active_space || prop_space) { active_space += prop_space; @@ -124,5 +127,5 @@ void dtext_opt(int x, int y, int fg, int bg, int halign, int valign, if(valign == DTEXT_MIDDLE) y -= ((h+1) >> 1); } - topti_render(x, y, str, strlen(str), topti_font, fg, bg); + topti_render(x, y, str, topti_font, fg, bg); } diff --git a/src/render-fx/topti.c b/src/render-fx/topti.c index f3da78e..3f1ad4e 100644 --- a/src/render-fx/topti.c +++ b/src/render-fx/topti.c @@ -82,9 +82,11 @@ static int topti_split(uint32_t const * glyph, int width, int height, int free, } /* topti_render(): Render a string on the VRAM */ -void topti_render(int x, int y, char const *str, font_t const *f, +void topti_render(int x, int y, char const *str_char, font_t const *f, asm_text_t *asm_fg, asm_text_t *asm_bg, uint32_t *v1, uint32_t *v2) { + uint8_t const *str = (void *)str_char; + /* Storage height and number of free bits in operators[] */ int height = f->data_height, free; /* Raw glyph data */ @@ -116,9 +118,12 @@ void topti_render(int x, int y, char const *str, font_t const *f, v2 += (y << 2) + x; /* Pull each character into the operator buffer */ - while(*str) + while(1) { - int glyph = topti_glyph_index(f, *str++); + uint32_t code_point = topti_utf8_next(&str); + if(!code_point) break; + + int glyph = topti_glyph_index(f, code_point); if(glyph < 0) continue; int index = topti_offset(f, glyph); diff --git a/src/render/render.h b/src/render/render.h index 23c963e..9f34060 100644 --- a/src/render/render.h +++ b/src/render/render.h @@ -26,6 +26,11 @@ extern font_t const *topti_font; /* Default font */ extern font_t const *gint_default_font; +/* topti_utf8_next(): Read the next UTF-8 code point of a string + Returns the next code point and advances the string. Returns 0 (NUL) at the + end of the string. */ +uint32_t topti_utf8_next(uint8_t const **str_pointer); + /* topti_glyph_index(): Obtain the glyph index of a Unicode code point Returns the position of code_point in the character table of the given font, or -1 if code_point is not part of that set. diff --git a/src/render/topti.c b/src/render/topti.c index 25fe0d9..406ea03 100644 --- a/src/render/topti.c +++ b/src/render/topti.c @@ -55,9 +55,57 @@ int topti_offset(font_t const *f, uint glyph) return offset; } -/* dsize(): Get the width and height of rendered text */ -void dsize(const char *str, font_t const * f, int *w, int *h) +/* topti_utf8_next(): Read the next UTF-8 code point of a string */ +uint32_t topti_utf8_next(uint8_t const **str_pointer) { + uint8_t const *str = *str_pointer; + uint8_t lead = *str++; + + /* Skip non-leaders which are invalid as starting bytes */ + while((lead >= 0x80 && lead <= 0xbf) || + lead == 0xc0 || lead == 0xc1 || lead == 0xfe || lead == 0xff) + { + lead = *str++; + } + + /* This base case will handle the NUL terminator */ + if(lead <= 0x7f) + { + *str_pointer = str; + return lead; + } + + uint8_t n2 = (*str++ & 0x3f); + if(lead <= 0xdf) + { + *str_pointer = str; + return ((lead & 0x1f) << 6) | n2; + } + + uint8_t n3 = (*str++ & 0x3f); + if(lead <= 0xef) + { + *str_pointer = str; + return ((lead & 0x0f) << 12) | (n2 << 6) | n3; + } + + uint8_t n4 = (*str++ & 0x3f); + if(lead <= 0xf7) + { + *str_pointer = str; + return ((lead & 0x07) << 18) | (n2 << 12) | (n3 << 6) | n4; + } + + /* It the string is too invalid, force a space and try to continue */ + *str_pointer = str; + return 0x20; +} + +/* dsize(): Get the width and height of rendered text */ +void dsize(char const *str_char, font_t const * f, int *w, int *h) +{ + uint8_t const *str = (void *)str_char; + if(!f) f = topti_font; if(h) *h = f->line_height; if(!w) return; @@ -74,17 +122,21 @@ void dsize(const char *str, font_t const * f, int *w, int *h) } /* For proportional fonts, fetch the width of each individual glyphs */ - int width = 0, c; + int width = 0; + uint32_t code_point; - while((c = *str++)) + while(1) { - if(c == ' ') + code_point = topti_utf8_next(&str); + if(!code_point) break; + + if(code_point == ' ') { width += PROP_SPACING + CHAR_SPACING; continue; } - int glyph = topti_glyph_index(f, c); + int glyph = topti_glyph_index(f, code_point); if(glyph >= 0) width += f->glyph_width[glyph] + CHAR_SPACING; } *w = width - CHAR_SPACING;