GCC Code Coverage Report


Directory: ./
File: submodules/raylib/src/rtext.c
Date: 2023-09-29 04:53:15
Exec Total Coverage
Lines: 0 683 0.0%
Branches: 0 480 0.0%

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