Logo

index : raylib-jai

---

  • summary
  • about
  • tree
  • log
  • branches
<< path: root/public/raylib-jai.git/html/Raylib/raylib/src/rtext.c blob: 453ed45072e823fbd19d88ac9fd21e8bdf49676f [raw] [clear marker]

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