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