From cc4eb8216261bdc1f6102c5b7ff2b13c48d9dca3 Mon Sep 17 00:00:00 2001 From: Clownacy Date: Sun, 21 Apr 2019 23:54:05 +0100 Subject: [PATCH] Added caching to the font system I never liked how much of a bottleneck the font renderer was. Maybe now the credits won't stutter like mad on the Raspberry Pi. --- src/Font.cpp | 171 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 112 insertions(+), 59 deletions(-) diff --git a/src/Font.cpp b/src/Font.cpp index 0d0293db..05576900 100644 --- a/src/Font.cpp +++ b/src/Font.cpp @@ -21,6 +21,18 @@ #define MIN(a,b) ((a) < (b) ? (a) : (b)) #define MAX(a,b) ((a) > (b) ? (a) : (b)) +typedef struct CachedGlyph +{ + unsigned long unicode_value; + FT_Bitmap bitmap; + unsigned char pixel_mode; + int x; + int y; + int x_advance; + + struct CachedGlyph *next; +} CachedGlyph; + typedef struct FontObject { FT_Library library; @@ -29,6 +41,7 @@ typedef struct FontObject #ifndef DISABLE_FONT_ANTIALIASING bool lcd_mode; #endif + CachedGlyph *glyph_list_head; } FontObject; #ifdef JAPANESE @@ -1691,6 +1704,55 @@ static unsigned long UTF8ToUnicode(const unsigned char *string, unsigned int *by } #endif +static CachedGlyph* GetGlyphCached(FontObject *font_object, unsigned long unicode_value) +{ + for (CachedGlyph *glyph = font_object->glyph_list_head; glyph != NULL; glyph = glyph->next) + if (glyph->unicode_value == unicode_value) + return glyph; + + CachedGlyph *glyph = (CachedGlyph*)malloc(sizeof(CachedGlyph)); + + if (glyph) + { + glyph->next = font_object->glyph_list_head; + font_object->glyph_list_head = glyph; + + unsigned int glyph_index = FT_Get_Char_Index(font_object->face, unicode_value); + +#ifndef DISABLE_FONT_ANTIALIASING + FT_Load_Glyph(font_object->face, glyph_index, FT_LOAD_RENDER | (font_object->lcd_mode ? FT_LOAD_TARGET_LCD : 0)); +#else + FT_Load_Glyph(font_object->face, glyph_index, FT_LOAD_RENDER | FT_LOAD_MONOCHROME); +#endif + + glyph->unicode_value = unicode_value; + FT_Bitmap_New(&glyph->bitmap); + FT_Bitmap_Convert(font_object->library, &font_object->face->glyph->bitmap, &glyph->bitmap, 1); + glyph->pixel_mode = font_object->face->glyph->bitmap.pixel_mode; + glyph->x = font_object->face->glyph->bitmap_left; + glyph->y = (FT_MulFix(font_object->face->ascender, font_object->face->size->metrics.y_scale) - font_object->face->glyph->metrics.horiBearingY + (64 / 2)) / 64; + glyph->x_advance = font_object->face->glyph->advance.x / 64; + } + + return glyph; +} + +static void UnloadCachedGlyphs(FontObject *font_object) +{ + CachedGlyph *glyph = font_object->glyph_list_head; + while (glyph != NULL) + { + CachedGlyph *next_glyph = glyph->next; + + FT_Bitmap_Done(font_object->library, &glyph->bitmap); + free(glyph); + + glyph = next_glyph; + } + + font_object->glyph_list_head = NULL; +} + FontObject* LoadFontFromData(const unsigned char *data, size_t data_size, unsigned int cell_width, unsigned int cell_height) { FontObject *font_object = (FontObject*)malloc(sizeof(FontObject)); @@ -1769,8 +1831,6 @@ void DrawText(FontObject *font_object, unsigned char *bitmap_buffer, size_t bitm { const unsigned char colours[3] = {(unsigned char)colour, (unsigned char)(colour >> 8), (unsigned char)(colour >> 16)}; - FT_Face face = font_object->face; - unsigned int pen_x = 0; const unsigned char *string_pointer = (unsigned char*)string; @@ -1780,93 +1840,84 @@ void DrawText(FontObject *font_object, unsigned char *bitmap_buffer, size_t bitm { unsigned int bytes_read; #ifdef JAPANESE - const unsigned short val = ShiftJISToUnicode(string_pointer, &bytes_read); + const unsigned short unicode_value = ShiftJISToUnicode(string_pointer, &bytes_read); #else - const unsigned long val = UTF8ToUnicode(string_pointer, &bytes_read); + const unsigned long unicode_value = UTF8ToUnicode(string_pointer, &bytes_read); #endif string_pointer += bytes_read; - unsigned int glyph_index = FT_Get_Char_Index(face, val); + CachedGlyph *glyph = GetGlyphCached(font_object, unicode_value); -#ifndef DISABLE_FONT_ANTIALIASING - FT_Load_Glyph(face, glyph_index, FT_LOAD_RENDER | (font_object->lcd_mode ? FT_LOAD_TARGET_LCD : 0)); -#else - FT_Load_Glyph(face, glyph_index, FT_LOAD_RENDER | FT_LOAD_MONOCHROME); -#endif - - FT_Bitmap converted; - FT_Bitmap_New(&converted); - FT_Bitmap_Convert(font_object->library, &face->glyph->bitmap, &converted, 1); - - const int letter_x = x + pen_x + face->glyph->bitmap_left; - const int letter_y = y + ((FT_MulFix(face->ascender, face->size->metrics.y_scale) - face->glyph->metrics.horiBearingY + (64 / 2)) / 64); - - switch (face->glyph->bitmap.pixel_mode) + if (glyph) { - case FT_PIXEL_MODE_LCD: - for (int iy = MAX(-letter_y, 0); letter_y + iy < MIN(letter_y + (int)converted.rows, bitmap_height); ++iy) - { - for (int ix = MAX(-letter_x, 0); letter_x + ix < MIN(letter_x + (int)converted.width / 3, bitmap_width); ++ix) + const int letter_x = x + pen_x + glyph->x; + const int letter_y = y + glyph->y; + + switch (glyph->pixel_mode) + { + case FT_PIXEL_MODE_LCD: + for (int iy = MAX(-letter_y, 0); letter_y + iy < MIN(letter_y + (int)glyph->bitmap.rows, bitmap_height); ++iy) { - const unsigned char *font_pixel = converted.buffer + iy * converted.pitch + ix * 3; - - if (font_pixel[0] || font_pixel[1] || font_pixel[2]) + for (int ix = MAX(-letter_x, 0); letter_x + ix < MIN(letter_x + (int)glyph->bitmap.width / 3, bitmap_width); ++ix) { - unsigned char *bitmap_pixel = bitmap_buffer + (letter_y + iy) * bitmap_pitch + (letter_x + ix) * 3; + const unsigned char *font_pixel = glyph->bitmap.buffer + iy * glyph->bitmap.pitch + ix * 3; - for (unsigned int j = 0; j < 3; ++j) + if (font_pixel[0] || font_pixel[1] || font_pixel[2]) { - const double alpha = pow((font_pixel[j] / 255.0), 1.0 / 1.8); // Gamma correction - bitmap_pixel[j] = (unsigned char)((colours[j] * alpha) + (bitmap_pixel[j] * (1.0 - alpha))); // Alpha blending + unsigned char *bitmap_pixel = bitmap_buffer + (letter_y + iy) * bitmap_pitch + (letter_x + ix) * 3; + + for (unsigned int j = 0; j < 3; ++j) + { + const double alpha = pow((font_pixel[j] / 255.0), 1.0 / 1.8); // Gamma correction + bitmap_pixel[j] = (unsigned char)((colours[j] * alpha) + (bitmap_pixel[j] * (1.0 - alpha))); // Alpha blending + } } } } - } - break; + break; - case FT_PIXEL_MODE_GRAY: - for (int iy = MAX(-letter_y, 0); letter_y + iy < MIN(letter_y + (int)converted.rows, bitmap_height); ++iy) - { - for (int ix = MAX(-letter_x, 0); letter_x + ix < MIN(letter_x + (int)converted.width, bitmap_width); ++ix) + case FT_PIXEL_MODE_GRAY: + for (int iy = MAX(-letter_y, 0); letter_y + iy < MIN(letter_y + (int)glyph->bitmap.rows, bitmap_height); ++iy) { - const unsigned char font_pixel = converted.buffer[iy * converted.pitch + ix]; - - if (font_pixel) + for (int ix = MAX(-letter_x, 0); letter_x + ix < MIN(letter_x + (int)glyph->bitmap.width, bitmap_width); ++ix) { - const double alpha = pow((double)font_pixel / (converted.num_grays - 1), 1.0 / 1.8); // Gamma-corrected + const unsigned char font_pixel = glyph->bitmap.buffer[iy * glyph->bitmap.pitch + ix]; - unsigned char *bitmap_pixel = bitmap_buffer + (letter_y + iy) * bitmap_pitch + (letter_x + ix) * 3; + if (font_pixel) + { + const double alpha = pow((double)font_pixel / (glyph->bitmap.num_grays - 1), 1.0 / 1.8); // Gamma-corrected - for (unsigned int j = 0; j < 3; ++j) - bitmap_pixel[j] = (unsigned char)((colours[j] * alpha) + (bitmap_pixel[j] * (1.0 - alpha))); // Alpha blending + unsigned char *bitmap_pixel = bitmap_buffer + (letter_y + iy) * bitmap_pitch + (letter_x + ix) * 3; + + for (unsigned int j = 0; j < 3; ++j) + bitmap_pixel[j] = (unsigned char)((colours[j] * alpha) + (bitmap_pixel[j] * (1.0 - alpha))); // Alpha blending + } } } - } - break; + break; - case FT_PIXEL_MODE_MONO: - for (int iy = MAX(-letter_y, 0); letter_y + iy < MIN(letter_y + (int)converted.rows, bitmap_height); ++iy) - { - for (int ix = MAX(-letter_x, 0); letter_x + ix < MIN(letter_x + (int)converted.width, bitmap_width); ++ix) + case FT_PIXEL_MODE_MONO: + for (int iy = MAX(-letter_y, 0); letter_y + iy < MIN(letter_y + (int)glyph->bitmap.rows, bitmap_height); ++iy) { - if (converted.buffer[iy * converted.pitch + ix]) + for (int ix = MAX(-letter_x, 0); letter_x + ix < MIN(letter_x + (int)glyph->bitmap.width, bitmap_width); ++ix) { - unsigned char *bitmap_pixel = bitmap_buffer + (letter_y + iy) * bitmap_pitch + (letter_x + ix) * 3; + if (glyph->bitmap.buffer[iy * glyph->bitmap.pitch + ix]) + { + unsigned char *bitmap_pixel = bitmap_buffer + (letter_y + iy) * bitmap_pitch + (letter_x + ix) * 3; - for (unsigned int j = 0; j < 3; ++j) - bitmap_pixel[j] = colours[j]; + for (unsigned int j = 0; j < 3; ++j) + bitmap_pixel[j] = colours[j]; + } } } - } - break; + break; + } + + pen_x += glyph->x_advance; } - - FT_Bitmap_Done(font_object->library, &converted); - - pen_x += face->glyph->advance.x / 64; } } } @@ -1875,6 +1926,8 @@ void UnloadFont(FontObject *font_object) { if (font_object != NULL) { + UnloadCachedGlyphs(font_object); + FT_Done_Face(font_object->face); free(font_object->data); FT_Done_FreeType(font_object->library);