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