shithub: cstory

Download patch

ref: cc4eb8216261bdc1f6102c5b7ff2b13c48d9dca3
parent: f25df2ce07dc39865958284782b99fd6178eae63
author: Clownacy <Clownacy@users.noreply.github.com>
date: Sun Apr 21 19:54:05 EDT 2019

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.

--- 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 @@
 #ifndef DISABLE_FONT_ANTIALIASING
 	bool lcd_mode;
 #endif
+	CachedGlyph *glyph_list_head;
 } FontObject;
 
 #ifdef JAPANESE
@@ -1691,6 +1704,55 @@
 }
 #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 @@
 	{
 		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 @@
 		{
 			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 unsigned char *font_pixel = converted.buffer + iy * converted.pitch + ix * 3;
+				const int letter_x = x + pen_x + glyph->x;
+				const int letter_y = y + glyph->y;
 
-							if (font_pixel[0] || font_pixel[1] || font_pixel[2])
+				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)
+						{
+							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;
+				}
 
-			FT_Bitmap_Done(font_object->library, &converted);
-
-			pen_x += face->glyph->advance.x / 64;
+				pen_x += glyph->x_advance;
+			}
 		}
 	}
 }
@@ -1875,6 +1926,8 @@
 {
 	if (font_object != NULL)
 	{
+		UnloadCachedGlyphs(font_object);
+
 		FT_Done_Face(font_object->face);
 		free(font_object->data);
 		FT_Done_FreeType(font_object->library);