rtext.c (102673B)
1 /********************************************************************************************** 2 * 3 * rtext - Basic functions to load fonts and draw text 4 * 5 * CONFIGURATION: 6 * #define SUPPORT_MODULE_RTEXT 7 * rtext module is included in the build 8 * 9 * #define SUPPORT_DEFAULT_FONT 10 * Load default raylib font on initialization to be used by DrawText() and MeasureText(). 11 * If no default font loaded, DrawTextEx() and MeasureTextEx() are required. 12 * 13 * #define SUPPORT_FILEFORMAT_FNT 14 * #define SUPPORT_FILEFORMAT_TTF 15 * #define SUPPORT_FILEFORMAT_BDF 16 * Selected desired fileformats to be supported for loading. Some of those formats are 17 * supported by default, to remove support, just comment unrequired #define in this module 18 * 19 * #define SUPPORT_FONT_ATLAS_WHITE_REC 20 * On font atlas image generation [GenImageFontAtlas()], add a 3x3 pixels white rectangle 21 * at the bottom-right corner of the atlas. It can be useful to for shapes drawing, to allow 22 * drawing text and shapes with a single draw call [SetShapesTexture()]. 23 * 24 * #define TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH 25 * TextSplit() function static buffer max size 26 * 27 * #define MAX_TEXTSPLIT_COUNT 28 * TextSplit() function static substrings pointers array (pointing to static buffer) 29 * 30 * DEPENDENCIES: 31 * stb_truetype - Load TTF file and rasterize characters data 32 * stb_rect_pack - Rectangles packing algorithms, required for font atlas generation 33 * 34 * 35 * LICENSE: zlib/libpng 36 * 37 * Copyright (c) 2013-2024 Ramon Santamaria (@raysan5) 38 * 39 * This software is provided "as-is", without any express or implied warranty. In no event 40 * will the authors be held liable for any damages arising from the use of this software. 41 * 42 * Permission is granted to anyone to use this software for any purpose, including commercial 43 * applications, and to alter it and redistribute it freely, subject to the following restrictions: 44 * 45 * 1. The origin of this software must not be misrepresented; you must not claim that you 46 * wrote the original software. If you use this software in a product, an acknowledgment 47 * in the product documentation would be appreciated but is not required. 48 * 49 * 2. Altered source versions must be plainly marked as such, and must not be misrepresented 50 * as being the original software. 51 * 52 * 3. This notice may not be removed or altered from any source distribution. 53 * 54 **********************************************************************************************/ 55 56 #include "raylib.h" // Declares module functions 57 58 // Check if config flags have been externally provided on compilation line 59 #if !defined(EXTERNAL_CONFIG_FLAGS) 60 #include "config.h" // Defines module configuration flags 61 #endif 62 63 #if defined(SUPPORT_MODULE_RTEXT) 64 65 #include "utils.h" // Required for: LoadFile*() 66 #include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2 -> Only DrawTextPro() 67 68 #include <stdlib.h> // Required for: malloc(), free() 69 #include <stdio.h> // Required for: vsprintf() 70 #include <string.h> // Required for: strcmp(), strstr(), strcpy(), strncpy() [Used in TextReplace()], sscanf() [Used in LoadBMFont()] 71 #include <stdarg.h> // Required for: va_list, va_start(), vsprintf(), va_end() [Used in TextFormat()] 72 #include <ctype.h> // Required for: toupper(), tolower() [Used in TextToUpper(), TextToLower()] 73 74 #if defined(SUPPORT_FILEFORMAT_TTF) || defined(SUPPORT_FILEFORMAT_BDF) 75 #if defined(__GNUC__) // GCC and Clang 76 #pragma GCC diagnostic push 77 #pragma GCC diagnostic ignored "-Wunused-function" 78 #endif 79 80 #define STB_RECT_PACK_IMPLEMENTATION 81 #include "external/stb_rect_pack.h" // Required for: ttf/bdf font rectangles packaging 82 83 #include <math.h> // Required for: ttf/bdf font rectangles packaging 84 85 #if defined(__GNUC__) // GCC and Clang 86 #pragma GCC diagnostic pop 87 #endif 88 #endif 89 90 #if defined(SUPPORT_FILEFORMAT_TTF) 91 #if defined(__GNUC__) // GCC and Clang 92 #pragma GCC diagnostic push 93 #pragma GCC diagnostic ignored "-Wunused-function" 94 #endif 95 96 #define STBTT_STATIC 97 #define STB_TRUETYPE_IMPLEMENTATION 98 #include "external/stb_truetype.h" // Required for: ttf font data reading 99 100 #if defined(__GNUC__) // GCC and Clang 101 #pragma GCC diagnostic pop 102 #endif 103 #endif 104 105 //---------------------------------------------------------------------------------- 106 // Defines and Macros 107 //---------------------------------------------------------------------------------- 108 #ifndef MAX_TEXT_BUFFER_LENGTH 109 #define MAX_TEXT_BUFFER_LENGTH 1024 // Size of internal static buffers used on some functions: 110 // TextFormat(), TextSubtext(), TextToUpper(), TextToLower(), TextToPascal(), TextSplit() 111 #endif 112 #ifndef MAX_TEXT_UNICODE_CHARS 113 #define MAX_TEXT_UNICODE_CHARS 512 // Maximum number of unicode codepoints: GetCodepoints() 114 #endif 115 #ifndef MAX_TEXTSPLIT_COUNT 116 #define MAX_TEXTSPLIT_COUNT 128 // Maximum number of substrings to split: TextSplit() 117 #endif 118 119 //---------------------------------------------------------------------------------- 120 // Types and Structures Definition 121 //---------------------------------------------------------------------------------- 122 //... 123 124 //---------------------------------------------------------------------------------- 125 // Global variables 126 //---------------------------------------------------------------------------------- 127 extern bool isGpuReady; 128 #if defined(SUPPORT_DEFAULT_FONT) 129 // Default font provided by raylib 130 // NOTE: Default font is loaded on InitWindow() and disposed on CloseWindow() [module: core] 131 static Font defaultFont = { 0 }; 132 #endif 133 134 //---------------------------------------------------------------------------------- 135 // Other Modules Functions Declaration (required by text) 136 //---------------------------------------------------------------------------------- 137 //... 138 139 //---------------------------------------------------------------------------------- 140 // Module specific Functions Declaration 141 //---------------------------------------------------------------------------------- 142 #if defined(SUPPORT_FILEFORMAT_FNT) 143 static Font LoadBMFont(const char *fileName); // Load a BMFont file (AngelCode font file) 144 #endif 145 #if defined(SUPPORT_FILEFORMAT_BDF) 146 static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, int *codepoints, int codepointCount, int *outFontSize); 147 #endif 148 static int textLineSpacing = 2; // Text vertical line spacing in pixels (between lines) 149 150 #if defined(SUPPORT_DEFAULT_FONT) 151 extern void LoadFontDefault(void); 152 extern void UnloadFontDefault(void); 153 #endif 154 155 //---------------------------------------------------------------------------------- 156 // Module Functions Definition 157 //---------------------------------------------------------------------------------- 158 #if defined(SUPPORT_DEFAULT_FONT) 159 // Load raylib default font 160 extern void LoadFontDefault(void) 161 { 162 #define BIT_CHECK(a,b) ((a) & (1u << (b))) 163 164 // NOTE: Using UTF-8 encoding table for Unicode U+0000..U+00FF Basic Latin + Latin-1 Supplement 165 // Ref: http://www.utf8-chartable.de/unicode-utf8-table.pl 166 167 defaultFont.glyphCount = 224; // Number of chars included in our default font 168 defaultFont.glyphPadding = 0; // Characters padding 169 170 // Default font is directly defined here (data generated from a sprite font image) 171 // This way, we reconstruct Font without creating large global variables 172 // This data is automatically allocated to Stack and automatically deallocated at the end of this function 173 unsigned int defaultFontData[512] = { 174 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00200020, 0x0001b000, 0x00000000, 0x00000000, 0x8ef92520, 0x00020a00, 0x7dbe8000, 0x1f7df45f, 175 0x4a2bf2a0, 0x0852091e, 0x41224000, 0x10041450, 0x2e292020, 0x08220812, 0x41222000, 0x10041450, 0x10f92020, 0x3efa084c, 0x7d22103c, 0x107df7de, 176 0xe8a12020, 0x08220832, 0x05220800, 0x10450410, 0xa4a3f000, 0x08520832, 0x05220400, 0x10450410, 0xe2f92020, 0x0002085e, 0x7d3e0281, 0x107df41f, 177 0x00200000, 0x8001b000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 178 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xc0000fbe, 0xfbf7e00f, 0x5fbf7e7d, 0x0050bee8, 0x440808a2, 0x0a142fe8, 0x50810285, 0x0050a048, 179 0x49e428a2, 0x0a142828, 0x40810284, 0x0048a048, 0x10020fbe, 0x09f7ebaf, 0xd89f3e84, 0x0047a04f, 0x09e48822, 0x0a142aa1, 0x50810284, 0x0048a048, 180 0x04082822, 0x0a142fa0, 0x50810285, 0x0050a248, 0x00008fbe, 0xfbf42021, 0x5f817e7d, 0x07d09ce8, 0x00008000, 0x00000fe0, 0x00000000, 0x00000000, 181 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000c0180, 182 0xdfbf4282, 0x0bfbf7ef, 0x42850505, 0x004804bf, 0x50a142c6, 0x08401428, 0x42852505, 0x00a808a0, 0x50a146aa, 0x08401428, 0x42852505, 0x00081090, 183 0x5fa14a92, 0x0843f7e8, 0x7e792505, 0x00082088, 0x40a15282, 0x08420128, 0x40852489, 0x00084084, 0x40a16282, 0x0842022a, 0x40852451, 0x00088082, 184 0xc0bf4282, 0xf843f42f, 0x7e85fc21, 0x3e0900bf, 0x00000000, 0x00000004, 0x00000000, 0x000c0180, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 185 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04000402, 0x41482000, 0x00000000, 0x00000800, 186 0x04000404, 0x4100203c, 0x00000000, 0x00000800, 0xf7df7df0, 0x514bef85, 0xbefbefbe, 0x04513bef, 0x14414500, 0x494a2885, 0xa28a28aa, 0x04510820, 187 0xf44145f0, 0x474a289d, 0xa28a28aa, 0x04510be0, 0x14414510, 0x494a2884, 0xa28a28aa, 0x02910a00, 0xf7df7df0, 0xd14a2f85, 0xbefbe8aa, 0x011f7be0, 188 0x00000000, 0x00400804, 0x20080000, 0x00000000, 0x00000000, 0x00600f84, 0x20080000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 189 0xac000000, 0x00000f01, 0x00000000, 0x00000000, 0x24000000, 0x00000f01, 0x00000000, 0x06000000, 0x24000000, 0x00000f01, 0x00000000, 0x09108000, 190 0x24fa28a2, 0x00000f01, 0x00000000, 0x013e0000, 0x2242252a, 0x00000f52, 0x00000000, 0x038a8000, 0x2422222a, 0x00000f29, 0x00000000, 0x010a8000, 191 0x2412252a, 0x00000f01, 0x00000000, 0x010a8000, 0x24fbe8be, 0x00000f01, 0x00000000, 0x0ebe8000, 0xac020000, 0x00000f01, 0x00000000, 0x00048000, 192 0x0003e000, 0x00000f00, 0x00000000, 0x00008000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000038, 0x8443b80e, 0x00203a03, 193 0x02bea080, 0xf0000020, 0xc452208a, 0x04202b02, 0xf8029122, 0x07f0003b, 0xe44b388e, 0x02203a02, 0x081e8a1c, 0x0411e92a, 0xf4420be0, 0x01248202, 194 0xe8140414, 0x05d104ba, 0xe7c3b880, 0x00893a0a, 0x283c0e1c, 0x04500902, 0xc4400080, 0x00448002, 0xe8208422, 0x04500002, 0x80400000, 0x05200002, 195 0x083e8e00, 0x04100002, 0x804003e0, 0x07000042, 0xf8008400, 0x07f00003, 0x80400000, 0x04000022, 0x00000000, 0x00000000, 0x80400000, 0x04000002, 196 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00800702, 0x1848a0c2, 0x84010000, 0x02920921, 0x01042642, 0x00005121, 0x42023f7f, 0x00291002, 197 0xefc01422, 0x7efdfbf7, 0xefdfa109, 0x03bbbbf7, 0x28440f12, 0x42850a14, 0x20408109, 0x01111010, 0x28440408, 0x42850a14, 0x2040817f, 0x01111010, 198 0xefc78204, 0x7efdfbf7, 0xe7cf8109, 0x011111f3, 0x2850a932, 0x42850a14, 0x2040a109, 0x01111010, 0x2850b840, 0x42850a14, 0xefdfbf79, 0x03bbbbf7, 199 0x001fa020, 0x00000000, 0x00001000, 0x00000000, 0x00002070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 200 0x08022800, 0x00012283, 0x02430802, 0x01010001, 0x8404147c, 0x20000144, 0x80048404, 0x00823f08, 0xdfbf4284, 0x7e03f7ef, 0x142850a1, 0x0000210a, 201 0x50a14684, 0x528a1428, 0x142850a1, 0x03efa17a, 0x50a14a9e, 0x52521428, 0x142850a1, 0x02081f4a, 0x50a15284, 0x4a221428, 0xf42850a1, 0x03efa14b, 202 0x50a16284, 0x4a521428, 0x042850a1, 0x0228a17a, 0xdfbf427c, 0x7e8bf7ef, 0xf7efdfbf, 0x03efbd0b, 0x00000000, 0x04000000, 0x00000000, 0x00000008, 203 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00200508, 0x00840400, 0x11458122, 0x00014210, 204 0x00514294, 0x51420800, 0x20a22a94, 0x0050a508, 0x00200000, 0x00000000, 0x00050000, 0x08000000, 0xfefbefbe, 0xfbefbefb, 0xfbeb9114, 0x00fbefbe, 205 0x20820820, 0x8a28a20a, 0x8a289114, 0x3e8a28a2, 0xfefbefbe, 0xfbefbe0b, 0x8a289114, 0x008a28a2, 0x228a28a2, 0x08208208, 0x8a289114, 0x088a28a2, 206 0xfefbefbe, 0xfbefbefb, 0xfa2f9114, 0x00fbefbe, 0x00000000, 0x00000040, 0x00000000, 0x00000000, 0x00000000, 0x00000020, 0x00000000, 0x00000000, 207 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00210100, 0x00000004, 0x00000000, 0x00000000, 0x14508200, 0x00001402, 0x00000000, 0x00000000, 208 0x00000010, 0x00000020, 0x00000000, 0x00000000, 0xa28a28be, 0x00002228, 0x00000000, 0x00000000, 0xa28a28aa, 0x000022e8, 0x00000000, 0x00000000, 209 0xa28a28aa, 0x000022a8, 0x00000000, 0x00000000, 0xa28a28aa, 0x000022e8, 0x00000000, 0x00000000, 0xbefbefbe, 0x00003e2f, 0x00000000, 0x00000000, 210 0x00000004, 0x00002028, 0x00000000, 0x00000000, 0x80000000, 0x00003e0f, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 211 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 212 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 213 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 214 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 215 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 216 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; 217 218 int charsHeight = 10; 219 int charsDivisor = 1; // Every char is separated from the consecutive by a 1 pixel divisor, horizontally and vertically 220 221 int charsWidth[224] = { 3, 1, 4, 6, 5, 7, 6, 2, 3, 3, 5, 5, 2, 4, 1, 7, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 3, 4, 3, 6, 222 7, 6, 6, 6, 6, 6, 6, 6, 6, 3, 5, 6, 5, 7, 6, 6, 6, 6, 6, 6, 7, 6, 7, 7, 6, 6, 6, 2, 7, 2, 3, 5, 223 2, 5, 5, 5, 5, 5, 4, 5, 5, 1, 2, 5, 2, 5, 5, 5, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 3, 1, 3, 4, 4, 224 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 225 1, 1, 5, 5, 5, 7, 1, 5, 3, 7, 3, 5, 4, 1, 7, 4, 3, 5, 3, 3, 2, 5, 6, 1, 2, 2, 3, 5, 6, 6, 6, 6, 226 6, 6, 6, 6, 6, 6, 7, 6, 6, 6, 6, 6, 3, 3, 3, 3, 7, 6, 6, 6, 6, 6, 6, 5, 6, 6, 6, 6, 6, 6, 4, 6, 227 5, 5, 5, 5, 5, 5, 9, 5, 5, 5, 5, 5, 2, 2, 3, 3, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 3, 5 }; 228 229 // Re-construct image from defaultFontData and generate OpenGL texture 230 //---------------------------------------------------------------------- 231 Image imFont = { 232 .data = RL_CALLOC(128*128, 2), // 2 bytes per pixel (gray + alpha) 233 .width = 128, 234 .height = 128, 235 .mipmaps = 1, 236 .format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA 237 }; 238 239 // Fill image.data with defaultFontData (convert from bit to pixel!) 240 for (int i = 0, counter = 0; i < imFont.width*imFont.height; i += 32) 241 { 242 for (int j = 31; j >= 0; j--) 243 { 244 if (BIT_CHECK(defaultFontData[counter], j)) 245 { 246 // NOTE: We are unreferencing data as short, so, 247 // we must consider data as little-endian order (alpha + gray) 248 ((unsigned short *)imFont.data)[i + j] = 0xffff; 249 } 250 else ((unsigned short *)imFont.data)[i + j] = 0x00ff; 251 } 252 253 counter++; 254 } 255 256 if (isGpuReady) defaultFont.texture = LoadTextureFromImage(imFont); 257 258 // Reconstruct charSet using charsWidth[], charsHeight, charsDivisor, glyphCount 259 //------------------------------------------------------------------------------ 260 261 // Allocate space for our characters info data 262 // NOTE: This memory must be freed at end! --> Done by CloseWindow() 263 defaultFont.glyphs = (GlyphInfo *)RL_CALLOC(defaultFont.glyphCount, sizeof(GlyphInfo)); 264 defaultFont.recs = (Rectangle *)RL_CALLOC(defaultFont.glyphCount, sizeof(Rectangle)); 265 266 int currentLine = 0; 267 int currentPosX = charsDivisor; 268 int testPosX = charsDivisor; 269 270 for (int i = 0; i < defaultFont.glyphCount; i++) 271 { 272 defaultFont.glyphs[i].value = 32 + i; // First char is 32 273 274 defaultFont.recs[i].x = (float)currentPosX; 275 defaultFont.recs[i].y = (float)(charsDivisor + currentLine*(charsHeight + charsDivisor)); 276 defaultFont.recs[i].width = (float)charsWidth[i]; 277 defaultFont.recs[i].height = (float)charsHeight; 278 279 testPosX += (int)(defaultFont.recs[i].width + (float)charsDivisor); 280 281 if (testPosX >= defaultFont.texture.width) 282 { 283 currentLine++; 284 currentPosX = 2*charsDivisor + charsWidth[i]; 285 testPosX = currentPosX; 286 287 defaultFont.recs[i].x = (float)charsDivisor; 288 defaultFont.recs[i].y = (float)(charsDivisor + currentLine*(charsHeight + charsDivisor)); 289 } 290 else currentPosX = testPosX; 291 292 // NOTE: On default font character offsets and xAdvance are not required 293 defaultFont.glyphs[i].offsetX = 0; 294 defaultFont.glyphs[i].offsetY = 0; 295 defaultFont.glyphs[i].advanceX = 0; 296 297 // Fill character image data from fontClear data 298 defaultFont.glyphs[i].image = ImageFromImage(imFont, defaultFont.recs[i]); 299 } 300 301 UnloadImage(imFont); 302 303 defaultFont.baseSize = (int)defaultFont.recs[0].height; 304 305 TRACELOG(LOG_INFO, "FONT: Default font loaded successfully (%i glyphs)", defaultFont.glyphCount); 306 } 307 308 // Unload raylib default font 309 extern void UnloadFontDefault(void) 310 { 311 for (int i = 0; i < defaultFont.glyphCount; i++) UnloadImage(defaultFont.glyphs[i].image); 312 if (isGpuReady) UnloadTexture(defaultFont.texture); 313 RL_FREE(defaultFont.glyphs); 314 RL_FREE(defaultFont.recs); 315 } 316 #endif // SUPPORT_DEFAULT_FONT 317 318 // Get the default font, useful to be used with extended parameters 319 Font GetFontDefault() 320 { 321 #if defined(SUPPORT_DEFAULT_FONT) 322 return defaultFont; 323 #else 324 Font font = { 0 }; 325 return font; 326 #endif 327 } 328 329 // Load Font from file into GPU memory (VRAM) 330 Font LoadFont(const char *fileName) 331 { 332 // Default values for ttf font generation 333 #ifndef FONT_TTF_DEFAULT_SIZE 334 #define FONT_TTF_DEFAULT_SIZE 32 // TTF font generation default char size (char-height) 335 #endif 336 #ifndef FONT_TTF_DEFAULT_NUMCHARS 337 #define FONT_TTF_DEFAULT_NUMCHARS 95 // TTF font generation default charset: 95 glyphs (ASCII 32..126) 338 #endif 339 #ifndef FONT_TTF_DEFAULT_FIRST_CHAR 340 #define FONT_TTF_DEFAULT_FIRST_CHAR 32 // TTF font generation default first char for image sprite font (32-Space) 341 #endif 342 #ifndef FONT_TTF_DEFAULT_CHARS_PADDING 343 #define FONT_TTF_DEFAULT_CHARS_PADDING 4 // TTF font generation default chars padding 344 #endif 345 346 Font font = { 0 }; 347 348 #if defined(SUPPORT_FILEFORMAT_TTF) 349 if (IsFileExtension(fileName, ".ttf") || IsFileExtension(fileName, ".otf")) font = LoadFontEx(fileName, FONT_TTF_DEFAULT_SIZE, NULL, FONT_TTF_DEFAULT_NUMCHARS); 350 else 351 #endif 352 #if defined(SUPPORT_FILEFORMAT_FNT) 353 if (IsFileExtension(fileName, ".fnt")) font = LoadBMFont(fileName); 354 else 355 #endif 356 #if defined(SUPPORT_FILEFORMAT_BDF) 357 if (IsFileExtension(fileName, ".bdf")) font = LoadFontEx(fileName, FONT_TTF_DEFAULT_SIZE, NULL, FONT_TTF_DEFAULT_NUMCHARS); 358 else 359 #endif 360 { 361 Image image = LoadImage(fileName); 362 if (image.data != NULL) font = LoadFontFromImage(image, MAGENTA, FONT_TTF_DEFAULT_FIRST_CHAR); 363 UnloadImage(image); 364 } 365 366 if (isGpuReady) 367 { 368 if (font.texture.id == 0) TRACELOG(LOG_WARNING, "FONT: [%s] Failed to load font texture -> Using default font", fileName); 369 else 370 { 371 SetTextureFilter(font.texture, TEXTURE_FILTER_POINT); // By default, we set point filter (the best performance) 372 TRACELOG(LOG_INFO, "FONT: Data loaded successfully (%i pixel size | %i glyphs)", FONT_TTF_DEFAULT_SIZE, FONT_TTF_DEFAULT_NUMCHARS); 373 } 374 } 375 376 return font; 377 } 378 379 // Load Font from TTF or BDF font file with generation parameters 380 // NOTE: You can pass an array with desired characters, those characters should be available in the font 381 // if array is NULL, default char set is selected 32..126 382 Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount) 383 { 384 Font font = { 0 }; 385 386 // Loading file to memory 387 int dataSize = 0; 388 unsigned char *fileData = LoadFileData(fileName, &dataSize); 389 390 if (fileData != NULL) 391 { 392 // Loading font from memory data 393 font = LoadFontFromMemory(GetFileExtension(fileName), fileData, dataSize, fontSize, codepoints, codepointCount); 394 395 UnloadFileData(fileData); 396 } 397 398 return font; 399 } 400 401 // Load an Image font file (XNA style) 402 Font LoadFontFromImage(Image image, Color key, int firstChar) 403 { 404 #ifndef MAX_GLYPHS_FROM_IMAGE 405 #define MAX_GLYPHS_FROM_IMAGE 256 // Maximum number of glyphs supported on image scan 406 #endif 407 408 #define COLOR_EQUAL(col1, col2) ((col1.r == col2.r) && (col1.g == col2.g) && (col1.b == col2.b) && (col1.a == col2.a)) 409 410 Font font = GetFontDefault(); 411 412 int charSpacing = 0; 413 int lineSpacing = 0; 414 415 int x = 0; 416 int y = 0; 417 418 // We allocate a temporal arrays for chars data measures, 419 // once we get the actual number of chars, we copy data to a sized arrays 420 int tempCharValues[MAX_GLYPHS_FROM_IMAGE] = { 0 }; 421 Rectangle tempCharRecs[MAX_GLYPHS_FROM_IMAGE] = { 0 }; 422 423 Color *pixels = LoadImageColors(image); 424 425 // Parse image data to get charSpacing and lineSpacing 426 for (y = 0; y < image.height; y++) 427 { 428 for (x = 0; x < image.width; x++) 429 { 430 if (!COLOR_EQUAL(pixels[y*image.width + x], key)) break; 431 } 432 433 if (!COLOR_EQUAL(pixels[y*image.width + x], key)) break; 434 } 435 436 if ((x == 0) || (y == 0)) return font; // Security check 437 438 charSpacing = x; 439 lineSpacing = y; 440 441 int charHeight = 0; 442 int j = 0; 443 444 while (!COLOR_EQUAL(pixels[(lineSpacing + j)*image.width + charSpacing], key)) j++; 445 446 charHeight = j; 447 448 // Check array values to get characters: value, x, y, w, h 449 int index = 0; 450 int lineToRead = 0; 451 int xPosToRead = charSpacing; 452 453 // Parse image data to get rectangle sizes 454 while ((lineSpacing + lineToRead*(charHeight + lineSpacing)) < image.height) 455 { 456 while ((xPosToRead < image.width) && 457 !COLOR_EQUAL((pixels[(lineSpacing + (charHeight+lineSpacing)*lineToRead)*image.width + xPosToRead]), key)) 458 { 459 tempCharValues[index] = firstChar + index; 460 461 tempCharRecs[index].x = (float)xPosToRead; 462 tempCharRecs[index].y = (float)(lineSpacing + lineToRead*(charHeight + lineSpacing)); 463 tempCharRecs[index].height = (float)charHeight; 464 465 int charWidth = 0; 466 467 while (!COLOR_EQUAL(pixels[(lineSpacing + (charHeight+lineSpacing)*lineToRead)*image.width + xPosToRead + charWidth], key)) charWidth++; 468 469 tempCharRecs[index].width = (float)charWidth; 470 471 index++; 472 473 xPosToRead += (charWidth + charSpacing); 474 } 475 476 lineToRead++; 477 xPosToRead = charSpacing; 478 } 479 480 // NOTE: We need to remove key color borders from image to avoid weird 481 // artifacts on texture scaling when using TEXTURE_FILTER_BILINEAR or TEXTURE_FILTER_TRILINEAR 482 for (int i = 0; i < image.height*image.width; i++) if (COLOR_EQUAL(pixels[i], key)) pixels[i] = BLANK; 483 484 // Create a new image with the processed color data (key color replaced by BLANK) 485 Image fontClear = { 486 .data = pixels, 487 .width = image.width, 488 .height = image.height, 489 .mipmaps = 1, 490 .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 491 }; 492 493 // Set font with all data parsed from image 494 if (isGpuReady) font.texture = LoadTextureFromImage(fontClear); // Convert processed image to OpenGL texture 495 font.glyphCount = index; 496 font.glyphPadding = 0; 497 498 // We got tempCharValues and tempCharsRecs populated with chars data 499 // Now we move temp data to sized charValues and charRecs arrays 500 font.glyphs = (GlyphInfo *)RL_MALLOC(font.glyphCount*sizeof(GlyphInfo)); 501 font.recs = (Rectangle *)RL_MALLOC(font.glyphCount*sizeof(Rectangle)); 502 503 for (int i = 0; i < font.glyphCount; i++) 504 { 505 font.glyphs[i].value = tempCharValues[i]; 506 507 // Get character rectangle in the font atlas texture 508 font.recs[i] = tempCharRecs[i]; 509 510 // NOTE: On image based fonts (XNA style), character offsets and xAdvance are not required (set to 0) 511 font.glyphs[i].offsetX = 0; 512 font.glyphs[i].offsetY = 0; 513 font.glyphs[i].advanceX = 0; 514 515 // Fill character image data from fontClear data 516 font.glyphs[i].image = ImageFromImage(fontClear, tempCharRecs[i]); 517 } 518 519 UnloadImage(fontClear); // Unload processed image once converted to texture 520 521 font.baseSize = (int)font.recs[0].height; 522 523 return font; 524 } 525 526 // Load font from memory buffer, fileType refers to extension: i.e. ".ttf" 527 Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount) 528 { 529 Font font = { 0 }; 530 531 char fileExtLower[16] = { 0 }; 532 strncpy(fileExtLower, TextToLower(fileType), 16 - 1); 533 534 font.baseSize = fontSize; 535 font.glyphCount = (codepointCount > 0)? codepointCount : 95; 536 font.glyphPadding = 0; 537 538 #if defined(SUPPORT_FILEFORMAT_TTF) 539 if (TextIsEqual(fileExtLower, ".ttf") || 540 TextIsEqual(fileExtLower, ".otf")) 541 { 542 font.glyphs = LoadFontData(fileData, dataSize, font.baseSize, codepoints, font.glyphCount, FONT_DEFAULT); 543 } 544 else 545 #endif 546 #if defined(SUPPORT_FILEFORMAT_BDF) 547 if (TextIsEqual(fileExtLower, ".bdf")) 548 { 549 font.glyphs = LoadFontDataBDF(fileData, dataSize, codepoints, font.glyphCount, &font.baseSize); 550 } 551 else 552 #endif 553 { 554 font.glyphs = NULL; 555 } 556 557 #if defined(SUPPORT_FILEFORMAT_TTF) || defined(SUPPORT_FILEFORMAT_BDF) 558 if (font.glyphs != NULL) 559 { 560 font.glyphPadding = FONT_TTF_DEFAULT_CHARS_PADDING; 561 562 Image atlas = GenImageFontAtlas(font.glyphs, &font.recs, font.glyphCount, font.baseSize, font.glyphPadding, 0); 563 if (isGpuReady) font.texture = LoadTextureFromImage(atlas); 564 565 // Update glyphs[i].image to use alpha, required to be used on ImageDrawText() 566 for (int i = 0; i < font.glyphCount; i++) 567 { 568 UnloadImage(font.glyphs[i].image); 569 font.glyphs[i].image = ImageFromImage(atlas, font.recs[i]); 570 } 571 572 UnloadImage(atlas); 573 574 TRACELOG(LOG_INFO, "FONT: Data loaded successfully (%i pixel size | %i glyphs)", font.baseSize, font.glyphCount); 575 } 576 else font = GetFontDefault(); 577 #else 578 font = GetFontDefault(); 579 #endif 580 581 return font; 582 } 583 584 // Check if a font is valid (font data loaded) 585 // WARNING: GPU texture not checked 586 bool IsFontValid(Font font) 587 { 588 return ((font.baseSize > 0) && // Validate font size 589 (font.glyphCount > 0) && // Validate font contains some glyph 590 (font.recs != NULL) && // Validate font recs defining glyphs on texture atlas 591 (font.glyphs != NULL)); // Validate glyph data is loaded 592 593 // NOTE: Further validations could be done to verify if recs and glyphs contain valid data (glyphs values, metrics...) 594 } 595 596 // Load font data for further use 597 // NOTE: Requires TTF font memory data and can generate SDF data 598 GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount, int type) 599 { 600 // NOTE: Using some SDF generation default values, 601 // trades off precision with ability to handle *smaller* sizes 602 #ifndef FONT_SDF_CHAR_PADDING 603 #define FONT_SDF_CHAR_PADDING 4 // SDF font generation char padding 604 #endif 605 #ifndef FONT_SDF_ON_EDGE_VALUE 606 #define FONT_SDF_ON_EDGE_VALUE 128 // SDF font generation on edge value 607 #endif 608 #ifndef FONT_SDF_PIXEL_DIST_SCALE 609 #define FONT_SDF_PIXEL_DIST_SCALE 64.0f // SDF font generation pixel distance scale 610 #endif 611 #ifndef FONT_BITMAP_ALPHA_THRESHOLD 612 #define FONT_BITMAP_ALPHA_THRESHOLD 80 // Bitmap (B&W) font generation alpha threshold 613 #endif 614 615 GlyphInfo *chars = NULL; 616 617 #if defined(SUPPORT_FILEFORMAT_TTF) 618 // Load font data (including pixel data) from TTF memory file 619 // NOTE: Loaded information should be enough to generate font image atlas, using any packaging method 620 if (fileData != NULL) 621 { 622 bool genFontChars = false; 623 stbtt_fontinfo fontInfo = { 0 }; 624 625 if (stbtt_InitFont(&fontInfo, (unsigned char *)fileData, 0)) // Initialize font for data reading 626 { 627 // Calculate font scale factor 628 float scaleFactor = stbtt_ScaleForPixelHeight(&fontInfo, (float)fontSize); 629 630 // Calculate font basic metrics 631 // NOTE: ascent is equivalent to font baseline 632 int ascent, descent, lineGap; 633 stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap); 634 635 // In case no chars count provided, default to 95 636 codepointCount = (codepointCount > 0)? codepointCount : 95; 637 638 // Fill fontChars in case not provided externally 639 // NOTE: By default we fill glyphCount consecutively, starting at 32 (Space) 640 if (codepoints == NULL) 641 { 642 codepoints = (int *)RL_MALLOC(codepointCount*sizeof(int)); 643 for (int i = 0; i < codepointCount; i++) codepoints[i] = i + 32; 644 genFontChars = true; 645 } 646 647 chars = (GlyphInfo *)RL_CALLOC(codepointCount, sizeof(GlyphInfo)); 648 649 // NOTE: Using simple packaging, one char after another 650 for (int i = 0; i < codepointCount; i++) 651 { 652 int chw = 0, chh = 0; // Character width and height (on generation) 653 int ch = codepoints[i]; // Character value to get info for 654 chars[i].value = ch; 655 656 // Render a unicode codepoint to a bitmap 657 // stbtt_GetCodepointBitmap() -- allocates and returns a bitmap 658 // stbtt_GetCodepointBitmapBox() -- how big the bitmap must be 659 // stbtt_MakeCodepointBitmap() -- renders into bitmap you provide 660 661 // Check if a glyph is available in the font 662 // WARNING: if (index == 0), glyph not found, it could fallback to default .notdef glyph (if defined in font) 663 int index = stbtt_FindGlyphIndex(&fontInfo, ch); 664 665 if (index > 0) 666 { 667 switch (type) 668 { 669 case FONT_DEFAULT: 670 case FONT_BITMAP: chars[i].image.data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, ch, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY); break; 671 case FONT_SDF: if (ch != 32) chars[i].image.data = stbtt_GetCodepointSDF(&fontInfo, scaleFactor, ch, FONT_SDF_CHAR_PADDING, FONT_SDF_ON_EDGE_VALUE, FONT_SDF_PIXEL_DIST_SCALE, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY); break; 672 default: break; 673 } 674 675 if (chars[i].image.data != NULL) // Glyph data has been found in the font 676 { 677 stbtt_GetCodepointHMetrics(&fontInfo, ch, &chars[i].advanceX, NULL); 678 chars[i].advanceX = (int)((float)chars[i].advanceX*scaleFactor); 679 680 if (chh > fontSize) TRACELOG(LOG_WARNING, "FONT: Character [0x%08x] size is bigger than expected font size", ch); 681 682 // Load characters images 683 chars[i].image.width = chw; 684 chars[i].image.height = chh; 685 chars[i].image.mipmaps = 1; 686 chars[i].image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; 687 688 chars[i].offsetY += (int)((float)ascent*scaleFactor); 689 } 690 691 // NOTE: We create an empty image for space character, 692 // it could be further required for atlas packing 693 if (ch == 32) 694 { 695 stbtt_GetCodepointHMetrics(&fontInfo, ch, &chars[i].advanceX, NULL); 696 chars[i].advanceX = (int)((float)chars[i].advanceX*scaleFactor); 697 698 Image imSpace = { 699 .data = RL_CALLOC(chars[i].advanceX*fontSize, 2), 700 .width = chars[i].advanceX, 701 .height = fontSize, 702 .mipmaps = 1, 703 .format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE 704 }; 705 706 chars[i].image = imSpace; 707 } 708 709 if (type == FONT_BITMAP) 710 { 711 // Aliased bitmap (black & white) font generation, avoiding anti-aliasing 712 // NOTE: For optimum results, bitmap font should be generated at base pixel size 713 for (int p = 0; p < chw*chh; p++) 714 { 715 if (((unsigned char *)chars[i].image.data)[p] < FONT_BITMAP_ALPHA_THRESHOLD) ((unsigned char *)chars[i].image.data)[p] = 0; 716 else ((unsigned char *)chars[i].image.data)[p] = 255; 717 } 718 } 719 } 720 else 721 { 722 // TODO: Use some fallback glyph for codepoints not found in the font 723 } 724 } 725 } 726 else TRACELOG(LOG_WARNING, "FONT: Failed to process TTF font data"); 727 728 if (genFontChars) RL_FREE(codepoints); 729 } 730 #endif 731 732 return chars; 733 } 734 735 // Generate image font atlas using chars info 736 // NOTE: Packing method: 0-Default, 1-Skyline 737 #if defined(SUPPORT_FILEFORMAT_TTF) || defined(SUPPORT_FILEFORMAT_BDF) 738 Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyphCount, int fontSize, int padding, int packMethod) 739 { 740 Image atlas = { 0 }; 741 742 if (glyphs == NULL) 743 { 744 TRACELOG(LOG_WARNING, "FONT: Provided chars info not valid, returning empty image atlas"); 745 return atlas; 746 } 747 748 *glyphRecs = NULL; 749 750 // In case no chars count provided we suppose default of 95 751 glyphCount = (glyphCount > 0)? glyphCount : 95; 752 753 // NOTE: Rectangles memory is loaded here! 754 Rectangle *recs = (Rectangle *)RL_MALLOC(glyphCount*sizeof(Rectangle)); 755 756 // Calculate image size based on total glyph width and glyph row count 757 int totalWidth = 0; 758 int maxGlyphWidth = 0; 759 760 for (int i = 0; i < glyphCount; i++) 761 { 762 if (glyphs[i].image.width > maxGlyphWidth) maxGlyphWidth = glyphs[i].image.width; 763 totalWidth += glyphs[i].image.width + 2*padding; 764 } 765 766 //#define SUPPORT_FONT_ATLAS_SIZE_CONSERVATIVE 767 #if defined(SUPPORT_FONT_ATLAS_SIZE_CONSERVATIVE) 768 int rowCount = 0; 769 int imageSize = 64; // Define minimum starting value to avoid unnecessary calculation steps for very small images 770 771 // NOTE: maxGlyphWidth is maximum possible space left at the end of row 772 while (totalWidth > (imageSize - maxGlyphWidth)*rowCount) 773 { 774 imageSize *= 2; // Double the size of image (to keep POT) 775 rowCount = imageSize/(fontSize + 2*padding); // Calculate new row count for the new image size 776 } 777 778 atlas.width = imageSize; // Atlas bitmap width 779 atlas.height = imageSize; // Atlas bitmap height 780 #else 781 int paddedFontSize = fontSize + 2*padding; 782 // No need for a so-conservative atlas generation 783 float totalArea = totalWidth*paddedFontSize*1.2f; 784 float imageMinSize = sqrtf(totalArea); 785 int imageSize = (int)powf(2, ceilf(logf(imageMinSize)/logf(2))); 786 787 if (totalArea < ((imageSize*imageSize)/2)) 788 { 789 atlas.width = imageSize; // Atlas bitmap width 790 atlas.height = imageSize/2; // Atlas bitmap height 791 } 792 else 793 { 794 atlas.width = imageSize; // Atlas bitmap width 795 atlas.height = imageSize; // Atlas bitmap height 796 } 797 #endif 798 799 atlas.data = (unsigned char *)RL_CALLOC(1, atlas.width*atlas.height); // Create a bitmap to store characters (8 bpp) 800 atlas.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; 801 atlas.mipmaps = 1; 802 803 // DEBUG: We can see padding in the generated image setting a gray background... 804 //for (int i = 0; i < atlas.width*atlas.height; i++) ((unsigned char *)atlas.data)[i] = 100; 805 806 if (packMethod == 0) // Use basic packing algorithm 807 { 808 int offsetX = padding; 809 int offsetY = padding; 810 811 // NOTE: Using simple packaging, one char after another 812 for (int i = 0; i < glyphCount; i++) 813 { 814 // Check remaining space for glyph 815 if (offsetX >= (atlas.width - glyphs[i].image.width - 2*padding)) 816 { 817 offsetX = padding; 818 819 // NOTE: Be careful on offsetY for SDF fonts, by default SDF 820 // use an internal padding of 4 pixels, it means char rectangle 821 // height is bigger than fontSize, it could be up to (fontSize + 8) 822 offsetY += (fontSize + 2*padding); 823 824 if (offsetY > (atlas.height - fontSize - padding)) 825 { 826 for (int j = i + 1; j < glyphCount; j++) 827 { 828 TRACELOG(LOG_WARNING, "FONT: Failed to package character (%i)", j); 829 // Make sure remaining recs contain valid data 830 recs[j].x = 0; 831 recs[j].y = 0; 832 recs[j].width = 0; 833 recs[j].height = 0; 834 } 835 break; 836 } 837 } 838 839 // Copy pixel data from glyph image to atlas 840 for (int y = 0; y < glyphs[i].image.height; y++) 841 { 842 for (int x = 0; x < glyphs[i].image.width; x++) 843 { 844 ((unsigned char *)atlas.data)[(offsetY + y)*atlas.width + (offsetX + x)] = ((unsigned char *)glyphs[i].image.data)[y*glyphs[i].image.width + x]; 845 } 846 } 847 848 // Fill chars rectangles in atlas info 849 recs[i].x = (float)offsetX; 850 recs[i].y = (float)offsetY; 851 recs[i].width = (float)glyphs[i].image.width; 852 recs[i].height = (float)glyphs[i].image.height; 853 854 // Move atlas position X for next character drawing 855 offsetX += (glyphs[i].image.width + 2*padding); 856 } 857 } 858 else if (packMethod == 1) // Use Skyline rect packing algorithm (stb_pack_rect) 859 { 860 stbrp_context *context = (stbrp_context *)RL_MALLOC(sizeof(*context)); 861 stbrp_node *nodes = (stbrp_node *)RL_MALLOC(glyphCount*sizeof(*nodes)); 862 863 stbrp_init_target(context, atlas.width, atlas.height, nodes, glyphCount); 864 stbrp_rect *rects = (stbrp_rect *)RL_MALLOC(glyphCount*sizeof(stbrp_rect)); 865 866 // Fill rectangles for packaging 867 for (int i = 0; i < glyphCount; i++) 868 { 869 rects[i].id = i; 870 rects[i].w = glyphs[i].image.width + 2*padding; 871 rects[i].h = glyphs[i].image.height + 2*padding; 872 } 873 874 // Package rectangles into atlas 875 stbrp_pack_rects(context, rects, glyphCount); 876 877 for (int i = 0; i < glyphCount; i++) 878 { 879 // It returns char rectangles in atlas 880 recs[i].x = rects[i].x + (float)padding; 881 recs[i].y = rects[i].y + (float)padding; 882 recs[i].width = (float)glyphs[i].image.width; 883 recs[i].height = (float)glyphs[i].image.height; 884 885 if (rects[i].was_packed) 886 { 887 // Copy pixel data from fc.data to atlas 888 for (int y = 0; y < glyphs[i].image.height; y++) 889 { 890 for (int x = 0; x < glyphs[i].image.width; x++) 891 { 892 ((unsigned char *)atlas.data)[(rects[i].y + padding + y)*atlas.width + (rects[i].x + padding + x)] = ((unsigned char *)glyphs[i].image.data)[y*glyphs[i].image.width + x]; 893 } 894 } 895 } 896 else TRACELOG(LOG_WARNING, "FONT: Failed to package character (%i)", i); 897 } 898 899 RL_FREE(rects); 900 RL_FREE(nodes); 901 RL_FREE(context); 902 } 903 904 #if defined(SUPPORT_FONT_ATLAS_WHITE_REC) 905 // Add a 3x3 white rectangle at the bottom-right corner of the generated atlas, 906 // useful to use as the white texture to draw shapes with raylib, using this rectangle 907 // shapes and text can be backed into a single draw call: SetShapesTexture() 908 for (int i = 0, k = atlas.width*atlas.height - 1; i < 3; i++) 909 { 910 ((unsigned char *)atlas.data)[k - 0] = 255; 911 ((unsigned char *)atlas.data)[k - 1] = 255; 912 ((unsigned char *)atlas.data)[k - 2] = 255; 913 k -= atlas.width; 914 } 915 #endif 916 917 // Convert image data from GRAYSCALE to GRAY_ALPHA 918 unsigned char *dataGrayAlpha = (unsigned char *)RL_MALLOC(atlas.width*atlas.height*sizeof(unsigned char)*2); // Two channels 919 920 for (int i = 0, k = 0; i < atlas.width*atlas.height; i++, k += 2) 921 { 922 dataGrayAlpha[k] = 255; 923 dataGrayAlpha[k + 1] = ((unsigned char *)atlas.data)[i]; 924 } 925 926 RL_FREE(atlas.data); 927 atlas.data = dataGrayAlpha; 928 atlas.format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA; 929 930 *glyphRecs = recs; 931 932 return atlas; 933 } 934 #endif 935 936 // Unload font glyphs info data (RAM) 937 void UnloadFontData(GlyphInfo *glyphs, int glyphCount) 938 { 939 if (glyphs != NULL) 940 { 941 for (int i = 0; i < glyphCount; i++) UnloadImage(glyphs[i].image); 942 943 RL_FREE(glyphs); 944 } 945 } 946 947 // Unload Font from GPU memory (VRAM) 948 void UnloadFont(Font font) 949 { 950 // NOTE: Make sure font is not default font (fallback) 951 if (font.texture.id != GetFontDefault().texture.id) 952 { 953 UnloadFontData(font.glyphs, font.glyphCount); 954 if (isGpuReady) UnloadTexture(font.texture); 955 RL_FREE(font.recs); 956 957 TRACELOGD("FONT: Unloaded font data from RAM and VRAM"); 958 } 959 } 960 961 // Export font as code file, returns true on success 962 bool ExportFontAsCode(Font font, const char *fileName) 963 { 964 bool success = false; 965 966 #ifndef TEXT_BYTES_PER_LINE 967 #define TEXT_BYTES_PER_LINE 20 968 #endif 969 970 #define MAX_FONT_DATA_SIZE 1024*1024 // 1 MB 971 972 // Get file name from path 973 char fileNamePascal[256] = { 0 }; 974 strncpy(fileNamePascal, TextToPascal(GetFileNameWithoutExt(fileName)), 256 - 1); 975 976 // NOTE: Text data buffer size is estimated considering image data size in bytes 977 // and requiring 6 char bytes for every byte: "0x00, " 978 char *txtData = (char *)RL_CALLOC(MAX_FONT_DATA_SIZE, sizeof(char)); 979 980 int byteCount = 0; 981 byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n"); 982 byteCount += sprintf(txtData + byteCount, "// //\n"); 983 byteCount += sprintf(txtData + byteCount, "// FontAsCode exporter v1.0 - Font data exported as an array of bytes //\n"); 984 byteCount += sprintf(txtData + byteCount, "// //\n"); 985 byteCount += sprintf(txtData + byteCount, "// more info and bugs-report: github.com/raysan5/raylib //\n"); 986 byteCount += sprintf(txtData + byteCount, "// feedback and support: ray[at]raylib.com //\n"); 987 byteCount += sprintf(txtData + byteCount, "// //\n"); 988 byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2024 Ramon Santamaria (@raysan5) //\n"); 989 byteCount += sprintf(txtData + byteCount, "// //\n"); 990 byteCount += sprintf(txtData + byteCount, "// ---------------------------------------------------------------------------------- //\n"); 991 byteCount += sprintf(txtData + byteCount, "// //\n"); 992 byteCount += sprintf(txtData + byteCount, "// TODO: Fill the information and license of the exported font here: //\n"); 993 byteCount += sprintf(txtData + byteCount, "// //\n"); 994 byteCount += sprintf(txtData + byteCount, "// Font name: .... //\n"); 995 byteCount += sprintf(txtData + byteCount, "// Font creator: .... //\n"); 996 byteCount += sprintf(txtData + byteCount, "// Font LICENSE: .... //\n"); 997 byteCount += sprintf(txtData + byteCount, "// //\n"); 998 byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n"); 999 1000 // Support font export and initialization 1001 // NOTE: This mechanism is highly coupled to raylib 1002 Image image = LoadImageFromTexture(font.texture); 1003 if (image.format != PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) TRACELOG(LOG_WARNING, "Font export as code: Font image format is not GRAY+ALPHA!"); 1004 int imageDataSize = GetPixelDataSize(image.width, image.height, image.format); 1005 1006 // Image data is usually GRAYSCALE + ALPHA and can be reduced to GRAYSCALE 1007 //ImageFormat(&image, PIXELFORMAT_UNCOMPRESSED_GRAYSCALE); 1008 1009 #define SUPPORT_COMPRESSED_FONT_ATLAS 1010 #if defined(SUPPORT_COMPRESSED_FONT_ATLAS) 1011 // WARNING: Data is compressed using raylib CompressData() DEFLATE, 1012 // it requires to be decompressed with raylib DecompressData(), that requires 1013 // compiling raylib with SUPPORT_COMPRESSION_API config flag enabled 1014 1015 // Compress font image data 1016 int compDataSize = 0; 1017 unsigned char *compData = CompressData((const unsigned char *)image.data, imageDataSize, &compDataSize); 1018 1019 // Save font image data (compressed) 1020 byteCount += sprintf(txtData + byteCount, "#define COMPRESSED_DATA_SIZE_FONT_%s %i\n\n", TextToUpper(fileNamePascal), compDataSize); 1021 byteCount += sprintf(txtData + byteCount, "// Font image pixels data compressed (DEFLATE)\n"); 1022 byteCount += sprintf(txtData + byteCount, "// NOTE: Original pixel data simplified to GRAYSCALE\n"); 1023 byteCount += sprintf(txtData + byteCount, "static unsigned char fontData_%s[COMPRESSED_DATA_SIZE_FONT_%s] = { ", fileNamePascal, TextToUpper(fileNamePascal)); 1024 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]); 1025 byteCount += sprintf(txtData + byteCount, "0x%02x };\n\n", compData[compDataSize - 1]); 1026 RL_FREE(compData); 1027 #else 1028 // Save font image data (uncompressed) 1029 byteCount += sprintf(txtData + byteCount, "// Font image pixels data\n"); 1030 byteCount += sprintf(txtData + byteCount, "// NOTE: 2 bytes per pixel, GRAY + ALPHA channels\n"); 1031 byteCount += sprintf(txtData + byteCount, "static unsigned char fontImageData_%s[%i] = { ", fileNamePascal, imageDataSize); 1032 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]); 1033 byteCount += sprintf(txtData + byteCount, "0x%02x };\n\n", ((unsigned char *)imFont.data)[imageDataSize - 1]); 1034 #endif 1035 1036 // Save font recs data 1037 byteCount += sprintf(txtData + byteCount, "// Font characters rectangles data\n"); 1038 byteCount += sprintf(txtData + byteCount, "static Rectangle fontRecs_%s[%i] = {\n", fileNamePascal, font.glyphCount); 1039 for (int i = 0; i < font.glyphCount; i++) 1040 { 1041 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); 1042 } 1043 byteCount += sprintf(txtData + byteCount, "};\n\n"); 1044 1045 // Save font glyphs data 1046 // NOTE: Glyphs image data not saved (grayscale pixels), 1047 // it could be generated from image and recs 1048 byteCount += sprintf(txtData + byteCount, "// Font glyphs info data\n"); 1049 byteCount += sprintf(txtData + byteCount, "// NOTE: No glyphs.image data provided\n"); 1050 byteCount += sprintf(txtData + byteCount, "static GlyphInfo fontGlyphs_%s[%i] = {\n", fileNamePascal, font.glyphCount); 1051 for (int i = 0; i < font.glyphCount; i++) 1052 { 1053 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); 1054 } 1055 byteCount += sprintf(txtData + byteCount, "};\n\n"); 1056 1057 // Custom font loading function 1058 byteCount += sprintf(txtData + byteCount, "// Font loading function: %s\n", fileNamePascal); 1059 byteCount += sprintf(txtData + byteCount, "static Font LoadFont_%s(void)\n{\n", fileNamePascal); 1060 byteCount += sprintf(txtData + byteCount, " Font font = { 0 };\n\n"); 1061 byteCount += sprintf(txtData + byteCount, " font.baseSize = %i;\n", font.baseSize); 1062 byteCount += sprintf(txtData + byteCount, " font.glyphCount = %i;\n", font.glyphCount); 1063 byteCount += sprintf(txtData + byteCount, " font.glyphPadding = %i;\n\n", font.glyphPadding); 1064 byteCount += sprintf(txtData + byteCount, " // Custom font loading\n"); 1065 #if defined(SUPPORT_COMPRESSED_FONT_ATLAS) 1066 byteCount += sprintf(txtData + byteCount, " // NOTE: Compressed font image data (DEFLATE), it requires DecompressData() function\n"); 1067 byteCount += sprintf(txtData + byteCount, " int fontDataSize_%s = 0;\n", fileNamePascal); 1068 byteCount += sprintf(txtData + byteCount, " unsigned char *data = DecompressData(fontData_%s, COMPRESSED_DATA_SIZE_FONT_%s, &fontDataSize_%s);\n", fileNamePascal, TextToUpper(fileNamePascal), fileNamePascal); 1069 byteCount += sprintf(txtData + byteCount, " Image imFont = { data, %i, %i, 1, %i };\n\n", image.width, image.height, image.format); 1070 #else 1071 byteCount += sprintf(txtData + byteCount, " Image imFont = { fontImageData_%s, %i, %i, 1, %i };\n\n", styleName, image.width, image.height, image.format); 1072 #endif 1073 byteCount += sprintf(txtData + byteCount, " // Load texture from image\n"); 1074 byteCount += sprintf(txtData + byteCount, " if (isGpuReady) font.texture = LoadTextureFromImage(imFont);\n"); 1075 #if defined(SUPPORT_COMPRESSED_FONT_ATLAS) 1076 byteCount += sprintf(txtData + byteCount, " UnloadImage(imFont); // Uncompressed data can be unloaded from memory\n\n"); 1077 #endif 1078 // We have two possible mechanisms to assign font.recs and font.glyphs data, 1079 // that data is already available as global arrays, we two options to assign that data: 1080 // - 1. Data copy. This option consumes more memory and Font MUST be unloaded by user, requiring additional code 1081 // - 2. Data assignment. This option consumes less memory and Font MUST NOT be unloaded by user because data is on protected DATA segment 1082 //#define SUPPORT_FONT_DATA_COPY 1083 #if defined(SUPPORT_FONT_DATA_COPY) 1084 byteCount += sprintf(txtData + byteCount, " // Copy glyph recs data from global fontRecs\n"); 1085 byteCount += sprintf(txtData + byteCount, " // NOTE: Required to avoid issues if trying to free font\n"); 1086 byteCount += sprintf(txtData + byteCount, " font.recs = (Rectangle *)malloc(font.glyphCount*sizeof(Rectangle));\n"); 1087 byteCount += sprintf(txtData + byteCount, " memcpy(font.recs, fontRecs_%s, font.glyphCount*sizeof(Rectangle));\n\n", fileNamePascal); 1088 1089 byteCount += sprintf(txtData + byteCount, " // Copy font glyph info data from global fontChars\n"); 1090 byteCount += sprintf(txtData + byteCount, " // NOTE: Required to avoid issues if trying to free font\n"); 1091 byteCount += sprintf(txtData + byteCount, " font.glyphs = (GlyphInfo *)malloc(font.glyphCount*sizeof(GlyphInfo));\n"); 1092 byteCount += sprintf(txtData + byteCount, " memcpy(font.glyphs, fontGlyphs_%s, font.glyphCount*sizeof(GlyphInfo));\n\n", fileNamePascal); 1093 #else 1094 byteCount += sprintf(txtData + byteCount, " // Assign glyph recs and info data directly\n"); 1095 byteCount += sprintf(txtData + byteCount, " // WARNING: This font data must not be unloaded\n"); 1096 byteCount += sprintf(txtData + byteCount, " font.recs = fontRecs_%s;\n", fileNamePascal); 1097 byteCount += sprintf(txtData + byteCount, " font.glyphs = fontGlyphs_%s;\n\n", fileNamePascal); 1098 #endif 1099 byteCount += sprintf(txtData + byteCount, " return font;\n"); 1100 byteCount += sprintf(txtData + byteCount, "}\n"); 1101 1102 UnloadImage(image); 1103 1104 // NOTE: Text data size exported is determined by '\0' (NULL) character 1105 success = SaveFileText(fileName, txtData); 1106 1107 RL_FREE(txtData); 1108 1109 if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Font as code exported successfully", fileName); 1110 else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export font as code", fileName); 1111 1112 return success; 1113 } 1114 1115 // Draw current FPS 1116 // NOTE: Uses default font 1117 void DrawFPS(int posX, int posY) 1118 { 1119 Color color = LIME; // Good FPS 1120 int fps = GetFPS(); 1121 1122 if ((fps < 30) && (fps >= 15)) color = ORANGE; // Warning FPS 1123 else if (fps < 15) color = RED; // Low FPS 1124 1125 DrawText(TextFormat("%2i FPS", fps), posX, posY, 20, color); 1126 } 1127 1128 // Draw text (using default font) 1129 // NOTE: fontSize work like in any drawing program but if fontSize is lower than font-base-size, then font-base-size is used 1130 // NOTE: chars spacing is proportional to fontSize 1131 void DrawText(const char *text, int posX, int posY, int fontSize, Color color) 1132 { 1133 // Check if default font has been loaded 1134 if (GetFontDefault().texture.id != 0) 1135 { 1136 Vector2 position = { (float)posX, (float)posY }; 1137 1138 int defaultFontSize = 10; // Default Font chars height in pixel 1139 if (fontSize < defaultFontSize) fontSize = defaultFontSize; 1140 int spacing = fontSize/defaultFontSize; 1141 1142 DrawTextEx(GetFontDefault(), text, position, (float)fontSize, (float)spacing, color); 1143 } 1144 } 1145 1146 // Draw text using Font 1147 // NOTE: chars spacing is NOT proportional to fontSize 1148 void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint) 1149 { 1150 if (font.texture.id == 0) font = GetFontDefault(); // Security check in case of not valid font 1151 1152 int size = TextLength(text); // Total size in bytes of the text, scanned by codepoints in loop 1153 1154 float textOffsetY = 0; // Offset between lines (on linebreak '\n') 1155 float textOffsetX = 0.0f; // Offset X to next character to draw 1156 1157 float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor 1158 1159 for (int i = 0; i < size;) 1160 { 1161 // Get next codepoint from byte string and glyph index in font 1162 int codepointByteCount = 0; 1163 int codepoint = GetCodepointNext(&text[i], &codepointByteCount); 1164 int index = GetGlyphIndex(font, codepoint); 1165 1166 if (codepoint == '\n') 1167 { 1168 // NOTE: Line spacing is a global variable, use SetTextLineSpacing() to setup 1169 textOffsetY += (fontSize + textLineSpacing); 1170 textOffsetX = 0.0f; 1171 } 1172 else 1173 { 1174 if ((codepoint != ' ') && (codepoint != '\t')) 1175 { 1176 DrawTextCodepoint(font, codepoint, (Vector2){ position.x + textOffsetX, position.y + textOffsetY }, fontSize, tint); 1177 } 1178 1179 if (font.glyphs[index].advanceX == 0) textOffsetX += ((float)font.recs[index].width*scaleFactor + spacing); 1180 else textOffsetX += ((float)font.glyphs[index].advanceX*scaleFactor + spacing); 1181 } 1182 1183 i += codepointByteCount; // Move text bytes counter to next codepoint 1184 } 1185 } 1186 1187 // Draw text using Font and pro parameters (rotation) 1188 void DrawTextPro(Font font, const char *text, Vector2 position, Vector2 origin, float rotation, float fontSize, float spacing, Color tint) 1189 { 1190 rlPushMatrix(); 1191 1192 rlTranslatef(position.x, position.y, 0.0f); 1193 rlRotatef(rotation, 0.0f, 0.0f, 1.0f); 1194 rlTranslatef(-origin.x, -origin.y, 0.0f); 1195 1196 DrawTextEx(font, text, (Vector2){ 0.0f, 0.0f }, fontSize, spacing, tint); 1197 1198 rlPopMatrix(); 1199 } 1200 1201 // Draw one character (codepoint) 1202 void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSize, Color tint) 1203 { 1204 // Character index position in sprite font 1205 // NOTE: In case a codepoint is not available in the font, index returned points to '?' 1206 int index = GetGlyphIndex(font, codepoint); 1207 float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor 1208 1209 // Character destination rectangle on screen 1210 // NOTE: We consider glyphPadding on drawing 1211 Rectangle dstRec = { position.x + font.glyphs[index].offsetX*scaleFactor - (float)font.glyphPadding*scaleFactor, 1212 position.y + font.glyphs[index].offsetY*scaleFactor - (float)font.glyphPadding*scaleFactor, 1213 (font.recs[index].width + 2.0f*font.glyphPadding)*scaleFactor, 1214 (font.recs[index].height + 2.0f*font.glyphPadding)*scaleFactor }; 1215 1216 // Character source rectangle from font texture atlas 1217 // NOTE: We consider chars padding when drawing, it could be required for outline/glow shader effects 1218 Rectangle srcRec = { font.recs[index].x - (float)font.glyphPadding, font.recs[index].y - (float)font.glyphPadding, 1219 font.recs[index].width + 2.0f*font.glyphPadding, font.recs[index].height + 2.0f*font.glyphPadding }; 1220 1221 // Draw the character texture on the screen 1222 DrawTexturePro(font.texture, srcRec, dstRec, (Vector2){ 0, 0 }, 0.0f, tint); 1223 } 1224 1225 // Draw multiple character (codepoints) 1226 void DrawTextCodepoints(Font font, const int *codepoints, int codepointCount, Vector2 position, float fontSize, float spacing, Color tint) 1227 { 1228 float textOffsetY = 0; // Offset between lines (on linebreak '\n') 1229 float textOffsetX = 0.0f; // Offset X to next character to draw 1230 1231 float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor 1232 1233 for (int i = 0; i < codepointCount; i++) 1234 { 1235 int index = GetGlyphIndex(font, codepoints[i]); 1236 1237 if (codepoints[i] == '\n') 1238 { 1239 // NOTE: Line spacing is a global variable, use SetTextLineSpacing() to setup 1240 textOffsetY += (fontSize + textLineSpacing); 1241 textOffsetX = 0.0f; 1242 } 1243 else 1244 { 1245 if ((codepoints[i] != ' ') && (codepoints[i] != '\t')) 1246 { 1247 DrawTextCodepoint(font, codepoints[i], (Vector2){ position.x + textOffsetX, position.y + textOffsetY }, fontSize, tint); 1248 } 1249 1250 if (font.glyphs[index].advanceX == 0) textOffsetX += ((float)font.recs[index].width*scaleFactor + spacing); 1251 else textOffsetX += ((float)font.glyphs[index].advanceX*scaleFactor + spacing); 1252 } 1253 } 1254 } 1255 1256 // Set vertical line spacing when drawing with line-breaks 1257 void SetTextLineSpacing(int spacing) 1258 { 1259 textLineSpacing = spacing; 1260 } 1261 1262 // Measure string width for default font 1263 int MeasureText(const char *text, int fontSize) 1264 { 1265 Vector2 textSize = { 0.0f, 0.0f }; 1266 1267 // Check if default font has been loaded 1268 if (GetFontDefault().texture.id != 0) 1269 { 1270 int defaultFontSize = 10; // Default Font chars height in pixel 1271 if (fontSize < defaultFontSize) fontSize = defaultFontSize; 1272 int spacing = fontSize/defaultFontSize; 1273 1274 textSize = MeasureTextEx(GetFontDefault(), text, (float)fontSize, (float)spacing); 1275 } 1276 1277 return (int)textSize.x; 1278 } 1279 1280 // Measure string size for Font 1281 Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing) 1282 { 1283 Vector2 textSize = { 0 }; 1284 1285 if ((isGpuReady && (font.texture.id == 0)) || 1286 (text == NULL) || (text[0] == '\0')) return textSize; // Security check 1287 1288 int size = TextLength(text); // Get size in bytes of text 1289 int tempByteCounter = 0; // Used to count longer text line num chars 1290 int byteCounter = 0; 1291 1292 float textWidth = 0.0f; 1293 float tempTextWidth = 0.0f; // Used to count longer text line width 1294 1295 float textHeight = fontSize; 1296 float scaleFactor = fontSize/(float)font.baseSize; 1297 1298 int letter = 0; // Current character 1299 int index = 0; // Index position in sprite font 1300 1301 for (int i = 0; i < size;) 1302 { 1303 byteCounter++; 1304 1305 int codepointByteCount = 0; 1306 letter = GetCodepointNext(&text[i], &codepointByteCount); 1307 index = GetGlyphIndex(font, letter); 1308 1309 i += codepointByteCount; 1310 1311 if (letter != '\n') 1312 { 1313 if (font.glyphs[index].advanceX > 0) textWidth += font.glyphs[index].advanceX; 1314 else textWidth += (font.recs[index].width + font.glyphs[index].offsetX); 1315 } 1316 else 1317 { 1318 if (tempTextWidth < textWidth) tempTextWidth = textWidth; 1319 byteCounter = 0; 1320 textWidth = 0; 1321 1322 // NOTE: Line spacing is a global variable, use SetTextLineSpacing() to setup 1323 textHeight += (fontSize + textLineSpacing); 1324 } 1325 1326 if (tempByteCounter < byteCounter) tempByteCounter = byteCounter; 1327 } 1328 1329 if (tempTextWidth < textWidth) tempTextWidth = textWidth; 1330 1331 textSize.x = tempTextWidth*scaleFactor + (float)((tempByteCounter - 1)*spacing); 1332 textSize.y = textHeight; 1333 1334 return textSize; 1335 } 1336 1337 // Get index position for a unicode character on font 1338 // NOTE: If codepoint is not found in the font it fallbacks to '?' 1339 int GetGlyphIndex(Font font, int codepoint) 1340 { 1341 int index = 0; 1342 1343 #define SUPPORT_UNORDERED_CHARSET 1344 #if defined(SUPPORT_UNORDERED_CHARSET) 1345 int fallbackIndex = 0; // Get index of fallback glyph '?' 1346 1347 // Look for character index in the unordered charset 1348 for (int i = 0; i < font.glyphCount; i++) 1349 { 1350 if (font.glyphs[i].value == 63) fallbackIndex = i; 1351 1352 if (font.glyphs[i].value == codepoint) 1353 { 1354 index = i; 1355 break; 1356 } 1357 } 1358 1359 if ((index == 0) && (font.glyphs[0].value != codepoint)) index = fallbackIndex; 1360 #else 1361 index = codepoint - 32; 1362 #endif 1363 1364 return index; 1365 } 1366 1367 // Get glyph font info data for a codepoint (unicode character) 1368 // NOTE: If codepoint is not found in the font it fallbacks to '?' 1369 GlyphInfo GetGlyphInfo(Font font, int codepoint) 1370 { 1371 GlyphInfo info = { 0 }; 1372 1373 info = font.glyphs[GetGlyphIndex(font, codepoint)]; 1374 1375 return info; 1376 } 1377 1378 // Get glyph rectangle in font atlas for a codepoint (unicode character) 1379 // NOTE: If codepoint is not found in the font it fallbacks to '?' 1380 Rectangle GetGlyphAtlasRec(Font font, int codepoint) 1381 { 1382 Rectangle rec = { 0 }; 1383 1384 rec = font.recs[GetGlyphIndex(font, codepoint)]; 1385 1386 return rec; 1387 } 1388 1389 //---------------------------------------------------------------------------------- 1390 // Text strings management functions 1391 //---------------------------------------------------------------------------------- 1392 // Get text length in bytes, check for \0 character 1393 unsigned int TextLength(const char *text) 1394 { 1395 unsigned int length = 0; 1396 1397 if (text != NULL) 1398 { 1399 // NOTE: Alternative: use strlen(text) 1400 1401 while (*text++) length++; 1402 } 1403 1404 return length; 1405 } 1406 1407 // Formatting of text with variables to 'embed' 1408 // WARNING: String returned will expire after this function is called MAX_TEXTFORMAT_BUFFERS times 1409 const char *TextFormat(const char *text, ...) 1410 { 1411 #ifndef MAX_TEXTFORMAT_BUFFERS 1412 #define MAX_TEXTFORMAT_BUFFERS 4 // Maximum number of static buffers for text formatting 1413 #endif 1414 1415 // We create an array of buffers so strings don't expire until MAX_TEXTFORMAT_BUFFERS invocations 1416 static char buffers[MAX_TEXTFORMAT_BUFFERS][MAX_TEXT_BUFFER_LENGTH] = { 0 }; 1417 static int index = 0; 1418 1419 char *currentBuffer = buffers[index]; 1420 memset(currentBuffer, 0, MAX_TEXT_BUFFER_LENGTH); // Clear buffer before using 1421 1422 va_list args; 1423 va_start(args, text); 1424 int requiredByteCount = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args); 1425 va_end(args); 1426 1427 // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured 1428 if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH) 1429 { 1430 // Inserting "..." at the end of the string to mark as truncated 1431 char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 4; // Adding 4 bytes = "...\0" 1432 sprintf(truncBuffer, "..."); 1433 } 1434 1435 index += 1; // Move to next buffer for next function call 1436 if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0; 1437 1438 return currentBuffer; 1439 } 1440 1441 // Get integer value from text 1442 // NOTE: This function replaces atoi() [stdlib.h] 1443 int TextToInteger(const char *text) 1444 { 1445 int value = 0; 1446 int sign = 1; 1447 1448 if ((text[0] == '+') || (text[0] == '-')) 1449 { 1450 if (text[0] == '-') sign = -1; 1451 text++; 1452 } 1453 1454 for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10 + (int)(text[i] - '0'); 1455 1456 return value*sign; 1457 } 1458 1459 // Get float value from text 1460 // NOTE: This function replaces atof() [stdlib.h] 1461 // WARNING: Only '.' character is understood as decimal point 1462 float TextToFloat(const char *text) 1463 { 1464 float value = 0.0f; 1465 float sign = 1.0f; 1466 1467 if ((text[0] == '+') || (text[0] == '-')) 1468 { 1469 if (text[0] == '-') sign = -1.0f; 1470 text++; 1471 } 1472 1473 int i = 0; 1474 for (; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10.0f + (float)(text[i] - '0'); 1475 1476 if (text[i++] == '.') 1477 { 1478 float divisor = 10.0f; 1479 for (; ((text[i] >= '0') && (text[i] <= '9')); i++) 1480 { 1481 value += ((float)(text[i] - '0'))/divisor; 1482 divisor = divisor*10.0f; 1483 } 1484 } 1485 1486 return value*sign; 1487 } 1488 1489 #if defined(SUPPORT_TEXT_MANIPULATION) 1490 // Copy one string to another, returns bytes copied 1491 int TextCopy(char *dst, const char *src) 1492 { 1493 int bytes = 0; 1494 1495 if ((src != NULL) && (dst != NULL)) 1496 { 1497 // NOTE: Alternative: use strcpy(dst, src) 1498 1499 while (*src != '\0') 1500 { 1501 *dst = *src; 1502 dst++; 1503 src++; 1504 1505 bytes++; 1506 } 1507 1508 *dst = '\0'; 1509 } 1510 1511 return bytes; 1512 } 1513 1514 // Check if two text string are equal 1515 // REQUIRES: strcmp() 1516 bool TextIsEqual(const char *text1, const char *text2) 1517 { 1518 bool result = false; 1519 1520 if ((text1 != NULL) && (text2 != NULL)) 1521 { 1522 if (strcmp(text1, text2) == 0) result = true; 1523 } 1524 1525 return result; 1526 } 1527 1528 // Get a piece of a text string 1529 const char *TextSubtext(const char *text, int position, int length) 1530 { 1531 static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; 1532 memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); 1533 1534 int textLength = TextLength(text); 1535 1536 if (position >= textLength) 1537 { 1538 position = textLength - 1; 1539 length = 0; 1540 } 1541 1542 if (length >= textLength) length = textLength; 1543 1544 // NOTE: Alternative: memcpy(buffer, text + position, length) 1545 1546 for (int c = 0 ; c < length ; c++) 1547 { 1548 *(buffer + c) = *(text + position); 1549 text++; 1550 } 1551 1552 *(buffer + length) = '\0'; 1553 1554 return buffer; 1555 } 1556 1557 // Replace text string 1558 // REQUIRES: strlen(), strstr(), strncpy(), strcpy() 1559 // WARNING: Allocated memory must be manually freed 1560 char *TextReplace(const char *text, const char *replace, const char *by) 1561 { 1562 // Sanity checks and initialization 1563 if (!text || !replace || !by) return NULL; 1564 1565 char *result = NULL; 1566 1567 char *insertPoint = NULL; // Next insert point 1568 char *temp = NULL; // Temp pointer 1569 int replaceLen = 0; // Replace string length of (the string to remove) 1570 int byLen = 0; // Replacement length (the string to replace by) 1571 int lastReplacePos = 0; // Distance between replace and end of last replace 1572 int count = 0; // Number of replacements 1573 1574 replaceLen = TextLength(replace); 1575 if (replaceLen == 0) return NULL; // Empty replace causes infinite loop during count 1576 1577 byLen = TextLength(by); 1578 1579 // Count the number of replacements needed 1580 insertPoint = (char *)text; 1581 for (count = 0; (temp = strstr(insertPoint, replace)); count++) insertPoint = temp + replaceLen; 1582 1583 // Allocate returning string and point temp to it 1584 temp = result = (char *)RL_MALLOC(TextLength(text) + (byLen - replaceLen)*count + 1); 1585 1586 if (!result) return NULL; // Memory could not be allocated 1587 1588 // First time through the loop, all the variable are set correctly from here on, 1589 // - 'temp' points to the end of the result string 1590 // - 'insertPoint' points to the next occurrence of replace in text 1591 // - 'text' points to the remainder of text after "end of replace" 1592 while (count--) 1593 { 1594 insertPoint = strstr(text, replace); 1595 lastReplacePos = (int)(insertPoint - text); 1596 temp = strncpy(temp, text, lastReplacePos) + lastReplacePos; 1597 temp = strcpy(temp, by) + byLen; 1598 text += lastReplacePos + replaceLen; // Move to next "end of replace" 1599 } 1600 1601 // Copy remaind text part after replacement to result (pointed by moving temp) 1602 strcpy(temp, text); 1603 1604 return result; 1605 } 1606 1607 // Insert text in a specific position, moves all text forward 1608 // WARNING: Allocated memory must be manually freed 1609 char *TextInsert(const char *text, const char *insert, int position) 1610 { 1611 int textLen = TextLength(text); 1612 int insertLen = TextLength(insert); 1613 1614 char *result = (char *)RL_MALLOC(textLen + insertLen + 1); 1615 1616 for (int i = 0; i < position; i++) result[i] = text[i]; 1617 for (int i = position; i < insertLen + position; i++) result[i] = insert[i]; 1618 for (int i = (insertLen + position); i < (textLen + insertLen); i++) result[i] = text[i]; 1619 1620 result[textLen + insertLen] = '\0'; // Make sure text string is valid! 1621 1622 return result; 1623 } 1624 1625 // Join text strings with delimiter 1626 // REQUIRES: memset(), memcpy() 1627 const char *TextJoin(const char **textList, int count, const char *delimiter) 1628 { 1629 static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; 1630 memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); 1631 char *textPtr = buffer; 1632 1633 int totalLength = 0; 1634 int delimiterLen = TextLength(delimiter); 1635 1636 for (int i = 0; i < count; i++) 1637 { 1638 int textLength = TextLength(textList[i]); 1639 1640 // Make sure joined text could fit inside MAX_TEXT_BUFFER_LENGTH 1641 if ((totalLength + textLength) < MAX_TEXT_BUFFER_LENGTH) 1642 { 1643 memcpy(textPtr, textList[i], textLength); 1644 totalLength += textLength; 1645 textPtr += textLength; 1646 1647 if ((delimiterLen > 0) && (i < (count - 1))) 1648 { 1649 memcpy(textPtr, delimiter, delimiterLen); 1650 totalLength += delimiterLen; 1651 textPtr += delimiterLen; 1652 } 1653 } 1654 } 1655 1656 return buffer; 1657 } 1658 1659 // Split string into multiple strings 1660 // REQUIRES: memset() 1661 const char **TextSplit(const char *text, char delimiter, int *count) 1662 { 1663 // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter) 1664 // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated, 1665 // all used memory is static... it has some limitations: 1666 // 1. Maximum number of possible split strings is set by MAX_TEXTSPLIT_COUNT 1667 // 2. Maximum size of text to split is MAX_TEXT_BUFFER_LENGTH 1668 1669 static const char *result[MAX_TEXTSPLIT_COUNT] = { NULL }; 1670 static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; 1671 memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); 1672 1673 result[0] = buffer; 1674 int counter = 0; 1675 1676 if (text != NULL) 1677 { 1678 counter = 1; 1679 1680 // Count how many substrings we have on text and point to every one 1681 for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++) 1682 { 1683 buffer[i] = text[i]; 1684 if (buffer[i] == '\0') break; 1685 else if (buffer[i] == delimiter) 1686 { 1687 buffer[i] = '\0'; // Set an end of string at this point 1688 result[counter] = buffer + i + 1; 1689 counter++; 1690 1691 if (counter == MAX_TEXTSPLIT_COUNT) break; 1692 } 1693 } 1694 } 1695 1696 *count = counter; 1697 return result; 1698 } 1699 1700 // Append text at specific position and move cursor 1701 // WARNING: It's up to the user to make sure appended text does not overflow the buffer! 1702 // REQUIRES: strcpy() 1703 void TextAppend(char *text, const char *append, int *position) 1704 { 1705 strcpy(text + *position, append); 1706 *position += TextLength(append); 1707 } 1708 1709 // Find first text occurrence within a string 1710 // REQUIRES: strstr() 1711 int TextFindIndex(const char *text, const char *find) 1712 { 1713 int position = -1; 1714 1715 char *ptr = strstr(text, find); 1716 1717 if (ptr != NULL) position = (int)(ptr - text); 1718 1719 return position; 1720 } 1721 1722 // Get upper case version of provided string 1723 // WARNING: Limited functionality, only basic characters set 1724 // TODO: Support UTF-8 diacritics to upper-case, check codepoints 1725 const char *TextToUpper(const char *text) 1726 { 1727 static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; 1728 memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); 1729 1730 if (text != NULL) 1731 { 1732 for (int i = 0; (i < MAX_TEXT_BUFFER_LENGTH - 1) && (text[i] != '\0'); i++) 1733 { 1734 if ((text[i] >= 'a') && (text[i] <= 'z')) buffer[i] = text[i] - 32; 1735 else buffer[i] = text[i]; 1736 } 1737 } 1738 1739 return buffer; 1740 } 1741 1742 // Get lower case version of provided string 1743 // WARNING: Limited functionality, only basic characters set 1744 const char *TextToLower(const char *text) 1745 { 1746 static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; 1747 memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); 1748 1749 if (text != NULL) 1750 { 1751 for (int i = 0; (i < MAX_TEXT_BUFFER_LENGTH - 1) && (text[i] != '\0'); i++) 1752 { 1753 if ((text[i] >= 'A') && (text[i] <= 'Z')) buffer[i] = text[i] + 32; 1754 else buffer[i] = text[i]; 1755 } 1756 } 1757 1758 return buffer; 1759 } 1760 1761 // Get Pascal case notation version of provided string 1762 // WARNING: Limited functionality, only basic characters set 1763 const char *TextToPascal(const char *text) 1764 { 1765 static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; 1766 memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); 1767 1768 if (text != NULL) 1769 { 1770 // Upper case first character 1771 if ((text[0] >= 'a') && (text[0] <= 'z')) buffer[0] = text[0] - 32; 1772 else buffer[0] = text[0]; 1773 1774 // Check for next separator to upper case another character 1775 for (int i = 1, j = 1; (i < MAX_TEXT_BUFFER_LENGTH - 1) && (text[j] != '\0'); i++, j++) 1776 { 1777 if (text[j] != '_') buffer[i] = text[j]; 1778 else 1779 { 1780 j++; 1781 if ((text[j] >= 'a') && (text[j] <= 'z')) buffer[i] = text[j] - 32; 1782 } 1783 } 1784 } 1785 1786 return buffer; 1787 } 1788 1789 // Get snake case notation version of provided string 1790 // WARNING: Limited functionality, only basic characters set 1791 const char *TextToSnake(const char *text) 1792 { 1793 static char buffer[MAX_TEXT_BUFFER_LENGTH] = {0}; 1794 memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); 1795 1796 if (text != NULL) 1797 { 1798 // Check for next separator to upper case another character 1799 for (int i = 0, j = 0; (i < MAX_TEXT_BUFFER_LENGTH - 1) && (text[j] != '\0'); i++, j++) 1800 { 1801 if ((text[j] >= 'A') && (text[j] <= 'Z')) 1802 { 1803 if (i >= 1) 1804 { 1805 buffer[i] = '_'; 1806 i++; 1807 } 1808 buffer[i] = text[j] + 32; 1809 } 1810 else buffer[i] = text[j]; 1811 } 1812 } 1813 1814 return buffer; 1815 } 1816 1817 // Get Camel case notation version of provided string 1818 // WARNING: Limited functionality, only basic characters set 1819 const char *TextToCamel(const char *text) 1820 { 1821 static char buffer[MAX_TEXT_BUFFER_LENGTH] = {0}; 1822 memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); 1823 1824 if (text != NULL) 1825 { 1826 // Lower case first character 1827 if ((text[0] >= 'A') && (text[0] <= 'Z')) buffer[0] = text[0] + 32; 1828 else buffer[0] = text[0]; 1829 1830 // Check for next separator to upper case another character 1831 for (int i = 1, j = 1; (i < MAX_TEXT_BUFFER_LENGTH - 1) && (text[j] != '\0'); i++, j++) 1832 { 1833 if (text[j] != '_') buffer[i] = text[j]; 1834 else 1835 { 1836 j++; 1837 if ((text[j] >= 'a') && (text[j] <= 'z')) buffer[i] = text[j] - 32; 1838 } 1839 } 1840 } 1841 1842 return buffer; 1843 } 1844 1845 // Encode text codepoint into UTF-8 text 1846 // REQUIRES: memcpy() 1847 // WARNING: Allocated memory must be manually freed 1848 char *LoadUTF8(const int *codepoints, int length) 1849 { 1850 // We allocate enough memory to fit all possible codepoints 1851 // NOTE: 5 bytes for every codepoint should be enough 1852 char *text = (char *)RL_CALLOC(length*5, 1); 1853 const char *utf8 = NULL; 1854 int size = 0; 1855 1856 for (int i = 0, bytes = 0; i < length; i++) 1857 { 1858 utf8 = CodepointToUTF8(codepoints[i], &bytes); 1859 memcpy(text + size, utf8, bytes); 1860 size += bytes; 1861 } 1862 1863 // Resize memory to text length + string NULL terminator 1864 void *ptr = RL_REALLOC(text, size + 1); 1865 1866 if (ptr != NULL) text = (char *)ptr; 1867 1868 return text; 1869 } 1870 1871 // Unload UTF-8 text encoded from codepoints array 1872 void UnloadUTF8(char *text) 1873 { 1874 RL_FREE(text); 1875 } 1876 1877 // Load all codepoints from a UTF-8 text string, codepoints count returned by parameter 1878 int *LoadCodepoints(const char *text, int *count) 1879 { 1880 int textLength = TextLength(text); 1881 1882 int codepointSize = 0; 1883 int codepointCount = 0; 1884 1885 // Allocate a big enough buffer to store as many codepoints as text bytes 1886 int *codepoints = (int *)RL_CALLOC(textLength, sizeof(int)); 1887 1888 for (int i = 0; i < textLength; codepointCount++) 1889 { 1890 codepoints[codepointCount] = GetCodepointNext(text + i, &codepointSize); 1891 i += codepointSize; 1892 } 1893 1894 // Re-allocate buffer to the actual number of codepoints loaded 1895 codepoints = (int *)RL_REALLOC(codepoints, codepointCount*sizeof(int)); 1896 1897 *count = codepointCount; 1898 1899 return codepoints; 1900 } 1901 1902 // Unload codepoints data from memory 1903 void UnloadCodepoints(int *codepoints) 1904 { 1905 RL_FREE(codepoints); 1906 } 1907 1908 // Get total number of characters(codepoints) in a UTF-8 encoded text, until '\0' is found 1909 // NOTE: If an invalid UTF-8 sequence is encountered a '?'(0x3f) codepoint is counted instead 1910 int GetCodepointCount(const char *text) 1911 { 1912 unsigned int length = 0; 1913 char *ptr = (char *)&text[0]; 1914 1915 while (*ptr != '\0') 1916 { 1917 int next = 0; 1918 GetCodepointNext(ptr, &next); 1919 1920 ptr += next; 1921 1922 length++; 1923 } 1924 1925 return length; 1926 } 1927 1928 // Encode codepoint into utf8 text (char array length returned as parameter) 1929 // NOTE: It uses a static array to store UTF-8 bytes 1930 const char *CodepointToUTF8(int codepoint, int *utf8Size) 1931 { 1932 static char utf8[6] = { 0 }; 1933 memset(utf8, 0, 6); // Clear static array 1934 int size = 0; // Byte size of codepoint 1935 1936 if (codepoint <= 0x7f) 1937 { 1938 utf8[0] = (char)codepoint; 1939 size = 1; 1940 } 1941 else if (codepoint <= 0x7ff) 1942 { 1943 utf8[0] = (char)(((codepoint >> 6) & 0x1f) | 0xc0); 1944 utf8[1] = (char)((codepoint & 0x3f) | 0x80); 1945 size = 2; 1946 } 1947 else if (codepoint <= 0xffff) 1948 { 1949 utf8[0] = (char)(((codepoint >> 12) & 0x0f) | 0xe0); 1950 utf8[1] = (char)(((codepoint >> 6) & 0x3f) | 0x80); 1951 utf8[2] = (char)((codepoint & 0x3f) | 0x80); 1952 size = 3; 1953 } 1954 else if (codepoint <= 0x10ffff) 1955 { 1956 utf8[0] = (char)(((codepoint >> 18) & 0x07) | 0xf0); 1957 utf8[1] = (char)(((codepoint >> 12) & 0x3f) | 0x80); 1958 utf8[2] = (char)(((codepoint >> 6) & 0x3f) | 0x80); 1959 utf8[3] = (char)((codepoint & 0x3f) | 0x80); 1960 size = 4; 1961 } 1962 1963 *utf8Size = size; 1964 1965 return utf8; 1966 } 1967 #endif // SUPPORT_TEXT_MANIPULATION 1968 1969 // Get next codepoint in a UTF-8 encoded text, scanning until '\0' is found 1970 // When an invalid UTF-8 byte is encountered we exit as soon as possible and a '?'(0x3f) codepoint is returned 1971 // Total number of bytes processed are returned as a parameter 1972 // NOTE: The standard says U+FFFD should be returned in case of errors 1973 // but that character is not supported by the default font in raylib 1974 int GetCodepoint(const char *text, int *codepointSize) 1975 { 1976 /* 1977 UTF-8 specs from https://www.ietf.org/rfc/rfc3629.txt 1978 1979 Char. number range | UTF-8 octet sequence 1980 (hexadecimal) | (binary) 1981 --------------------+--------------------------------------------- 1982 0000 0000-0000 007F | 0xxxxxxx 1983 0000 0080-0000 07FF | 110xxxxx 10xxxxxx 1984 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx 1985 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 1986 */ 1987 // NOTE: on decode errors we return as soon as possible 1988 1989 int codepoint = 0x3f; // Codepoint (defaults to '?') 1990 int octet = (unsigned char)(text[0]); // The first UTF8 octet 1991 *codepointSize = 1; 1992 1993 if (octet <= 0x7f) 1994 { 1995 // Only one octet (ASCII range x00-7F) 1996 codepoint = text[0]; 1997 } 1998 else if ((octet & 0xe0) == 0xc0) 1999 { 2000 // Two octets 2001 2002 // [0]xC2-DF [1]UTF8-tail(x80-BF) 2003 unsigned char octet1 = text[1]; 2004 2005 if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *codepointSize = 2; return codepoint; } // Unexpected sequence 2006 2007 if ((octet >= 0xc2) && (octet <= 0xdf)) 2008 { 2009 codepoint = ((octet & 0x1f) << 6) | (octet1 & 0x3f); 2010 *codepointSize = 2; 2011 } 2012 } 2013 else if ((octet & 0xf0) == 0xe0) 2014 { 2015 // Three octets 2016 unsigned char octet1 = text[1]; 2017 unsigned char octet2 = '\0'; 2018 2019 if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *codepointSize = 2; return codepoint; } // Unexpected sequence 2020 2021 octet2 = text[2]; 2022 2023 if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *codepointSize = 3; return codepoint; } // Unexpected sequence 2024 2025 // [0]xE0 [1]xA0-BF [2]UTF8-tail(x80-BF) 2026 // [0]xE1-EC [1]UTF8-tail [2]UTF8-tail(x80-BF) 2027 // [0]xED [1]x80-9F [2]UTF8-tail(x80-BF) 2028 // [0]xEE-EF [1]UTF8-tail [2]UTF8-tail(x80-BF) 2029 2030 if (((octet == 0xe0) && !((octet1 >= 0xa0) && (octet1 <= 0xbf))) || 2031 ((octet == 0xed) && !((octet1 >= 0x80) && (octet1 <= 0x9f)))) { *codepointSize = 2; return codepoint; } 2032 2033 if ((octet >= 0xe0) && (octet <= 0xef)) 2034 { 2035 codepoint = ((octet & 0xf) << 12) | ((octet1 & 0x3f) << 6) | (octet2 & 0x3f); 2036 *codepointSize = 3; 2037 } 2038 } 2039 else if ((octet & 0xf8) == 0xf0) 2040 { 2041 // Four octets 2042 if (octet > 0xf4) return codepoint; 2043 2044 unsigned char octet1 = text[1]; 2045 unsigned char octet2 = '\0'; 2046 unsigned char octet3 = '\0'; 2047 2048 if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *codepointSize = 2; return codepoint; } // Unexpected sequence 2049 2050 octet2 = text[2]; 2051 2052 if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *codepointSize = 3; return codepoint; } // Unexpected sequence 2053 2054 octet3 = text[3]; 2055 2056 if ((octet3 == '\0') || ((octet3 >> 6) != 2)) { *codepointSize = 4; return codepoint; } // Unexpected sequence 2057 2058 // [0]xF0 [1]x90-BF [2]UTF8-tail [3]UTF8-tail 2059 // [0]xF1-F3 [1]UTF8-tail [2]UTF8-tail [3]UTF8-tail 2060 // [0]xF4 [1]x80-8F [2]UTF8-tail [3]UTF8-tail 2061 2062 if (((octet == 0xf0) && !((octet1 >= 0x90) && (octet1 <= 0xbf))) || 2063 ((octet == 0xf4) && !((octet1 >= 0x80) && (octet1 <= 0x8f)))) { *codepointSize = 2; return codepoint; } // Unexpected sequence 2064 2065 if (octet >= 0xf0) 2066 { 2067 codepoint = ((octet & 0x7) << 18) | ((octet1 & 0x3f) << 12) | ((octet2 & 0x3f) << 6) | (octet3 & 0x3f); 2068 *codepointSize = 4; 2069 } 2070 } 2071 2072 if (codepoint > 0x10ffff) codepoint = 0x3f; // Codepoints after U+10ffff are invalid 2073 2074 return codepoint; 2075 } 2076 2077 // Get next codepoint in a byte sequence and bytes processed 2078 int GetCodepointNext(const char *text, int *codepointSize) 2079 { 2080 const char *ptr = text; 2081 int codepoint = 0x3f; // Codepoint (defaults to '?') 2082 *codepointSize = 1; 2083 2084 // Get current codepoint and bytes processed 2085 if (0xf0 == (0xf8 & ptr[0])) 2086 { 2087 // 4 byte UTF-8 codepoint 2088 if (((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80) || ((ptr[3] & 0xC0) ^ 0x80)) { return codepoint; } // 10xxxxxx checks 2089 codepoint = ((0x07 & ptr[0]) << 18) | ((0x3f & ptr[1]) << 12) | ((0x3f & ptr[2]) << 6) | (0x3f & ptr[3]); 2090 *codepointSize = 4; 2091 } 2092 else if (0xe0 == (0xf0 & ptr[0])) 2093 { 2094 // 3 byte UTF-8 codepoint */ 2095 if (((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80)) { return codepoint; } // 10xxxxxx checks 2096 codepoint = ((0x0f & ptr[0]) << 12) | ((0x3f & ptr[1]) << 6) | (0x3f & ptr[2]); 2097 *codepointSize = 3; 2098 } 2099 else if (0xc0 == (0xe0 & ptr[0])) 2100 { 2101 // 2 byte UTF-8 codepoint 2102 if ((ptr[1] & 0xC0) ^ 0x80) { return codepoint; } // 10xxxxxx checks 2103 codepoint = ((0x1f & ptr[0]) << 6) | (0x3f & ptr[1]); 2104 *codepointSize = 2; 2105 } 2106 else if (0x00 == (0x80 & ptr[0])) 2107 { 2108 // 1 byte UTF-8 codepoint 2109 codepoint = ptr[0]; 2110 *codepointSize = 1; 2111 } 2112 2113 return codepoint; 2114 } 2115 2116 // Get previous codepoint in a byte sequence and bytes processed 2117 int GetCodepointPrevious(const char *text, int *codepointSize) 2118 { 2119 const char *ptr = text; 2120 int codepoint = 0x3f; // Codepoint (defaults to '?') 2121 int cpSize = 0; 2122 *codepointSize = 0; 2123 2124 // Move to previous codepoint 2125 do ptr--; 2126 while (((0x80 & ptr[0]) != 0) && ((0xc0 & ptr[0]) == 0x80)); 2127 2128 codepoint = GetCodepointNext(ptr, &cpSize); 2129 2130 if (codepoint != 0) *codepointSize = cpSize; 2131 2132 return codepoint; 2133 } 2134 2135 //---------------------------------------------------------------------------------- 2136 // Module specific Functions Definition 2137 //---------------------------------------------------------------------------------- 2138 #if defined(SUPPORT_FILEFORMAT_FNT) || defined(SUPPORT_FILEFORMAT_BDF) 2139 // Read a line from memory 2140 // REQUIRES: memcpy() 2141 // NOTE: Returns the number of bytes read 2142 static int GetLine(const char *origin, char *buffer, int maxLength) 2143 { 2144 int count = 0; 2145 for (; count < maxLength - 1; count++) if (origin[count] == '\n') break; 2146 memcpy(buffer, origin, count); 2147 buffer[count] = '\0'; 2148 return count; 2149 } 2150 #endif 2151 2152 #if defined(SUPPORT_FILEFORMAT_FNT) 2153 // Load a BMFont file (AngelCode font file) 2154 // REQUIRES: strstr(), sscanf(), strrchr(), memcpy() 2155 static Font LoadBMFont(const char *fileName) 2156 { 2157 #define MAX_BUFFER_SIZE 256 2158 #define MAX_FONT_IMAGE_PAGES 8 2159 2160 Font font = { 0 }; 2161 2162 char buffer[MAX_BUFFER_SIZE] = { 0 }; 2163 char *searchPoint = NULL; 2164 2165 int fontSize = 0; 2166 int glyphCount = 0; 2167 2168 int imWidth = 0; 2169 int imHeight = 0; 2170 int pageCount = 1; 2171 char imFileName[MAX_FONT_IMAGE_PAGES][129] = { 0 }; 2172 2173 int base = 0; // Useless data 2174 int readBytes = 0; // Data bytes read 2175 int readVars = 0; // Variables filled by sscanf() 2176 2177 char *fileText = LoadFileText(fileName); 2178 2179 if (fileText == NULL) return font; 2180 2181 char *fileTextPtr = fileText; 2182 2183 // NOTE: We skip first line, it contains no useful information 2184 readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); 2185 fileTextPtr += (readBytes + 1); 2186 2187 // Read line data 2188 readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); 2189 searchPoint = strstr(buffer, "lineHeight"); 2190 readVars = sscanf(searchPoint, "lineHeight=%i base=%i scaleW=%i scaleH=%i pages=%i", &fontSize, &base, &imWidth, &imHeight, &pageCount); 2191 fileTextPtr += (readBytes + 1); 2192 2193 if (readVars < 4) { UnloadFileText(fileText); return font; } // Some data not available, file malformed 2194 2195 if (pageCount > MAX_FONT_IMAGE_PAGES) 2196 { 2197 TRACELOG(LOG_WARNING, "FONT: [%s] Font defines more pages than supported: %i/%i", fileName, pageCount, MAX_FONT_IMAGE_PAGES); 2198 pageCount = MAX_FONT_IMAGE_PAGES; 2199 } 2200 2201 for (int i = 0; i < pageCount; i++) 2202 { 2203 readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); 2204 searchPoint = strstr(buffer, "file"); 2205 readVars = sscanf(searchPoint, "file=\"%128[^\"]\"", imFileName[i]); 2206 fileTextPtr += (readBytes + 1); 2207 2208 if (readVars < 1) { UnloadFileText(fileText); return font; } // No fileName read 2209 } 2210 2211 readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); 2212 searchPoint = strstr(buffer, "count"); 2213 readVars = sscanf(searchPoint, "count=%i", &glyphCount); 2214 fileTextPtr += (readBytes + 1); 2215 2216 if (readVars < 1) { UnloadFileText(fileText); return font; } // No glyphCount read 2217 2218 // Load all required images for further compose 2219 Image *imFonts = (Image *)RL_CALLOC(pageCount, sizeof(Image)); // Font atlases, multiple images 2220 2221 for (int i = 0; i < pageCount; i++) 2222 { 2223 imFonts[i] = LoadImage(TextFormat("%s/%s", GetDirectoryPath(fileName), imFileName[i])); 2224 2225 if (imFonts[i].format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) 2226 { 2227 // Convert image to GRAYSCALE + ALPHA, using the mask as the alpha channel 2228 Image imFontAlpha = { 2229 .data = RL_CALLOC(imFonts[i].width*imFonts[i].height, 2), 2230 .width = imFonts[i].width, 2231 .height = imFonts[i].height, 2232 .mipmaps = 1, 2233 .format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA 2234 }; 2235 2236 for (int p = 0, pi = 0; p < (imFonts[i].width*imFonts[i].height*2); p += 2, pi++) 2237 { 2238 ((unsigned char *)(imFontAlpha.data))[p] = 0xff; 2239 ((unsigned char *)(imFontAlpha.data))[p + 1] = ((unsigned char *)imFonts[i].data)[pi]; 2240 } 2241 2242 UnloadImage(imFonts[i]); 2243 imFonts[i] = imFontAlpha; 2244 } 2245 } 2246 2247 Image fullFont = imFonts[0]; 2248 for (int i = 1; i < pageCount; i++) UnloadImage(imFonts[i]); 2249 2250 // If multiple atlas, then merge atlas 2251 // NOTE: WARNING: This process could be really slow! 2252 if (pageCount > 1) 2253 { 2254 // Resize font atlas to draw additional images 2255 ImageResizeCanvas(&fullFont, imWidth, imHeight*pageCount, 0, 0, BLACK); 2256 2257 for (int i = 1; i < pageCount; i++) 2258 { 2259 Rectangle srcRec = { 0.0f, 0.0f, (float)imWidth, (float)imHeight }; 2260 Rectangle destRec = { 0.0f, (float)imHeight*(float)i, (float)imWidth, (float)imHeight }; 2261 ImageDraw(&fullFont, imFonts[i], srcRec, destRec, WHITE); 2262 } 2263 } 2264 2265 RL_FREE(imFonts); 2266 2267 if (isGpuReady) font.texture = LoadTextureFromImage(fullFont); 2268 2269 // Fill font characters info data 2270 font.baseSize = fontSize; 2271 font.glyphCount = glyphCount; 2272 font.glyphPadding = 0; 2273 font.glyphs = (GlyphInfo *)RL_MALLOC(glyphCount*sizeof(GlyphInfo)); 2274 font.recs = (Rectangle *)RL_MALLOC(glyphCount*sizeof(Rectangle)); 2275 2276 int charId, charX, charY, charWidth, charHeight, charOffsetX, charOffsetY, charAdvanceX, pageID; 2277 2278 for (int i = 0; i < glyphCount; i++) 2279 { 2280 readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); 2281 readVars = sscanf(buffer, "char id=%i x=%i y=%i width=%i height=%i xoffset=%i yoffset=%i xadvance=%i page=%i", 2282 &charId, &charX, &charY, &charWidth, &charHeight, &charOffsetX, &charOffsetY, &charAdvanceX, &pageID); 2283 fileTextPtr += (readBytes + 1); 2284 2285 if (readVars == 9) // Make sure all char data has been properly read 2286 { 2287 // Get character rectangle in the font atlas texture 2288 font.recs[i] = (Rectangle){ (float)charX, (float)charY + (float)imHeight*pageID, (float)charWidth, (float)charHeight }; 2289 2290 // Save data properly in sprite font 2291 font.glyphs[i].value = charId; 2292 font.glyphs[i].offsetX = charOffsetX; 2293 font.glyphs[i].offsetY = charOffsetY; 2294 font.glyphs[i].advanceX = charAdvanceX; 2295 2296 // Fill character image data from full font data 2297 font.glyphs[i].image = ImageFromImage(fullFont, font.recs[i]); 2298 } 2299 else 2300 { 2301 font.glyphs[i].image = GenImageColor((int)font.recs[i].width, (int)font.recs[i].height, BLACK); 2302 TRACELOG(LOG_WARNING, "FONT: [%s] Some characters data not correctly provided", fileName); 2303 } 2304 } 2305 2306 UnloadImage(fullFont); 2307 UnloadFileText(fileText); 2308 2309 if (isGpuReady && (font.texture.id == 0)) 2310 { 2311 UnloadFont(font); 2312 font = GetFontDefault(); 2313 TRACELOG(LOG_WARNING, "FONT: [%s] Failed to load texture, reverted to default font", fileName); 2314 } 2315 else TRACELOG(LOG_INFO, "FONT: [%s] Font loaded successfully (%i glyphs)", fileName, font.glyphCount); 2316 2317 return font; 2318 } 2319 2320 #endif 2321 2322 #if defined(SUPPORT_FILEFORMAT_BDF) 2323 2324 // Convert hexadecimal to decimal (single digit) 2325 static unsigned char HexToInt(char hex) 2326 { 2327 if (hex >= '0' && hex <= '9') return hex - '0'; 2328 else if (hex >= 'a' && hex <= 'f') return hex - 'a' + 10; 2329 else if (hex >= 'A' && hex <= 'F') return hex - 'A' + 10; 2330 else return 0; 2331 } 2332 2333 // Load font data for further use 2334 // NOTE: Requires BDF font memory data 2335 static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, int *codepoints, int codepointCount, int *outFontSize) 2336 { 2337 #define MAX_BUFFER_SIZE 256 2338 2339 char buffer[MAX_BUFFER_SIZE] = { 0 }; 2340 2341 GlyphInfo *glyphs = NULL; 2342 bool genFontChars = false; 2343 2344 int totalReadBytes = 0; // Data bytes read (total) 2345 int readBytes = 0; // Data bytes read (line) 2346 int readVars = 0; // Variables filled by sscanf() 2347 2348 const char *fileText = (const char *)fileData; 2349 const char *fileTextPtr = fileText; 2350 2351 bool fontMalformed = false; // Is the font malformed 2352 bool fontStarted = false; // Has font started (STARTFONT) 2353 int fontBBw = 0; // Font base character bounding box width 2354 int fontBBh = 0; // Font base character bounding box height 2355 int fontBBxoff0 = 0; // Font base character bounding box X0 offset 2356 int fontBByoff0 = 0; // Font base character bounding box Y0 offset 2357 int fontAscent = 0; // Font ascent 2358 2359 bool charStarted = false; // Has character started (STARTCHAR) 2360 bool charBitmapStarted = false; // Has bitmap data started (BITMAP) 2361 int charBitmapNextRow = 0; // Y position for the next row of bitmap data 2362 int charEncoding = -1; // The unicode value of the character (-1 if not set) 2363 int charBBw = 0; // Character bounding box width 2364 int charBBh = 0; // Character bounding box height 2365 int charBBxoff0 = 0; // Character bounding box X0 offset 2366 int charBByoff0 = 0; // Character bounding box Y0 offset 2367 int charDWidthX = 0; // Character advance X 2368 int charDWidthY = 0; // Character advance Y (unused) 2369 GlyphInfo *charGlyphInfo = NULL; // Pointer to output glyph info (NULL if not set) 2370 2371 if (fileData == NULL) return glyphs; 2372 2373 // In case no chars count provided, default to 95 2374 codepointCount = (codepointCount > 0)? codepointCount : 95; 2375 2376 // Fill fontChars in case not provided externally 2377 // NOTE: By default we fill glyphCount consecutively, starting at 32 (Space) 2378 if (codepoints == NULL) 2379 { 2380 codepoints = (int *)RL_MALLOC(codepointCount*sizeof(int)); 2381 for (int i = 0; i < codepointCount; i++) codepoints[i] = i + 32; 2382 genFontChars = true; 2383 } 2384 2385 glyphs = (GlyphInfo *)RL_CALLOC(codepointCount, sizeof(GlyphInfo)); 2386 2387 while (totalReadBytes <= dataSize) 2388 { 2389 readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); 2390 totalReadBytes += (readBytes + 1); 2391 fileTextPtr += (readBytes + 1); 2392 2393 // Line: COMMENT 2394 if (strstr(buffer, "COMMENT") != NULL) continue; // Ignore line 2395 2396 if (charStarted) 2397 { 2398 // Line: ENDCHAR 2399 if (strstr(buffer, "ENDCHAR") != NULL) 2400 { 2401 charStarted = false; 2402 continue; 2403 } 2404 2405 if (charBitmapStarted) 2406 { 2407 if (charGlyphInfo != NULL) 2408 { 2409 int pixelY = charBitmapNextRow++; 2410 2411 if (pixelY >= charGlyphInfo->image.height) break; 2412 2413 for (int x = 0; x < readBytes; x++) 2414 { 2415 unsigned char byte = HexToInt(buffer[x]); 2416 2417 for (int bitX = 0; bitX < 4; bitX++) 2418 { 2419 int pixelX = ((x*4) + bitX); 2420 2421 if (pixelX >= charGlyphInfo->image.width) break; 2422 2423 if ((byte & (8 >> bitX)) > 0) ((unsigned char *)charGlyphInfo->image.data)[(pixelY*charGlyphInfo->image.width) + pixelX] = 255; 2424 } 2425 } 2426 } 2427 continue; 2428 } 2429 2430 // Line: ENCODING 2431 if (strstr(buffer, "ENCODING") != NULL) 2432 { 2433 readVars = sscanf(buffer, "ENCODING %i", &charEncoding); 2434 continue; 2435 } 2436 2437 // Line: BBX 2438 if (strstr(buffer, "BBX") != NULL) 2439 { 2440 readVars = sscanf(buffer, "BBX %i %i %i %i", &charBBw, &charBBh, &charBBxoff0, &charBByoff0); 2441 continue; 2442 } 2443 2444 // Line: DWIDTH 2445 if (strstr(buffer, "DWIDTH") != NULL) 2446 { 2447 readVars = sscanf(buffer, "DWIDTH %i %i", &charDWidthX, &charDWidthY); 2448 continue; 2449 } 2450 2451 // Line: BITMAP 2452 if (strstr(buffer, "BITMAP") != NULL) 2453 { 2454 // Search for glyph index in codepoints 2455 charGlyphInfo = NULL; 2456 2457 for (int codepointIndex = 0; codepointIndex < codepointCount; codepointIndex++) 2458 { 2459 if (codepoints[codepointIndex] == charEncoding) 2460 { 2461 charGlyphInfo = &glyphs[codepointIndex]; 2462 break; 2463 } 2464 } 2465 2466 // Init glyph info 2467 if (charGlyphInfo != NULL) 2468 { 2469 charGlyphInfo->value = charEncoding; 2470 charGlyphInfo->offsetX = charBBxoff0 + fontBByoff0; 2471 charGlyphInfo->offsetY = fontBBh - (charBBh + charBByoff0 + fontBByoff0 + fontAscent); 2472 charGlyphInfo->advanceX = charDWidthX; 2473 2474 charGlyphInfo->image.data = RL_CALLOC(charBBw*charBBh, 1); 2475 charGlyphInfo->image.width = charBBw; 2476 charGlyphInfo->image.height = charBBh; 2477 charGlyphInfo->image.mipmaps = 1; 2478 charGlyphInfo->image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; 2479 } 2480 2481 charBitmapStarted = true; 2482 charBitmapNextRow = 0; 2483 2484 continue; 2485 } 2486 } 2487 else if (fontStarted) 2488 { 2489 // Line: ENDFONT 2490 if (strstr(buffer, "ENDFONT") != NULL) 2491 { 2492 fontStarted = false; 2493 break; 2494 } 2495 2496 // Line: SIZE 2497 if (strstr(buffer, "SIZE") != NULL) 2498 { 2499 if (outFontSize != NULL) readVars = sscanf(buffer, "SIZE %i", outFontSize); 2500 continue; 2501 } 2502 2503 // PIXEL_SIZE 2504 if (strstr(buffer, "PIXEL_SIZE") != NULL) 2505 { 2506 if (outFontSize != NULL) readVars = sscanf(buffer, "PIXEL_SIZE %i", outFontSize); 2507 continue; 2508 } 2509 2510 // FONTBOUNDINGBOX 2511 if (strstr(buffer, "FONTBOUNDINGBOX") != NULL) 2512 { 2513 readVars = sscanf(buffer, "FONTBOUNDINGBOX %i %i %i %i", &fontBBw, &fontBBh, &fontBBxoff0, &fontBByoff0); 2514 continue; 2515 } 2516 2517 // FONT_ASCENT 2518 if (strstr(buffer, "FONT_ASCENT") != NULL) 2519 { 2520 readVars = sscanf(buffer, "FONT_ASCENT %i", &fontAscent); 2521 continue; 2522 } 2523 2524 // STARTCHAR 2525 if (strstr(buffer, "STARTCHAR") != NULL) 2526 { 2527 charStarted = true; 2528 charEncoding = -1; 2529 charGlyphInfo = NULL; 2530 charBBw = 0; 2531 charBBh = 0; 2532 charBBxoff0 = 0; 2533 charBByoff0 = 0; 2534 charDWidthX = 0; 2535 charDWidthY = 0; 2536 charGlyphInfo = NULL; 2537 charBitmapStarted = false; 2538 charBitmapNextRow = 0; 2539 continue; 2540 } 2541 } 2542 else 2543 { 2544 // STARTFONT 2545 if (strstr(buffer, "STARTFONT") != NULL) 2546 { 2547 if (fontStarted) 2548 { 2549 fontMalformed = true; 2550 break; 2551 } 2552 else 2553 { 2554 fontStarted = true; 2555 continue; 2556 } 2557 } 2558 } 2559 } 2560 2561 if (genFontChars) RL_FREE(codepoints); 2562 2563 if (fontMalformed) 2564 { 2565 RL_FREE(glyphs); 2566 glyphs = NULL; 2567 } 2568 2569 return glyphs; 2570 } 2571 #endif // SUPPORT_FILEFORMAT_BDF 2572 2573 #endif // SUPPORT_MODULE_RTEXT