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