From 0d945a81d756d667cf21b49df21c7eb8b25b7d53 Mon Sep 17 00:00:00 2001 From: aliaspider Date: Sun, 19 Feb 2017 14:40:05 +0100 Subject: [PATCH] stb_unicode: use the same unicode handling code as the freetype renderer, which supports codepoints > 0xFFFF. --- gfx/drivers_font_renderer/stb_unicode.c | 166 ++++++++++++++---------- 1 file changed, 95 insertions(+), 71 deletions(-) diff --git a/gfx/drivers_font_renderer/stb_unicode.c b/gfx/drivers_font_renderer/stb_unicode.c index 40a7b5dd3b..a90e3dc3b4 100644 --- a/gfx/drivers_font_renderer/stb_unicode.c +++ b/gfx/drivers_font_renderer/stb_unicode.c @@ -34,6 +34,18 @@ #undef static #endif +#define STB_UNICODE_ATLAS_ROWS 16 +#define STB_UNICODE_ATLAS_COLS 16 +#define STB_UNICODE_ATLAS_SIZE (STB_UNICODE_ATLAS_ROWS * STB_UNICODE_ATLAS_COLS) + +typedef struct stb_unicode_atlas_slot +{ + struct font_glyph glyph; + unsigned charcode; + unsigned last_used; + struct stb_unicode_atlas_slot* next; +}stb_unicode_atlas_slot_t; + typedef struct { uint8_t *font_data; @@ -43,13 +55,11 @@ typedef struct int max_glyph_height; int line_height; float scale_factor; - struct font_atlas atlas; - struct font_glyph glyphs[256]; - uint8_t uc_to_id[0x10000]; - uint16_t id_to_uc[256]; - unsigned last_used[256]; - unsigned usage_counter; + struct font_atlas atlas; + stb_unicode_atlas_slot_t atlas_slots[STB_UNICODE_ATLAS_SIZE]; + stb_unicode_atlas_slot_t* uc_map[0x100]; + unsigned usage_counter; } stb_unicode_font_renderer_t; static struct font_atlas *font_renderer_stb_unicode_get_atlas(void *data) @@ -67,50 +77,67 @@ static void font_renderer_stb_unicode_free(void *data) free(self); } -static unsigned font_renderer_stb_unicode_get_slot(stb_unicode_font_renderer_t *handle) +static stb_unicode_atlas_slot_t* font_renderer_stb_unicode_get_slot(stb_unicode_font_renderer_t *handle) { - int i; - unsigned oldest = 1; + int i, map_id; + unsigned oldest = 0; - for (i = 2; i < 256; i++) - if(handle->last_used[i] < handle->last_used[oldest]) + for (i = 1; i < STB_UNICODE_ATLAS_SIZE; i++) + if((handle->usage_counter - handle->atlas_slots[i].last_used) > + (handle->usage_counter - handle->atlas_slots[oldest].last_used)) oldest = i; - handle->uc_to_id[handle->id_to_uc[oldest]] = 0; - handle->id_to_uc[oldest] = 0; - return oldest; + /* remove from map */ + map_id = handle->atlas_slots[oldest].charcode & 0xFF; + if(handle->uc_map[map_id] == &handle->atlas_slots[oldest]) + handle->uc_map[map_id] = handle->atlas_slots[oldest].next; + else if (handle->uc_map[map_id]) + { + stb_unicode_atlas_slot_t* ptr = handle->uc_map[map_id]; + while(ptr->next && ptr->next != &handle->atlas_slots[oldest]) + ptr = ptr->next; + ptr->next = handle->atlas_slots[oldest].next; + } + + return &handle->atlas_slots[oldest]; } -static uint32_t font_renderer_stb_unicode_update_atlas( - stb_unicode_font_renderer_t *self, uint32_t charcode) +static const struct font_glyph *font_renderer_stb_unicode_get_glyph( + void *data, uint32_t charcode) { + int glyph_index, x0, y1; int advance_width, left_side_bearing; - int id, glyph_index, offset_x, offset_y; - struct font_glyph *glyph = NULL; - uint8_t *dst = NULL; - int x0 = 0; - int y1 = 0; + unsigned map_id; + uint8_t *dst; + stb_unicode_atlas_slot_t* atlas_slot; + stb_unicode_font_renderer_t *self = (stb_unicode_font_renderer_t*)data; - if(charcode > 0xFFFF) - return 0; + if(!self) + return NULL; - if(self->uc_to_id[charcode]) - return self->uc_to_id[charcode]; + map_id = charcode & 0xFF; + atlas_slot = self->uc_map[map_id]; - id = font_renderer_stb_unicode_get_slot(self); - self->id_to_uc[id] = charcode; - self->uc_to_id[charcode] = id; - self->atlas.dirty = true; + while(atlas_slot) + { + if(atlas_slot->charcode == charcode) + { + atlas_slot->last_used = self->usage_counter++; + return &atlas_slot->glyph; + } + atlas_slot = atlas_slot->next; + } - glyph = &self->glyphs[id]; + atlas_slot = font_renderer_stb_unicode_get_slot(self); + atlas_slot->charcode = charcode; + atlas_slot->next = self->uc_map[map_id]; + self->uc_map[map_id] = atlas_slot; glyph_index = stbtt_FindGlyphIndex(&self->info, charcode); - offset_x = (id % 16) * self->max_glyph_width; - offset_y = (id / 16) * self->max_glyph_height; + dst = (uint8_t*)self->atlas.buffer + atlas_slot->glyph.atlas_offset_x + + atlas_slot->glyph.atlas_offset_y * self->atlas.width; - dst = self->atlas.buffer + offset_x + offset_y - * self->atlas.width; stbtt_MakeGlyphBitmap(&self->info, dst, self->max_glyph_width, self->max_glyph_height, self->atlas.width, self->scale_factor, self->scale_factor, glyph_index); @@ -118,63 +145,60 @@ static uint32_t font_renderer_stb_unicode_update_atlas( stbtt_GetGlyphHMetrics(&self->info, glyph_index, &advance_width, &left_side_bearing); stbtt_GetGlyphBox(&self->info, glyph_index, &x0, NULL, NULL, &y1); - glyph->advance_x = advance_width * self->scale_factor; - glyph->atlas_offset_x = offset_x; - glyph->atlas_offset_y = offset_y; - glyph->draw_offset_x = x0 * self->scale_factor; - glyph->draw_offset_y = - y1 * self->scale_factor; - glyph->width = self->max_glyph_width; - glyph->height = self->max_glyph_height; + atlas_slot->glyph.width = self->max_glyph_width; + atlas_slot->glyph.height = self->max_glyph_height; + atlas_slot->glyph.advance_x = advance_width * self->scale_factor; + /* atlas_slot->glyph.advance_y = 0 ; */ + atlas_slot->glyph.draw_offset_x = x0 * self->scale_factor; + atlas_slot->glyph.draw_offset_y = - y1 * self->scale_factor; + + + self->atlas.dirty = true; + atlas_slot->last_used = self->usage_counter++; + return &atlas_slot->glyph; - return id; } static bool font_renderer_stb_unicode_create_atlas( stb_unicode_font_renderer_t *self, float font_size) { - int i; + unsigned i, x, y; + stb_unicode_atlas_slot_t* slot = NULL; self->max_glyph_width = font_size < 0 ? -font_size : font_size; self->max_glyph_height = font_size < 0 ? -font_size : font_size; - self->atlas.width = self->max_glyph_width * 16; - self->atlas.height = self->max_glyph_height * 16; - self->atlas.buffer = (uint8_t*)calloc(self->atlas.height, self->atlas.width); + + self->atlas.width = self->max_glyph_width * STB_UNICODE_ATLAS_COLS; + self->atlas.height = self->max_glyph_height * STB_UNICODE_ATLAS_ROWS; + + self->atlas.buffer = (uint8_t*) + calloc(self->atlas.width * self->atlas.height, 1); if (!self->atlas.buffer) return false; - self->usage_counter = 1; + slot = self->atlas_slots; - for (i = 0; i < 256; ++i) + for (y = 0; y < STB_UNICODE_ATLAS_ROWS; y++) { - int id = font_renderer_stb_unicode_update_atlas(self, i); - if(id) - self->last_used[id] = self->usage_counter++; - + for (x = 0; x < STB_UNICODE_ATLAS_COLS; x++) + { + slot->glyph.atlas_offset_x = x * self->max_glyph_width; + slot->glyph.atlas_offset_y = y * self->max_glyph_height; + slot++; + } } + for (i = 0; i < 256; i++) + font_renderer_stb_unicode_get_glyph(self, i); + + for (i = 0; i < 256; i++) + if(isalnum(i)) + font_renderer_stb_unicode_get_glyph(self, i); + return true; } -static const struct font_glyph *font_renderer_stb_unicode_get_glyph( - void *data, uint32_t code) -{ - unsigned id; - stb_unicode_font_renderer_t *self = (stb_unicode_font_renderer_t*)data; - - if(!self || code > 0xFFFF) - return NULL; - - id = self->uc_to_id[code]; - - if(!id) - id = font_renderer_stb_unicode_update_atlas(self, code); - - self->last_used[id] = self->usage_counter++; - - return &self->glyphs[id]; -} - static void *font_renderer_stb_unicode_init(const char *font_path, float font_size) { int ascent, descent, line_gap;