1 | // |
2 | // NOTE sokol: all IO functions have been removed |
3 | // |
4 | // Copyright (c) 2009-2013 Mikko Mononen [email protected] |
5 | // |
6 | // This software is provided 'as-is', without any express or implied |
7 | // warranty. In no event will the authors be held liable for any damages |
8 | // arising from the use of this software. |
9 | // Permission is granted to anyone to use this software for any purpose, |
10 | // including commercial applications, and to alter it and redistribute it |
11 | // freely, subject to the following restrictions: |
12 | // 1. The origin of this software must not be misrepresented; you must not |
13 | // claim that you wrote the original software. If you use this software |
14 | // in a product, an acknowledgment in the product documentation would be |
15 | // appreciated but is not required. |
16 | // 2. Altered source versions must be plainly marked as such, and must not be |
17 | // misrepresented as being the original software. |
18 | // 3. This notice may not be removed or altered from any source distribution. |
19 | // |
20 | |
21 | #ifndef FONS_H |
22 | #define FONS_H |
23 | |
24 | #ifdef __cplusplus |
25 | extern "C" { |
26 | #endif |
27 | |
28 | // To make the implementation private to the file that generates the implementation |
29 | #ifdef FONS_STATIC |
30 | #define FONS_DEF static |
31 | #else |
32 | #define FONS_DEF extern |
33 | #endif |
34 | |
35 | #define FONS_INVALID -1 |
36 | |
37 | #if !defined(FONTSTASH_MALLOC) |
38 | #define FONTSTASH_MALLOC malloc |
39 | #define FONTSTASH_REALLOC realloc |
40 | #define FONTSTASH_FREE free |
41 | #endif |
42 | |
43 | enum FONSflags { |
44 | FONS_ZERO_TOPLEFT = 1, |
45 | FONS_ZERO_BOTTOMLEFT = 2, |
46 | }; |
47 | |
48 | enum FONSalign { |
49 | // Horizontal align |
50 | FONS_ALIGN_LEFT = 1<<0, // Default |
51 | FONS_ALIGN_CENTER = 1<<1, |
52 | FONS_ALIGN_RIGHT = 1<<2, |
53 | // Vertical align |
54 | FONS_ALIGN_TOP = 1<<3, |
55 | FONS_ALIGN_MIDDLE = 1<<4, |
56 | FONS_ALIGN_BOTTOM = 1<<5, |
57 | FONS_ALIGN_BASELINE = 1<<6, // Default |
58 | }; |
59 | |
60 | enum FONSerrorCode { |
61 | // Font atlas is full. |
62 | FONS_ATLAS_FULL = 1, |
63 | // Scratch memory used to render glyphs is full, requested size reported in 'val', you may need to bump up FONS_SCRATCH_BUF_SIZE. |
64 | FONS_SCRATCH_FULL = 2, |
65 | // Calls to fonsPushState has created too large stack, if you need deep state stack bump up FONS_MAX_STATES. |
66 | FONS_STATES_OVERFLOW = 3, |
67 | // Trying to pop too many states fonsPopState(). |
68 | FONS_STATES_UNDERFLOW = 4, |
69 | }; |
70 | |
71 | struct FONSparams { |
72 | int width, height; |
73 | unsigned char flags; |
74 | void* userPtr; |
75 | int (*renderCreate)(void* uptr, int width, int height); |
76 | int (*renderResize)(void* uptr, int width, int height); |
77 | void (*renderUpdate)(void* uptr, int* rect, const unsigned char* data); |
78 | void (*renderDraw)(void* uptr, const float* verts, const float* tcoords, const unsigned int* colors, int nverts); |
79 | void (*renderDelete)(void* uptr); |
80 | }; |
81 | typedef struct FONSparams FONSparams; |
82 | |
83 | struct FONSquad |
84 | { |
85 | float x0,y0,s0,t0; |
86 | float x1,y1,s1,t1; |
87 | }; |
88 | typedef struct FONSquad FONSquad; |
89 | |
90 | struct FONStextIter { |
91 | float x, y, nextx, nexty, scale, spacing; |
92 | unsigned int codepoint; |
93 | short isize, iblur; |
94 | struct FONSfont* font; |
95 | int prevGlyphIndex; |
96 | const char* str; |
97 | const char* next; |
98 | const char* end; |
99 | unsigned int utf8state; |
100 | }; |
101 | typedef struct FONStextIter FONStextIter; |
102 | |
103 | typedef struct FONScontext FONScontext; |
104 | |
105 | // Contructor and destructor. |
106 | FONS_DEF FONScontext* fonsCreateInternal(FONSparams* params); |
107 | FONS_DEF void fonsDeleteInternal(FONScontext* s); |
108 | |
109 | FONS_DEF void fonsSetErrorCallback(FONScontext* s, void (*callback)(void* uptr, int error, int val), void* uptr); |
110 | // Returns current atlas size. |
111 | FONS_DEF void fonsGetAtlasSize(FONScontext* s, int* width, int* height); |
112 | // Expands the atlas size. |
113 | FONS_DEF int fonsExpandAtlas(FONScontext* s, int width, int height); |
114 | // Resets the whole stash. |
115 | FONS_DEF int fonsResetAtlas(FONScontext* stash, int width, int height); |
116 | |
117 | // Add fonts |
118 | FONS_DEF int fonsGetFontByName(FONScontext* s, const char* name); |
119 | FONS_DEF int fonsAddFallbackFont(FONScontext* stash, int base, int fallback); |
120 | |
121 | // State handling |
122 | FONS_DEF void fonsPushState(FONScontext* s); |
123 | FONS_DEF void fonsPopState(FONScontext* s); |
124 | FONS_DEF void fonsClearState(FONScontext* s); |
125 | |
126 | // State setting |
127 | FONS_DEF void fonsSetSize(FONScontext* s, float size); |
128 | FONS_DEF void fonsSetColor(FONScontext* s, unsigned int color); |
129 | FONS_DEF void fonsSetSpacing(FONScontext* s, float spacing); |
130 | FONS_DEF void fonsSetBlur(FONScontext* s, float blur); |
131 | FONS_DEF void fonsSetAlign(FONScontext* s, int align); |
132 | FONS_DEF void fonsSetFont(FONScontext* s, int font); |
133 | |
134 | // Draw text |
135 | FONS_DEF float fonsDrawText(FONScontext* s, float x, float y, const char* string, const char* end); |
136 | |
137 | // Measure text |
138 | FONS_DEF float fonsTextBounds(FONScontext* s, float x, float y, const char* string, const char* end, float* bounds); |
139 | FONS_DEF void fonsLineBounds(FONScontext* s, float y, float* miny, float* maxy); |
140 | FONS_DEF void fonsVertMetrics(FONScontext* s, float* ascender, float* descender, float* lineh); |
141 | |
142 | // Text iterator |
143 | FONS_DEF int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, float x, float y, const char* str, const char* end); |
144 | FONS_DEF int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, struct FONSquad* quad); |
145 | |
146 | // Pull texture changes |
147 | FONS_DEF const unsigned char* fonsGetTextureData(FONScontext* stash, int* width, int* height); |
148 | FONS_DEF int fonsValidateTexture(FONScontext* s, int* dirty); |
149 | |
150 | // Draws the stash texture for debugging |
151 | FONS_DEF void fonsDrawDebug(FONScontext* s, float x, float y); |
152 | |
153 | #ifdef __cplusplus |
154 | } |
155 | #endif |
156 | |
157 | #endif // FONS_H |
158 | |
159 | |
160 | #ifdef FONTSTASH_IMPLEMENTATION |
161 | |
162 | #define FONS_NOTUSED(v) (void)sizeof(v) |
163 | |
164 | |
165 | // Use FreeType on non-Windows systems |
166 | #ifndef _WIN32 |
167 | //#define FONS_USE_FREETYPE |
168 | #endif |
169 | |
170 | #ifdef _WIN32 |
171 | #undef FONS_USE_FREETYPE |
172 | #endif |
173 | |
174 | #ifdef __APPLE__ |
175 | #include "TargetConditionals.h" |
176 | #if TARGET_OS_IPHONE |
177 | #undef FONS_USE_FREETYPE |
178 | #endif |
179 | #endif |
180 | |
181 | //#undef FONS_USE_FREETYPE |
182 | |
183 | //#define FONS_USE_FREETYPE 1 |
184 | |
185 | #ifdef FONS_USE_FREETYPE |
186 | |
187 | #include <ft2build.h> |
188 | #include FT_FREETYPE_H |
189 | #include FT_ADVANCES_H |
190 | #include <math.h> |
191 | |
192 | struct FONSttFontImpl { |
193 | FT_Face font; |
194 | }; |
195 | typedef struct FONSttFontImpl FONSttFontImpl; |
196 | |
197 | static FT_Library ftLibrary; |
198 | |
199 | static int fons__tt_init() |
200 | { |
201 | puts("free type fons init"); |
202 | FT_Error ftError; |
203 | //FONS_NOTUSED(context); |
204 | ftError = FT_Init_FreeType(&ftLibrary); |
205 | return ftError == 0; |
206 | } |
207 | |
208 | static int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize) |
209 | { |
210 | FT_Error ftError; |
211 | FONS_NOTUSED(context); |
212 | |
213 | //font->font.userdata = stash; |
214 | ftError = FT_New_Memory_Face(ftLibrary, (const FT_Byte*)data, dataSize, 0, &font->font); |
215 | return ftError == 0; |
216 | } |
217 | |
218 | static void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, int *lineGap) |
219 | { |
220 | *ascent = font->font->ascender; |
221 | *descent = font->font->descender; |
222 | *lineGap = font->font->height - (*ascent - *descent); |
223 | } |
224 | |
225 | static float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) |
226 | { |
227 | return size / (font->font->ascender - font->font->descender); |
228 | } |
229 | |
230 | static int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) |
231 | { |
232 | return FT_Get_Char_Index(font->font, codepoint); |
233 | } |
234 | |
235 | static int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float scale, |
236 | int *advance, int *lsb, int *x0, int *y0, int *x1, int *y1) |
237 | { |
238 | FT_Error ftError; |
239 | FT_GlyphSlot ftGlyph; |
240 | FT_Fixed advFixed; |
241 | FONS_NOTUSED(scale); |
242 | |
243 | ftError = FT_Set_Pixel_Sizes(font->font, 0, (FT_UInt)(size * (float)font->font->units_per_EM / (float)(font->font->ascender - font->font->descender))); |
244 | if (ftError) return 0; |
245 | ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT); |
246 | if (ftError) return 0; |
247 | ftError = FT_Get_Advance(font->font, glyph, FT_LOAD_NO_SCALE, &advFixed); |
248 | if (ftError) return 0; |
249 | ftGlyph = font->font->glyph; |
250 | *advance = (int)advFixed; |
251 | *lsb = (int)ftGlyph->metrics.horiBearingX; |
252 | *x0 = ftGlyph->bitmap_left; |
253 | *x1 = *x0 + ftGlyph->bitmap.width; |
254 | *y0 = -ftGlyph->bitmap_top; |
255 | *y1 = *y0 + ftGlyph->bitmap.rows; |
256 | return 1; |
257 | } |
258 | |
259 | static void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int outWidth, int outHeight, int outStride, |
260 | float scaleX, float scaleY, int glyph) |
261 | { |
262 | FT_GlyphSlot ftGlyph = font->font->glyph; |
263 | int ftGlyphOffset = 0; |
264 | int x, y; |
265 | FONS_NOTUSED(outWidth); |
266 | FONS_NOTUSED(outHeight); |
267 | FONS_NOTUSED(scaleX); |
268 | FONS_NOTUSED(scaleY); |
269 | FONS_NOTUSED(glyph); // glyph has already been loaded by fons__tt_buildGlyphBitmap |
270 | |
271 | for ( y = 0; y < ftGlyph->bitmap.rows; y++ ) { |
272 | for ( x = 0; x < ftGlyph->bitmap.width; x++ ) { |
273 | output[(y * outStride) + x] = ftGlyph->bitmap.buffer[ftGlyphOffset++]; |
274 | } |
275 | } |
276 | } |
277 | |
278 | static int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) |
279 | { |
280 | FT_Vector ftKerning; |
281 | FT_Get_Kerning(font->font, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning); |
282 | return (int)((ftKerning.x + 32) >> 6); // Round up and convert to integer |
283 | } |
284 | |
285 | #else |
286 | |
287 | #define STB_TRUETYPE_IMPLEMENTATION |
288 | #define STBTT_STATIC |
289 | static void* fons__tmpalloc(size_t size, void* up); |
290 | static void fons__tmpfree(void* ptr, void* up); |
291 | #define STBTT_malloc(x,u) fons__tmpalloc(x,u) |
292 | #define STBTT_free(x,u) fons__tmpfree(x,u) |
293 | #include "stb_truetype.h" |
294 | |
295 | struct FONSttFontImpl { |
296 | stbtt_fontinfo font; |
297 | }; |
298 | typedef struct FONSttFontImpl FONSttFontImpl; |
299 | |
300 | static int fons__tt_init(FONScontext *context) |
301 | { |
302 | FONS_NOTUSED(context); |
303 | return 1; |
304 | } |
305 | |
306 | static int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize) |
307 | { |
308 | int stbError; |
309 | FONS_NOTUSED(dataSize); |
310 | |
311 | font->font.userdata = context; |
312 | stbError = stbtt_InitFont(&font->font, data, 0); |
313 | return stbError; |
314 | } |
315 | |
316 | static void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, int *lineGap) |
317 | { |
318 | stbtt_GetFontVMetrics(&font->font, ascent, descent, lineGap); |
319 | } |
320 | |
321 | static float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) |
322 | { |
323 | return stbtt_ScaleForPixelHeight(&font->font, size); |
324 | } |
325 | |
326 | static int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) |
327 | { |
328 | return stbtt_FindGlyphIndex(&font->font, codepoint); |
329 | } |
330 | |
331 | static int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float scale, |
332 | int *advance, int *lsb, int *x0, int *y0, int *x1, int *y1) |
333 | { |
334 | FONS_NOTUSED(size); |
335 | stbtt_GetGlyphHMetrics(&font->font, glyph, advance, lsb); |
336 | stbtt_GetGlyphBitmapBox(&font->font, glyph, scale, scale, x0, y0, x1, y1); |
337 | return 1; |
338 | } |
339 | |
340 | static void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int outWidth, int outHeight, int outStride, |
341 | float scaleX, float scaleY, int glyph) |
342 | { |
343 | stbtt_MakeGlyphBitmap(&font->font, output, outWidth, outHeight, outStride, scaleX, scaleY, glyph); |
344 | } |
345 | |
346 | static int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) |
347 | { |
348 | return stbtt_GetGlyphKernAdvance(&font->font, glyph1, glyph2); |
349 | } |
350 | |
351 | #endif |
352 | |
353 | #ifndef FONS_SCRATCH_BUF_SIZE |
354 | # define FONS_SCRATCH_BUF_SIZE 64000 |
355 | #endif |
356 | #ifndef FONS_HASH_LUT_SIZE |
357 | # define FONS_HASH_LUT_SIZE 256 |
358 | #endif |
359 | #ifndef FONS_INIT_FONTS |
360 | # define FONS_INIT_FONTS 4 |
361 | #endif |
362 | #ifndef FONS_INIT_GLYPHS |
363 | # define FONS_INIT_GLYPHS 256 |
364 | #endif |
365 | #ifndef FONS_INIT_ATLAS_NODES |
366 | # define FONS_INIT_ATLAS_NODES 256 |
367 | #endif |
368 | #ifndef FONS_VERTEX_COUNT |
369 | # define FONS_VERTEX_COUNT 1024 |
370 | #endif |
371 | #ifndef FONS_MAX_STATES |
372 | # define FONS_MAX_STATES 20 |
373 | #endif |
374 | #ifndef FONS_MAX_FALLBACKS |
375 | # define FONS_MAX_FALLBACKS 20 |
376 | #endif |
377 | |
378 | static unsigned int fons__hashint(unsigned int a) |
379 | { |
380 | a += ~(a<<15); |
381 | a ^= (a>>10); |
382 | a += (a<<3); |
383 | a ^= (a>>6); |
384 | a += ~(a<<11); |
385 | a ^= (a>>16); |
386 | return a; |
387 | } |
388 | |
389 | static int fons__mini(int a, int b) |
390 | { |
391 | return a < b ? a : b; |
392 | } |
393 | |
394 | static int fons__maxi(int a, int b) |
395 | { |
396 | return a > b ? a : b; |
397 | } |
398 | |
399 | struct FONSglyph |
400 | { |
401 | unsigned int codepoint; |
402 | int index; |
403 | int next; |
404 | short size, blur; |
405 | short x0,y0,x1,y1; |
406 | short xadv,xoff,yoff; |
407 | }; |
408 | typedef struct FONSglyph FONSglyph; |
409 | |
410 | struct FONSfont |
411 | { |
412 | FONSttFontImpl font; |
413 | char name[64]; |
414 | unsigned char* data; |
415 | int dataSize; |
416 | unsigned char freeData; |
417 | float ascender; |
418 | float descender; |
419 | float lineh; |
420 | FONSglyph* glyphs; |
421 | int cglyphs; |
422 | int nglyphs; |
423 | int lut[FONS_HASH_LUT_SIZE]; |
424 | int fallbacks[FONS_MAX_FALLBACKS]; |
425 | int nfallbacks; |
426 | }; |
427 | typedef struct FONSfont FONSfont; |
428 | |
429 | struct FONSstate |
430 | { |
431 | int font; |
432 | int align; |
433 | float size; |
434 | unsigned int color; |
435 | float blur; |
436 | float spacing; |
437 | }; |
438 | typedef struct FONSstate FONSstate; |
439 | |
440 | struct FONSatlasNode { |
441 | short x, y, width; |
442 | }; |
443 | typedef struct FONSatlasNode FONSatlasNode; |
444 | |
445 | struct FONSatlas |
446 | { |
447 | int width, height; |
448 | FONSatlasNode* nodes; |
449 | int nnodes; |
450 | int cnodes; |
451 | }; |
452 | typedef struct FONSatlas FONSatlas; |
453 | |
454 | struct FONScontext |
455 | { |
456 | FONSparams params; |
457 | float itw,ith; |
458 | unsigned char* texData; |
459 | int dirtyRect[4]; |
460 | FONSfont** fonts; |
461 | FONSatlas* atlas; |
462 | int cfonts; |
463 | int nfonts; |
464 | float verts[FONS_VERTEX_COUNT*2]; |
465 | float tcoords[FONS_VERTEX_COUNT*2]; |
466 | unsigned int colors[FONS_VERTEX_COUNT]; |
467 | int nverts; |
468 | unsigned char* scratch; |
469 | int nscratch; |
470 | FONSstate states[FONS_MAX_STATES]; |
471 | int nstates; |
472 | void (*handleError)(void* uptr, int error, int val); |
473 | void* errorUptr; |
474 | }; |
475 | |
476 | #ifdef STB_TRUETYPE_IMPLEMENTATION |
477 | |
478 | static void* fons__tmpalloc(size_t size, void* up) |
479 | { |
480 | unsigned char* ptr; |
481 | FONScontext* stash = (FONScontext*)up; |
482 | |
483 | // 16-byte align the returned pointer |
484 | size = (size + 0xf) & ~0xf; |
485 | |
486 | if (stash->nscratch+(int)size > FONS_SCRATCH_BUF_SIZE) { |
487 | if (stash->handleError) |
488 | stash->handleError(stash->errorUptr, FONS_SCRATCH_FULL, stash->nscratch+(int)size); |
489 | return NULL; |
490 | } |
491 | ptr = stash->scratch + stash->nscratch; |
492 | stash->nscratch += (int)size; |
493 | return ptr; |
494 | } |
495 | |
496 | static void fons__tmpfree(void* ptr, void* up) |
497 | { |
498 | (void)ptr; |
499 | (void)up; |
500 | // empty |
501 | } |
502 | |
503 | #endif // STB_TRUETYPE_IMPLEMENTATION |
504 | |
505 | // Copyright (c) 2008-2010 Bjoern Hoehrmann <[email protected]> |
506 | // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. |
507 | |
508 | #define FONS_UTF8_ACCEPT 0 |
509 | #define FONS_UTF8_REJECT 12 |
510 | |
511 | static unsigned int fons__decutf8(unsigned int* state, unsigned int* codep, unsigned int byte) |
512 | { |
513 | static const unsigned char utf8d[] = { |
514 | // The first part of the table maps bytes to character classes that |
515 | // to reduce the size of the transition table and create bitmasks. |
516 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, |
517 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, |
518 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, |
519 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, |
520 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, |
521 | 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, |
522 | 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, |
523 | 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, |
524 | |
525 | // The second part is a transition table that maps a combination |
526 | // of a state of the automaton and a character class to a state. |
527 | 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12, |
528 | 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12, |
529 | 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12, |
530 | 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12, |
531 | 12,36,12,12,12,12,12,12,12,12,12,12, |
532 | }; |
533 | |
534 | unsigned int type = utf8d[byte]; |
535 | |
536 | *codep = (*state != FONS_UTF8_ACCEPT) ? |
537 | (byte & 0x3fu) | (*codep << 6) : |
538 | (0xff >> type) & (byte); |
539 | |
540 | *state = utf8d[256 + *state + type]; |
541 | return *state; |
542 | } |
543 | |
544 | // Atlas based on Skyline Bin Packer by Jukka Jylänki |
545 | |
546 | static void fons__deleteAtlas(FONSatlas* atlas) |
547 | { |
548 | if (atlas == NULL) return; |
549 | if (atlas->nodes != NULL) FONTSTASH_FREE(atlas->nodes); |
550 | FONTSTASH_FREE(atlas); |
551 | } |
552 | |
553 | static FONSatlas* fons__allocAtlas(int w, int h, int nnodes) |
554 | { |
555 | FONSatlas* atlas = NULL; |
556 | |
557 | // Allocate memory for the font stash. |
558 | atlas = (FONSatlas*)FONTSTASH_MALLOC(sizeof(FONSatlas)); |
559 | if (atlas == NULL) goto error; |
560 | memset(atlas, 0, sizeof(FONSatlas)); |
561 | |
562 | atlas->width = w; |
563 | atlas->height = h; |
564 | |
565 | // Allocate space for skyline nodes |
566 | atlas->nodes = (FONSatlasNode*)FONTSTASH_MALLOC(sizeof(FONSatlasNode) * nnodes); |
567 | if (atlas->nodes == NULL) goto error; |
568 | memset(atlas->nodes, 0, sizeof(FONSatlasNode) * nnodes); |
569 | atlas->nnodes = 0; |
570 | atlas->cnodes = nnodes; |
571 | |
572 | // Init root node. |
573 | atlas->nodes[0].x = 0; |
574 | atlas->nodes[0].y = 0; |
575 | atlas->nodes[0].width = (short)w; |
576 | atlas->nnodes++; |
577 | |
578 | return atlas; |
579 | |
580 | error: |
581 | if (atlas) fons__deleteAtlas(atlas); |
582 | return NULL; |
583 | } |
584 | |
585 | static int fons__atlasInsertNode(FONSatlas* atlas, int idx, int x, int y, int w) |
586 | { |
587 | int i; |
588 | // Insert node |
589 | if (atlas->nnodes+1 > atlas->cnodes) { |
590 | atlas->cnodes = atlas->cnodes == 0 ? 8 : atlas->cnodes * 2; |
591 | atlas->nodes = (FONSatlasNode*)FONTSTASH_REALLOC(atlas->nodes, sizeof(FONSatlasNode) * atlas->cnodes); |
592 | if (atlas->nodes == NULL) |
593 | return 0; |
594 | } |
595 | for (i = atlas->nnodes; i > idx; i--) |
596 | atlas->nodes[i] = atlas->nodes[i-1]; |
597 | atlas->nodes[idx].x = (short)x; |
598 | atlas->nodes[idx].y = (short)y; |
599 | atlas->nodes[idx].width = (short)w; |
600 | atlas->nnodes++; |
601 | |
602 | return 1; |
603 | } |
604 | |
605 | static void fons__atlasRemoveNode(FONSatlas* atlas, int idx) |
606 | { |
607 | int i; |
608 | if (atlas->nnodes == 0) return; |
609 | for (i = idx; i < atlas->nnodes-1; i++) |
610 | atlas->nodes[i] = atlas->nodes[i+1]; |
611 | atlas->nnodes--; |
612 | } |
613 | |
614 | static void fons__atlasExpand(FONSatlas* atlas, int w, int h) |
615 | { |
616 | // Insert node for empty space |
617 | if (w > atlas->width) |
618 | fons__atlasInsertNode(atlas, atlas->nnodes, atlas->width, 0, w - atlas->width); |
619 | atlas->width = w; |
620 | atlas->height = h; |
621 | } |
622 | |
623 | static void fons__atlasReset(FONSatlas* atlas, int w, int h) |
624 | { |
625 | atlas->width = w; |
626 | atlas->height = h; |
627 | atlas->nnodes = 0; |
628 | |
629 | // Init root node. |
630 | atlas->nodes[0].x = 0; |
631 | atlas->nodes[0].y = 0; |
632 | atlas->nodes[0].width = (short)w; |
633 | atlas->nnodes++; |
634 | } |
635 | |
636 | static int fons__atlasAddSkylineLevel(FONSatlas* atlas, int idx, int x, int y, int w, int h) |
637 | { |
638 | int i; |
639 | |
640 | // Insert new node |
641 | if (fons__atlasInsertNode(atlas, idx, x, y+h, w) == 0) |
642 | return 0; |
643 | |
644 | // Delete skyline segments that fall under the shadow of the new segment. |
645 | for (i = idx+1; i < atlas->nnodes; i++) { |
646 | if (atlas->nodes[i].x < atlas->nodes[i-1].x + atlas->nodes[i-1].width) { |
647 | int shrink = atlas->nodes[i-1].x + atlas->nodes[i-1].width - atlas->nodes[i].x; |
648 | atlas->nodes[i].x += (short)shrink; |
649 | atlas->nodes[i].width -= (short)shrink; |
650 | if (atlas->nodes[i].width <= 0) { |
651 | fons__atlasRemoveNode(atlas, i); |
652 | i--; |
653 | } else { |
654 | break; |
655 | } |
656 | } else { |
657 | break; |
658 | } |
659 | } |
660 | |
661 | // Merge same height skyline segments that are next to each other. |
662 | for (i = 0; i < atlas->nnodes-1; i++) { |
663 | if (atlas->nodes[i].y == atlas->nodes[i+1].y) { |
664 | atlas->nodes[i].width += atlas->nodes[i+1].width; |
665 | fons__atlasRemoveNode(atlas, i+1); |
666 | i--; |
667 | } |
668 | } |
669 | |
670 | return 1; |
671 | } |
672 | |
673 | static int fons__atlasRectFits(FONSatlas* atlas, int i, int w, int h) |
674 | { |
675 | // Checks if there is enough space at the location of skyline span 'i', |
676 | // and return the max height of all skyline spans under that at that location, |
677 | // (think tetris block being dropped at that position). Or -1 if no space found. |
678 | int x = atlas->nodes[i].x; |
679 | int y = atlas->nodes[i].y; |
680 | int spaceLeft; |
681 | if (x + w > atlas->width) |
682 | return -1; |
683 | spaceLeft = w; |
684 | while (spaceLeft > 0) { |
685 | if (i == atlas->nnodes) return -1; |
686 | y = fons__maxi(y, atlas->nodes[i].y); |
687 | if (y + h > atlas->height) return -1; |
688 | spaceLeft -= atlas->nodes[i].width; |
689 | ++i; |
690 | } |
691 | return y; |
692 | } |
693 | |
694 | static int fons__atlasAddRect(FONSatlas* atlas, int rw, int rh, int* rx, int* ry) |
695 | { |
696 | int besth = atlas->height, bestw = atlas->width, besti = -1; |
697 | int bestx = -1, besty = -1, i; |
698 | |
699 | // Bottom left fit heuristic. |
700 | for (i = 0; i < atlas->nnodes; i++) { |
701 | int y = fons__atlasRectFits(atlas, i, rw, rh); |
702 | if (y != -1) { |
703 | if (y + rh < besth || (y + rh == besth && atlas->nodes[i].width < bestw)) { |
704 | besti = i; |
705 | bestw = atlas->nodes[i].width; |
706 | besth = y + rh; |
707 | bestx = atlas->nodes[i].x; |
708 | besty = y; |
709 | } |
710 | } |
711 | } |
712 | |
713 | if (besti == -1) |
714 | return 0; |
715 | |
716 | // Perform the actual packing. |
717 | if (fons__atlasAddSkylineLevel(atlas, besti, bestx, besty, rw, rh) == 0) |
718 | return 0; |
719 | |
720 | *rx = bestx; |
721 | *ry = besty; |
722 | |
723 | return 1; |
724 | } |
725 | |
726 | static void fons__addWhiteRect(FONScontext* stash, int w, int h) |
727 | { |
728 | int x, y, gx, gy; |
729 | unsigned char* dst; |
730 | if (fons__atlasAddRect(stash->atlas, w, h, &gx, &gy) == 0) |
731 | return; |
732 | |
733 | // Rasterize |
734 | dst = &stash->texData[gx + gy * stash->params.width]; |
735 | for (y = 0; y < h; y++) { |
736 | for (x = 0; x < w; x++) |
737 | dst[x] = 0xff; |
738 | dst += stash->params.width; |
739 | } |
740 | |
741 | stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], gx); |
742 | stash->dirtyRect[1] = fons__mini(stash->dirtyRect[1], gy); |
743 | stash->dirtyRect[2] = fons__maxi(stash->dirtyRect[2], gx+w); |
744 | stash->dirtyRect[3] = fons__maxi(stash->dirtyRect[3], gy+h); |
745 | } |
746 | |
747 | FONScontext* fonsCreateInternal(FONSparams* params) |
748 | { |
749 | FONScontext* stash = NULL; |
750 | |
751 | // Allocate memory for the font stash. |
752 | stash = (FONScontext*)FONTSTASH_MALLOC(sizeof(FONScontext)); |
753 | if (stash == NULL) goto error; |
754 | memset(stash, 0, sizeof(FONScontext)); |
755 | |
756 | stash->params = *params; |
757 | |
758 | // Allocate scratch buffer. |
759 | stash->scratch = (unsigned char*)FONTSTASH_MALLOC(FONS_SCRATCH_BUF_SIZE); |
760 | if (stash->scratch == NULL) goto error; |
761 | |
762 | // Initialize implementation library |
763 | if (!fons__tt_init(stash)) goto error; |
764 | |
765 | if (stash->params.renderCreate != NULL) { |
766 | if (stash->params.renderCreate(stash->params.userPtr, stash->params.width, stash->params.height) == 0) |
767 | goto error; |
768 | } |
769 | |
770 | stash->atlas = fons__allocAtlas(stash->params.width, stash->params.height, FONS_INIT_ATLAS_NODES); |
771 | if (stash->atlas == NULL) goto error; |
772 | |
773 | // Allocate space for fonts. |
774 | stash->fonts = (FONSfont**)FONTSTASH_MALLOC(sizeof(FONSfont*) * FONS_INIT_FONTS); |
775 | if (stash->fonts == NULL) goto error; |
776 | memset(stash->fonts, 0, sizeof(FONSfont*) * FONS_INIT_FONTS); |
777 | stash->cfonts = FONS_INIT_FONTS; |
778 | stash->nfonts = 0; |
779 | |
780 | // Create texture for the cache. |
781 | stash->itw = 1.0f/stash->params.width; |
782 | stash->ith = 1.0f/stash->params.height; |
783 | stash->texData = (unsigned char*)FONTSTASH_MALLOC(stash->params.width * stash->params.height); |
784 | if (stash->texData == NULL) goto error; |
785 | memset(stash->texData, 0, stash->params.width * stash->params.height); |
786 | |
787 | stash->dirtyRect[0] = stash->params.width; |
788 | stash->dirtyRect[1] = stash->params.height; |
789 | stash->dirtyRect[2] = 0; |
790 | stash->dirtyRect[3] = 0; |
791 | |
792 | // Add white rect at 0,0 for debug drawing. |
793 | fons__addWhiteRect(stash, 2,2); |
794 | |
795 | fonsPushState(stash); |
796 | fonsClearState(stash); |
797 | |
798 | return stash; |
799 | |
800 | error: |
801 | fonsDeleteInternal(stash); |
802 | return NULL; |
803 | } |
804 | |
805 | static FONSstate* fons__getState(FONScontext* stash) |
806 | { |
807 | return &stash->states[stash->nstates-1]; |
808 | } |
809 | |
810 | int fonsAddFallbackFont(FONScontext* stash, int base, int fallback) |
811 | { |
812 | FONSfont* baseFont = stash->fonts[base]; |
813 | if (baseFont->nfallbacks < FONS_MAX_FALLBACKS) { |
814 | baseFont->fallbacks[baseFont->nfallbacks++] = fallback; |
815 | return 1; |
816 | } |
817 | return 0; |
818 | } |
819 | |
820 | void fonsSetSize(FONScontext* stash, float size) |
821 | { |
822 | fons__getState(stash)->size = size; |
823 | } |
824 | |
825 | void fonsSetColor(FONScontext* stash, unsigned int color) |
826 | { |
827 | fons__getState(stash)->color = color; |
828 | } |
829 | |
830 | void fonsSetSpacing(FONScontext* stash, float spacing) |
831 | { |
832 | fons__getState(stash)->spacing = spacing; |
833 | } |
834 | |
835 | void fonsSetBlur(FONScontext* stash, float blur) |
836 | { |
837 | fons__getState(stash)->blur = blur; |
838 | } |
839 | |
840 | void fonsSetAlign(FONScontext* stash, int align) |
841 | { |
842 | fons__getState(stash)->align = align; |
843 | } |
844 | |
845 | void fonsSetFont(FONScontext* stash, int font) |
846 | { |
847 | fons__getState(stash)->font = font; |
848 | } |
849 | |
850 | void fonsPushState(FONScontext* stash) |
851 | { |
852 | if (stash->nstates >= FONS_MAX_STATES) { |
853 | if (stash->handleError) |
854 | stash->handleError(stash->errorUptr, FONS_STATES_OVERFLOW, 0); |
855 | return; |
856 | } |
857 | if (stash->nstates > 0) |
858 | memcpy(&stash->states[stash->nstates], &stash->states[stash->nstates-1], sizeof(FONSstate)); |
859 | stash->nstates++; |
860 | } |
861 | |
862 | void fonsPopState(FONScontext* stash) |
863 | { |
864 | if (stash->nstates <= 1) { |
865 | if (stash->handleError) |
866 | stash->handleError(stash->errorUptr, FONS_STATES_UNDERFLOW, 0); |
867 | return; |
868 | } |
869 | stash->nstates--; |
870 | } |
871 | |
872 | void fonsClearState(FONScontext* stash) |
873 | { |
874 | FONSstate* state = fons__getState(stash); |
875 | state->size = 12.0f; |
876 | state->color = 0xffffffff; |
877 | state->font = 0; |
878 | state->blur = 0; |
879 | state->spacing = 0; |
880 | state->align = FONS_ALIGN_LEFT | FONS_ALIGN_BASELINE; |
881 | } |
882 | |
883 | static void fons__freeFont(FONSfont* font) |
884 | { |
885 | if (font == NULL) return; |
886 | if (font->glyphs) FONTSTASH_FREE(font->glyphs); |
887 | if (font->freeData && font->data) FONTSTASH_FREE(font->data); |
888 | FONTSTASH_FREE(font); |
889 | } |
890 | |
891 | static int fons__allocFont(FONScontext* stash) |
892 | { |
893 | FONSfont* font = NULL; |
894 | if (stash->nfonts+1 > stash->cfonts) { |
895 | stash->cfonts = stash->cfonts == 0 ? 8 : stash->cfonts * 2; |
896 | stash->fonts = (FONSfont**)FONTSTASH_REALLOC(stash->fonts, sizeof(FONSfont*) * stash->cfonts); |
897 | if (stash->fonts == NULL) |
898 | return -1; |
899 | } |
900 | font = (FONSfont*)FONTSTASH_MALLOC(sizeof(FONSfont)); |
901 | if (font == NULL) goto error; |
902 | memset(font, 0, sizeof(FONSfont)); |
903 | |
904 | font->glyphs = (FONSglyph*)FONTSTASH_MALLOC(sizeof(FONSglyph) * FONS_INIT_GLYPHS); |
905 | if (font->glyphs == NULL) goto error; |
906 | font->cglyphs = FONS_INIT_GLYPHS; |
907 | font->nglyphs = 0; |
908 | |
909 | stash->fonts[stash->nfonts++] = font; |
910 | return stash->nfonts-1; |
911 | |
912 | error: |
913 | fons__freeFont(font); |
914 | |
915 | return FONS_INVALID; |
916 | } |
917 | |
918 | int fonsAddFontMem(FONScontext* stash, const char* name, unsigned char* data, int dataSize, int freeData) |
919 | { |
920 | int i, ascent, descent, fh, lineGap; |
921 | FONSfont* font; |
922 | |
923 | int idx = fons__allocFont(stash); |
924 | if (idx == FONS_INVALID) |
925 | return FONS_INVALID; |
926 | |
927 | font = stash->fonts[idx]; |
928 | |
929 | strncpy(font->name, name, sizeof(font->name)); |
930 | font->name[sizeof(font->name)-1] = '\0'; |
931 | |
932 | // Init hash lookup. |
933 | for (i = 0; i < FONS_HASH_LUT_SIZE; ++i) |
934 | font->lut[i] = -1; |
935 | |
936 | // Read in the font data. |
937 | font->dataSize = dataSize; |
938 | font->data = data; |
939 | font->freeData = (unsigned char)freeData; |
940 | |
941 | // Init font |
942 | stash->nscratch = 0; |
943 | if (!fons__tt_loadFont(stash, &font->font, data, dataSize)) goto error; |
944 | |
945 | // Store normalized line height. The real line height is got |
946 | // by multiplying the lineh by font size. |
947 | fons__tt_getFontVMetrics( &font->font, &ascent, &descent, &lineGap); |
948 | fh = ascent - descent; |
949 | font->ascender = (float)ascent / (float)fh; |
950 | font->descender = (float)descent / (float)fh; |
951 | font->lineh = (float)(fh + lineGap) / (float)fh; |
952 | |
953 | return idx; |
954 | |
955 | error: |
956 | fons__freeFont(font); |
957 | stash->nfonts--; |
958 | return FONS_INVALID; |
959 | } |
960 | |
961 | int fonsGetFontByName(FONScontext* s, const char* name) |
962 | { |
963 | int i; |
964 | for (i = 0; i < s->nfonts; i++) { |
965 | if (strcmp(s->fonts[i]->name, name) == 0) |
966 | return i; |
967 | } |
968 | return FONS_INVALID; |
969 | } |
970 | |
971 | |
972 | static FONSglyph* fons__allocGlyph(FONSfont* font) |
973 | { |
974 | if (font->nglyphs+1 > font->cglyphs) { |
975 | font->cglyphs = font->cglyphs == 0 ? 8 : font->cglyphs * 2; |
976 | font->glyphs = (FONSglyph*)FONTSTASH_REALLOC(font->glyphs, sizeof(FONSglyph) * font->cglyphs); |
977 | if (font->glyphs == NULL) return NULL; |
978 | } |
979 | font->nglyphs++; |
980 | return &font->glyphs[font->nglyphs-1]; |
981 | } |
982 | |
983 | |
984 | // Based on Exponential blur, Jani Huhtanen, 2006 |
985 | |
986 | #define APREC 16 |
987 | #define ZPREC 7 |
988 | |
989 | static void fons__blurCols(unsigned char* dst, int w, int h, int dstStride, int alpha) |
990 | { |
991 | int x, y; |
992 | for (y = 0; y < h; y++) { |
993 | int z = 0; // force zero border |
994 | for (x = 1; x < w; x++) { |
995 | z += (alpha * (((int)(dst[x]) << ZPREC) - z)) >> APREC; |
996 | dst[x] = (unsigned char)(z >> ZPREC); |
997 | } |
998 | dst[w-1] = 0; // force zero border |
999 | z = 0; |
1000 | for (x = w-2; x >= 0; x--) { |
1001 | z += (alpha * (((int)(dst[x]) << ZPREC) - z)) >> APREC; |
1002 | dst[x] = (unsigned char)(z >> ZPREC); |
1003 | } |
1004 | dst[0] = 0; // force zero border |
1005 | dst += dstStride; |
1006 | } |
1007 | } |
1008 | |
1009 | static void fons__blurRows(unsigned char* dst, int w, int h, int dstStride, int alpha) |
1010 | { |
1011 | int x, y; |
1012 | for (x = 0; x < w; x++) { |
1013 | int z = 0; // force zero border |
1014 | for (y = dstStride; y < h*dstStride; y += dstStride) { |
1015 | z += (alpha * (((int)(dst[y]) << ZPREC) - z)) >> APREC; |
1016 | dst[y] = (unsigned char)(z >> ZPREC); |
1017 | } |
1018 | dst[(h-1)*dstStride] = 0; // force zero border |
1019 | z = 0; |
1020 | for (y = (h-2)*dstStride; y >= 0; y -= dstStride) { |
1021 | z += (alpha * (((int)(dst[y]) << ZPREC) - z)) >> APREC; |
1022 | dst[y] = (unsigned char)(z >> ZPREC); |
1023 | } |
1024 | dst[0] = 0; // force zero border |
1025 | dst++; |
1026 | } |
1027 | } |
1028 | |
1029 | |
1030 | static void fons__blur(FONScontext* stash, unsigned char* dst, int w, int h, int dstStride, int blur) |
1031 | { |
1032 | int alpha; |
1033 | float sigma; |
1034 | (void)stash; |
1035 | |
1036 | if (blur < 1) |
1037 | return; |
1038 | // Calculate the alpha such that 90% of the kernel is within the radius. (Kernel extends to infinity) |
1039 | sigma = (float)blur * 0.57735f; // 1 / sqrt(3) |
1040 | alpha = (int)((1<<APREC) * (1.0f - expf(-2.3f / (sigma+1.0f)))); |
1041 | fons__blurRows(dst, w, h, dstStride, alpha); |
1042 | fons__blurCols(dst, w, h, dstStride, alpha); |
1043 | fons__blurRows(dst, w, h, dstStride, alpha); |
1044 | fons__blurCols(dst, w, h, dstStride, alpha); |
1045 | // fons__blurrows(dst, w, h, dstStride, alpha); |
1046 | // fons__blurcols(dst, w, h, dstStride, alpha); |
1047 | } |
1048 | |
1049 | static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned int codepoint, |
1050 | short isize, short iblur) |
1051 | { |
1052 | int i, g, advance, lsb, x0, y0, x1, y1, gw, gh, gx, gy, x, y; |
1053 | float scale; |
1054 | FONSglyph* glyph = NULL; |
1055 | unsigned int h; |
1056 | float size = isize/10.0f; |
1057 | int pad, added; |
1058 | unsigned char* bdst; |
1059 | unsigned char* dst; |
1060 | FONSfont* renderFont = font; |
1061 | |
1062 | if (isize < 2) return NULL; |
1063 | if (iblur > 20) iblur = 20; |
1064 | pad = iblur+2; |
1065 | |
1066 | // Reset allocator. |
1067 | stash->nscratch = 0; |
1068 | |
1069 | // Find code point and size. |
1070 | h = fons__hashint(codepoint) & (FONS_HASH_LUT_SIZE-1); |
1071 | i = font->lut[h]; |
1072 | while (i != -1) { |
1073 | if (font->glyphs[i].codepoint == codepoint && font->glyphs[i].size == isize && font->glyphs[i].blur == iblur) |
1074 | return &font->glyphs[i]; |
1075 | i = font->glyphs[i].next; |
1076 | } |
1077 | |
1078 | // Could not find glyph, create it. |
1079 | g = fons__tt_getGlyphIndex(&font->font, codepoint); |
1080 | // Try to find the glyph in fallback fonts. |
1081 | if (g == 0) { |
1082 | for (i = 0; i < font->nfallbacks; ++i) { |
1083 | FONSfont* fallbackFont = stash->fonts[font->fallbacks[i]]; |
1084 | int fallbackIndex = fons__tt_getGlyphIndex(&fallbackFont->font, codepoint); |
1085 | if (fallbackIndex != 0) { |
1086 | g = fallbackIndex; |
1087 | renderFont = fallbackFont; |
1088 | break; |
1089 | } |
1090 | } |
1091 | // It is possible that we did not find a fallback glyph. |
1092 | // In that case the glyph index 'g' is 0, and we'll proceed below and cache empty glyph. |
1093 | } |
1094 | scale = fons__tt_getPixelHeightScale(&renderFont->font, size); |
1095 | fons__tt_buildGlyphBitmap(&renderFont->font, g, size, scale, &advance, &lsb, &x0, &y0, &x1, &y1); |
1096 | gw = x1-x0 + pad*2; |
1097 | gh = y1-y0 + pad*2; |
1098 | |
1099 | // Find free spot for the rect in the atlas |
1100 | added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy); |
1101 | if (added == 0 && stash->handleError != NULL) { |
1102 | // Atlas is full, let the user to resize the atlas (or not), and try again. |
1103 | stash->handleError(stash->errorUptr, FONS_ATLAS_FULL, 0); |
1104 | added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy); |
1105 | } |
1106 | if (added == 0) return NULL; |
1107 | |
1108 | // Init glyph. |
1109 | glyph = fons__allocGlyph(font); |
1110 | glyph->codepoint = codepoint; |
1111 | glyph->size = isize; |
1112 | glyph->blur = iblur; |
1113 | glyph->index = g; |
1114 | glyph->x0 = (short)gx; |
1115 | glyph->y0 = (short)gy; |
1116 | glyph->x1 = (short)(glyph->x0+gw); |
1117 | glyph->y1 = (short)(glyph->y0+gh); |
1118 | glyph->xadv = (short)(scale * advance * 10.0f); |
1119 | glyph->xoff = (short)(x0 - pad); |
1120 | glyph->yoff = (short)(y0 - pad); |
1121 | glyph->next = 0; |
1122 | |
1123 | // Insert char to hash lookup. |
1124 | glyph->next = font->lut[h]; |
1125 | font->lut[h] = font->nglyphs-1; |
1126 | |
1127 | // Rasterize |
1128 | dst = &stash->texData[(glyph->x0+pad) + (glyph->y0+pad) * stash->params.width]; |
1129 | fons__tt_renderGlyphBitmap(&renderFont->font, dst, gw-pad*2,gh-pad*2, stash->params.width, scale,scale, g); |
1130 | |
1131 | // Make sure there is one pixel empty border. |
1132 | dst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; |
1133 | for (y = 0; y < gh; y++) { |
1134 | dst[y*stash->params.width] = 0; |
1135 | dst[gw-1 + y*stash->params.width] = 0; |
1136 | } |
1137 | for (x = 0; x < gw; x++) { |
1138 | dst[x] = 0; |
1139 | dst[x + (gh-1)*stash->params.width] = 0; |
1140 | } |
1141 | |
1142 | // Debug code to color the glyph background |
1143 | /* unsigned char* fdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; |
1144 | for (y = 0; y < gh; y++) { |
1145 | for (x = 0; x < gw; x++) { |
1146 | int a = (int)fdst[x+y*stash->params.width] + 20; |
1147 | if (a > 255) a = 255; |
1148 | fdst[x+y*stash->params.width] = a; |
1149 | } |
1150 | }*/ |
1151 | |
1152 | // Blur |
1153 | if (iblur > 0) { |
1154 | stash->nscratch = 0; |
1155 | bdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; |
1156 | fons__blur(stash, bdst, gw,gh, stash->params.width, iblur); |
1157 | } |
1158 | |
1159 | stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], glyph->x0); |
1160 | stash->dirtyRect[1] = fons__mini(stash->dirtyRect[1], glyph->y0); |
1161 | stash->dirtyRect[2] = fons__maxi(stash->dirtyRect[2], glyph->x1); |
1162 | stash->dirtyRect[3] = fons__maxi(stash->dirtyRect[3], glyph->y1); |
1163 | |
1164 | return glyph; |
1165 | } |
1166 | |
1167 | static void fons__getQuad(FONScontext* stash, FONSfont* font, |
1168 | int prevGlyphIndex, FONSglyph* glyph, |
1169 | float scale, float spacing, float* x, float* y, FONSquad* q) |
1170 | { |
1171 | float rx,ry,xoff,yoff,x0,y0,x1,y1; |
1172 | |
1173 | if (prevGlyphIndex != -1) { |
1174 | float adv = fons__tt_getGlyphKernAdvance(&font->font, prevGlyphIndex, glyph->index) * scale; |
1175 | *x += (int)(adv + spacing + 0.5f); |
1176 | } |
1177 | |
1178 | // Each glyph has 2px border to allow good interpolation, |
1179 | // one pixel to prevent leaking, and one to allow good interpolation for rendering. |
1180 | // Inset the texture region by one pixel for correct interpolation. |
1181 | xoff = (short)(glyph->xoff+1); |
1182 | yoff = (short)(glyph->yoff+1); |
1183 | x0 = (float)(glyph->x0+1); |
1184 | y0 = (float)(glyph->y0+1); |
1185 | x1 = (float)(glyph->x1-1); |
1186 | y1 = (float)(glyph->y1-1); |
1187 | |
1188 | if (stash->params.flags & FONS_ZERO_TOPLEFT) { |
1189 | rx = (float)(int)(*x + xoff); |
1190 | ry = (float)(int)(*y + yoff); |
1191 | |
1192 | q->x0 = rx; |
1193 | q->y0 = ry; |
1194 | q->x1 = rx + x1 - x0; |
1195 | q->y1 = ry + y1 - y0; |
1196 | |
1197 | q->s0 = x0 * stash->itw; |
1198 | q->t0 = y0 * stash->ith; |
1199 | q->s1 = x1 * stash->itw; |
1200 | q->t1 = y1 * stash->ith; |
1201 | } else { |
1202 | rx = (float)(int)(*x + xoff); |
1203 | ry = (float)(int)(*y - yoff); |
1204 | |
1205 | q->x0 = rx; |
1206 | q->y0 = ry; |
1207 | q->x1 = rx + x1 - x0; |
1208 | q->y1 = ry - y1 + y0; |
1209 | |
1210 | q->s0 = x0 * stash->itw; |
1211 | q->t0 = y0 * stash->ith; |
1212 | q->s1 = x1 * stash->itw; |
1213 | q->t1 = y1 * stash->ith; |
1214 | } |
1215 | |
1216 | *x += (int)(glyph->xadv / 10.0f + 0.5f); |
1217 | } |
1218 | |
1219 | static void fons__flush(FONScontext* stash) |
1220 | { |
1221 | // Flush texture |
1222 | if (stash->dirtyRect[0] < stash->dirtyRect[2] && stash->dirtyRect[1] < stash->dirtyRect[3]) { |
1223 | if (stash->params.renderUpdate != NULL) |
1224 | stash->params.renderUpdate(stash->params.userPtr, stash->dirtyRect, stash->texData); |
1225 | // Reset dirty rect |
1226 | stash->dirtyRect[0] = stash->params.width; |
1227 | stash->dirtyRect[1] = stash->params.height; |
1228 | stash->dirtyRect[2] = 0; |
1229 | stash->dirtyRect[3] = 0; |
1230 | } |
1231 | |
1232 | // Flush triangles |
1233 | if (stash->nverts > 0) { |
1234 | if (stash->params.renderDraw != NULL) |
1235 | stash->params.renderDraw(stash->params.userPtr, stash->verts, stash->tcoords, stash->colors, stash->nverts); |
1236 | stash->nverts = 0; |
1237 | } |
1238 | } |
1239 | |
1240 | static __inline void fons__vertex(FONScontext* stash, float x, float y, float s, float t, unsigned int c) |
1241 | { |
1242 | stash->verts[stash->nverts*2+0] = x; |
1243 | stash->verts[stash->nverts*2+1] = y; |
1244 | stash->tcoords[stash->nverts*2+0] = s; |
1245 | stash->tcoords[stash->nverts*2+1] = t; |
1246 | stash->colors[stash->nverts] = c; |
1247 | stash->nverts++; |
1248 | } |
1249 | |
1250 | static float fons__getVertAlign(FONScontext* stash, FONSfont* font, int align, short isize) |
1251 | { |
1252 | if (stash->params.flags & FONS_ZERO_TOPLEFT) { |
1253 | if (align & FONS_ALIGN_TOP) { |
1254 | return font->ascender * (float)isize/10.0f; |
1255 | } else if (align & FONS_ALIGN_MIDDLE) { |
1256 | return (font->ascender + font->descender) / 2.0f * (float)isize/10.0f; |
1257 | } else if (align & FONS_ALIGN_BASELINE) { |
1258 | return 0.0f; |
1259 | } else if (align & FONS_ALIGN_BOTTOM) { |
1260 | return font->descender * (float)isize/10.0f; |
1261 | } |
1262 | } else { |
1263 | if (align & FONS_ALIGN_TOP) { |
1264 | return -font->ascender * (float)isize/10.0f; |
1265 | } else if (align & FONS_ALIGN_MIDDLE) { |
1266 | return -(font->ascender + font->descender) / 2.0f * (float)isize/10.0f; |
1267 | } else if (align & FONS_ALIGN_BASELINE) { |
1268 | return 0.0f; |
1269 | } else if (align & FONS_ALIGN_BOTTOM) { |
1270 | return -font->descender * (float)isize/10.0f; |
1271 | } |
1272 | } |
1273 | return 0.0; |
1274 | } |
1275 | |
1276 | FONS_DEF float fonsDrawText(FONScontext* stash, |
1277 | float x, float y, |
1278 | const char* str, const char* end) |
1279 | { |
1280 | FONSstate* state = fons__getState(stash); |
1281 | unsigned int codepoint; |
1282 | unsigned int utf8state = 0; |
1283 | FONSglyph* glyph = NULL; |
1284 | FONSquad q; |
1285 | int prevGlyphIndex = -1; |
1286 | short isize = (short)(state->size*10.0f); |
1287 | short iblur = (short)state->blur; |
1288 | float scale; |
1289 | FONSfont* font; |
1290 | float width; |
1291 | |
1292 | if (stash == NULL) return x; |
1293 | if (state->font < 0 || state->font >= stash->nfonts) return x; |
1294 | font = stash->fonts[state->font]; |
1295 | if (font->data == NULL) return x; |
1296 | |
1297 | scale = fons__tt_getPixelHeightScale(&font->font, (float)isize/10.0f); |
1298 | |
1299 | if (end == NULL) |
1300 | end = str + strlen(str); |
1301 | |
1302 | // Align horizontally |
1303 | if (state->align & FONS_ALIGN_LEFT) { |
1304 | // empty |
1305 | } else if (state->align & FONS_ALIGN_RIGHT) { |
1306 | width = fonsTextBounds(stash, x,y, str, end, NULL); |
1307 | x -= width; |
1308 | } else if (state->align & FONS_ALIGN_CENTER) { |
1309 | width = fonsTextBounds(stash, x,y, str, end, NULL); |
1310 | x -= width * 0.5f; |
1311 | } |
1312 | // Align vertically. |
1313 | y += fons__getVertAlign(stash, font, state->align, isize); |
1314 | |
1315 | for (; str != end; ++str) { |
1316 | if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str)) |
1317 | continue; |
1318 | glyph = fons__getGlyph(stash, font, codepoint, isize, iblur); |
1319 | if (glyph != NULL) { |
1320 | fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q); |
1321 | |
1322 | if (stash->nverts+6 > FONS_VERTEX_COUNT) |
1323 | fons__flush(stash); |
1324 | |
1325 | fons__vertex(stash, q.x0, q.y0, q.s0, q.t0, state->color); |
1326 | fons__vertex(stash, q.x1, q.y1, q.s1, q.t1, state->color); |
1327 | fons__vertex(stash, q.x1, q.y0, q.s1, q.t0, state->color); |
1328 | |
1329 | fons__vertex(stash, q.x0, q.y0, q.s0, q.t0, state->color); |
1330 | fons__vertex(stash, q.x0, q.y1, q.s0, q.t1, state->color); |
1331 | fons__vertex(stash, q.x1, q.y1, q.s1, q.t1, state->color); |
1332 | } |
1333 | prevGlyphIndex = glyph != NULL ? glyph->index : -1; |
1334 | } |
1335 | fons__flush(stash); |
1336 | |
1337 | return x; |
1338 | } |
1339 | |
1340 | FONS_DEF int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, |
1341 | float x, float y, const char* str, const char* end) |
1342 | { |
1343 | FONSstate* state = fons__getState(stash); |
1344 | float width; |
1345 | |
1346 | memset(iter, 0, sizeof(*iter)); |
1347 | |
1348 | if (stash == NULL) return 0; |
1349 | if (state->font < 0 || state->font >= stash->nfonts) return 0; |
1350 | iter->font = stash->fonts[state->font]; |
1351 | if (iter->font->data == NULL) return 0; |
1352 | |
1353 | iter->isize = (short)(state->size*10.0f); |
1354 | iter->iblur = (short)state->blur; |
1355 | iter->scale = fons__tt_getPixelHeightScale(&iter->font->font, (float)iter->isize/10.0f); |
1356 | |
1357 | // Align horizontally |
1358 | if (state->align & FONS_ALIGN_LEFT) { |
1359 | // empty |
1360 | } else if (state->align & FONS_ALIGN_RIGHT) { |
1361 | width = fonsTextBounds(stash, x,y, str, end, NULL); |
1362 | x -= width; |
1363 | } else if (state->align & FONS_ALIGN_CENTER) { |
1364 | width = fonsTextBounds(stash, x,y, str, end, NULL); |
1365 | x -= width * 0.5f; |
1366 | } |
1367 | // Align vertically. |
1368 | y += fons__getVertAlign(stash, iter->font, state->align, iter->isize); |
1369 | |
1370 | if (end == NULL) |
1371 | end = str + strlen(str); |
1372 | |
1373 | iter->x = iter->nextx = x; |
1374 | iter->y = iter->nexty = y; |
1375 | iter->spacing = state->spacing; |
1376 | iter->str = str; |
1377 | iter->next = str; |
1378 | iter->end = end; |
1379 | iter->codepoint = 0; |
1380 | iter->prevGlyphIndex = -1; |
1381 | |
1382 | return 1; |
1383 | } |
1384 | |
1385 | FONS_DEF int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, FONSquad* quad) |
1386 | { |
1387 | FONSglyph* glyph = NULL; |
1388 | const char* str = iter->next; |
1389 | iter->str = iter->next; |
1390 | |
1391 | if (str == iter->end) |
1392 | return 0; |
1393 | |
1394 | for (; str != iter->end; str++) { |
1395 | if (fons__decutf8(&iter->utf8state, &iter->codepoint, *(const unsigned char*)str)) |
1396 | continue; |
1397 | str++; |
1398 | // Get glyph and quad |
1399 | iter->x = iter->nextx; |
1400 | iter->y = iter->nexty; |
1401 | glyph = fons__getGlyph(stash, iter->font, iter->codepoint, iter->isize, iter->iblur); |
1402 | if (glyph != NULL) |
1403 | fons__getQuad(stash, iter->font, iter->prevGlyphIndex, glyph, iter->scale, iter->spacing, &iter->nextx, &iter->nexty, quad); |
1404 | iter->prevGlyphIndex = glyph != NULL ? glyph->index : -1; |
1405 | break; |
1406 | } |
1407 | iter->next = str; |
1408 | |
1409 | return 1; |
1410 | } |
1411 | |
1412 | FONS_DEF void fonsDrawDebug(FONScontext* stash, float x, float y) |
1413 | { |
1414 | int i; |
1415 | int w = stash->params.width; |
1416 | int h = stash->params.height; |
1417 | float u = w == 0 ? 0 : (1.0f / w); |
1418 | float v = h == 0 ? 0 : (1.0f / h); |
1419 | |
1420 | if (stash->nverts+6+6 > FONS_VERTEX_COUNT) |
1421 | fons__flush(stash); |
1422 | |
1423 | // Draw background |
1424 | fons__vertex(stash, x+0, y+0, u, v, 0x0fffffff); |
1425 | fons__vertex(stash, x+w, y+h, u, v, 0x0fffffff); |
1426 | fons__vertex(stash, x+w, y+0, u, v, 0x0fffffff); |
1427 | |
1428 | fons__vertex(stash, x+0, y+0, u, v, 0x0fffffff); |
1429 | fons__vertex(stash, x+0, y+h, u, v, 0x0fffffff); |
1430 | fons__vertex(stash, x+w, y+h, u, v, 0x0fffffff); |
1431 | |
1432 | // Draw texture |
1433 | fons__vertex(stash, x+0, y+0, 0, 0, 0xffffffff); |
1434 | fons__vertex(stash, x+w, y+h, 1, 1, 0xffffffff); |
1435 | fons__vertex(stash, x+w, y+0, 1, 0, 0xffffffff); |
1436 | |
1437 | fons__vertex(stash, x+0, y+0, 0, 0, 0xffffffff); |
1438 | fons__vertex(stash, x+0, y+h, 0, 1, 0xffffffff); |
1439 | fons__vertex(stash, x+w, y+h, 1, 1, 0xffffffff); |
1440 | |
1441 | // Drawbug draw atlas |
1442 | for (i = 0; i < stash->atlas->nnodes; i++) { |
1443 | FONSatlasNode* n = &stash->atlas->nodes[i]; |
1444 | |
1445 | if (stash->nverts+6 > FONS_VERTEX_COUNT) |
1446 | fons__flush(stash); |
1447 | |
1448 | fons__vertex(stash, x+n->x+0, y+n->y+0, u, v, 0xc00000ff); |
1449 | fons__vertex(stash, x+n->x+n->width, y+n->y+1, u, v, 0xc00000ff); |
1450 | fons__vertex(stash, x+n->x+n->width, y+n->y+0, u, v, 0xc00000ff); |
1451 | |
1452 | fons__vertex(stash, x+n->x+0, y+n->y+0, u, v, 0xc00000ff); |
1453 | fons__vertex(stash, x+n->x+0, y+n->y+1, u, v, 0xc00000ff); |
1454 | fons__vertex(stash, x+n->x+n->width, y+n->y+1, u, v, 0xc00000ff); |
1455 | } |
1456 | |
1457 | fons__flush(stash); |
1458 | } |
1459 | |
1460 | FONS_DEF float fonsTextBounds(FONScontext* stash, |
1461 | float x, float y, |
1462 | const char* str, const char* end, |
1463 | float* bounds) |
1464 | { |
1465 | FONSstate* state = fons__getState(stash); |
1466 | unsigned int codepoint; |
1467 | unsigned int utf8state = 0; |
1468 | FONSquad q; |
1469 | FONSglyph* glyph = NULL; |
1470 | int prevGlyphIndex = -1; |
1471 | short isize = (short)(state->size*10.0f); |
1472 | short iblur = (short)state->blur; |
1473 | float scale; |
1474 | FONSfont* font; |
1475 | float startx, advance; |
1476 | float minx, miny, maxx, maxy; |
1477 | |
1478 | if (stash == NULL) return 0; |
1479 | if (state->font < 0 || state->font >= stash->nfonts) return 0; |
1480 | font = stash->fonts[state->font]; |
1481 | if (font->data == NULL) return 0; |
1482 | |
1483 | scale = fons__tt_getPixelHeightScale(&font->font, (float)isize/10.0f); |
1484 | |
1485 | // Align vertically. |
1486 | y += fons__getVertAlign(stash, font, state->align, isize); |
1487 | |
1488 | minx = maxx = x; |
1489 | miny = maxy = y; |
1490 | startx = x; |
1491 | |
1492 | if (end == NULL) |
1493 | end = str + strlen(str); |
1494 | |
1495 | for (; str != end; ++str) { |
1496 | if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str)) |
1497 | continue; |
1498 | glyph = fons__getGlyph(stash, font, codepoint, isize, iblur); |
1499 | if (glyph != NULL) { |
1500 | fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q); |
1501 | if (q.x0 < minx) minx = q.x0; |
1502 | if (q.x1 > maxx) maxx = q.x1; |
1503 | if (stash->params.flags & FONS_ZERO_TOPLEFT) { |
1504 | if (q.y0 < miny) miny = q.y0; |
1505 | if (q.y1 > maxy) maxy = q.y1; |
1506 | } else { |
1507 | if (q.y1 < miny) miny = q.y1; |
1508 | if (q.y0 > maxy) maxy = q.y0; |
1509 | } |
1510 | } |
1511 | prevGlyphIndex = glyph != NULL ? glyph->index : -1; |
1512 | } |
1513 | |
1514 | advance = x - startx; |
1515 | |
1516 | // Align horizontally |
1517 | if (state->align & FONS_ALIGN_LEFT) { |
1518 | // empty |
1519 | } else if (state->align & FONS_ALIGN_RIGHT) { |
1520 | minx -= advance; |
1521 | maxx -= advance; |
1522 | } else if (state->align & FONS_ALIGN_CENTER) { |
1523 | minx -= advance * 0.5f; |
1524 | maxx -= advance * 0.5f; |
1525 | } |
1526 | |
1527 | if (bounds) { |
1528 | bounds[0] = minx; |
1529 | bounds[1] = miny; |
1530 | bounds[2] = maxx; |
1531 | bounds[3] = maxy; |
1532 | } |
1533 | |
1534 | return advance; |
1535 | } |
1536 | |
1537 | FONS_DEF void fonsVertMetrics(FONScontext* stash, |
1538 | float* ascender, float* descender, float* lineh) |
1539 | { |
1540 | FONSfont* font; |
1541 | FONSstate* state = fons__getState(stash); |
1542 | short isize; |
1543 | |
1544 | if (stash == NULL) return; |
1545 | if (state->font < 0 || state->font >= stash->nfonts) return; |
1546 | font = stash->fonts[state->font]; |
1547 | isize = (short)(state->size*10.0f); |
1548 | if (font->data == NULL) return; |
1549 | |
1550 | if (ascender) |
1551 | *ascender = font->ascender*isize/10.0f; |
1552 | if (descender) |
1553 | *descender = font->descender*isize/10.0f; |
1554 | if (lineh) |
1555 | *lineh = font->lineh*isize/10.0f; |
1556 | } |
1557 | |
1558 | FONS_DEF void fonsLineBounds(FONScontext* stash, float y, float* miny, float* maxy) |
1559 | { |
1560 | FONSfont* font; |
1561 | FONSstate* state = fons__getState(stash); |
1562 | short isize; |
1563 | |
1564 | if (stash == NULL) return; |
1565 | if (state->font < 0 || state->font >= stash->nfonts) return; |
1566 | font = stash->fonts[state->font]; |
1567 | isize = (short)(state->size*10.0f); |
1568 | if (font->data == NULL) return; |
1569 | |
1570 | y += fons__getVertAlign(stash, font, state->align, isize); |
1571 | |
1572 | if (stash->params.flags & FONS_ZERO_TOPLEFT) { |
1573 | *miny = y - font->ascender * (float)isize/10.0f; |
1574 | *maxy = *miny + font->lineh*isize/10.0f; |
1575 | } else { |
1576 | *maxy = y + font->descender * (float)isize/10.0f; |
1577 | *miny = *maxy - font->lineh*isize/10.0f; |
1578 | } |
1579 | } |
1580 | |
1581 | FONS_DEF const unsigned char* fonsGetTextureData(FONScontext* stash, int* width, int* height) |
1582 | { |
1583 | if (width != NULL) |
1584 | *width = stash->params.width; |
1585 | if (height != NULL) |
1586 | *height = stash->params.height; |
1587 | return stash->texData; |
1588 | } |
1589 | |
1590 | FONS_DEF int fonsValidateTexture(FONScontext* stash, int* dirty) |
1591 | { |
1592 | if (stash->dirtyRect[0] < stash->dirtyRect[2] && stash->dirtyRect[1] < stash->dirtyRect[3]) { |
1593 | dirty[0] = stash->dirtyRect[0]; |
1594 | dirty[1] = stash->dirtyRect[1]; |
1595 | dirty[2] = stash->dirtyRect[2]; |
1596 | dirty[3] = stash->dirtyRect[3]; |
1597 | // Reset dirty rect |
1598 | stash->dirtyRect[0] = stash->params.width; |
1599 | stash->dirtyRect[1] = stash->params.height; |
1600 | stash->dirtyRect[2] = 0; |
1601 | stash->dirtyRect[3] = 0; |
1602 | return 1; |
1603 | } |
1604 | return 0; |
1605 | } |
1606 | |
1607 | FONS_DEF void fonsDeleteInternal(FONScontext* stash) |
1608 | { |
1609 | int i; |
1610 | if (stash == NULL) return; |
1611 | |
1612 | if (stash->params.renderDelete) |
1613 | stash->params.renderDelete(stash->params.userPtr); |
1614 | |
1615 | for (i = 0; i < stash->nfonts; ++i) |
1616 | fons__freeFont(stash->fonts[i]); |
1617 | |
1618 | if (stash->atlas) fons__deleteAtlas(stash->atlas); |
1619 | if (stash->fonts) FONTSTASH_FREE(stash->fonts); |
1620 | if (stash->texData) FONTSTASH_FREE(stash->texData); |
1621 | if (stash->scratch) FONTSTASH_FREE(stash->scratch); |
1622 | FONTSTASH_FREE(stash); |
1623 | } |
1624 | |
1625 | FONS_DEF void fonsSetErrorCallback(FONScontext* stash, void (*callback)(void* uptr, int error, int val), void* uptr) |
1626 | { |
1627 | if (stash == NULL) return; |
1628 | stash->handleError = callback; |
1629 | stash->errorUptr = uptr; |
1630 | } |
1631 | |
1632 | FONS_DEF void fonsGetAtlasSize(FONScontext* stash, int* width, int* height) |
1633 | { |
1634 | if (stash == NULL) return; |
1635 | *width = stash->params.width; |
1636 | *height = stash->params.height; |
1637 | } |
1638 | |
1639 | FONS_DEF int fonsExpandAtlas(FONScontext* stash, int width, int height) |
1640 | { |
1641 | int i, maxy = 0; |
1642 | unsigned char* data = NULL; |
1643 | if (stash == NULL) return 0; |
1644 | |
1645 | width = fons__maxi(width, stash->params.width); |
1646 | height = fons__maxi(height, stash->params.height); |
1647 | |
1648 | if (width == stash->params.width && height == stash->params.height) |
1649 | return 1; |
1650 | |
1651 | // Flush pending glyphs. |
1652 | fons__flush(stash); |
1653 | |
1654 | // Create new texture |
1655 | if (stash->params.renderResize != NULL) { |
1656 | if (stash->params.renderResize(stash->params.userPtr, width, height) == 0) |
1657 | return 0; |
1658 | } |
1659 | // Copy old texture data over. |
1660 | data = (unsigned char*)FONTSTASH_MALLOC(width * height); |
1661 | if (data == NULL) |
1662 | return 0; |
1663 | for (i = 0; i < stash->params.height; i++) { |
1664 | unsigned char* dst = &data[i*width]; |
1665 | unsigned char* src = &stash->texData[i*stash->params.width]; |
1666 | memcpy(dst, src, stash->params.width); |
1667 | if (width > stash->params.width) |
1668 | memset(dst+stash->params.width, 0, width - stash->params.width); |
1669 | } |
1670 | if (height > stash->params.height) |
1671 | memset(&data[stash->params.height * width], 0, (height - stash->params.height) * width); |
1672 | |
1673 | FONTSTASH_FREE(stash->texData); |
1674 | stash->texData = data; |
1675 | |
1676 | // Increase atlas size |
1677 | fons__atlasExpand(stash->atlas, width, height); |
1678 | |
1679 | // Add existing data as dirty. |
1680 | for (i = 0; i < stash->atlas->nnodes; i++) |
1681 | maxy = fons__maxi(maxy, stash->atlas->nodes[i].y); |
1682 | stash->dirtyRect[0] = 0; |
1683 | stash->dirtyRect[1] = 0; |
1684 | stash->dirtyRect[2] = stash->params.width; |
1685 | stash->dirtyRect[3] = maxy; |
1686 | |
1687 | stash->params.width = width; |
1688 | stash->params.height = height; |
1689 | stash->itw = 1.0f/stash->params.width; |
1690 | stash->ith = 1.0f/stash->params.height; |
1691 | |
1692 | return 1; |
1693 | } |
1694 | |
1695 | FONS_DEF int fonsResetAtlas(FONScontext* stash, int width, int height) |
1696 | { |
1697 | int i, j; |
1698 | if (stash == NULL) return 0; |
1699 | |
1700 | // Flush pending glyphs. |
1701 | fons__flush(stash); |
1702 | |
1703 | // Create new texture |
1704 | if (stash->params.renderResize != NULL) { |
1705 | if (stash->params.renderResize(stash->params.userPtr, width, height) == 0) |
1706 | return 0; |
1707 | } |
1708 | |
1709 | // Reset atlas |
1710 | fons__atlasReset(stash->atlas, width, height); |
1711 | |
1712 | // Clear texture data. |
1713 | stash->texData = (unsigned char*)FONTSTASH_REALLOC(stash->texData, width * height); |
1714 | if (stash->texData == NULL) return 0; |
1715 | memset(stash->texData, 0, width * height); |
1716 | |
1717 | // Reset dirty rect |
1718 | stash->dirtyRect[0] = width; |
1719 | stash->dirtyRect[1] = height; |
1720 | stash->dirtyRect[2] = 0; |
1721 | stash->dirtyRect[3] = 0; |
1722 | |
1723 | // Reset cached glyphs |
1724 | for (i = 0; i < stash->nfonts; i++) { |
1725 | FONSfont* font = stash->fonts[i]; |
1726 | font->nglyphs = 0; |
1727 | for (j = 0; j < FONS_HASH_LUT_SIZE; j++) |
1728 | font->lut[j] = -1; |
1729 | } |
1730 | |
1731 | stash->params.width = width; |
1732 | stash->params.height = height; |
1733 | stash->itw = 1.0f/stash->params.width; |
1734 | stash->ith = 1.0f/stash->params.height; |
1735 | |
1736 | // Add white rect at 0,0 for debug drawing. |
1737 | fons__addWhiteRect(stash, 2,2); |
1738 | |
1739 | return 1; |
1740 | } |
1741 | |
1742 | #endif // FONTSTASH_IMPLEMENTATION |