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