0001-rtext-add-ExportFontAsCodeEx.patch (14844B)
1 From a466c6cf1f47620aa6ecdea19636c561879e9649 Mon Sep 17 00:00:00 2001 2 From: Randy Palamar <randy@rnpnr.xyz> 3 Date: Mon, 5 Aug 2024 12:07:33 -0600 4 Subject: [PATCH] [rtext] add ExportFontAsCodeEx() 5 6 ExportFontAsCode() loads image data from a font that is already 7 uploaded to the GPU; this means that it requires a full graphics 8 context/window. ExportFontAsCodeEx() loads the relevant font data 9 into an atlas itself without ever creating a texture. This makes 10 it possible to export font data as part of a build step when 11 compiling a project when there is no GPU present (e.g. github CI). 12 13 One minor change is that ExportFontAsCode() no longer converts the 14 name to PascalCase. Its up to the user to pass the name in in that 15 format if they want it. 16 --- 17 src/raylib.h | 1 + 18 src/rtext.c | 126 +++++++++++++++++++++++++++++++++++++-------------- 19 2 files changed, 93 insertions(+), 34 deletions(-) 20 21 diff --git a/src/raylib.h b/src/raylib.h 22 index 1cf34f00..c62d1909 100644 23 --- a/src/raylib.h 24 +++ b/src/raylib.h 25 @@ -1452,6 +1452,7 @@ RLAPI Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, in 26 RLAPI void UnloadFontData(GlyphInfo *glyphs, int glyphCount); // Unload font chars info data (RAM) 27 RLAPI void UnloadFont(Font font); // Unload font from GPU memory (VRAM) 28 RLAPI bool ExportFontAsCode(Font font, const char *fileName); // Export font as code file, returns true on success 29 +RLAPI bool ExportFontAsCodeEx(const char *fontFileName, const char *outputFileName, int fontSize, int *codepoints, int codepointCount); // Export font as code file with extra parameters; returns true on success 30 31 // Text drawing functions 32 RLAPI void DrawFPS(int posX, int posY); // Draw current FPS 33 diff --git a/src/rtext.c b/src/rtext.c 34 index 755b15ef..a3d31028 100644 35 --- a/src/rtext.c 36 +++ b/src/rtext.c 37 @@ -953,20 +953,19 @@ void UnloadFont(Font font) 38 } 39 } 40 41 -// Export font as code file, returns true on success 42 -bool ExportFontAsCode(Font font, const char *fileName) 43 + 44 +static bool 45 +WriteOutputFontAsCode(Font font, Image atlas, const char *fileName) 46 { 47 - bool success = false; 48 - 49 -#ifndef TEXT_BYTES_PER_LINE 50 - #define TEXT_BYTES_PER_LINE 20 51 -#endif 52 - 53 - #define MAX_FONT_DATA_SIZE 1024*1024 // 1 MB 54 - 55 // Get file name from path 56 - char fileNamePascal[256] = { 0 }; 57 - strncpy(fileNamePascal, TextToPascal(GetFileNameWithoutExt(fileName)), 256 - 1); 58 + char baseFileName[256] = { 0 }; 59 + strncpy(baseFileName, GetFileNameWithoutExt(fileName), 256 - 1); 60 + 61 + #ifndef TEXT_BYTES_PER_LINE 62 + #define TEXT_BYTES_PER_LINE 20 63 + #endif 64 + 65 + #define MAX_FONT_DATA_SIZE 1024*1024 66 67 // NOTE: Text data buffer size is estimated considering image data size in bytes 68 // and requiring 6 char bytes for every byte: "0x00, " 69 @@ -994,9 +993,8 @@ bool ExportFontAsCode(Font font, const char *fileName) 70 71 // Support font export and initialization 72 // NOTE: This mechanism is highly coupled to raylib 73 - Image image = LoadImageFromTexture(font.texture); 74 - if (image.format != PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) TRACELOG(LOG_WARNING, "Font export as code: Font image format is not GRAY+ALPHA!"); 75 - int imageDataSize = GetPixelDataSize(image.width, image.height, image.format); 76 + if (atlas.format != PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) TRACELOG(LOG_WARNING, "Font export as code: Font image format is not GRAY+ALPHA!"); 77 + int imageDataSize = GetPixelDataSize(atlas.width, atlas.height, atlas.format); 78 79 // Image data is usually GRAYSCALE + ALPHA and can be reduced to GRAYSCALE 80 //ImageFormat(&image, PIXELFORMAT_UNCOMPRESSED_GRAYSCALE); 81 @@ -1009,13 +1007,13 @@ bool ExportFontAsCode(Font font, const char *fileName) 82 83 // Compress font image data 84 int compDataSize = 0; 85 - unsigned char *compData = CompressData((const unsigned char *)image.data, imageDataSize, &compDataSize); 86 + unsigned char *compData = CompressData((const unsigned char *)atlas.data, imageDataSize, &compDataSize); 87 88 // Save font image data (compressed) 89 - byteCount += sprintf(txtData + byteCount, "#define COMPRESSED_DATA_SIZE_FONT_%s %i\n\n", TextToUpper(fileNamePascal), compDataSize); 90 + byteCount += sprintf(txtData + byteCount, "#define COMPRESSED_DATA_SIZE_FONT_%s %i\n\n", TextToUpper(baseFileName), compDataSize); 91 byteCount += sprintf(txtData + byteCount, "// Font image pixels data compressed (DEFLATE)\n"); 92 byteCount += sprintf(txtData + byteCount, "// NOTE: Original pixel data simplified to GRAYSCALE\n"); 93 - byteCount += sprintf(txtData + byteCount, "static unsigned char fontData_%s[COMPRESSED_DATA_SIZE_FONT_%s] = { ", fileNamePascal, TextToUpper(fileNamePascal)); 94 + byteCount += sprintf(txtData + byteCount, "static unsigned char fontData_%s[COMPRESSED_DATA_SIZE_FONT_%s] = { ", baseFileName, TextToUpper(baseFileName)); 95 for (int i = 0; i < compDataSize - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "0x%02x,\n " : "0x%02x, "), compData[i]); 96 byteCount += sprintf(txtData + byteCount, "0x%02x };\n\n", compData[compDataSize - 1]); 97 RL_FREE(compData); 98 @@ -1023,14 +1021,14 @@ bool ExportFontAsCode(Font font, const char *fileName) 99 // Save font image data (uncompressed) 100 byteCount += sprintf(txtData + byteCount, "// Font image pixels data\n"); 101 byteCount += sprintf(txtData + byteCount, "// NOTE: 2 bytes per pixel, GRAY + ALPHA channels\n"); 102 - byteCount += sprintf(txtData + byteCount, "static unsigned char fontImageData_%s[%i] = { ", fileNamePascal, imageDataSize); 103 + byteCount += sprintf(txtData + byteCount, "static unsigned char fontImageData_%s[%i] = { ", baseFileName, imageDataSize); 104 for (int i = 0; i < imageDataSize - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "0x%02x,\n " : "0x%02x, "), ((unsigned char *)imFont.data)[i]); 105 byteCount += sprintf(txtData + byteCount, "0x%02x };\n\n", ((unsigned char *)imFont.data)[imageDataSize - 1]); 106 #endif 107 108 // Save font recs data 109 byteCount += sprintf(txtData + byteCount, "// Font characters rectangles data\n"); 110 - byteCount += sprintf(txtData + byteCount, "static Rectangle fontRecs_%s[%i] = {\n", fileNamePascal, font.glyphCount); 111 + byteCount += sprintf(txtData + byteCount, "static Rectangle fontRecs_%s[%i] = {\n", baseFileName, font.glyphCount); 112 for (int i = 0; i < font.glyphCount; i++) 113 { 114 byteCount += sprintf(txtData + byteCount, " { %1.0f, %1.0f, %1.0f , %1.0f },\n", font.recs[i].x, font.recs[i].y, font.recs[i].width, font.recs[i].height); 115 @@ -1042,7 +1040,7 @@ bool ExportFontAsCode(Font font, const char *fileName) 116 // it could be generated from image and recs 117 byteCount += sprintf(txtData + byteCount, "// Font glyphs info data\n"); 118 byteCount += sprintf(txtData + byteCount, "// NOTE: No glyphs.image data provided\n"); 119 - byteCount += sprintf(txtData + byteCount, "static GlyphInfo fontGlyphs_%s[%i] = {\n", fileNamePascal, font.glyphCount); 120 + byteCount += sprintf(txtData + byteCount, "static GlyphInfo fontGlyphs_%s[%i] = {\n", baseFileName, font.glyphCount); 121 for (int i = 0; i < font.glyphCount; i++) 122 { 123 byteCount += sprintf(txtData + byteCount, " { %i, %i, %i, %i, { 0 }},\n", font.glyphs[i].value, font.glyphs[i].offsetX, font.glyphs[i].offsetY, font.glyphs[i].advanceX); 124 @@ -1050,8 +1048,8 @@ bool ExportFontAsCode(Font font, const char *fileName) 125 byteCount += sprintf(txtData + byteCount, "};\n\n"); 126 127 // Custom font loading function 128 - byteCount += sprintf(txtData + byteCount, "// Font loading function: %s\n", fileNamePascal); 129 - byteCount += sprintf(txtData + byteCount, "static Font LoadFont_%s(void)\n{\n", fileNamePascal); 130 + byteCount += sprintf(txtData + byteCount, "// Font loading function: %s\n", baseFileName); 131 + byteCount += sprintf(txtData + byteCount, "static Font LoadFont_%s(void)\n{\n", baseFileName); 132 byteCount += sprintf(txtData + byteCount, " Font font = { 0 };\n\n"); 133 byteCount += sprintf(txtData + byteCount, " font.baseSize = %i;\n", font.baseSize); 134 byteCount += sprintf(txtData + byteCount, " font.glyphCount = %i;\n", font.glyphCount); 135 @@ -1059,11 +1057,11 @@ bool ExportFontAsCode(Font font, const char *fileName) 136 byteCount += sprintf(txtData + byteCount, " // Custom font loading\n"); 137 #if defined(SUPPORT_COMPRESSED_FONT_ATLAS) 138 byteCount += sprintf(txtData + byteCount, " // NOTE: Compressed font image data (DEFLATE), it requires DecompressData() function\n"); 139 - byteCount += sprintf(txtData + byteCount, " int fontDataSize_%s = 0;\n", fileNamePascal); 140 - byteCount += sprintf(txtData + byteCount, " unsigned char *data = DecompressData(fontData_%s, COMPRESSED_DATA_SIZE_FONT_%s, &fontDataSize_%s);\n", fileNamePascal, TextToUpper(fileNamePascal), fileNamePascal); 141 - byteCount += sprintf(txtData + byteCount, " Image imFont = { data, %i, %i, 1, %i };\n\n", image.width, image.height, image.format); 142 + byteCount += sprintf(txtData + byteCount, " int fontDataSize_%s = 0;\n", baseFileName); 143 + byteCount += sprintf(txtData + byteCount, " unsigned char *data = DecompressData(fontData_%s, COMPRESSED_DATA_SIZE_FONT_%s, &fontDataSize_%s);\n", baseFileName, TextToUpper(baseFileName), baseFileName); 144 + byteCount += sprintf(txtData + byteCount, " Image imFont = { data, %i, %i, 1, %i };\n\n", atlas.width, atlas.height, atlas.format); 145 #else 146 - byteCount += sprintf(txtData + byteCount, " Image imFont = { fontImageData_%s, %i, %i, 1, %i };\n\n", styleName, image.width, image.height, image.format); 147 + byteCount += sprintf(txtData + byteCount, " Image imFont = { fontImageData_%s, %i, %i, 1, %i };\n\n", styleName, atlas.width, atlas.height, atlas.format); 148 #endif 149 byteCount += sprintf(txtData + byteCount, " // Load texture from image\n"); 150 byteCount += sprintf(txtData + byteCount, " font.texture = LoadTextureFromImage(imFont);\n"); 151 @@ -1079,25 +1077,23 @@ bool ExportFontAsCode(Font font, const char *fileName) 152 byteCount += sprintf(txtData + byteCount, " // Copy glyph recs data from global fontRecs\n"); 153 byteCount += sprintf(txtData + byteCount, " // NOTE: Required to avoid issues if trying to free font\n"); 154 byteCount += sprintf(txtData + byteCount, " font.recs = (Rectangle *)malloc(font.glyphCount*sizeof(Rectangle));\n"); 155 - byteCount += sprintf(txtData + byteCount, " memcpy(font.recs, fontRecs_%s, font.glyphCount*sizeof(Rectangle));\n\n", fileNamePascal); 156 + byteCount += sprintf(txtData + byteCount, " memcpy(font.recs, fontRecs_%s, font.glyphCount*sizeof(Rectangle));\n\n", baseFileName); 157 158 byteCount += sprintf(txtData + byteCount, " // Copy font glyph info data from global fontChars\n"); 159 byteCount += sprintf(txtData + byteCount, " // NOTE: Required to avoid issues if trying to free font\n"); 160 byteCount += sprintf(txtData + byteCount, " font.glyphs = (GlyphInfo *)malloc(font.glyphCount*sizeof(GlyphInfo));\n"); 161 - byteCount += sprintf(txtData + byteCount, " memcpy(font.glyphs, fontGlyphs_%s, font.glyphCount*sizeof(GlyphInfo));\n\n", fileNamePascal); 162 + byteCount += sprintf(txtData + byteCount, " memcpy(font.glyphs, fontGlyphs_%s, font.glyphCount*sizeof(GlyphInfo));\n\n", baseFileName); 163 #else 164 byteCount += sprintf(txtData + byteCount, " // Assign glyph recs and info data directly\n"); 165 byteCount += sprintf(txtData + byteCount, " // WARNING: This font data must not be unloaded\n"); 166 - byteCount += sprintf(txtData + byteCount, " font.recs = fontRecs_%s;\n", fileNamePascal); 167 - byteCount += sprintf(txtData + byteCount, " font.glyphs = fontGlyphs_%s;\n\n", fileNamePascal); 168 + byteCount += sprintf(txtData + byteCount, " font.recs = fontRecs_%s;\n", baseFileName); 169 + byteCount += sprintf(txtData + byteCount, " font.glyphs = fontGlyphs_%s;\n\n", baseFileName); 170 #endif 171 byteCount += sprintf(txtData + byteCount, " return font;\n"); 172 byteCount += sprintf(txtData + byteCount, "}\n"); 173 174 - UnloadImage(image); 175 - 176 // NOTE: Text data size exported is determined by '\0' (NULL) character 177 - success = SaveFileText(fileName, txtData); 178 + bool success = SaveFileText(fileName, txtData); 179 180 RL_FREE(txtData); 181 182 @@ -1107,6 +1103,68 @@ bool ExportFontAsCode(Font font, const char *fileName) 183 return success; 184 } 185 186 +// Export font as code file, returns true on success 187 +bool ExportFontAsCode(Font font, const char *fileName) 188 +{ 189 + Image atlas = LoadImageFromTexture(font.texture); 190 + bool success = WriteOutputFontAsCode(font, atlas, fileName); 191 + UnloadImage(atlas); 192 + return success; 193 +} 194 + 195 +// Export font as code file with extra parameters; returns true on success 196 +bool ExportFontAsCodeEx(const char *fontFileName, const char *outputFileName, int fontSize, int *codepoints, int codepointCount) 197 +{ 198 + int dataSize = 0; 199 + unsigned char *fileData = LoadFileData(fontFileName, &dataSize); 200 + 201 + if (fileData == NULL) { 202 + TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export font as code", fontFileName); 203 + return false; 204 + } 205 + 206 + Font font = {0}; 207 + font.baseSize = fontSize; 208 + font.glyphCount = (codepointCount > 0)? codepointCount : 95; 209 + 210 + char fileExtLower[16] = { 0 }; 211 + strncpy(fileExtLower, TextToLower(GetFileExtension(fontFileName)), 16 - 1); 212 + 213 +#if defined(SUPPORT_FILEFORMAT_TTF) 214 + if (TextIsEqual(fileExtLower, ".ttf") || 215 + TextIsEqual(fileExtLower, ".otf")) 216 + { 217 + font.glyphs = LoadFontData(fileData, dataSize, font.baseSize, codepoints, font.glyphCount, FONT_DEFAULT); 218 + } 219 +#endif 220 +#if defined(SUPPORT_FILEFORMAT_BDF) 221 + if (TextIsEqual(fileExtLower, ".bdf")) 222 + { 223 + font.glyphs = LoadFontDataBDF(fileData, dataSize, codepoints, font.glyphCount, &font.baseSize); 224 + } 225 +#endif 226 + 227 + Image atlas; 228 +#if defined(SUPPORT_FILEFORMAT_TTF) || defined(SUPPORT_FILEFORMAT_BDF) 229 + if (font.glyphs != NULL) { 230 + font.glyphPadding = FONT_TTF_DEFAULT_CHARS_PADDING; 231 + atlas = GenImageFontAtlas(font.glyphs, &font.recs, font.glyphCount, font.baseSize, font.glyphPadding, 0); 232 + } else { 233 + TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export font as code", fontFileName); 234 + } 235 +#endif 236 + 237 + bool success = false; 238 + if (font.glyphs != NULL) { 239 + success = WriteOutputFontAsCode(font, atlas, outputFileName); 240 + UnloadFileData(fileData); 241 + UnloadFontData(font.glyphs, font.glyphCount); 242 + UnloadImage(atlas); 243 + } 244 + 245 + return success; 246 +} 247 + 248 // Draw current FPS 249 // NOTE: Uses default font 250 void DrawFPS(int posX, int posY) 251 -- 252 2.44.0 253