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

Generated with cgit - Back to sebastiano.tronto.net