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