1 /**********************************************************************************************
2 *
3 *   raylib.text - Basic functions to load Fonts and draw Text
4 *
5 *   CONFIGURATION:
6 *
7 *   #define SUPPORT_FILEFORMAT_FNT
8 *   #define SUPPORT_FILEFORMAT_TTF
9 *       Selected desired fileformats to be supported for loading. Some of those formats are
10 *       supported by default, to remove support, just comment unrequired #define in this module
11 *
12 *   #define SUPPORT_DEFAULT_FONT
13 *       Load default raylib font on initialization to be used by DrawText() and MeasureText().
14 *       If no default font loaded, DrawTextEx() and MeasureTextEx() are required.
15 *
16 *   #define TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH
17 *       TextSplit() function static buffer max size
18 *
19 *   #define MAX_TEXTSPLIT_COUNT
20 *       TextSplit() function static substrings pointers array (pointing to static buffer)
21 *
22 *
23 *   DEPENDENCIES:
24 *       stb_truetype  - Load TTF file and rasterize characters data
25 *       stb_rect_pack - Rectangles packing algorythms, required for font atlas generation
26 *
27 *
28 *   LICENSE: zlib/libpng
29 *
30 *   Copyright (c) 2013-2021 Ramon Santamaria (@raysan5)
31 *
32 *   This software is provided "as-is", without any express or implied warranty. In no event
33 *   will the authors be held liable for any damages arising from the use of this software.
34 *
35 *   Permission is granted to anyone to use this software for any purpose, including commercial
36 *   applications, and to alter it and redistribute it freely, subject to the following restrictions:
37 *
38 *     1. The origin of this software must not be misrepresented; you must not claim that you
39 *     wrote the original software. If you use this software in a product, an acknowledgment
40 *     in the product documentation would be appreciated but is not required.
41 *
42 *     2. Altered source versions must be plainly marked as such, and must not be misrepresented
43 *     as being the original software.
44 *
45 *     3. This notice may not be removed or altered from any source distribution.
46 *
47 **********************************************************************************************/
48 
49 #include "raylib.h"         // Declares module functions
50 
51 // Check if config flags have been externally provided on compilation line
52 #if !defined(EXTERNAL_CONFIG_FLAGS)
53     #include "config.h"     // Defines module configuration flags
54 #endif
55 
56 #include <stdlib.h>         // Required for: malloc(), free()
57 #include <stdio.h>          // Required for: vsprintf()
58 #include <string.h>         // Required for: strcmp(), strstr(), strcpy(), strncpy() [Used in TextReplace()], sscanf() [Used in LoadBMFont()]
59 #include <stdarg.h>         // Required for: va_list, va_start(), vsprintf(), va_end() [Used in TextFormat()]
60 #include <ctype.h>          // Requried for: toupper(), tolower() [Used in TextToUpper(), TextToLower()]
61 
62 #include "utils.h"          // Required for: LoadFileText()
63 
64 #if defined(SUPPORT_FILEFORMAT_TTF)
65     #define STB_RECT_PACK_IMPLEMENTATION
66     #include "external/stb_rect_pack.h"     // Required for: ttf font rectangles packaging
67 
68     #define STBTT_STATIC
69     #define STB_TRUETYPE_IMPLEMENTATION
70     #include "external/stb_truetype.h"      // Required for: ttf font data reading
71 #endif
72 
73 //----------------------------------------------------------------------------------
74 // Defines and Macros
75 //----------------------------------------------------------------------------------
76 #ifndef MAX_TEXT_BUFFER_LENGTH
77     #define MAX_TEXT_BUFFER_LENGTH              1024        // Size of internal static buffers used on some functions:
78                                                             // TextFormat(), TextSubtext(), TextToUpper(), TextToLower(), TextToPascal(), TextSplit()
79 #endif
80 #ifndef MAX_TEXT_UNICODE_CHARS
81     #define MAX_TEXT_UNICODE_CHARS               512        // Maximum number of unicode codepoints: GetCodepoints()
82 #endif
83 #ifndef MAX_TEXTSPLIT_COUNT
84     #define MAX_TEXTSPLIT_COUNT                  128        // Maximum number of substrings to split: TextSplit()
85 #endif
86 
87 //----------------------------------------------------------------------------------
88 // Types and Structures Definition
89 //----------------------------------------------------------------------------------
90 // ...
91 
92 //----------------------------------------------------------------------------------
93 // Global variables
94 //----------------------------------------------------------------------------------
95 #if defined(SUPPORT_DEFAULT_FONT)
96 // Default font provided by raylib
97 // NOTE: Default font is loaded on InitWindow() and disposed on CloseWindow() [module: core]
98 static Font defaultFont = { 0 };
99 #endif
100 
101 //----------------------------------------------------------------------------------
102 // Other Modules Functions Declaration (required by text)
103 //----------------------------------------------------------------------------------
104 //...
105 
106 //----------------------------------------------------------------------------------
107 // Module specific Functions Declaration
108 //----------------------------------------------------------------------------------
109 #if defined(SUPPORT_FILEFORMAT_FNT)
110 static Font LoadBMFont(const char *fileName);     // Load a BMFont file (AngelCode font file)
111 #endif
112 
113 #if defined(SUPPORT_DEFAULT_FONT)
114 extern void LoadFontDefault(void);
115 extern void UnloadFontDefault(void);
116 #endif
117 
118 //----------------------------------------------------------------------------------
119 // Module Functions Definition
120 //----------------------------------------------------------------------------------
121 #if defined(SUPPORT_DEFAULT_FONT)
122 
123 // Load raylib default font
LoadFontDefault(void)124 extern void LoadFontDefault(void)
125 {
126     #define BIT_CHECK(a,b) ((a) & (1u << (b)))
127 
128     // NOTE: Using UTF8 encoding table for Unicode U+0000..U+00FF Basic Latin + Latin-1 Supplement
129     // Ref: http://www.utf8-chartable.de/unicode-utf8-table.pl
130 
131     defaultFont.charsCount = 224;   // Number of chars included in our default font
132     defaultFont.charsPadding = 0;   // Characters padding
133 
134     // Default font is directly defined here (data generated from a sprite font image)
135     // This way, we reconstruct Font without creating large global variables
136     // This data is automatically allocated to Stack and automatically deallocated at the end of this function
137     unsigned int defaultFontData[512] = {
138         0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00200020, 0x0001b000, 0x00000000, 0x00000000, 0x8ef92520, 0x00020a00, 0x7dbe8000, 0x1f7df45f,
139         0x4a2bf2a0, 0x0852091e, 0x41224000, 0x10041450, 0x2e292020, 0x08220812, 0x41222000, 0x10041450, 0x10f92020, 0x3efa084c, 0x7d22103c, 0x107df7de,
140         0xe8a12020, 0x08220832, 0x05220800, 0x10450410, 0xa4a3f000, 0x08520832, 0x05220400, 0x10450410, 0xe2f92020, 0x0002085e, 0x7d3e0281, 0x107df41f,
141         0x00200000, 0x8001b000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
142         0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xc0000fbe, 0xfbf7e00f, 0x5fbf7e7d, 0x0050bee8, 0x440808a2, 0x0a142fe8, 0x50810285, 0x0050a048,
143         0x49e428a2, 0x0a142828, 0x40810284, 0x0048a048, 0x10020fbe, 0x09f7ebaf, 0xd89f3e84, 0x0047a04f, 0x09e48822, 0x0a142aa1, 0x50810284, 0x0048a048,
144         0x04082822, 0x0a142fa0, 0x50810285, 0x0050a248, 0x00008fbe, 0xfbf42021, 0x5f817e7d, 0x07d09ce8, 0x00008000, 0x00000fe0, 0x00000000, 0x00000000,
145         0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000c0180,
146         0xdfbf4282, 0x0bfbf7ef, 0x42850505, 0x004804bf, 0x50a142c6, 0x08401428, 0x42852505, 0x00a808a0, 0x50a146aa, 0x08401428, 0x42852505, 0x00081090,
147         0x5fa14a92, 0x0843f7e8, 0x7e792505, 0x00082088, 0x40a15282, 0x08420128, 0x40852489, 0x00084084, 0x40a16282, 0x0842022a, 0x40852451, 0x00088082,
148         0xc0bf4282, 0xf843f42f, 0x7e85fc21, 0x3e0900bf, 0x00000000, 0x00000004, 0x00000000, 0x000c0180, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
149         0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04000402, 0x41482000, 0x00000000, 0x00000800,
150         0x04000404, 0x4100203c, 0x00000000, 0x00000800, 0xf7df7df0, 0x514bef85, 0xbefbefbe, 0x04513bef, 0x14414500, 0x494a2885, 0xa28a28aa, 0x04510820,
151         0xf44145f0, 0x474a289d, 0xa28a28aa, 0x04510be0, 0x14414510, 0x494a2884, 0xa28a28aa, 0x02910a00, 0xf7df7df0, 0xd14a2f85, 0xbefbe8aa, 0x011f7be0,
152         0x00000000, 0x00400804, 0x20080000, 0x00000000, 0x00000000, 0x00600f84, 0x20080000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
153         0xac000000, 0x00000f01, 0x00000000, 0x00000000, 0x24000000, 0x00000f01, 0x00000000, 0x06000000, 0x24000000, 0x00000f01, 0x00000000, 0x09108000,
154         0x24fa28a2, 0x00000f01, 0x00000000, 0x013e0000, 0x2242252a, 0x00000f52, 0x00000000, 0x038a8000, 0x2422222a, 0x00000f29, 0x00000000, 0x010a8000,
155         0x2412252a, 0x00000f01, 0x00000000, 0x010a8000, 0x24fbe8be, 0x00000f01, 0x00000000, 0x0ebe8000, 0xac020000, 0x00000f01, 0x00000000, 0x00048000,
156         0x0003e000, 0x00000f00, 0x00000000, 0x00008000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000038, 0x8443b80e, 0x00203a03,
157         0x02bea080, 0xf0000020, 0xc452208a, 0x04202b02, 0xf8029122, 0x07f0003b, 0xe44b388e, 0x02203a02, 0x081e8a1c, 0x0411e92a, 0xf4420be0, 0x01248202,
158         0xe8140414, 0x05d104ba, 0xe7c3b880, 0x00893a0a, 0x283c0e1c, 0x04500902, 0xc4400080, 0x00448002, 0xe8208422, 0x04500002, 0x80400000, 0x05200002,
159         0x083e8e00, 0x04100002, 0x804003e0, 0x07000042, 0xf8008400, 0x07f00003, 0x80400000, 0x04000022, 0x00000000, 0x00000000, 0x80400000, 0x04000002,
160         0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00800702, 0x1848a0c2, 0x84010000, 0x02920921, 0x01042642, 0x00005121, 0x42023f7f, 0x00291002,
161         0xefc01422, 0x7efdfbf7, 0xefdfa109, 0x03bbbbf7, 0x28440f12, 0x42850a14, 0x20408109, 0x01111010, 0x28440408, 0x42850a14, 0x2040817f, 0x01111010,
162         0xefc78204, 0x7efdfbf7, 0xe7cf8109, 0x011111f3, 0x2850a932, 0x42850a14, 0x2040a109, 0x01111010, 0x2850b840, 0x42850a14, 0xefdfbf79, 0x03bbbbf7,
163         0x001fa020, 0x00000000, 0x00001000, 0x00000000, 0x00002070, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
164         0x08022800, 0x00012283, 0x02430802, 0x01010001, 0x8404147c, 0x20000144, 0x80048404, 0x00823f08, 0xdfbf4284, 0x7e03f7ef, 0x142850a1, 0x0000210a,
165         0x50a14684, 0x528a1428, 0x142850a1, 0x03efa17a, 0x50a14a9e, 0x52521428, 0x142850a1, 0x02081f4a, 0x50a15284, 0x4a221428, 0xf42850a1, 0x03efa14b,
166         0x50a16284, 0x4a521428, 0x042850a1, 0x0228a17a, 0xdfbf427c, 0x7e8bf7ef, 0xf7efdfbf, 0x03efbd0b, 0x00000000, 0x04000000, 0x00000000, 0x00000008,
167         0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00200508, 0x00840400, 0x11458122, 0x00014210,
168         0x00514294, 0x51420800, 0x20a22a94, 0x0050a508, 0x00200000, 0x00000000, 0x00050000, 0x08000000, 0xfefbefbe, 0xfbefbefb, 0xfbeb9114, 0x00fbefbe,
169         0x20820820, 0x8a28a20a, 0x8a289114, 0x3e8a28a2, 0xfefbefbe, 0xfbefbe0b, 0x8a289114, 0x008a28a2, 0x228a28a2, 0x08208208, 0x8a289114, 0x088a28a2,
170         0xfefbefbe, 0xfbefbefb, 0xfa2f9114, 0x00fbefbe, 0x00000000, 0x00000040, 0x00000000, 0x00000000, 0x00000000, 0x00000020, 0x00000000, 0x00000000,
171         0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00210100, 0x00000004, 0x00000000, 0x00000000, 0x14508200, 0x00001402, 0x00000000, 0x00000000,
172         0x00000010, 0x00000020, 0x00000000, 0x00000000, 0xa28a28be, 0x00002228, 0x00000000, 0x00000000, 0xa28a28aa, 0x000022e8, 0x00000000, 0x00000000,
173         0xa28a28aa, 0x000022a8, 0x00000000, 0x00000000, 0xa28a28aa, 0x000022e8, 0x00000000, 0x00000000, 0xbefbefbe, 0x00003e2f, 0x00000000, 0x00000000,
174         0x00000004, 0x00002028, 0x00000000, 0x00000000, 0x80000000, 0x00003e0f, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
175         0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
176         0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
177         0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
178         0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
179         0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
180         0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 };
181 
182     int charsHeight = 10;
183     int charsDivisor = 1;    // Every char is separated from the consecutive by a 1 pixel divisor, horizontally and vertically
184 
185     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,
186                             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,
187                             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,
188                             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,
189                             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,
190                             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,
191                             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 };
192 
193     // Re-construct image from defaultFontData and generate OpenGL texture
194     //----------------------------------------------------------------------
195     Image imFont = {
196         .data = calloc(128*128, 2),  // 2 bytes per pixel (gray + alpha)
197         .width = 128,
198         .height = 128,
199         .format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA,
200         .mipmaps = 1
201     };
202 
203     // Fill image.data with defaultFontData (convert from bit to pixel!)
204     for (int i = 0, counter = 0; i < imFont.width*imFont.height; i += 32)
205     {
206         for (int j = 31; j >= 0; j--)
207         {
208             if (BIT_CHECK(defaultFontData[counter], j))
209             {
210                 // NOTE: We are unreferencing data as short, so,
211                 // we must consider data as little-endian order (alpha + gray)
212                 ((unsigned short *)imFont.data)[i + j] = 0xffff;
213             }
214             else ((unsigned short *)imFont.data)[i + j] = 0x00ff;
215         }
216 
217         counter++;
218     }
219 
220     defaultFont.texture = LoadTextureFromImage(imFont);
221 
222     // Reconstruct charSet using charsWidth[], charsHeight, charsDivisor, charsCount
223     //------------------------------------------------------------------------------
224 
225     // Allocate space for our characters info data
226     // NOTE: This memory should be freed at end! --> CloseWindow()
227     defaultFont.chars = (CharInfo *)RL_MALLOC(defaultFont.charsCount*sizeof(CharInfo));
228     defaultFont.recs = (Rectangle *)RL_MALLOC(defaultFont.charsCount*sizeof(Rectangle));
229 
230     int currentLine = 0;
231     int currentPosX = charsDivisor;
232     int testPosX = charsDivisor;
233 
234     for (int i = 0; i < defaultFont.charsCount; i++)
235     {
236         defaultFont.chars[i].value = 32 + i;  // First char is 32
237 
238         defaultFont.recs[i].x = (float)currentPosX;
239         defaultFont.recs[i].y = (float)(charsDivisor + currentLine*(charsHeight + charsDivisor));
240         defaultFont.recs[i].width = (float)charsWidth[i];
241         defaultFont.recs[i].height = (float)charsHeight;
242 
243         testPosX += (int)(defaultFont.recs[i].width + (float)charsDivisor);
244 
245         if (testPosX >= defaultFont.texture.width)
246         {
247             currentLine++;
248             currentPosX = 2*charsDivisor + charsWidth[i];
249             testPosX = currentPosX;
250 
251             defaultFont.recs[i].x = (float)charsDivisor;
252             defaultFont.recs[i].y = (float)(charsDivisor + currentLine*(charsHeight + charsDivisor));
253         }
254         else currentPosX = testPosX;
255 
256         // NOTE: On default font character offsets and xAdvance are not required
257         defaultFont.chars[i].offsetX = 0;
258         defaultFont.chars[i].offsetY = 0;
259         defaultFont.chars[i].advanceX = 0;
260 
261         // Fill character image data from fontClear data
262         defaultFont.chars[i].image = ImageFromImage(imFont, defaultFont.recs[i]);
263     }
264 
265     UnloadImage(imFont);
266 
267     defaultFont.baseSize = (int)defaultFont.recs[0].height;
268 
269     TRACELOG(LOG_INFO, "FONT: Default font loaded successfully");
270 }
271 
272 // Unload raylib default font
UnloadFontDefault(void)273 extern void UnloadFontDefault(void)
274 {
275     for (int i = 0; i < defaultFont.charsCount; i++) UnloadImage(defaultFont.chars[i].image);
276     UnloadTexture(defaultFont.texture);
277     RL_FREE(defaultFont.chars);
278     RL_FREE(defaultFont.recs);
279 }
280 #endif      // SUPPORT_DEFAULT_FONT
281 
282 // Get the default font, useful to be used with extended parameters
GetFontDefault()283 Font GetFontDefault()
284 {
285 #if defined(SUPPORT_DEFAULT_FONT)
286     return defaultFont;
287 #else
288     Font font = { 0 };
289     return font;
290 #endif
291 }
292 
293 // Load Font from file into GPU memory (VRAM)
LoadFont(const char * fileName)294 Font LoadFont(const char *fileName)
295 {
296     // Default values for ttf font generation
297 #ifndef FONT_TTF_DEFAULT_SIZE
298     #define FONT_TTF_DEFAULT_SIZE           32      // TTF font generation default char size (char-height)
299 #endif
300 #ifndef FONT_TTF_DEFAULT_NUMCHARS
301     #define FONT_TTF_DEFAULT_NUMCHARS       95      // TTF font generation default charset: 95 glyphs (ASCII 32..126)
302 #endif
303 #ifndef FONT_TTF_DEFAULT_FIRST_CHAR
304     #define FONT_TTF_DEFAULT_FIRST_CHAR     32      // TTF font generation default first char for image sprite font (32-Space)
305 #endif
306 #ifndef FONT_TTF_DEFAULT_CHARS_PADDING
307     #define FONT_TTF_DEFAULT_CHARS_PADDING   4      // TTF font generation default chars padding
308 #endif
309 
310     Font font = { 0 };
311 
312 #if defined(SUPPORT_FILEFORMAT_TTF)
313     if (IsFileExtension(fileName, ".ttf;.otf")) font = LoadFontEx(fileName, FONT_TTF_DEFAULT_SIZE, NULL, FONT_TTF_DEFAULT_NUMCHARS);
314     else
315 #endif
316 #if defined(SUPPORT_FILEFORMAT_FNT)
317     if (IsFileExtension(fileName, ".fnt")) font = LoadBMFont(fileName);
318     else
319 #endif
320     {
321         Image image = LoadImage(fileName);
322         if (image.data != NULL) font = LoadFontFromImage(image, MAGENTA, FONT_TTF_DEFAULT_FIRST_CHAR);
323         UnloadImage(image);
324     }
325 
326     if (font.texture.id == 0)
327     {
328         TRACELOG(LOG_WARNING, "FONT: [%s] Failed to load font texture -> Using default font", fileName);
329         font = GetFontDefault();
330     }
331     else SetTextureFilter(font.texture, TEXTURE_FILTER_POINT);    // By default we set point filter (best performance)
332 
333     return font;
334 }
335 
336 // Load Font from TTF font file with generation parameters
337 // NOTE: You can pass an array with desired characters, those characters should be available in the font
338 // if array is NULL, default char set is selected 32..126
LoadFontEx(const char * fileName,int fontSize,int * fontChars,int charsCount)339 Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int charsCount)
340 {
341     Font font = { 0 };
342 
343     // Loading file to memory
344     unsigned int fileSize = 0;
345     unsigned char *fileData = LoadFileData(fileName, &fileSize);
346 
347     if (fileData != NULL)
348     {
349         // Loading font from memory data
350         font = LoadFontFromMemory(GetFileExtension(fileName), fileData, fileSize, fontSize, fontChars, charsCount);
351 
352         RL_FREE(fileData);
353     }
354     else font = GetFontDefault();
355 
356     return font;
357 }
358 
359 // Load an Image font file (XNA style)
LoadFontFromImage(Image image,Color key,int firstChar)360 Font LoadFontFromImage(Image image, Color key, int firstChar)
361 {
362 #ifndef MAX_GLYPHS_FROM_IMAGE
363     #define MAX_GLYPHS_FROM_IMAGE   256     // Maximum number of glyphs supported on image scan
364 #endif
365 
366     #define COLOR_EQUAL(col1, col2) ((col1.r == col2.r)&&(col1.g == col2.g)&&(col1.b == col2.b)&&(col1.a == col2.a))
367 
368     int charSpacing = 0;
369     int lineSpacing = 0;
370 
371     int x = 0;
372     int y = 0;
373 
374     // We allocate a temporal arrays for chars data measures,
375     // once we get the actual number of chars, we copy data to a sized arrays
376     int tempCharValues[MAX_GLYPHS_FROM_IMAGE];
377     Rectangle tempCharRecs[MAX_GLYPHS_FROM_IMAGE];
378 
379     Color *pixels = LoadImageColors(image);
380 
381     // Parse image data to get charSpacing and lineSpacing
382     for (y = 0; y < image.height; y++)
383     {
384         for (x = 0; x < image.width; x++)
385         {
386             if (!COLOR_EQUAL(pixels[y*image.width + x], key)) break;
387         }
388 
389         if (!COLOR_EQUAL(pixels[y*image.width + x], key)) break;
390     }
391 
392     charSpacing = x;
393     lineSpacing = y;
394 
395     int charHeight = 0;
396     int j = 0;
397 
398     while (!COLOR_EQUAL(pixels[(lineSpacing + j)*image.width + charSpacing], key)) j++;
399 
400     charHeight = j;
401 
402     // Check array values to get characters: value, x, y, w, h
403     int index = 0;
404     int lineToRead = 0;
405     int xPosToRead = charSpacing;
406 
407     // Parse image data to get rectangle sizes
408     while ((lineSpacing + lineToRead*(charHeight + lineSpacing)) < image.height)
409     {
410         while ((xPosToRead < image.width) &&
411               !COLOR_EQUAL((pixels[(lineSpacing + (charHeight+lineSpacing)*lineToRead)*image.width + xPosToRead]), key))
412         {
413             tempCharValues[index] = firstChar + index;
414 
415             tempCharRecs[index].x = (float)xPosToRead;
416             tempCharRecs[index].y = (float)(lineSpacing + lineToRead*(charHeight + lineSpacing));
417             tempCharRecs[index].height = (float)charHeight;
418 
419             int charWidth = 0;
420 
421             while (!COLOR_EQUAL(pixels[(lineSpacing + (charHeight+lineSpacing)*lineToRead)*image.width + xPosToRead + charWidth], key)) charWidth++;
422 
423             tempCharRecs[index].width = (float)charWidth;
424 
425             index++;
426 
427             xPosToRead += (charWidth + charSpacing);
428         }
429 
430         lineToRead++;
431         xPosToRead = charSpacing;
432     }
433 
434     // NOTE: We need to remove key color borders from image to avoid weird
435     // artifacts on texture scaling when using TEXTURE_FILTER_BILINEAR or TEXTURE_FILTER_TRILINEAR
436     for (int i = 0; i < image.height*image.width; i++) if (COLOR_EQUAL(pixels[i], key)) pixels[i] = BLANK;
437 
438     // Create a new image with the processed color data (key color replaced by BLANK)
439     Image fontClear = {
440         .data = pixels,
441         .width = image.width,
442         .height = image.height,
443         .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
444         .mipmaps = 1
445     };
446 
447     // Create spritefont with all data parsed from image
448     Font font = { 0 };
449 
450     font.texture = LoadTextureFromImage(fontClear); // Convert processed image to OpenGL texture
451     font.charsCount = index;
452     font.charsPadding = 0;
453 
454     // We got tempCharValues and tempCharsRecs populated with chars data
455     // Now we move temp data to sized charValues and charRecs arrays
456     font.chars = (CharInfo *)RL_MALLOC(font.charsCount*sizeof(CharInfo));
457     font.recs = (Rectangle *)RL_MALLOC(font.charsCount*sizeof(Rectangle));
458 
459     for (int i = 0; i < font.charsCount; i++)
460     {
461         font.chars[i].value = tempCharValues[i];
462 
463         // Get character rectangle in the font atlas texture
464         font.recs[i] = tempCharRecs[i];
465 
466         // NOTE: On image based fonts (XNA style), character offsets and xAdvance are not required (set to 0)
467         font.chars[i].offsetX = 0;
468         font.chars[i].offsetY = 0;
469         font.chars[i].advanceX = 0;
470 
471         // Fill character image data from fontClear data
472         font.chars[i].image = ImageFromImage(fontClear, tempCharRecs[i]);
473     }
474 
475     UnloadImage(fontClear);     // Unload processed image once converted to texture
476 
477     font.baseSize = (int)font.recs[0].height;
478 
479     return font;
480 }
481 
482 // Load font from memory buffer, fileType refers to extension: i.e. ".ttf"
LoadFontFromMemory(const char * fileType,const unsigned char * fileData,int dataSize,int fontSize,int * fontChars,int charsCount)483 Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int charsCount)
484 {
485     Font font = { 0 };
486 
487     char fileExtLower[16] = { 0 };
488     strcpy(fileExtLower, TextToLower(fileType));
489 
490 #if defined(SUPPORT_FILEFORMAT_TTF)
491     if (TextIsEqual(fileExtLower, ".ttf") ||
492         TextIsEqual(fileExtLower, ".otf"))
493     {
494         font.baseSize = fontSize;
495         font.charsCount = (charsCount > 0)? charsCount : 95;
496         font.charsPadding = 0;
497         font.chars = LoadFontData(fileData, dataSize, font.baseSize, fontChars, font.charsCount, FONT_DEFAULT);
498 
499         if (font.chars != NULL)
500         {
501             font.charsPadding = FONT_TTF_DEFAULT_CHARS_PADDING;
502 
503             Image atlas = GenImageFontAtlas(font.chars, &font.recs, font.charsCount, font.baseSize, font.charsPadding, 0);
504             font.texture = LoadTextureFromImage(atlas);
505 
506             // Update chars[i].image to use alpha, required to be used on ImageDrawText()
507             for (int i = 0; i < font.charsCount; i++)
508             {
509                 UnloadImage(font.chars[i].image);
510                 font.chars[i].image = ImageFromImage(atlas, font.recs[i]);
511             }
512 
513             UnloadImage(atlas);
514         }
515         else font = GetFontDefault();
516     }
517 #else
518     font = GetFontDefault();
519 #endif
520 
521     return font;
522 }
523 
524 // Load font data for further use
525 // NOTE: Requires TTF font memory data and can generate SDF data
LoadFontData(const unsigned char * fileData,int dataSize,int fontSize,int * fontChars,int charsCount,int type)526 CharInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int charsCount, int type)
527 {
528     // NOTE: Using some SDF generation default values,
529     // trades off precision with ability to handle *smaller* sizes
530 #ifndef FONT_SDF_CHAR_PADDING
531     #define FONT_SDF_CHAR_PADDING            4      // SDF font generation char padding
532 #endif
533 #ifndef FONT_SDF_ON_EDGE_VALUE
534     #define FONT_SDF_ON_EDGE_VALUE         128      // SDF font generation on edge value
535 #endif
536 #ifndef FONT_SDF_PIXEL_DIST_SCALE
537     #define FONT_SDF_PIXEL_DIST_SCALE     64.0f     // SDF font generation pixel distance scale
538 #endif
539 #ifndef FONT_BITMAP_ALPHA_THRESHOLD
540     #define FONT_BITMAP_ALPHA_THRESHOLD     80      // Bitmap (B&W) font generation alpha threshold
541 #endif
542 
543     CharInfo *chars = NULL;
544 
545 #if defined(SUPPORT_FILEFORMAT_TTF)
546     // Load font data (including pixel data) from TTF memory file
547     // NOTE: Loaded information should be enough to generate font image atlas, using any packaging method
548     if (fileData != NULL)
549     {
550         int genFontChars = false;
551         stbtt_fontinfo fontInfo = { 0 };
552 
553         if (stbtt_InitFont(&fontInfo, (unsigned char *)fileData, 0))     // Init font for data reading
554         {
555             // Calculate font scale factor
556             float scaleFactor = stbtt_ScaleForPixelHeight(&fontInfo, (float)fontSize);
557 
558             // Calculate font basic metrics
559             // NOTE: ascent is equivalent to font baseline
560             int ascent, descent, lineGap;
561             stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap);
562 
563             // In case no chars count provided, default to 95
564             charsCount = (charsCount > 0)? charsCount : 95;
565 
566             // Fill fontChars in case not provided externally
567             // NOTE: By default we fill charsCount consecutevely, starting at 32 (Space)
568 
569             if (fontChars == NULL)
570             {
571                 fontChars = (int *)RL_MALLOC(charsCount*sizeof(int));
572                 for (int i = 0; i < charsCount; i++) fontChars[i] = i + 32;
573                 genFontChars = true;
574             }
575 
576             chars = (CharInfo *)RL_MALLOC(charsCount*sizeof(CharInfo));
577 
578             // NOTE: Using simple packaging, one char after another
579             for (int i = 0; i < charsCount; i++)
580             {
581                 int chw = 0, chh = 0;   // Character width and height (on generation)
582                 int ch = fontChars[i];  // Character value to get info for
583                 chars[i].value = ch;
584 
585                 //  Render a unicode codepoint to a bitmap
586                 //      stbtt_GetCodepointBitmap()           -- allocates and returns a bitmap
587                 //      stbtt_GetCodepointBitmapBox()        -- how big the bitmap must be
588                 //      stbtt_MakeCodepointBitmap()          -- renders into bitmap you provide
589 
590                 if (type != FONT_SDF) chars[i].image.data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, ch, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY);
591                 else 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);
592                 else chars[i].image.data = NULL;
593 
594                 stbtt_GetCodepointHMetrics(&fontInfo, ch, &chars[i].advanceX, NULL);
595                 chars[i].advanceX = (int)((float)chars[i].advanceX*scaleFactor);
596 
597                 // Load characters images
598                 chars[i].image.width = chw;
599                 chars[i].image.height = chh;
600                 chars[i].image.mipmaps = 1;
601                 chars[i].image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
602 
603                 chars[i].offsetY += (int)((float)ascent*scaleFactor);
604 
605                 // NOTE: We create an empty image for space character, it could be further required for atlas packing
606                 if (ch == 32)
607                 {
608                     Image imSpace = {
609                         .data = calloc(chars[i].advanceX*fontSize, 2),
610                         .width = chars[i].advanceX,
611                         .height = fontSize,
612                         .format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE,
613                         .mipmaps = 1
614                     };
615 
616                     chars[i].image = imSpace;
617                 }
618 
619                 if (type == FONT_BITMAP)
620                 {
621                     // Aliased bitmap (black & white) font generation, avoiding anti-aliasing
622                     // NOTE: For optimum results, bitmap font should be generated at base pixel size
623                     for (int p = 0; p < chw*chh; p++)
624                     {
625                         if (((unsigned char *)chars[i].image.data)[p] < FONT_BITMAP_ALPHA_THRESHOLD) ((unsigned char *)chars[i].image.data)[p] = 0;
626                         else ((unsigned char *)chars[i].image.data)[p] = 255;
627                     }
628                 }
629 
630                 // Get bounding box for character (may be offset to account for chars that dip above or below the line)
631                 /*
632                 int chX1, chY1, chX2, chY2;
633                 stbtt_GetCodepointBitmapBox(&fontInfo, ch, scaleFactor, scaleFactor, &chX1, &chY1, &chX2, &chY2);
634 
635                 TRACELOGD("FONT: Character box measures: %i, %i, %i, %i", chX1, chY1, chX2 - chX1, chY2 - chY1);
636                 TRACELOGD("FONT: Character offsetY: %i", (int)((float)ascent*scaleFactor) + chY1);
637                 */
638             }
639         }
640         else TRACELOG(LOG_WARNING, "FONT: Failed to process TTF font data");
641 
642         if (genFontChars) RL_FREE(fontChars);
643     }
644 #endif
645 
646     return chars;
647 }
648 
649 // Generate image font atlas using chars info
650 // NOTE: Packing method: 0-Default, 1-Skyline
651 #if defined(SUPPORT_FILEFORMAT_TTF)
GenImageFontAtlas(const CharInfo * chars,Rectangle ** charRecs,int charsCount,int fontSize,int padding,int packMethod)652 Image GenImageFontAtlas(const CharInfo *chars, Rectangle **charRecs, int charsCount, int fontSize, int padding, int packMethod)
653 {
654     Image atlas = { 0 };
655 
656     if (chars == NULL)
657     {
658         TraceLog(LOG_WARNING, "FONT: Provided chars info not valid, returning empty image atlas");
659         return atlas;
660     }
661 
662     *charRecs = NULL;
663 
664     // In case no chars count provided we suppose default of 95
665     charsCount = (charsCount > 0)? charsCount : 95;
666 
667     // NOTE: Rectangles memory is loaded here!
668     Rectangle *recs = (Rectangle *)RL_MALLOC(charsCount*sizeof(Rectangle));
669 
670     // Calculate image size based on required pixel area
671     // NOTE 1: Image is forced to be squared and POT... very conservative!
672     // NOTE 2: SDF font characters already contain an internal padding,
673     // so image size would result bigger than default font type
674     float requiredArea = 0;
675     for (int i = 0; i < charsCount; i++) requiredArea += ((chars[i].image.width + 2*padding)*(chars[i].image.height + 2*padding));
676     float guessSize = sqrtf(requiredArea)*1.3f;
677     int imageSize = (int)powf(2, ceilf(logf((float)guessSize)/logf(2)));  // Calculate next POT
678 
679     atlas.width = imageSize;   // Atlas bitmap width
680     atlas.height = imageSize;  // Atlas bitmap height
681     atlas.data = (unsigned char *)RL_CALLOC(1, atlas.width*atlas.height);      // Create a bitmap to store characters (8 bpp)
682     atlas.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
683     atlas.mipmaps = 1;
684 
685     // DEBUG: We can see padding in the generated image setting a gray background...
686     //for (int i = 0; i < atlas.width*atlas.height; i++) ((unsigned char *)atlas.data)[i] = 100;
687 
688     if (packMethod == 0)   // Use basic packing algorythm
689     {
690         int offsetX = padding;
691         int offsetY = padding;
692 
693         // NOTE: Using simple packaging, one char after another
694         for (int i = 0; i < charsCount; i++)
695         {
696             // Copy pixel data from fc.data to atlas
697             for (int y = 0; y < chars[i].image.height; y++)
698             {
699                 for (int x = 0; x < chars[i].image.width; x++)
700                 {
701                     ((unsigned char *)atlas.data)[(offsetY + y)*atlas.width + (offsetX + x)] = ((unsigned char *)chars[i].image.data)[y*chars[i].image.width + x];
702                 }
703             }
704 
705             // Fill chars rectangles in atlas info
706             recs[i].x = (float)offsetX;
707             recs[i].y = (float)offsetY;
708             recs[i].width = (float)chars[i].image.width;
709             recs[i].height = (float)chars[i].image.height;
710 
711             // Move atlas position X for next character drawing
712             offsetX += (chars[i].image.width + 2*padding);
713 
714             if (offsetX >= (atlas.width - chars[i].image.width - 2*padding))
715             {
716                 offsetX = padding;
717 
718                 // NOTE: Be careful on offsetY for SDF fonts, by default SDF
719                 // use an internal padding of 4 pixels, it means char rectangle
720                 // height is bigger than fontSize, it could be up to (fontSize + 8)
721                 offsetY += (fontSize + 2*padding);
722 
723                 if (offsetY > (atlas.height - fontSize - padding)) break;
724             }
725         }
726     }
727     else if (packMethod == 1)  // Use Skyline rect packing algorythm (stb_pack_rect)
728     {
729         stbrp_context *context = (stbrp_context *)RL_MALLOC(sizeof(*context));
730         stbrp_node *nodes = (stbrp_node *)RL_MALLOC(charsCount*sizeof(*nodes));
731 
732         stbrp_init_target(context, atlas.width, atlas.height, nodes, charsCount);
733         stbrp_rect *rects = (stbrp_rect *)RL_MALLOC(charsCount*sizeof(stbrp_rect));
734 
735         // Fill rectangles for packaging
736         for (int i = 0; i < charsCount; i++)
737         {
738             rects[i].id = i;
739             rects[i].w = chars[i].image.width + 2*padding;
740             rects[i].h = chars[i].image.height + 2*padding;
741         }
742 
743         // Package rectangles into atlas
744         stbrp_pack_rects(context, rects, charsCount);
745 
746         for (int i = 0; i < charsCount; i++)
747         {
748             // It return char rectangles in atlas
749             recs[i].x = rects[i].x + (float)padding;
750             recs[i].y = rects[i].y + (float)padding;
751             recs[i].width = (float)chars[i].image.width;
752             recs[i].height = (float)chars[i].image.height;
753 
754             if (rects[i].was_packed)
755             {
756                 // Copy pixel data from fc.data to atlas
757                 for (int y = 0; y < chars[i].image.height; y++)
758                 {
759                     for (int x = 0; x < chars[i].image.width; x++)
760                     {
761                         ((unsigned char *)atlas.data)[(rects[i].y + padding + y)*atlas.width + (rects[i].x + padding + x)] = ((unsigned char *)chars[i].image.data)[y*chars[i].image.width + x];
762                     }
763                 }
764             }
765             else TRACELOG(LOG_WARNING, "FONT: Failed to package character (%i)", i);
766         }
767 
768         RL_FREE(rects);
769         RL_FREE(nodes);
770         RL_FREE(context);
771     }
772 
773     // TODO: Crop image if required for smaller size
774 
775     // Convert image data from GRAYSCALE to GRAY_ALPHA
776     unsigned char *dataGrayAlpha = (unsigned char *)RL_MALLOC(atlas.width*atlas.height*sizeof(unsigned char)*2); // Two channels
777 
778     for (int i = 0, k = 0; i < atlas.width*atlas.height; i++, k += 2)
779     {
780         dataGrayAlpha[k] = 255;
781         dataGrayAlpha[k + 1] = ((unsigned char *)atlas.data)[i];
782     }
783 
784     RL_FREE(atlas.data);
785     atlas.data = dataGrayAlpha;
786     atlas.format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA;
787 
788     *charRecs = recs;
789 
790     return atlas;
791 }
792 #endif
793 
794 // Unload font chars info data (RAM)
UnloadFontData(CharInfo * chars,int charsCount)795 void UnloadFontData(CharInfo *chars, int charsCount)
796 {
797     for (int i = 0; i < charsCount; i++) UnloadImage(chars[i].image);
798 
799     RL_FREE(chars);
800 }
801 
802 // Unload Font from GPU memory (VRAM)
UnloadFont(Font font)803 void UnloadFont(Font font)
804 {
805     // NOTE: Make sure font is not default font (fallback)
806     if (font.texture.id != GetFontDefault().texture.id)
807     {
808         UnloadFontData(font.chars, font.charsCount);
809         UnloadTexture(font.texture);
810         RL_FREE(font.recs);
811 
812         TRACELOGD("FONT: Unloaded font data from RAM and VRAM");
813     }
814 }
815 
816 // Draw current FPS
817 // NOTE: Uses default font
DrawFPS(int posX,int posY)818 void DrawFPS(int posX, int posY)
819 {
820     Color color = LIME; // good fps
821     int fps = GetFPS();
822 
823     if (fps < 30 && fps >= 15) color = ORANGE;  // warning FPS
824     else if (fps < 15) color = RED;    // bad FPS
825 
826     DrawText(TextFormat("%2i FPS", GetFPS()), posX, posY, 20, color);
827 }
828 
829 // Draw text (using default font)
830 // NOTE: fontSize work like in any drawing program but if fontSize is lower than font-base-size, then font-base-size is used
831 // NOTE: chars spacing is proportional to fontSize
DrawText(const char * text,int posX,int posY,int fontSize,Color color)832 void DrawText(const char *text, int posX, int posY, int fontSize, Color color)
833 {
834     // Check if default font has been loaded
835     if (GetFontDefault().texture.id != 0)
836     {
837         Vector2 position = { (float)posX, (float)posY };
838 
839         int defaultFontSize = 10;   // Default Font chars height in pixel
840         if (fontSize < defaultFontSize) fontSize = defaultFontSize;
841         int spacing = fontSize/defaultFontSize;
842 
843         DrawTextEx(GetFontDefault(), text, position, (float)fontSize, (float)spacing, color);
844     }
845 }
846 
847 // Draw text using Font
848 // NOTE: chars spacing is NOT proportional to fontSize
DrawTextEx(Font font,const char * text,Vector2 position,float fontSize,float spacing,Color tint)849 void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint)
850 {
851     int length = TextLength(text);      // Total length in bytes of the text, scanned by codepoints in loop
852 
853     int textOffsetY = 0;            // Offset between lines (on line break '\n')
854     float textOffsetX = 0.0f;       // Offset X to next character to draw
855 
856     float scaleFactor = fontSize/font.baseSize;     // Character quad scaling factor
857 
858     for (int i = 0; i < length;)
859     {
860         // Get next codepoint from byte string and glyph index in font
861         int codepointByteCount = 0;
862         int codepoint = GetNextCodepoint(&text[i], &codepointByteCount);
863         int index = GetGlyphIndex(font, codepoint);
864 
865         // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
866         // but we need to draw all of the bad bytes using the '?' symbol moving one byte
867         if (codepoint == 0x3f) codepointByteCount = 1;
868 
869         if (codepoint == '\n')
870         {
871             // NOTE: Fixed line spacing of 1.5 line-height
872             // TODO: Support custom line spacing defined by user
873             textOffsetY += (int)((font.baseSize + font.baseSize/2)*scaleFactor);
874             textOffsetX = 0.0f;
875         }
876         else
877         {
878             if ((codepoint != ' ') && (codepoint != '\t'))
879             {
880                 DrawTextCodepoint(font, codepoint, (Vector2){ position.x + textOffsetX, position.y + textOffsetY }, fontSize, tint);
881             }
882 
883             if (font.chars[index].advanceX == 0) textOffsetX += ((float)font.recs[index].width*scaleFactor + spacing);
884             else textOffsetX += ((float)font.chars[index].advanceX*scaleFactor + spacing);
885         }
886 
887         i += codepointByteCount;   // Move text bytes counter to next codepoint
888     }
889 }
890 
891 // Draw text using font inside rectangle limits
DrawTextRec(Font font,const char * text,Rectangle rec,float fontSize,float spacing,bool wordWrap,Color tint)892 void DrawTextRec(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint)
893 {
894     DrawTextRecEx(font, text, rec, fontSize, spacing, wordWrap, tint, 0, 0, WHITE, WHITE);
895 }
896 
897 // Draw text using font inside rectangle limits with support for text selection
DrawTextRecEx(Font font,const char * text,Rectangle rec,float fontSize,float spacing,bool wordWrap,Color tint,int selectStart,int selectLength,Color selectTint,Color selectBackTint)898 void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, int selectStart, int selectLength, Color selectTint, Color selectBackTint)
899 {
900     int length = TextLength(text);  // Total length in bytes of the text, scanned by codepoints in loop
901 
902     int textOffsetY = 0;            // Offset between lines (on line break '\n')
903     float textOffsetX = 0.0f;       // Offset X to next character to draw
904 
905     float scaleFactor = fontSize/font.baseSize;     // Character quad scaling factor
906 
907     // Word/character wrapping mechanism variables
908     enum { MEASURE_STATE = 0, DRAW_STATE = 1 };
909     int state = wordWrap? MEASURE_STATE : DRAW_STATE;
910 
911     int startLine = -1;         // Index where to begin drawing (where a line begins)
912     int endLine = -1;           // Index where to stop drawing (where a line ends)
913     int lastk = -1;             // Holds last value of the character position
914 
915     for (int i = 0, k = 0; i < length; i++, k++)
916     {
917         // Get next codepoint from byte string and glyph index in font
918         int codepointByteCount = 0;
919         int codepoint = GetNextCodepoint(&text[i], &codepointByteCount);
920         int index = GetGlyphIndex(font, codepoint);
921 
922         // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
923         // but we need to draw all of the bad bytes using the '?' symbol moving one byte
924         if (codepoint == 0x3f) codepointByteCount = 1;
925         i += (codepointByteCount - 1);
926 
927         int glyphWidth = 0;
928         if (codepoint != '\n')
929         {
930             glyphWidth = (font.chars[index].advanceX == 0)?
931                          (int)(font.recs[index].width*scaleFactor + spacing):
932                          (int)(font.chars[index].advanceX*scaleFactor + spacing);
933         }
934 
935         // NOTE: When wordWrap is ON we first measure how much of the text we can draw before going outside of the rec container
936         // We store this info in startLine and endLine, then we change states, draw the text between those two variables
937         // and change states again and again recursively until the end of the text (or until we get outside of the container).
938         // When wordWrap is OFF we don't need the measure state so we go to the drawing state immediately
939         // and begin drawing on the next line before we can get outside the container.
940         if (state == MEASURE_STATE)
941         {
942             // TODO: There are multiple types of spaces in UNICODE, maybe it's a good idea to add support for more
943             // Ref: http://jkorpela.fi/chars/spaces.html
944             if ((codepoint == ' ') || (codepoint == '\t') || (codepoint == '\n')) endLine = i;
945 
946             if ((textOffsetX + glyphWidth + 1) >= rec.width)
947             {
948                 endLine = (endLine < 1)? i : endLine;
949                 if (i == endLine) endLine -= codepointByteCount;
950                 if ((startLine + codepointByteCount) == endLine) endLine = (i - codepointByteCount);
951 
952                 state = !state;
953             }
954             else if ((i + 1) == length)
955             {
956                 endLine = i;
957 
958                 state = !state;
959             }
960             else if (codepoint == '\n') state = !state;
961 
962             if (state == DRAW_STATE)
963             {
964                 textOffsetX = 0;
965                 i = startLine;
966                 glyphWidth = 0;
967 
968                 // Save character position when we switch states
969                 int tmp = lastk;
970                 lastk = k - 1;
971                 k = tmp;
972             }
973         }
974         else
975         {
976             if (codepoint == '\n')
977             {
978                 if (!wordWrap)
979                 {
980                     textOffsetY += (int)((font.baseSize + font.baseSize/2)*scaleFactor);
981                     textOffsetX = 0;
982                 }
983             }
984             else
985             {
986                 if (!wordWrap && ((textOffsetX + glyphWidth + 1) >= rec.width))
987                 {
988                     textOffsetY += (int)((font.baseSize + font.baseSize/2)*scaleFactor);
989                     textOffsetX = 0;
990                 }
991 
992                 // When text overflows rectangle height limit, just stop drawing
993                 if ((textOffsetY + (int)(font.baseSize*scaleFactor)) > rec.height) break;
994 
995                 // Draw selection background
996                 bool isGlyphSelected = false;
997                 if ((selectStart >= 0) && (k >= selectStart) && (k < (selectStart + selectLength)))
998                 {
999                     DrawRectangleRec((Rectangle){ rec.x + textOffsetX - 1, rec.y + textOffsetY, (float)glyphWidth, (float)font.baseSize*scaleFactor }, selectBackTint);
1000                     isGlyphSelected = true;
1001                 }
1002 
1003                 // Draw current character glyph
1004                 if ((codepoint != ' ') && (codepoint != '\t'))
1005                 {
1006                     DrawTextCodepoint(font, codepoint, (Vector2){ rec.x + textOffsetX, rec.y + textOffsetY }, fontSize, isGlyphSelected? selectTint : tint);
1007                 }
1008             }
1009 
1010             if (wordWrap && (i == endLine))
1011             {
1012                 textOffsetY += (int)((font.baseSize + font.baseSize/2)*scaleFactor);
1013                 textOffsetX = 0;
1014                 startLine = endLine;
1015                 endLine = -1;
1016                 glyphWidth = 0;
1017                 selectStart += lastk - k;
1018                 k = lastk;
1019 
1020                 state = !state;
1021             }
1022         }
1023 
1024         textOffsetX += glyphWidth;
1025     }
1026 }
1027 
1028 // Draw one character (codepoint)
DrawTextCodepoint(Font font,int codepoint,Vector2 position,float fontSize,Color tint)1029 void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSize, Color tint)
1030 {
1031     // Character index position in sprite font
1032     // NOTE: In case a codepoint is not available in the font, index returned points to '?'
1033     int index = GetGlyphIndex(font, codepoint);
1034     float scaleFactor = fontSize/font.baseSize;     // Character quad scaling factor
1035 
1036     // Character destination rectangle on screen
1037     // NOTE: We consider charsPadding on drawing
1038     Rectangle dstRec = { position.x + font.chars[index].offsetX*scaleFactor - (float)font.charsPadding*scaleFactor,
1039                       position.y + font.chars[index].offsetY*scaleFactor - (float)font.charsPadding*scaleFactor,
1040                       (font.recs[index].width + 2.0f*font.charsPadding)*scaleFactor,
1041                       (font.recs[index].height + 2.0f*font.charsPadding)*scaleFactor };
1042 
1043     // Character source rectangle from font texture atlas
1044     // NOTE: We consider chars padding when drawing, it could be required for outline/glow shader effects
1045     Rectangle srcRec = { font.recs[index].x - (float)font.charsPadding, font.recs[index].y - (float)font.charsPadding,
1046                          font.recs[index].width + 2.0f*font.charsPadding, font.recs[index].height + 2.0f*font.charsPadding };
1047 
1048     // Draw the character texture on the screen
1049     DrawTexturePro(font.texture, srcRec, dstRec, (Vector2){ 0, 0 }, 0.0f, tint);
1050 }
1051 
1052 // Measure string width for default font
MeasureText(const char * text,int fontSize)1053 int MeasureText(const char *text, int fontSize)
1054 {
1055     Vector2 vec = { 0.0f, 0.0f };
1056 
1057     // Check if default font has been loaded
1058     if (GetFontDefault().texture.id != 0)
1059     {
1060         int defaultFontSize = 10;   // Default Font chars height in pixel
1061         if (fontSize < defaultFontSize) fontSize = defaultFontSize;
1062         int spacing = fontSize/defaultFontSize;
1063 
1064         vec = MeasureTextEx(GetFontDefault(), text, (float)fontSize, (float)spacing);
1065     }
1066 
1067     return (int)vec.x;
1068 }
1069 
1070 // Measure string size for Font
MeasureTextEx(Font font,const char * text,float fontSize,float spacing)1071 Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing)
1072 {
1073     int len = TextLength(text);
1074     int tempLen = 0;                // Used to count longer text line num chars
1075     int lenCounter = 0;
1076 
1077     float textWidth = 0.0f;
1078     float tempTextWidth = 0.0f;     // Used to count longer text line width
1079 
1080     float textHeight = (float)font.baseSize;
1081     float scaleFactor = fontSize/(float)font.baseSize;
1082 
1083     int letter = 0;                 // Current character
1084     int index = 0;                  // Index position in sprite font
1085 
1086     for (int i = 0; i < len; i++)
1087     {
1088         lenCounter++;
1089 
1090         int next = 0;
1091         letter = GetNextCodepoint(&text[i], &next);
1092         index = GetGlyphIndex(font, letter);
1093 
1094         // NOTE: normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
1095         // but we need to draw all of the bad bytes using the '?' symbol so to not skip any we set next = 1
1096         if (letter == 0x3f) next = 1;
1097         i += next - 1;
1098 
1099         if (letter != '\n')
1100         {
1101             if (font.chars[index].advanceX != 0) textWidth += font.chars[index].advanceX;
1102             else textWidth += (font.recs[index].width + font.chars[index].offsetX);
1103         }
1104         else
1105         {
1106             if (tempTextWidth < textWidth) tempTextWidth = textWidth;
1107             lenCounter = 0;
1108             textWidth = 0;
1109             textHeight += ((float)font.baseSize*1.5f); // NOTE: Fixed line spacing of 1.5 lines
1110         }
1111 
1112         if (tempLen < lenCounter) tempLen = lenCounter;
1113     }
1114 
1115     if (tempTextWidth < textWidth) tempTextWidth = textWidth;
1116 
1117     Vector2 vec = { 0 };
1118     vec.x = tempTextWidth*scaleFactor + (float)((tempLen - 1)*spacing); // Adds chars spacing to measure
1119     vec.y = textHeight*scaleFactor;
1120 
1121     return vec;
1122 }
1123 
1124 // Returns index position for a unicode character on spritefont
GetGlyphIndex(Font font,int codepoint)1125 int GetGlyphIndex(Font font, int codepoint)
1126 {
1127 #ifndef GLYPH_NOTFOUND_CHAR_FALLBACK
1128     #define GLYPH_NOTFOUND_CHAR_FALLBACK     63      // Character used if requested codepoint is not found: '?'
1129 #endif
1130 
1131 // Support charsets with any characters order
1132 #define SUPPORT_UNORDERED_CHARSET
1133 #if defined(SUPPORT_UNORDERED_CHARSET)
1134     int index = GLYPH_NOTFOUND_CHAR_FALLBACK;
1135 
1136     for (int i = 0; i < font.charsCount; i++)
1137     {
1138         if (font.chars[i].value == codepoint)
1139         {
1140             index = i;
1141             break;
1142         }
1143     }
1144 
1145     return index;
1146 #else
1147     return (codepoint - 32);
1148 #endif
1149 }
1150 
1151 //----------------------------------------------------------------------------------
1152 // Text strings management functions
1153 //----------------------------------------------------------------------------------
1154 // Get text length in bytes, check for \0 character
TextLength(const char * text)1155 unsigned int TextLength(const char *text)
1156 {
1157     unsigned int length = 0; //strlen(text)
1158 
1159     if (text != NULL)
1160     {
1161         while (*text++) length++;
1162     }
1163 
1164     return length;
1165 }
1166 
1167 // Formatting of text with variables to 'embed'
1168 // WARNING: String returned will expire after this function is called MAX_TEXTFORMAT_BUFFERS times
TextFormat(const char * text,...)1169 const char *TextFormat(const char *text, ...)
1170 {
1171 #ifndef MAX_TEXTFORMAT_BUFFERS
1172     #define MAX_TEXTFORMAT_BUFFERS 4        // Maximum number of static buffers for text formatting
1173 #endif
1174 
1175     // We create an array of buffers so strings don't expire until MAX_TEXTFORMAT_BUFFERS invocations
1176     static char buffers[MAX_TEXTFORMAT_BUFFERS][MAX_TEXT_BUFFER_LENGTH] = { 0 };
1177     static int  index = 0;
1178 
1179     char *currentBuffer = buffers[index];
1180     memset(currentBuffer, 0, MAX_TEXT_BUFFER_LENGTH);   // Clear buffer before using
1181 
1182     va_list args;
1183     va_start(args, text);
1184     vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args);
1185     va_end(args);
1186 
1187     index += 1;     // Move to next buffer for next function call
1188     if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0;
1189 
1190     return currentBuffer;
1191 }
1192 
1193 // Get integer value from text
1194 // NOTE: This function replaces atoi() [stdlib.h]
TextToInteger(const char * text)1195 int TextToInteger(const char *text)
1196 {
1197     int value = 0;
1198     int sign = 1;
1199 
1200     if ((text[0] == '+') || (text[0] == '-'))
1201     {
1202         if (text[0] == '-') sign = -1;
1203         text++;
1204     }
1205 
1206     for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); ++i) value = value*10 + (int)(text[i] - '0');
1207 
1208     return value*sign;
1209 }
1210 
1211 #if defined(SUPPORT_TEXT_MANIPULATION)
1212 // Copy one string to another, returns bytes copied
TextCopy(char * dst,const char * src)1213 int TextCopy(char *dst, const char *src)
1214 {
1215     int bytes = 0;
1216 
1217     if (dst != NULL)
1218     {
1219         while (*src != '\0')
1220         {
1221             *dst = *src;
1222             dst++;
1223             src++;
1224 
1225             bytes++;
1226         }
1227 
1228         *dst = '\0';
1229     }
1230 
1231     return bytes;
1232 }
1233 
1234 // Check if two text string are equal
1235 // REQUIRES: strcmp()
TextIsEqual(const char * text1,const char * text2)1236 bool TextIsEqual(const char *text1, const char *text2)
1237 {
1238     bool result = false;
1239 
1240     if (strcmp(text1, text2) == 0) result = true;
1241 
1242     return result;
1243 }
1244 
1245 // Get a piece of a text string
TextSubtext(const char * text,int position,int length)1246 const char *TextSubtext(const char *text, int position, int length)
1247 {
1248     static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
1249 
1250     int textLength = TextLength(text);
1251 
1252     if (position >= textLength)
1253     {
1254         position = textLength - 1;
1255         length = 0;
1256     }
1257 
1258     if (length >= textLength) length = textLength;
1259 
1260     for (int c = 0 ; c < length ; c++)
1261     {
1262         *(buffer + c) = *(text + position);
1263         text++;
1264     }
1265 
1266     *(buffer + length) = '\0';
1267 
1268     return buffer;
1269 }
1270 
1271 // Replace text string
1272 // REQUIRES: strstr(), strncpy(), strcpy()
1273 // WARNING: Internally allocated memory must be freed by the user (if return != NULL)
TextReplace(char * text,const char * replace,const char * by)1274 char *TextReplace(char *text, const char *replace, const char *by)
1275 {
1276     // Sanity checks and initialization
1277     if (!text || !replace || !by) return NULL;
1278 
1279     char *result;
1280 
1281     char *insertPoint;      // Next insert point
1282     char *temp;             // Temp pointer
1283     int replaceLen;         // Replace string length of (the string to remove)
1284     int byLen;              // Replacement length (the string to replace replace by)
1285     int lastReplacePos;     // Distance between replace and end of last replace
1286     int count;              // Number of replacements
1287 
1288     replaceLen = TextLength(replace);
1289     if (replaceLen == 0) return NULL;  // Empty replace causes infinite loop during count
1290 
1291     byLen = TextLength(by);
1292 
1293     // Count the number of replacements needed
1294     insertPoint = text;
1295     for (count = 0; (temp = strstr(insertPoint, replace)); count++) insertPoint = temp + replaceLen;
1296 
1297     // Allocate returning string and point temp to it
1298     temp = result = RL_MALLOC(TextLength(text) + (byLen - replaceLen)*count + 1);
1299 
1300     if (!result) return NULL;   // Memory could not be allocated
1301 
1302     // First time through the loop, all the variable are set correctly from here on,
1303     //    temp points to the end of the result string
1304     //    insertPoint points to the next occurrence of replace in text
1305     //    text points to the remainder of text after "end of replace"
1306     while (count--)
1307     {
1308         insertPoint = strstr(text, replace);
1309         lastReplacePos = (int)(insertPoint - text);
1310         temp = strncpy(temp, text, lastReplacePos) + lastReplacePos;
1311         temp = strcpy(temp, by) + byLen;
1312         text += lastReplacePos + replaceLen; // Move to next "end of replace"
1313     }
1314 
1315     // Copy remaind text part after replacement to result (pointed by moving temp)
1316     strcpy(temp, text);
1317 
1318     return result;
1319 }
1320 
1321 // Insert text in a specific position, moves all text forward
1322 // WARNING: Allocated memory should be manually freed
TextInsert(const char * text,const char * insert,int position)1323 char *TextInsert(const char *text, const char *insert, int position)
1324 {
1325     int textLen = TextLength(text);
1326     int insertLen =  TextLength(insert);
1327 
1328     char *result = (char *)RL_MALLOC(textLen + insertLen + 1);
1329 
1330     for (int i = 0; i < position; i++) result[i] = text[i];
1331     for (int i = position; i < insertLen + position; i++) result[i] = insert[i];
1332     for (int i = (insertLen + position); i < (textLen + insertLen); i++) result[i] = text[i];
1333 
1334     result[textLen + insertLen] = '\0';     // Make sure text string is valid!
1335 
1336     return result;
1337 }
1338 
1339 // Join text strings with delimiter
1340 // REQUIRES: memset(), memcpy()
TextJoin(const char ** textList,int count,const char * delimiter)1341 const char *TextJoin(const char **textList, int count, const char *delimiter)
1342 {
1343     static char text[MAX_TEXT_BUFFER_LENGTH] = { 0 };
1344     memset(text, 0, MAX_TEXT_BUFFER_LENGTH);
1345     char *textPtr = text;
1346 
1347     int totalLength = 0;
1348     int delimiterLen = TextLength(delimiter);
1349 
1350     for (int i = 0; i < count; i++)
1351     {
1352         int textLength = TextLength(textList[i]);
1353 
1354         // Make sure joined text could fit inside MAX_TEXT_BUFFER_LENGTH
1355         if ((totalLength + textLength) < MAX_TEXT_BUFFER_LENGTH)
1356         {
1357             memcpy(textPtr, textList[i], textLength);
1358             totalLength += textLength;
1359             textPtr += textLength;
1360 
1361             if ((delimiterLen > 0) && (i < (count - 1)))
1362             {
1363                 memcpy(textPtr, delimiter, delimiterLen);
1364                 totalLength += delimiterLen;
1365                 textPtr += delimiterLen;
1366             }
1367         }
1368     }
1369 
1370     return text;
1371 }
1372 
1373 // Split string into multiple strings
1374 // REQUIRES: memset()
TextSplit(const char * text,char delimiter,int * count)1375 const char **TextSplit(const char *text, char delimiter, int *count)
1376 {
1377     // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter)
1378     // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated,
1379     // all used memory is static... it has some limitations:
1380     //      1. Maximum number of possible split strings is set by MAX_TEXTSPLIT_COUNT
1381     //      2. Maximum size of text to split is MAX_TEXT_BUFFER_LENGTH
1382 
1383     static const char *result[MAX_TEXTSPLIT_COUNT] = { NULL };
1384     static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
1385     memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
1386 
1387     result[0] = buffer;
1388     int counter = 0;
1389 
1390     if (text != NULL)
1391     {
1392         counter = 1;
1393 
1394         // Count how many substrings we have on text and point to every one
1395         for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++)
1396         {
1397             buffer[i] = text[i];
1398             if (buffer[i] == '\0') break;
1399             else if (buffer[i] == delimiter)
1400             {
1401                 buffer[i] = '\0';   // Set an end of string at this point
1402                 result[counter] = buffer + i + 1;
1403                 counter++;
1404 
1405                 if (counter == MAX_TEXTSPLIT_COUNT) break;
1406             }
1407         }
1408     }
1409 
1410     *count = counter;
1411     return result;
1412 }
1413 
1414 // Append text at specific position and move cursor!
1415 // REQUIRES: strcpy()
TextAppend(char * text,const char * append,int * position)1416 void TextAppend(char *text, const char *append, int *position)
1417 {
1418     strcpy(text + *position, append);
1419     *position += TextLength(append);
1420 }
1421 
1422 // Find first text occurrence within a string
1423 // REQUIRES: strstr()
TextFindIndex(const char * text,const char * find)1424 int TextFindIndex(const char *text, const char *find)
1425 {
1426     int position = -1;
1427 
1428     char *ptr = strstr(text, find);
1429 
1430     if (ptr != NULL) position = (int)(ptr - text);
1431 
1432     return position;
1433 }
1434 
1435 // Get upper case version of provided string
1436 // REQUIRES: toupper()
TextToUpper(const char * text)1437 const char *TextToUpper(const char *text)
1438 {
1439     static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
1440 
1441     for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++)
1442     {
1443         if (text[i] != '\0')
1444         {
1445             buffer[i] = (char)toupper(text[i]);
1446             //if ((text[i] >= 'a') && (text[i] <= 'z')) buffer[i] = text[i] - 32;
1447 
1448             // TODO: Support Utf8 diacritics!
1449             //if ((text[i] >= 'à') && (text[i] <= 'ý')) buffer[i] = text[i] - 32;
1450         }
1451         else { buffer[i] = '\0'; break; }
1452     }
1453 
1454     return buffer;
1455 }
1456 
1457 // Get lower case version of provided string
1458 // REQUIRES: tolower()
TextToLower(const char * text)1459 const char *TextToLower(const char *text)
1460 {
1461     static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
1462 
1463     for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++)
1464     {
1465         if (text[i] != '\0')
1466         {
1467             buffer[i] = (char)tolower(text[i]);
1468             //if ((text[i] >= 'A') && (text[i] <= 'Z')) buffer[i] = text[i] + 32;
1469         }
1470         else { buffer[i] = '\0'; break; }
1471     }
1472 
1473     return buffer;
1474 }
1475 
1476 // Get Pascal case notation version of provided string
1477 // REQUIRES: toupper()
TextToPascal(const char * text)1478 const char *TextToPascal(const char *text)
1479 {
1480     static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
1481 
1482     buffer[0] = (char)toupper(text[0]);
1483 
1484     for (int i = 1, j = 1; i < MAX_TEXT_BUFFER_LENGTH; i++, j++)
1485     {
1486         if (text[j] != '\0')
1487         {
1488             if (text[j] != '_') buffer[i] = text[j];
1489             else
1490             {
1491                 j++;
1492                 buffer[i] = (char)toupper(text[j]);
1493             }
1494         }
1495         else { buffer[i] = '\0'; break; }
1496     }
1497 
1498     return buffer;
1499 }
1500 
1501 // Encode text codepoint into utf8 text
1502 // REQUIRES: memcpy()
1503 // WARNING: Allocated memory should be manually freed
TextToUtf8(int * codepoints,int length)1504 char *TextToUtf8(int *codepoints, int length)
1505 {
1506     // We allocate enough memory fo fit all possible codepoints
1507     // NOTE: 5 bytes for every codepoint should be enough
1508     char *text = (char *)RL_CALLOC(length*5, 1);
1509     const char *utf8 = NULL;
1510     int size = 0;
1511 
1512     for (int i = 0, bytes = 0; i < length; i++)
1513     {
1514         utf8 = CodepointToUtf8(codepoints[i], &bytes);
1515         memcpy(text + size, utf8, bytes);
1516         size += bytes;
1517     }
1518 
1519     // Resize memory to text length + string NULL terminator
1520     void *ptr = RL_REALLOC(text, size + 1);
1521 
1522     if (ptr != NULL) text = (char *)ptr;
1523 
1524     return text;
1525 }
1526 
1527 // Encode codepoint into utf8 text (char array length returned as parameter)
CodepointToUtf8(int codepoint,int * byteLength)1528 RLAPI const char *CodepointToUtf8(int codepoint, int *byteLength)
1529 {
1530     static char utf8[6] = { 0 };
1531     int length = 0;
1532 
1533     if (codepoint <= 0x7f)
1534     {
1535         utf8[0] = (char)codepoint;
1536         length = 1;
1537     }
1538     else if (codepoint <= 0x7ff)
1539     {
1540         utf8[0] = (char)(((codepoint >> 6) & 0x1f) | 0xc0);
1541         utf8[1] = (char)((codepoint & 0x3f) | 0x80);
1542         length = 2;
1543     }
1544     else if (codepoint <= 0xffff)
1545     {
1546         utf8[0] = (char)(((codepoint >> 12) & 0x0f) | 0xe0);
1547         utf8[1] = (char)(((codepoint >>  6) & 0x3f) | 0x80);
1548         utf8[2] = (char)((codepoint & 0x3f) | 0x80);
1549         length = 3;
1550     }
1551     else if (codepoint <= 0x10ffff)
1552     {
1553         utf8[0] = (char)(((codepoint >> 18) & 0x07) | 0xf0);
1554         utf8[1] = (char)(((codepoint >> 12) & 0x3f) | 0x80);
1555         utf8[2] = (char)(((codepoint >>  6) & 0x3f) | 0x80);
1556         utf8[3] = (char)((codepoint & 0x3f) | 0x80);
1557         length = 4;
1558     }
1559 
1560     *byteLength = length;
1561 
1562     return utf8;
1563 }
1564 
1565 // Get all codepoints in a string, codepoints count returned by parameters
1566 // REQUIRES: memset()
GetCodepoints(const char * text,int * count)1567 int *GetCodepoints(const char *text, int *count)
1568 {
1569     static int codepoints[MAX_TEXT_UNICODE_CHARS] = { 0 };
1570     memset(codepoints, 0, MAX_TEXT_UNICODE_CHARS*sizeof(int));
1571 
1572     int bytesProcessed = 0;
1573     int textLength = TextLength(text);
1574     int codepointsCount = 0;
1575 
1576     for (int i = 0; i < textLength; codepointsCount++)
1577     {
1578         codepoints[codepointsCount] = GetNextCodepoint(text + i, &bytesProcessed);
1579         i += bytesProcessed;
1580     }
1581 
1582     *count = codepointsCount;
1583 
1584     return codepoints;
1585 }
1586 
1587 // Returns total number of characters(codepoints) in a UTF8 encoded text, until '\0' is found
1588 // NOTE: If an invalid UTF8 sequence is encountered a '?'(0x3f) codepoint is counted instead
GetCodepointsCount(const char * text)1589 int GetCodepointsCount(const char *text)
1590 {
1591     unsigned int len = 0;
1592     char *ptr = (char *)&text[0];
1593 
1594     while (*ptr != '\0')
1595     {
1596         int next = 0;
1597         int letter = GetNextCodepoint(ptr, &next);
1598 
1599         if (letter == 0x3f) ptr += 1;
1600         else ptr += next;
1601 
1602         len++;
1603     }
1604 
1605     return len;
1606 }
1607 #endif      // SUPPORT_TEXT_MANIPULATION
1608 
1609 // Returns next codepoint in a UTF8 encoded text, scanning until '\0' is found
1610 // When a invalid UTF8 byte is encountered we exit as soon as possible and a '?'(0x3f) codepoint is returned
1611 // Total number of bytes processed are returned as a parameter
1612 // NOTE: the standard says U+FFFD should be returned in case of errors
1613 // but that character is not supported by the default font in raylib
1614 // TODO: Optimize this code for speed!!
GetNextCodepoint(const char * text,int * bytesProcessed)1615 int GetNextCodepoint(const char *text, int *bytesProcessed)
1616 {
1617 /*
1618     UTF8 specs from https://www.ietf.org/rfc/rfc3629.txt
1619 
1620     Char. number range  |        UTF-8 octet sequence
1621       (hexadecimal)    |              (binary)
1622     --------------------+---------------------------------------------
1623     0000 0000-0000 007F | 0xxxxxxx
1624     0000 0080-0000 07FF | 110xxxxx 10xxxxxx
1625     0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
1626     0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
1627 */
1628     // NOTE: on decode errors we return as soon as possible
1629 
1630     int code = 0x3f;   // Codepoint (defaults to '?')
1631     int octet = (unsigned char)(text[0]); // The first UTF8 octet
1632     *bytesProcessed = 1;
1633 
1634     if (octet <= 0x7f)
1635     {
1636         // Only one octet (ASCII range x00-7F)
1637         code = text[0];
1638     }
1639     else if ((octet & 0xe0) == 0xc0)
1640     {
1641         // Two octets
1642         // [0]xC2-DF    [1]UTF8-tail(x80-BF)
1643         unsigned char octet1 = text[1];
1644 
1645         if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence
1646 
1647         if ((octet >= 0xc2) && (octet <= 0xdf))
1648         {
1649             code = ((octet & 0x1f) << 6) | (octet1 & 0x3f);
1650             *bytesProcessed = 2;
1651         }
1652     }
1653     else if ((octet & 0xf0) == 0xe0)
1654     {
1655         // Three octets
1656         unsigned char octet1 = text[1];
1657         unsigned char octet2 = '\0';
1658 
1659         if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence
1660 
1661         octet2 = text[2];
1662 
1663         if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *bytesProcessed = 3; return code; } // Unexpected sequence
1664 
1665         /*
1666             [0]xE0    [1]xA0-BF       [2]UTF8-tail(x80-BF)
1667             [0]xE1-EC [1]UTF8-tail    [2]UTF8-tail(x80-BF)
1668             [0]xED    [1]x80-9F       [2]UTF8-tail(x80-BF)
1669             [0]xEE-EF [1]UTF8-tail    [2]UTF8-tail(x80-BF)
1670         */
1671 
1672         if (((octet == 0xe0) && !((octet1 >= 0xa0) && (octet1 <= 0xbf))) ||
1673             ((octet == 0xed) && !((octet1 >= 0x80) && (octet1 <= 0x9f)))) { *bytesProcessed = 2; return code; }
1674 
1675         if ((octet >= 0xe0) && (0 <= 0xef))
1676         {
1677             code = ((octet & 0xf) << 12) | ((octet1 & 0x3f) << 6) | (octet2 & 0x3f);
1678             *bytesProcessed = 3;
1679         }
1680     }
1681     else if ((octet & 0xf8) == 0xf0)
1682     {
1683         // Four octets
1684         if (octet > 0xf4) return code;
1685 
1686         unsigned char octet1 = text[1];
1687         unsigned char octet2 = '\0';
1688         unsigned char octet3 = '\0';
1689 
1690         if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; }  // Unexpected sequence
1691 
1692         octet2 = text[2];
1693 
1694         if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *bytesProcessed = 3; return code; }  // Unexpected sequence
1695 
1696         octet3 = text[3];
1697 
1698         if ((octet3 == '\0') || ((octet3 >> 6) != 2)) { *bytesProcessed = 4; return code; }  // Unexpected sequence
1699 
1700         /*
1701             [0]xF0       [1]x90-BF       [2]UTF8-tail  [3]UTF8-tail
1702             [0]xF1-F3    [1]UTF8-tail    [2]UTF8-tail  [3]UTF8-tail
1703             [0]xF4       [1]x80-8F       [2]UTF8-tail  [3]UTF8-tail
1704         */
1705 
1706         if (((octet == 0xf0) && !((octet1 >= 0x90) && (octet1 <= 0xbf))) ||
1707             ((octet == 0xf4) && !((octet1 >= 0x80) && (octet1 <= 0x8f)))) { *bytesProcessed = 2; return code; } // Unexpected sequence
1708 
1709         if (octet >= 0xf0)
1710         {
1711             code = ((octet & 0x7) << 18) | ((octet1 & 0x3f) << 12) | ((octet2 & 0x3f) << 6) | (octet3 & 0x3f);
1712             *bytesProcessed = 4;
1713         }
1714     }
1715 
1716     if (code > 0x10ffff) code = 0x3f;     // Codepoints after U+10ffff are invalid
1717 
1718     return code;
1719 }
1720 
1721 //----------------------------------------------------------------------------------
1722 // Module specific Functions Definition
1723 //----------------------------------------------------------------------------------
1724 #if defined(SUPPORT_FILEFORMAT_FNT)
1725 
1726 // Read a line from memory
1727 // REQUIRES: memcpy()
1728 // NOTE: Returns the number of bytes read
GetLine(const char * origin,char * buffer,int maxLength)1729 static int GetLine(const char *origin, char *buffer, int maxLength)
1730 {
1731     int count = 0;
1732     for (; count < maxLength; count++) if (origin[count] == '\n') break;
1733     memcpy(buffer, origin, count);
1734     return count;
1735 }
1736 
1737 // Load a BMFont file (AngelCode font file)
1738 // REQUIRES: strstr(), sscanf(), strrchr(), memcpy()
LoadBMFont(const char * fileName)1739 static Font LoadBMFont(const char *fileName)
1740 {
1741     #define MAX_BUFFER_SIZE     256
1742 
1743     Font font = { 0 };
1744 
1745     char buffer[MAX_BUFFER_SIZE] = { 0 };
1746     char *searchPoint = NULL;
1747 
1748     int fontSize = 0;
1749     int charsCount = 0;
1750 
1751     int imWidth = 0;
1752     int imHeight = 0;
1753     char imFileName[129];
1754 
1755     int base = 0;   // Useless data
1756 
1757     char *fileText = LoadFileText(fileName);
1758 
1759     if (fileText == NULL) return font;
1760 
1761     char *fileTextPtr = fileText;
1762 
1763     // NOTE: We skip first line, it contains no useful information
1764     int lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
1765     fileTextPtr += (lineBytes + 1);
1766 
1767     // Read line data
1768     lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
1769     searchPoint = strstr(buffer, "lineHeight");
1770     sscanf(searchPoint, "lineHeight=%i base=%i scaleW=%i scaleH=%i", &fontSize, &base, &imWidth, &imHeight);
1771     fileTextPtr += (lineBytes + 1);
1772 
1773     TRACELOGD("FONT: [%s] Loaded font info:", fileName);
1774     TRACELOGD("    > Base size: %i", fontSize);
1775     TRACELOGD("    > Texture scale: %ix%i", imWidth, imHeight);
1776 
1777     lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
1778     searchPoint = strstr(buffer, "file");
1779     sscanf(searchPoint, "file=\"%128[^\"]\"", imFileName);
1780     fileTextPtr += (lineBytes + 1);
1781 
1782     TRACELOGD("    > Texture filename: %s", imFileName);
1783 
1784     lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
1785     searchPoint = strstr(buffer, "count");
1786     sscanf(searchPoint, "count=%i", &charsCount);
1787     fileTextPtr += (lineBytes + 1);
1788 
1789     TRACELOGD("    > Chars count: %i", charsCount);
1790 
1791     // Compose correct path using route of .fnt file (fileName) and imFileName
1792     char *imPath = NULL;
1793     char *lastSlash = NULL;
1794 
1795     lastSlash = strrchr(fileName, '/');
1796     if (lastSlash == NULL) lastSlash = strrchr(fileName, '\\');
1797 
1798     if (lastSlash != NULL)
1799     {
1800         // NOTE: We need some extra space to avoid memory corruption on next allocations!
1801         imPath = RL_CALLOC(TextLength(fileName) - TextLength(lastSlash) + TextLength(imFileName) + 4, 1);
1802         memcpy(imPath, fileName, TextLength(fileName) - TextLength(lastSlash) + 1);
1803         memcpy(imPath + TextLength(fileName) - TextLength(lastSlash) + 1, imFileName, TextLength(imFileName));
1804     }
1805     else imPath = imFileName;
1806 
1807     TRACELOGD("    > Image loading path: %s", imPath);
1808 
1809     Image imFont = LoadImage(imPath);
1810 
1811     if (imFont.format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE)
1812     {
1813         // Convert image to GRAYSCALE + ALPHA, using the mask as the alpha channel
1814         Image imFontAlpha = {
1815             .data = calloc(imFont.width*imFont.height, 2),
1816             .width = imFont.width,
1817             .height = imFont.height,
1818             .format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA,
1819             .mipmaps = 1
1820         };
1821 
1822         for (int p = 0, i = 0; p < (imFont.width*imFont.height*2); p += 2, i++)
1823         {
1824             ((unsigned char *)(imFontAlpha.data))[p] = 0xff;
1825             ((unsigned char *)(imFontAlpha.data))[p + 1] = ((unsigned char *)imFont.data)[i];
1826         }
1827 
1828         UnloadImage(imFont);
1829         imFont = imFontAlpha;
1830     }
1831 
1832     font.texture = LoadTextureFromImage(imFont);
1833 
1834     if (lastSlash != NULL) RL_FREE(imPath);
1835 
1836     // Fill font characters info data
1837     font.baseSize = fontSize;
1838     font.charsCount = charsCount;
1839     font.charsPadding = 0;
1840     font.chars = (CharInfo *)RL_MALLOC(charsCount*sizeof(CharInfo));
1841     font.recs = (Rectangle *)RL_MALLOC(charsCount*sizeof(Rectangle));
1842 
1843     int charId, charX, charY, charWidth, charHeight, charOffsetX, charOffsetY, charAdvanceX;
1844 
1845     for (int i = 0; i < charsCount; i++)
1846     {
1847         lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
1848         sscanf(buffer, "char id=%i x=%i y=%i width=%i height=%i xoffset=%i yoffset=%i xadvance=%i",
1849                        &charId, &charX, &charY, &charWidth, &charHeight, &charOffsetX, &charOffsetY, &charAdvanceX);
1850         fileTextPtr += (lineBytes + 1);
1851 
1852         // Get character rectangle in the font atlas texture
1853         font.recs[i] = (Rectangle){ (float)charX, (float)charY, (float)charWidth, (float)charHeight };
1854 
1855         // Save data properly in sprite font
1856         font.chars[i].value = charId;
1857         font.chars[i].offsetX = charOffsetX;
1858         font.chars[i].offsetY = charOffsetY;
1859         font.chars[i].advanceX = charAdvanceX;
1860 
1861         // Fill character image data from imFont data
1862         font.chars[i].image = ImageFromImage(imFont, font.recs[i]);
1863     }
1864 
1865     UnloadImage(imFont);
1866     RL_FREE(fileText);
1867 
1868     if (font.texture.id == 0)
1869     {
1870         UnloadFont(font);
1871         font = GetFontDefault();
1872         TRACELOG(LOG_WARNING, "FONT: [%s] Failed to load texture, reverted to default font", fileName);
1873     }
1874     else TRACELOG(LOG_INFO, "FONT: [%s] Font loaded successfully", fileName);
1875 
1876     return font;
1877 }
1878 #endif
1879