2 // Copyright (c) 2009-2013 Mikko Mononen memon@inside.org
4 // This software is provided 'as-is', without any express or implied
5 // warranty. In no event will the authors be held liable for any damages
6 // arising from the use of this software.
7 // Permission is granted to anyone to use this software for any purpose,
8 // including commercial applications, and to alter it and redistribute it
9 // freely, subject to the following restrictions:
10 // 1. The origin of this software must not be misrepresented; you must not
11 // claim that you wrote the original software. If you use this software
12 // in a product, an acknowledgment in the product documentation would be
13 // appreciated but is not required.
14 // 2. Altered source versions must be plainly marked as such, and must not be
15 // misrepresented as being the original software.
16 // 3. This notice may not be removed or altered from any source distribution.
22 #define FONS_INVALID -1
25 FONS_ZERO_TOPLEFT
= 1,
26 FONS_ZERO_BOTTOMLEFT
= 2,
31 FONS_ALIGN_LEFT
= 1<<0, // Default
32 FONS_ALIGN_CENTER
= 1<<1,
33 FONS_ALIGN_RIGHT
= 1<<2,
35 FONS_ALIGN_TOP
= 1<<3,
36 FONS_ALIGN_MIDDLE
= 1<<4,
37 FONS_ALIGN_BOTTOM
= 1<<5,
38 FONS_ALIGN_BASELINE
= 1<<6, // Default
41 enum FONSglyphBitmap
{
42 FONS_GLYPH_BITMAP_OPTIONAL
= 1,
43 FONS_GLYPH_BITMAP_REQUIRED
= 2,
47 // Font atlas is full.
49 // Scratch memory used to render glyphs is full, requested size reported in 'val', you may need to bump up FONS_SCRATCH_BUF_SIZE.
50 FONS_SCRATCH_FULL
= 2,
51 // Calls to fonsPushState has created too large stack, if you need deep state stack bump up FONS_MAX_STATES.
52 FONS_STATES_OVERFLOW
= 3,
53 // Trying to pop too many states fonsPopState().
54 FONS_STATES_UNDERFLOW
= 4,
61 int (*renderCreate
)(void* uptr
, int width
, int height
);
62 int (*renderResize
)(void* uptr
, int width
, int height
);
63 void (*renderUpdate
)(void* uptr
, int* rect
, const unsigned char* data
);
64 void (*renderDraw
)(void* uptr
, const float* verts
, const float* tcoords
, const unsigned int* colors
, int nverts
);
65 void (*renderDelete
)(void* uptr
);
67 typedef struct FONSparams FONSparams
;
74 typedef struct FONSquad FONSquad
;
77 float x
, y
, nextx
, nexty
, scale
, spacing
;
78 unsigned int codepoint
;
80 struct FONSfont
* font
;
85 unsigned int utf8state
;
88 typedef struct FONStextIter FONStextIter
;
90 typedef struct FONScontext FONScontext
;
92 // Constructor and destructor.
93 FONScontext
* fonsCreateInternal(FONSparams
* params
);
94 void fonsDeleteInternal(FONScontext
* s
);
96 void fonsSetErrorCallback(FONScontext
* s
, void (*callback
)(void* uptr
, int error
, int val
), void* uptr
);
97 // Returns current atlas size.
98 void fonsGetAtlasSize(FONScontext
* s
, int* width
, int* height
);
99 // Expands the atlas size.
100 int fonsExpandAtlas(FONScontext
* s
, int width
, int height
);
101 // Resets the whole stash.
102 int fonsResetAtlas(FONScontext
* stash
, int width
, int height
);
105 int fonsAddFont(FONScontext
* s
, const char* name
, const char* path
, int fontIndex
);
106 int fonsAddFontMem(FONScontext
* s
, const char* name
, unsigned char* data
, int ndata
, int freeData
, int fontIndex
);
107 int fonsGetFontByName(FONScontext
* s
, const char* name
);
110 void fonsPushState(FONScontext
* s
);
111 void fonsPopState(FONScontext
* s
);
112 void fonsClearState(FONScontext
* s
);
115 void fonsSetSize(FONScontext
* s
, float size
);
116 void fonsSetColor(FONScontext
* s
, unsigned int color
);
117 void fonsSetSpacing(FONScontext
* s
, float spacing
);
118 void fonsSetBlur(FONScontext
* s
, float blur
);
119 void fonsSetAlign(FONScontext
* s
, int align
);
120 void fonsSetFont(FONScontext
* s
, int font
);
123 float fonsDrawText(FONScontext
* s
, float x
, float y
, const char* string
, const char* end
);
126 float fonsTextBounds(FONScontext
* s
, float x
, float y
, const char* string
, const char* end
, float* bounds
);
127 void fonsLineBounds(FONScontext
* s
, float y
, float* miny
, float* maxy
);
128 void fonsVertMetrics(FONScontext
* s
, float* ascender
, float* descender
, float* lineh
);
131 int fonsTextIterInit(FONScontext
* stash
, FONStextIter
* iter
, float x
, float y
, const char* str
, const char* end
, int bitmapOption
);
132 int fonsTextIterNext(FONScontext
* stash
, FONStextIter
* iter
, struct FONSquad
* quad
);
134 // Pull texture changes
135 const unsigned char* fonsGetTextureData(FONScontext
* stash
, int* width
, int* height
);
136 int fonsValidateTexture(FONScontext
* s
, int* dirty
);
138 // Draws the stash texture for debugging
139 void fonsDrawDebug(FONScontext
* s
, float x
, float y
);
141 #endif // FONTSTASH_H
144 #ifdef FONTSTASH_IMPLEMENTATION
146 #define FONS_NOTUSED(v) (void)sizeof(v)
148 #ifdef FONS_USE_FREETYPE
150 #include <ft2build.h>
151 #include FT_FREETYPE_H
152 #include FT_ADVANCES_H
155 struct FONSttFontImpl
{
158 typedef struct FONSttFontImpl FONSttFontImpl
;
162 #define STB_TRUETYPE_IMPLEMENTATION
163 static void* fons__tmpalloc(size_t size
, void* up
);
164 static void fons__tmpfree(void* ptr
, void* up
);
165 #define STBTT_malloc(x,u) fons__tmpalloc(x,u)
166 #define STBTT_free(x,u) fons__tmpfree(x,u)
167 #include "stb_truetype.h"
169 struct FONSttFontImpl
{
172 typedef struct FONSttFontImpl FONSttFontImpl
;
176 #ifndef FONS_SCRATCH_BUF_SIZE
177 # define FONS_SCRATCH_BUF_SIZE 96000
179 #ifndef FONS_HASH_LUT_SIZE
180 # define FONS_HASH_LUT_SIZE 256
182 #ifndef FONS_INIT_FONTS
183 # define FONS_INIT_FONTS 4
185 #ifndef FONS_INIT_GLYPHS
186 # define FONS_INIT_GLYPHS 256
188 #ifndef FONS_INIT_ATLAS_NODES
189 # define FONS_INIT_ATLAS_NODES 256
191 #ifndef FONS_VERTEX_COUNT
192 # define FONS_VERTEX_COUNT 1024
194 #ifndef FONS_MAX_STATES
195 # define FONS_MAX_STATES 20
197 #ifndef FONS_MAX_FALLBACKS
198 # define FONS_MAX_FALLBACKS 20
201 static unsigned int fons__hashint(unsigned int a
)
212 static int fons__mini(int a
, int b
)
214 return a
< b
? a
: b
;
217 static int fons__maxi(int a
, int b
)
219 return a
> b
? a
: b
;
224 unsigned int codepoint
;
229 short xadv
,xoff
,yoff
;
231 typedef struct FONSglyph FONSglyph
;
239 unsigned char freeData
;
246 int lut
[FONS_HASH_LUT_SIZE
];
247 int fallbacks
[FONS_MAX_FALLBACKS
];
250 typedef struct FONSfont FONSfont
;
261 typedef struct FONSstate FONSstate
;
263 struct FONSatlasNode
{
266 typedef struct FONSatlasNode FONSatlasNode
;
271 FONSatlasNode
* nodes
;
275 typedef struct FONSatlas FONSatlas
;
281 unsigned char* texData
;
287 float verts
[FONS_VERTEX_COUNT
*2];
288 float tcoords
[FONS_VERTEX_COUNT
*2];
289 unsigned int colors
[FONS_VERTEX_COUNT
];
291 unsigned char* scratch
;
293 FONSstate states
[FONS_MAX_STATES
];
295 void (*handleError
)(void* uptr
, int error
, int val
);
297 #ifdef FONS_USE_FREETYPE
298 FT_Library ftLibrary
;
302 #ifdef FONS_USE_FREETYPE
304 int fons__tt_init(FONScontext
*context
)
307 ftError
= FT_Init_FreeType(&context
->ftLibrary
);
311 int fons__tt_done(FONScontext
*context
)
314 ftError
= FT_Done_FreeType(context
->ftLibrary
);
318 int fons__tt_loadFont(FONScontext
*context
, FONSttFontImpl
*font
, unsigned char *data
, int dataSize
, int fontIndex
)
322 //font->font.userdata = stash;
323 ftError
= FT_New_Memory_Face(context
->ftLibrary
, (const FT_Byte
*)data
, dataSize
, fontIndex
, &font
->font
);
327 void fons__tt_getFontVMetrics(FONSttFontImpl
*font
, int *ascent
, int *descent
, int *lineGap
)
329 *ascent
= font
->font
->ascender
;
330 *descent
= font
->font
->descender
;
331 *lineGap
= font
->font
->height
- (*ascent
- *descent
);
334 float fons__tt_getPixelHeightScale(FONSttFontImpl
*font
, float size
)
337 // Note(DPF) maintain pixel-based units for compat after nanovg update
338 return size
/ (font
->font
->ascender
- font
->font
->descender
);
340 return size
/ font
->font
->units_per_EM
;
344 int fons__tt_getGlyphIndex(FONSttFontImpl
*font
, int codepoint
)
346 return FT_Get_Char_Index(font
->font
, codepoint
);
349 int fons__tt_buildGlyphBitmap(FONSttFontImpl
*font
, int glyph
, float size
, float scale
,
350 int *advance
, int *lsb
, int *x0
, int *y0
, int *x1
, int *y1
)
353 FT_GlyphSlot ftGlyph
;
358 // Note(DPF) maintain pixel-based units for compat after nanovg update
359 ftError
= FT_Set_Pixel_Sizes(font
->font
, 0, (FT_UInt
)(size
* (float)font
->font
->units_per_EM
/ (float)(font
->font
->ascender
- font
->font
->descender
)));
361 ftError
= FT_Set_Pixel_Sizes(font
->font
, 0, size
);
363 if (ftError
) return 0;
365 // Note(DPF) maintain pixel-based units for compat after nanovg update
366 ftError
= FT_Load_Glyph(font
->font
, glyph
, FT_LOAD_RENDER
);
368 ftError
= FT_Load_Glyph(font
->font
, glyph
, FT_LOAD_RENDER
| FT_LOAD_FORCE_AUTOHINT
);
370 if (ftError
) return 0;
371 ftError
= FT_Get_Advance(font
->font
, glyph
, FT_LOAD_NO_SCALE
, &advFixed
);
372 if (ftError
) return 0;
373 ftGlyph
= font
->font
->glyph
;
374 *advance
= (int)advFixed
;
375 *lsb
= (int)ftGlyph
->metrics
.horiBearingX
;
376 *x0
= ftGlyph
->bitmap_left
;
377 *x1
= *x0
+ ftGlyph
->bitmap
.width
;
378 *y0
= -ftGlyph
->bitmap_top
;
379 *y1
= *y0
+ ftGlyph
->bitmap
.rows
;
383 void fons__tt_renderGlyphBitmap(FONSttFontImpl
*font
, unsigned char *output
, int outWidth
, int outHeight
, int outStride
,
384 float scaleX
, float scaleY
, int glyph
)
386 FT_GlyphSlot ftGlyph
= font
->font
->glyph
;
387 int ftGlyphOffset
= 0;
389 FONS_NOTUSED(outWidth
);
390 FONS_NOTUSED(outHeight
);
391 FONS_NOTUSED(scaleX
);
392 FONS_NOTUSED(scaleY
);
393 FONS_NOTUSED(glyph
); // glyph has already been loaded by fons__tt_buildGlyphBitmap
395 for ( y
= 0; y
< ftGlyph
->bitmap
.rows
; y
++ ) {
396 for ( x
= 0; x
< ftGlyph
->bitmap
.width
; x
++ ) {
397 output
[(y
* outStride
) + x
] = ftGlyph
->bitmap
.buffer
[ftGlyphOffset
++];
402 int fons__tt_getGlyphKernAdvance(FONSttFontImpl
*font
, int glyph1
, int glyph2
)
405 FT_Get_Kerning(font
->font
, glyph1
, glyph2
, FT_KERNING_DEFAULT
, &ftKerning
);
406 return (int)((ftKerning
.x
+ 32) >> 6); // Round up and convert to integer
411 int fons__tt_init(FONScontext
*context
)
413 FONS_NOTUSED(context
);
417 int fons__tt_done(FONScontext
*context
)
419 FONS_NOTUSED(context
);
423 int fons__tt_loadFont(FONScontext
*context
, FONSttFontImpl
*font
, unsigned char *data
, int dataSize
, int fontIndex
)
425 int offset
, stbError
;
426 FONS_NOTUSED(dataSize
);
428 font
->font
.userdata
= context
;
429 offset
= stbtt_GetFontOffsetForIndex(data
, fontIndex
);
433 stbError
= stbtt_InitFont(&font
->font
, data
, offset
);
438 void fons__tt_getFontVMetrics(FONSttFontImpl
*font
, int *ascent
, int *descent
, int *lineGap
)
440 stbtt_GetFontVMetrics(&font
->font
, ascent
, descent
, lineGap
);
443 float fons__tt_getPixelHeightScale(FONSttFontImpl
*font
, float size
)
446 // Note(DPF) maintain pixel-based units for compat after nanovg update
447 return stbtt_ScaleForPixelHeight(&font
->font
, size
);
449 return stbtt_ScaleForMappingEmToPixels(&font
->font
, size
);
453 int fons__tt_getGlyphIndex(FONSttFontImpl
*font
, int codepoint
)
455 return stbtt_FindGlyphIndex(&font
->font
, codepoint
);
458 int fons__tt_buildGlyphBitmap(FONSttFontImpl
*font
, int glyph
, float size
, float scale
,
459 int *advance
, int *lsb
, int *x0
, int *y0
, int *x1
, int *y1
)
462 stbtt_GetGlyphHMetrics(&font
->font
, glyph
, advance
, lsb
);
463 stbtt_GetGlyphBitmapBox(&font
->font
, glyph
, scale
, scale
, x0
, y0
, x1
, y1
);
467 void fons__tt_renderGlyphBitmap(FONSttFontImpl
*font
, unsigned char *output
, int outWidth
, int outHeight
, int outStride
,
468 float scaleX
, float scaleY
, int glyph
)
470 stbtt_MakeGlyphBitmap(&font
->font
, output
, outWidth
, outHeight
, outStride
, scaleX
, scaleY
, glyph
);
473 int fons__tt_getGlyphKernAdvance(FONSttFontImpl
*font
, int glyph1
, int glyph2
)
475 return stbtt_GetGlyphKernAdvance(&font
->font
, glyph1
, glyph2
);
480 #ifdef STB_TRUETYPE_IMPLEMENTATION
482 static void* fons__tmpalloc(size_t size
, void* up
)
485 FONScontext
* stash
= (FONScontext
*)up
;
487 // 16-byte align the returned pointer
488 size
= (size
+ 0xf) & ~0xf;
490 if (stash
->nscratch
+(int)size
> FONS_SCRATCH_BUF_SIZE
) {
491 if (stash
->handleError
)
492 stash
->handleError(stash
->errorUptr
, FONS_SCRATCH_FULL
, stash
->nscratch
+(int)size
);
495 ptr
= stash
->scratch
+ stash
->nscratch
;
496 stash
->nscratch
+= (int)size
;
500 static void fons__tmpfree(void* ptr
, void* up
)
507 #endif // STB_TRUETYPE_IMPLEMENTATION
509 // Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
510 // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
512 #define FONS_UTF8_ACCEPT 0
513 #define FONS_UTF8_REJECT 12
515 static unsigned int fons__decutf8(unsigned int* state
, unsigned int* codep
, unsigned int byte
)
517 static const unsigned char utf8d
[] = {
518 // The first part of the table maps bytes to character classes that
519 // to reduce the size of the transition table and create bitmasks.
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 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,
524 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,
525 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,
526 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,
527 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,
529 // The second part is a transition table that maps a combination
530 // of a state of the automaton and a character class to a state.
531 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
532 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
533 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
534 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
535 12,36,12,12,12,12,12,12,12,12,12,12,
538 unsigned int type
= utf8d
[byte
];
540 *codep
= (*state
!= FONS_UTF8_ACCEPT
) ?
541 (byte
& 0x3fu
) | (*codep
<< 6) :
542 (0xff >> type
) & (byte
);
544 *state
= utf8d
[256 + *state
+ type
];
548 // Atlas based on Skyline Bin Packer by Jukka Jylänki
550 static void fons__deleteAtlas(FONSatlas
* atlas
)
552 if (atlas
== NULL
) return;
553 if (atlas
->nodes
!= NULL
) free(atlas
->nodes
);
557 static FONSatlas
* fons__allocAtlas(int w
, int h
, int nnodes
)
559 FONSatlas
* atlas
= NULL
;
561 // Allocate memory for the font stash.
562 atlas
= (FONSatlas
*)malloc(sizeof(FONSatlas
));
563 if (atlas
== NULL
) goto error
;
564 memset(atlas
, 0, sizeof(FONSatlas
));
569 // Allocate space for skyline nodes
570 atlas
->nodes
= (FONSatlasNode
*)malloc(sizeof(FONSatlasNode
) * nnodes
);
571 if (atlas
->nodes
== NULL
) goto error
;
572 memset(atlas
->nodes
, 0, sizeof(FONSatlasNode
) * nnodes
);
574 atlas
->cnodes
= nnodes
;
577 atlas
->nodes
[0].x
= 0;
578 atlas
->nodes
[0].y
= 0;
579 atlas
->nodes
[0].width
= (short)w
;
585 if (atlas
) fons__deleteAtlas(atlas
);
589 static int fons__atlasInsertNode(FONSatlas
* atlas
, int idx
, int x
, int y
, int w
)
593 if (atlas
->nnodes
+1 > atlas
->cnodes
) {
594 atlas
->cnodes
= atlas
->cnodes
== 0 ? 8 : atlas
->cnodes
* 2;
595 atlas
->nodes
= (FONSatlasNode
*)realloc(atlas
->nodes
, sizeof(FONSatlasNode
) * atlas
->cnodes
);
596 if (atlas
->nodes
== NULL
)
599 for (i
= atlas
->nnodes
; i
> idx
; i
--)
600 atlas
->nodes
[i
] = atlas
->nodes
[i
-1];
601 atlas
->nodes
[idx
].x
= (short)x
;
602 atlas
->nodes
[idx
].y
= (short)y
;
603 atlas
->nodes
[idx
].width
= (short)w
;
609 static void fons__atlasRemoveNode(FONSatlas
* atlas
, int idx
)
612 if (atlas
->nnodes
== 0) return;
613 for (i
= idx
; i
< atlas
->nnodes
-1; i
++)
614 atlas
->nodes
[i
] = atlas
->nodes
[i
+1];
618 static void fons__atlasExpand(FONSatlas
* atlas
, int w
, int h
)
620 // Insert node for empty space
621 if (w
> atlas
->width
)
622 fons__atlasInsertNode(atlas
, atlas
->nnodes
, atlas
->width
, 0, w
- atlas
->width
);
627 static void fons__atlasReset(FONSatlas
* atlas
, int w
, int h
)
634 atlas
->nodes
[0].x
= 0;
635 atlas
->nodes
[0].y
= 0;
636 atlas
->nodes
[0].width
= (short)w
;
640 static int fons__atlasAddSkylineLevel(FONSatlas
* atlas
, int idx
, int x
, int y
, int w
, int h
)
645 if (fons__atlasInsertNode(atlas
, idx
, x
, y
+h
, w
) == 0)
648 // Delete skyline segments that fall under the shadow of the new segment.
649 for (i
= idx
+1; i
< atlas
->nnodes
; i
++) {
650 if (atlas
->nodes
[i
].x
< atlas
->nodes
[i
-1].x
+ atlas
->nodes
[i
-1].width
) {
651 int shrink
= atlas
->nodes
[i
-1].x
+ atlas
->nodes
[i
-1].width
- atlas
->nodes
[i
].x
;
652 atlas
->nodes
[i
].x
+= (short)shrink
;
653 atlas
->nodes
[i
].width
-= (short)shrink
;
654 if (atlas
->nodes
[i
].width
<= 0) {
655 fons__atlasRemoveNode(atlas
, i
);
665 // Merge same height skyline segments that are next to each other.
666 for (i
= 0; i
< atlas
->nnodes
-1; i
++) {
667 if (atlas
->nodes
[i
].y
== atlas
->nodes
[i
+1].y
) {
668 atlas
->nodes
[i
].width
+= atlas
->nodes
[i
+1].width
;
669 fons__atlasRemoveNode(atlas
, i
+1);
677 static int fons__atlasRectFits(FONSatlas
* atlas
, int i
, int w
, int h
)
679 // Checks if there is enough space at the location of skyline span 'i',
680 // and return the max height of all skyline spans under that at that location,
681 // (think tetris block being dropped at that position). Or -1 if no space found.
682 int x
= atlas
->nodes
[i
].x
;
683 int y
= atlas
->nodes
[i
].y
;
685 if (x
+ w
> atlas
->width
)
688 while (spaceLeft
> 0) {
689 if (i
== atlas
->nnodes
) return -1;
690 y
= fons__maxi(y
, atlas
->nodes
[i
].y
);
691 if (y
+ h
> atlas
->height
) return -1;
692 spaceLeft
-= atlas
->nodes
[i
].width
;
698 static int fons__atlasAddRect(FONSatlas
* atlas
, int rw
, int rh
, int* rx
, int* ry
)
700 int besth
= atlas
->height
, bestw
= atlas
->width
, besti
= -1;
701 int bestx
= -1, besty
= -1, i
;
703 // Bottom left fit heuristic.
704 for (i
= 0; i
< atlas
->nnodes
; i
++) {
705 int y
= fons__atlasRectFits(atlas
, i
, rw
, rh
);
707 if (y
+ rh
< besth
|| (y
+ rh
== besth
&& atlas
->nodes
[i
].width
< bestw
)) {
709 bestw
= atlas
->nodes
[i
].width
;
711 bestx
= atlas
->nodes
[i
].x
;
720 // Perform the actual packing.
721 if (fons__atlasAddSkylineLevel(atlas
, besti
, bestx
, besty
, rw
, rh
) == 0)
730 static void fons__addWhiteRect(FONScontext
* stash
, int w
, int h
)
734 if (fons__atlasAddRect(stash
->atlas
, w
, h
, &gx
, &gy
) == 0)
738 dst
= &stash
->texData
[gx
+ gy
* stash
->params
.width
];
739 for (y
= 0; y
< h
; y
++) {
740 for (x
= 0; x
< w
; x
++)
742 dst
+= stash
->params
.width
;
745 stash
->dirtyRect
[0] = fons__mini(stash
->dirtyRect
[0], gx
);
746 stash
->dirtyRect
[1] = fons__mini(stash
->dirtyRect
[1], gy
);
747 stash
->dirtyRect
[2] = fons__maxi(stash
->dirtyRect
[2], gx
+w
);
748 stash
->dirtyRect
[3] = fons__maxi(stash
->dirtyRect
[3], gy
+h
);
751 FONScontext
* fonsCreateInternal(FONSparams
* params
)
753 FONScontext
* stash
= NULL
;
755 // Allocate memory for the font stash.
756 stash
= (FONScontext
*)malloc(sizeof(FONScontext
));
757 if (stash
== NULL
) goto error
;
758 memset(stash
, 0, sizeof(FONScontext
));
760 stash
->params
= *params
;
762 // Allocate scratch buffer.
763 stash
->scratch
= (unsigned char*)malloc(FONS_SCRATCH_BUF_SIZE
);
764 if (stash
->scratch
== NULL
) goto error
;
766 // Initialize implementation library
767 if (!fons__tt_init(stash
)) goto error
;
769 if (stash
->params
.renderCreate
!= NULL
) {
770 if (stash
->params
.renderCreate(stash
->params
.userPtr
, stash
->params
.width
, stash
->params
.height
) == 0)
774 stash
->atlas
= fons__allocAtlas(stash
->params
.width
, stash
->params
.height
, FONS_INIT_ATLAS_NODES
);
775 if (stash
->atlas
== NULL
) goto error
;
777 // Allocate space for fonts.
778 stash
->fonts
= (FONSfont
**)malloc(sizeof(FONSfont
*) * FONS_INIT_FONTS
);
779 if (stash
->fonts
== NULL
) goto error
;
780 memset(stash
->fonts
, 0, sizeof(FONSfont
*) * FONS_INIT_FONTS
);
781 stash
->cfonts
= FONS_INIT_FONTS
;
784 // Create texture for the cache.
785 stash
->itw
= 1.0f
/stash
->params
.width
;
786 stash
->ith
= 1.0f
/stash
->params
.height
;
787 stash
->texData
= (unsigned char*)malloc(stash
->params
.width
* stash
->params
.height
);
788 if (stash
->texData
== NULL
) goto error
;
789 memset(stash
->texData
, 0, stash
->params
.width
* stash
->params
.height
);
791 stash
->dirtyRect
[0] = stash
->params
.width
;
792 stash
->dirtyRect
[1] = stash
->params
.height
;
793 stash
->dirtyRect
[2] = 0;
794 stash
->dirtyRect
[3] = 0;
796 // Add white rect at 0,0 for debug drawing.
797 fons__addWhiteRect(stash
, 2,2);
799 fonsPushState(stash
);
800 fonsClearState(stash
);
805 fonsDeleteInternal(stash
);
809 static FONSstate
* fons__getState(FONScontext
* stash
)
811 return &stash
->states
[stash
->nstates
-1];
814 int fonsAddFallbackFont(FONScontext
* stash
, int base
, int fallback
)
816 FONSfont
* baseFont
= stash
->fonts
[base
];
817 if (baseFont
->nfallbacks
< FONS_MAX_FALLBACKS
) {
818 baseFont
->fallbacks
[baseFont
->nfallbacks
++] = fallback
;
824 void fonsResetFallbackFont(FONScontext
* stash
, int base
)
828 FONSfont
* baseFont
= stash
->fonts
[base
];
829 baseFont
->nfallbacks
= 0;
830 baseFont
->nglyphs
= 0;
831 for (i
= 0; i
< FONS_HASH_LUT_SIZE
; i
++)
832 baseFont
->lut
[i
] = -1;
835 void fonsSetSize(FONScontext
* stash
, float size
)
837 fons__getState(stash
)->size
= size
;
840 void fonsSetColor(FONScontext
* stash
, unsigned int color
)
842 fons__getState(stash
)->color
= color
;
845 void fonsSetSpacing(FONScontext
* stash
, float spacing
)
847 fons__getState(stash
)->spacing
= spacing
;
850 void fonsSetBlur(FONScontext
* stash
, float blur
)
852 fons__getState(stash
)->blur
= blur
;
855 void fonsSetAlign(FONScontext
* stash
, int align
)
857 fons__getState(stash
)->align
= align
;
860 void fonsSetFont(FONScontext
* stash
, int font
)
862 fons__getState(stash
)->font
= font
;
865 void fonsPushState(FONScontext
* stash
)
867 if (stash
->nstates
>= FONS_MAX_STATES
) {
868 if (stash
->handleError
)
869 stash
->handleError(stash
->errorUptr
, FONS_STATES_OVERFLOW
, 0);
872 if (stash
->nstates
> 0)
873 memcpy(&stash
->states
[stash
->nstates
], &stash
->states
[stash
->nstates
-1], sizeof(FONSstate
));
877 void fonsPopState(FONScontext
* stash
)
879 if (stash
->nstates
<= 1) {
880 if (stash
->handleError
)
881 stash
->handleError(stash
->errorUptr
, FONS_STATES_UNDERFLOW
, 0);
887 void fonsClearState(FONScontext
* stash
)
889 FONSstate
* state
= fons__getState(stash
);
891 state
->color
= 0xffffffff;
895 state
->align
= FONS_ALIGN_LEFT
| FONS_ALIGN_BASELINE
;
898 static void fons__freeFont(FONSfont
* font
)
900 if (font
== NULL
) return;
901 if (font
->glyphs
) free(font
->glyphs
);
902 if (font
->freeData
&& font
->data
) free(font
->data
);
906 static int fons__allocFont(FONScontext
* stash
)
908 FONSfont
* font
= NULL
;
909 if (stash
->nfonts
+1 > stash
->cfonts
) {
910 stash
->cfonts
= stash
->cfonts
== 0 ? 8 : stash
->cfonts
* 2;
911 stash
->fonts
= (FONSfont
**)realloc(stash
->fonts
, sizeof(FONSfont
*) * stash
->cfonts
);
912 if (stash
->fonts
== NULL
)
914 for (int i
=stash
->nfonts
; i
<stash
->cfonts
; ++i
)
915 stash
->fonts
[i
] = NULL
;
917 font
= (FONSfont
*)malloc(sizeof(FONSfont
));
918 if (font
== NULL
) goto error
;
919 memset(font
, 0, sizeof(FONSfont
));
921 font
->glyphs
= (FONSglyph
*)malloc(sizeof(FONSglyph
) * FONS_INIT_GLYPHS
);
922 if (font
->glyphs
== NULL
) goto error
;
923 font
->cglyphs
= FONS_INIT_GLYPHS
;
926 stash
->fonts
[stash
->nfonts
++] = font
;
927 return stash
->nfonts
-1;
930 fons__freeFont(font
);
935 int fonsAddFont(FONScontext
* stash
, const char* name
, const char* path
, int fontIndex
)
940 unsigned char* data
= NULL
;
942 // Read in the font data.
943 fp
= fopen(path
, "rb");
944 if (fp
== NULL
) goto error
;
945 fseek(fp
,0,SEEK_END
);
946 dataSize
= (int)ftell(fp
);
947 fseek(fp
,0,SEEK_SET
);
948 data
= (unsigned char*)malloc(dataSize
);
949 if (data
== NULL
) goto error
;
950 readed
= fread(data
, 1, dataSize
, fp
);
953 if (readed
!= (size_t)dataSize
) goto error
;
955 return fonsAddFontMem(stash
, name
, data
, dataSize
, 1, fontIndex
);
958 if (data
) free(data
);
963 int fonsAddFontMem(FONScontext
* stash
, const char* name
, unsigned char* data
, int dataSize
, int freeData
, int fontIndex
)
965 int i
, ascent
, descent
, fh
, lineGap
;
968 int idx
= fons__allocFont(stash
);
969 if (idx
== FONS_INVALID
)
971 if (freeData
&& data
) free(data
);
975 font
= stash
->fonts
[idx
];
977 strncpy(font
->name
, name
, sizeof(font
->name
));
978 font
->name
[sizeof(font
->name
)-1] = '\0';
981 for (i
= 0; i
< FONS_HASH_LUT_SIZE
; ++i
)
984 // Read in the font data.
985 font
->dataSize
= dataSize
;
987 font
->freeData
= (unsigned char)freeData
;
991 if (!fons__tt_loadFont(stash
, &font
->font
, data
, dataSize
, fontIndex
)) goto error
;
993 // Store normalized line height. The real line height is got
994 // by multiplying the lineh by font size.
995 fons__tt_getFontVMetrics( &font
->font
, &ascent
, &descent
, &lineGap
);
997 fh
= ascent
- descent
;
998 font
->ascender
= (float)ascent
/ (float)fh
;
999 font
->descender
= (float)descent
/ (float)fh
;
1000 font
->lineh
= font
->ascender
- font
->descender
;
1005 fons__freeFont(font
);
1007 return FONS_INVALID
;
1010 int fonsGetFontByName(FONScontext
* s
, const char* name
)
1013 for (i
= 0; i
< s
->nfonts
; i
++) {
1014 if (strcmp(s
->fonts
[i
]->name
, name
) == 0)
1017 return FONS_INVALID
;
1021 static FONSglyph
* fons__allocGlyph(FONSfont
* font
)
1023 if (font
->nglyphs
+1 > font
->cglyphs
) {
1024 font
->cglyphs
= font
->cglyphs
== 0 ? 8 : font
->cglyphs
* 2;
1025 font
->glyphs
= (FONSglyph
*)realloc(font
->glyphs
, sizeof(FONSglyph
) * font
->cglyphs
);
1026 if (font
->glyphs
== NULL
) return NULL
;
1027 for (int i
=font
->nglyphs
; i
<font
->cglyphs
; ++i
)
1028 memset(&font
->glyphs
[i
], 0, sizeof(*font
->glyphs
));
1031 return &font
->glyphs
[font
->nglyphs
-1];
1035 // Based on Exponential blur, Jani Huhtanen, 2006
1040 static void fons__blurCols(unsigned char* dst
, int w
, int h
, int dstStride
, int alpha
)
1043 for (y
= 0; y
< h
; y
++) {
1044 int z
= 0; // force zero border
1045 for (x
= 1; x
< w
; x
++) {
1046 z
+= (alpha
* (((int)(dst
[x
]) << ZPREC
) - z
)) >> APREC
;
1047 dst
[x
] = (unsigned char)(z
>> ZPREC
);
1049 dst
[w
-1] = 0; // force zero border
1051 for (x
= w
-2; x
>= 0; x
--) {
1052 z
+= (alpha
* (((int)(dst
[x
]) << ZPREC
) - z
)) >> APREC
;
1053 dst
[x
] = (unsigned char)(z
>> ZPREC
);
1055 dst
[0] = 0; // force zero border
1060 static void fons__blurRows(unsigned char* dst
, int w
, int h
, int dstStride
, int alpha
)
1063 for (x
= 0; x
< w
; x
++) {
1064 int z
= 0; // force zero border
1065 for (y
= dstStride
; y
< h
*dstStride
; y
+= dstStride
) {
1066 z
+= (alpha
* (((int)(dst
[y
]) << ZPREC
) - z
)) >> APREC
;
1067 dst
[y
] = (unsigned char)(z
>> ZPREC
);
1069 dst
[(h
-1)*dstStride
] = 0; // force zero border
1071 for (y
= (h
-2)*dstStride
; y
>= 0; y
-= dstStride
) {
1072 z
+= (alpha
* (((int)(dst
[y
]) << ZPREC
) - z
)) >> APREC
;
1073 dst
[y
] = (unsigned char)(z
>> ZPREC
);
1075 dst
[0] = 0; // force zero border
1081 static void fons__blur(FONScontext
* stash
, unsigned char* dst
, int w
, int h
, int dstStride
, int blur
)
1089 // Calculate the alpha such that 90% of the kernel is within the radius. (Kernel extends to infinity)
1090 sigma
= (float)blur
* 0.57735f
; // 1 / sqrt(3)
1091 alpha
= (int)((1<<APREC
) * (1.0f
- expf(-2.3f
/ (sigma
+1.0f
))));
1092 fons__blurRows(dst
, w
, h
, dstStride
, alpha
);
1093 fons__blurCols(dst
, w
, h
, dstStride
, alpha
);
1094 fons__blurRows(dst
, w
, h
, dstStride
, alpha
);
1095 fons__blurCols(dst
, w
, h
, dstStride
, alpha
);
1096 // fons__blurrows(dst, w, h, dstStride, alpha);
1097 // fons__blurcols(dst, w, h, dstStride, alpha);
1100 static FONSglyph
* fons__getGlyph(FONScontext
* stash
, FONSfont
* font
, unsigned int codepoint
,
1101 short isize
, short iblur
, int bitmapOption
)
1103 int i
, g
, advance
, lsb
, x0
, y0
, x1
, y1
, gw
, gh
, gx
, gy
, x
, y
;
1105 FONSglyph
* glyph
= NULL
;
1107 float size
= isize
/10.0f
;
1109 unsigned char* bdst
;
1111 FONSfont
* renderFont
= font
;
1113 if (isize
< 2) return NULL
;
1114 if (iblur
> 20) iblur
= 20;
1118 stash
->nscratch
= 0;
1120 // Find code point and size.
1121 h
= fons__hashint(codepoint
) & (FONS_HASH_LUT_SIZE
-1);
1124 if (font
->glyphs
[i
].codepoint
== codepoint
&& font
->glyphs
[i
].size
== isize
&& font
->glyphs
[i
].blur
== iblur
) {
1125 glyph
= &font
->glyphs
[i
];
1126 if (bitmapOption
== FONS_GLYPH_BITMAP_OPTIONAL
|| (glyph
->x0
>= 0 && glyph
->y0
>= 0)) {
1129 // At this point, glyph exists but the bitmap data is not yet created.
1132 i
= font
->glyphs
[i
].next
;
1135 // Create a new glyph or rasterize bitmap data for a cached glyph.
1136 g
= fons__tt_getGlyphIndex(&font
->font
, codepoint
);
1137 // Try to find the glyph in fallback fonts.
1139 for (i
= 0; i
< font
->nfallbacks
; ++i
) {
1140 FONSfont
* fallbackFont
= stash
->fonts
[font
->fallbacks
[i
]];
1141 int fallbackIndex
= fons__tt_getGlyphIndex(&fallbackFont
->font
, codepoint
);
1142 if (fallbackIndex
!= 0) {
1144 renderFont
= fallbackFont
;
1148 // It is possible that we did not find a fallback glyph.
1149 // In that case the glyph index 'g' is 0, and we'll proceed below and cache empty glyph.
1151 scale
= fons__tt_getPixelHeightScale(&renderFont
->font
, size
);
1152 fons__tt_buildGlyphBitmap(&renderFont
->font
, g
, size
, scale
, &advance
, &lsb
, &x0
, &y0
, &x1
, &y1
);
1156 // Determines the spot to draw glyph in the atlas.
1157 if (bitmapOption
== FONS_GLYPH_BITMAP_REQUIRED
) {
1158 // Find free spot for the rect in the atlas
1159 added
= fons__atlasAddRect(stash
->atlas
, gw
, gh
, &gx
, &gy
);
1160 if (added
== 0 && stash
->handleError
!= NULL
) {
1161 // Atlas is full, let the user to resize the atlas (or not), and try again.
1162 stash
->handleError(stash
->errorUptr
, FONS_ATLAS_FULL
, 0);
1163 added
= fons__atlasAddRect(stash
->atlas
, gw
, gh
, &gx
, &gy
);
1165 if (added
== 0) return NULL
;
1167 // Negative coordinate indicates there is no bitmap data created.
1173 if (glyph
== NULL
) {
1174 glyph
= fons__allocGlyph(font
);
1175 glyph
->codepoint
= codepoint
;
1176 glyph
->size
= isize
;
1177 glyph
->blur
= iblur
;
1180 // Insert char to hash lookup.
1181 glyph
->next
= font
->lut
[h
];
1182 font
->lut
[h
] = font
->nglyphs
-1;
1185 glyph
->x0
= (short)gx
;
1186 glyph
->y0
= (short)gy
;
1187 glyph
->x1
= (short)(glyph
->x0
+gw
);
1188 glyph
->y1
= (short)(glyph
->y0
+gh
);
1189 glyph
->xadv
= (short)(scale
* advance
* 10.0f
);
1190 glyph
->xoff
= (short)(x0
- pad
);
1191 glyph
->yoff
= (short)(y0
- pad
);
1193 if (bitmapOption
== FONS_GLYPH_BITMAP_OPTIONAL
) {
1198 dst
= &stash
->texData
[(glyph
->x0
+pad
) + (glyph
->y0
+pad
) * stash
->params
.width
];
1199 fons__tt_renderGlyphBitmap(&renderFont
->font
, dst
, gw
-pad
*2,gh
-pad
*2, stash
->params
.width
, scale
, scale
, g
);
1201 // Make sure there is one pixel empty border.
1202 dst
= &stash
->texData
[glyph
->x0
+ glyph
->y0
* stash
->params
.width
];
1203 for (y
= 0; y
< gh
; y
++) {
1204 dst
[y
*stash
->params
.width
] = 0;
1205 dst
[gw
-1 + y
*stash
->params
.width
] = 0;
1207 for (x
= 0; x
< gw
; x
++) {
1209 dst
[x
+ (gh
-1)*stash
->params
.width
] = 0;
1212 // Debug code to color the glyph background
1213 /* unsigned char* fdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width];
1214 for (y = 0; y < gh; y++) {
1215 for (x = 0; x < gw; x++) {
1216 int a = (int)fdst[x+y*stash->params.width] + 20;
1217 if (a > 255) a = 255;
1218 fdst[x+y*stash->params.width] = a;
1224 stash
->nscratch
= 0;
1225 bdst
= &stash
->texData
[glyph
->x0
+ glyph
->y0
* stash
->params
.width
];
1226 fons__blur(stash
, bdst
, gw
, gh
, stash
->params
.width
, iblur
);
1229 stash
->dirtyRect
[0] = fons__mini(stash
->dirtyRect
[0], glyph
->x0
);
1230 stash
->dirtyRect
[1] = fons__mini(stash
->dirtyRect
[1], glyph
->y0
);
1231 stash
->dirtyRect
[2] = fons__maxi(stash
->dirtyRect
[2], glyph
->x1
);
1232 stash
->dirtyRect
[3] = fons__maxi(stash
->dirtyRect
[3], glyph
->y1
);
1237 static void fons__getQuad(FONScontext
* stash
, FONSfont
* font
,
1238 int prevGlyphIndex
, FONSglyph
* glyph
,
1239 float scale
, float spacing
, float* x
, float* y
, FONSquad
* q
)
1241 float rx
,ry
,xoff
,yoff
,x0
,y0
,x1
,y1
;
1243 if (prevGlyphIndex
!= -1) {
1244 float adv
= fons__tt_getGlyphKernAdvance(&font
->font
, prevGlyphIndex
, glyph
->index
) * scale
;
1245 *x
+= (int)(adv
+ spacing
+ 0.5f
);
1248 // Each glyph has 2px border to allow good interpolation,
1249 // one pixel to prevent leaking, and one to allow good interpolation for rendering.
1250 // Inset the texture region by one pixel for correct interpolation.
1251 xoff
= (short)(glyph
->xoff
+1);
1252 yoff
= (short)(glyph
->yoff
+1);
1253 x0
= (float)(glyph
->x0
+1);
1254 y0
= (float)(glyph
->y0
+1);
1255 x1
= (float)(glyph
->x1
-1);
1256 y1
= (float)(glyph
->y1
-1);
1258 if (stash
->params
.flags
& FONS_ZERO_TOPLEFT
) {
1259 rx
= floorf(*x
+ xoff
);
1260 ry
= floorf(*y
+ yoff
);
1264 q
->x1
= rx
+ x1
- x0
;
1265 q
->y1
= ry
+ y1
- y0
;
1267 q
->s0
= x0
* stash
->itw
;
1268 q
->t0
= y0
* stash
->ith
;
1269 q
->s1
= x1
* stash
->itw
;
1270 q
->t1
= y1
* stash
->ith
;
1272 rx
= floorf(*x
+ xoff
);
1273 ry
= floorf(*y
- yoff
);
1277 q
->x1
= rx
+ x1
- x0
;
1278 q
->y1
= ry
- y1
+ y0
;
1280 q
->s0
= x0
* stash
->itw
;
1281 q
->t0
= y0
* stash
->ith
;
1282 q
->s1
= x1
* stash
->itw
;
1283 q
->t1
= y1
* stash
->ith
;
1286 *x
+= (int)(glyph
->xadv
/ 10.0f
+ 0.5f
);
1289 static void fons__flush(FONScontext
* stash
)
1292 if (stash
->dirtyRect
[0] < stash
->dirtyRect
[2] && stash
->dirtyRect
[1] < stash
->dirtyRect
[3]) {
1293 if (stash
->params
.renderUpdate
!= NULL
)
1294 stash
->params
.renderUpdate(stash
->params
.userPtr
, stash
->dirtyRect
, stash
->texData
);
1296 stash
->dirtyRect
[0] = stash
->params
.width
;
1297 stash
->dirtyRect
[1] = stash
->params
.height
;
1298 stash
->dirtyRect
[2] = 0;
1299 stash
->dirtyRect
[3] = 0;
1303 if (stash
->nverts
> 0) {
1304 if (stash
->params
.renderDraw
!= NULL
)
1305 stash
->params
.renderDraw(stash
->params
.userPtr
, stash
->verts
, stash
->tcoords
, stash
->colors
, stash
->nverts
);
1310 static __inline
void fons__vertex(FONScontext
* stash
, float x
, float y
, float s
, float t
, unsigned int c
)
1312 stash
->verts
[stash
->nverts
*2+0] = x
;
1313 stash
->verts
[stash
->nverts
*2+1] = y
;
1314 stash
->tcoords
[stash
->nverts
*2+0] = s
;
1315 stash
->tcoords
[stash
->nverts
*2+1] = t
;
1316 stash
->colors
[stash
->nverts
] = c
;
1320 static float fons__getVertAlign(FONScontext
* stash
, FONSfont
* font
, int align
, short isize
)
1322 if (stash
->params
.flags
& FONS_ZERO_TOPLEFT
) {
1323 if (align
& FONS_ALIGN_TOP
) {
1324 return font
->ascender
* (float)isize
/10.0f
;
1325 } else if (align
& FONS_ALIGN_MIDDLE
) {
1326 return (font
->ascender
+ font
->descender
) / 2.0f
* (float)isize
/10.0f
;
1327 } else if (align
& FONS_ALIGN_BASELINE
) {
1329 } else if (align
& FONS_ALIGN_BOTTOM
) {
1330 return font
->descender
* (float)isize
/10.0f
;
1333 if (align
& FONS_ALIGN_TOP
) {
1334 return -font
->ascender
* (float)isize
/10.0f
;
1335 } else if (align
& FONS_ALIGN_MIDDLE
) {
1336 return -(font
->ascender
+ font
->descender
) / 2.0f
* (float)isize
/10.0f
;
1337 } else if (align
& FONS_ALIGN_BASELINE
) {
1339 } else if (align
& FONS_ALIGN_BOTTOM
) {
1340 return -font
->descender
* (float)isize
/10.0f
;
1346 float fonsDrawText(FONScontext
* stash
,
1348 const char* str
, const char* end
)
1350 FONSstate
* state
= fons__getState(stash
);
1351 unsigned int codepoint
;
1352 unsigned int utf8state
= 0;
1353 FONSglyph
* glyph
= NULL
;
1355 int prevGlyphIndex
= -1;
1356 short isize
= (short)(state
->size
*10.0f
);
1357 short iblur
= (short)state
->blur
;
1362 if (stash
== NULL
) return x
;
1363 if (state
->font
< 0 || state
->font
>= stash
->nfonts
) return x
;
1364 font
= stash
->fonts
[state
->font
];
1365 if (font
->data
== NULL
) return x
;
1367 scale
= fons__tt_getPixelHeightScale(&font
->font
, (float)isize
/10.0f
);
1370 end
= str
+ strlen(str
);
1372 // Align horizontally
1373 if (state
->align
& FONS_ALIGN_LEFT
) {
1375 } else if (state
->align
& FONS_ALIGN_RIGHT
) {
1376 width
= fonsTextBounds(stash
, x
,y
, str
, end
, NULL
);
1378 } else if (state
->align
& FONS_ALIGN_CENTER
) {
1379 width
= fonsTextBounds(stash
, x
,y
, str
, end
, NULL
);
1382 // Align vertically.
1383 y
+= fons__getVertAlign(stash
, font
, state
->align
, isize
);
1385 for (; str
!= end
; ++str
) {
1386 if (fons__decutf8(&utf8state
, &codepoint
, *(const unsigned char*)str
))
1388 glyph
= fons__getGlyph(stash
, font
, codepoint
, isize
, iblur
, FONS_GLYPH_BITMAP_REQUIRED
);
1389 if (glyph
!= NULL
) {
1390 fons__getQuad(stash
, font
, prevGlyphIndex
, glyph
, scale
, state
->spacing
, &x
, &y
, &q
);
1392 if (stash
->nverts
+6 > FONS_VERTEX_COUNT
)
1395 fons__vertex(stash
, q
.x0
, q
.y0
, q
.s0
, q
.t0
, state
->color
);
1396 fons__vertex(stash
, q
.x1
, q
.y1
, q
.s1
, q
.t1
, state
->color
);
1397 fons__vertex(stash
, q
.x1
, q
.y0
, q
.s1
, q
.t0
, state
->color
);
1399 fons__vertex(stash
, q
.x0
, q
.y0
, q
.s0
, q
.t0
, state
->color
);
1400 fons__vertex(stash
, q
.x0
, q
.y1
, q
.s0
, q
.t1
, state
->color
);
1401 fons__vertex(stash
, q
.x1
, q
.y1
, q
.s1
, q
.t1
, state
->color
);
1403 prevGlyphIndex
= glyph
!= NULL
? glyph
->index
: -1;
1410 int fonsTextIterInit(FONScontext
* stash
, FONStextIter
* iter
,
1411 float x
, float y
, const char* str
, const char* end
, int bitmapOption
)
1413 FONSstate
* state
= fons__getState(stash
);
1416 memset(iter
, 0, sizeof(*iter
));
1418 if (stash
== NULL
) return 0;
1419 if (state
->font
< 0 || state
->font
>= stash
->nfonts
) return 0;
1420 iter
->font
= stash
->fonts
[state
->font
];
1421 if (iter
->font
->data
== NULL
) return 0;
1423 iter
->isize
= (short)(state
->size
*10.0f
);
1424 iter
->iblur
= (short)state
->blur
;
1425 iter
->scale
= fons__tt_getPixelHeightScale(&iter
->font
->font
, (float)iter
->isize
/10.0f
);
1427 // Align horizontally
1428 if (state
->align
& FONS_ALIGN_LEFT
) {
1430 } else if (state
->align
& FONS_ALIGN_RIGHT
) {
1431 width
= fonsTextBounds(stash
, x
,y
, str
, end
, NULL
);
1433 } else if (state
->align
& FONS_ALIGN_CENTER
) {
1434 width
= fonsTextBounds(stash
, x
,y
, str
, end
, NULL
);
1437 // Align vertically.
1438 y
+= fons__getVertAlign(stash
, iter
->font
, state
->align
, iter
->isize
);
1441 end
= str
+ strlen(str
);
1443 iter
->x
= iter
->nextx
= x
;
1444 iter
->y
= iter
->nexty
= y
;
1445 iter
->spacing
= state
->spacing
;
1449 iter
->codepoint
= 0;
1450 iter
->prevGlyphIndex
= -1;
1451 iter
->bitmapOption
= bitmapOption
;
1456 int fonsTextIterNext(FONScontext
* stash
, FONStextIter
* iter
, FONSquad
* quad
)
1458 FONSglyph
* glyph
= NULL
;
1459 const char* str
= iter
->next
;
1460 iter
->str
= iter
->next
;
1462 if (str
== iter
->end
)
1465 for (; str
!= iter
->end
; str
++) {
1466 if (fons__decutf8(&iter
->utf8state
, &iter
->codepoint
, *(const unsigned char*)str
))
1469 // Get glyph and quad
1470 iter
->x
= iter
->nextx
;
1471 iter
->y
= iter
->nexty
;
1472 glyph
= fons__getGlyph(stash
, iter
->font
, iter
->codepoint
, iter
->isize
, iter
->iblur
, iter
->bitmapOption
);
1473 // If the iterator was initialized with FONS_GLYPH_BITMAP_OPTIONAL, then the UV coordinates of the quad will be invalid.
1475 fons__getQuad(stash
, iter
->font
, iter
->prevGlyphIndex
, glyph
, iter
->scale
, iter
->spacing
, &iter
->nextx
, &iter
->nexty
, quad
);
1476 iter
->prevGlyphIndex
= glyph
!= NULL
? glyph
->index
: -1;
1484 void fonsDrawDebug(FONScontext
* stash
, float x
, float y
)
1487 int w
= stash
->params
.width
;
1488 int h
= stash
->params
.height
;
1489 float u
= w
== 0 ? 0 : (1.0f
/ w
);
1490 float v
= h
== 0 ? 0 : (1.0f
/ h
);
1492 if (stash
->nverts
+6+6 > FONS_VERTEX_COUNT
)
1496 fons__vertex(stash
, x
+0, y
+0, u
, v
, 0x0fffffff);
1497 fons__vertex(stash
, x
+w
, y
+h
, u
, v
, 0x0fffffff);
1498 fons__vertex(stash
, x
+w
, y
+0, u
, v
, 0x0fffffff);
1500 fons__vertex(stash
, x
+0, y
+0, u
, v
, 0x0fffffff);
1501 fons__vertex(stash
, x
+0, y
+h
, u
, v
, 0x0fffffff);
1502 fons__vertex(stash
, x
+w
, y
+h
, u
, v
, 0x0fffffff);
1505 fons__vertex(stash
, x
+0, y
+0, 0, 0, 0xffffffff);
1506 fons__vertex(stash
, x
+w
, y
+h
, 1, 1, 0xffffffff);
1507 fons__vertex(stash
, x
+w
, y
+0, 1, 0, 0xffffffff);
1509 fons__vertex(stash
, x
+0, y
+0, 0, 0, 0xffffffff);
1510 fons__vertex(stash
, x
+0, y
+h
, 0, 1, 0xffffffff);
1511 fons__vertex(stash
, x
+w
, y
+h
, 1, 1, 0xffffffff);
1513 // Drawbug draw atlas
1514 for (i
= 0; i
< stash
->atlas
->nnodes
; i
++) {
1515 FONSatlasNode
* n
= &stash
->atlas
->nodes
[i
];
1517 if (stash
->nverts
+6 > FONS_VERTEX_COUNT
)
1520 fons__vertex(stash
, x
+n
->x
+0, y
+n
->y
+0, u
, v
, 0xc00000ff);
1521 fons__vertex(stash
, x
+n
->x
+n
->width
, y
+n
->y
+1, u
, v
, 0xc00000ff);
1522 fons__vertex(stash
, x
+n
->x
+n
->width
, y
+n
->y
+0, u
, v
, 0xc00000ff);
1524 fons__vertex(stash
, x
+n
->x
+0, y
+n
->y
+0, u
, v
, 0xc00000ff);
1525 fons__vertex(stash
, x
+n
->x
+0, y
+n
->y
+1, u
, v
, 0xc00000ff);
1526 fons__vertex(stash
, x
+n
->x
+n
->width
, y
+n
->y
+1, u
, v
, 0xc00000ff);
1532 float fonsTextBounds(FONScontext
* stash
,
1534 const char* str
, const char* end
,
1537 FONSstate
* state
= fons__getState(stash
);
1538 unsigned int codepoint
;
1539 unsigned int utf8state
= 0;
1541 FONSglyph
* glyph
= NULL
;
1542 int prevGlyphIndex
= -1;
1543 short isize
= (short)(state
->size
*10.0f
);
1544 short iblur
= (short)state
->blur
;
1547 float startx
, advance
;
1548 float minx
, miny
, maxx
, maxy
;
1550 if (stash
== NULL
) return 0;
1551 if (state
->font
< 0 || state
->font
>= stash
->nfonts
) return 0;
1552 font
= stash
->fonts
[state
->font
];
1553 if (font
->data
== NULL
) return 0;
1555 scale
= fons__tt_getPixelHeightScale(&font
->font
, (float)isize
/10.0f
);
1557 // Align vertically.
1558 y
+= fons__getVertAlign(stash
, font
, state
->align
, isize
);
1565 end
= str
+ strlen(str
);
1567 for (; str
!= end
; ++str
) {
1568 if (fons__decutf8(&utf8state
, &codepoint
, *(const unsigned char*)str
))
1570 glyph
= fons__getGlyph(stash
, font
, codepoint
, isize
, iblur
, FONS_GLYPH_BITMAP_OPTIONAL
);
1571 if (glyph
!= NULL
) {
1572 fons__getQuad(stash
, font
, prevGlyphIndex
, glyph
, scale
, state
->spacing
, &x
, &y
, &q
);
1573 if (q
.x0
< minx
) minx
= q
.x0
;
1574 if (q
.x1
> maxx
) maxx
= q
.x1
;
1575 if (stash
->params
.flags
& FONS_ZERO_TOPLEFT
) {
1576 if (q
.y0
< miny
) miny
= q
.y0
;
1577 if (q
.y1
> maxy
) maxy
= q
.y1
;
1579 if (q
.y1
< miny
) miny
= q
.y1
;
1580 if (q
.y0
> maxy
) maxy
= q
.y0
;
1583 prevGlyphIndex
= glyph
!= NULL
? glyph
->index
: -1;
1586 advance
= x
- startx
;
1588 // Align horizontally
1589 if (state
->align
& FONS_ALIGN_LEFT
) {
1591 } else if (state
->align
& FONS_ALIGN_RIGHT
) {
1594 } else if (state
->align
& FONS_ALIGN_CENTER
) {
1595 minx
-= advance
* 0.5f
;
1596 maxx
-= advance
* 0.5f
;
1609 void fonsVertMetrics(FONScontext
* stash
,
1610 float* ascender
, float* descender
, float* lineh
)
1613 FONSstate
* state
= fons__getState(stash
);
1616 if (stash
== NULL
) return;
1617 if (state
->font
< 0 || state
->font
>= stash
->nfonts
) return;
1618 font
= stash
->fonts
[state
->font
];
1619 isize
= (short)(state
->size
*10.0f
);
1620 if (font
->data
== NULL
) return;
1623 *ascender
= font
->ascender
*isize
/10.0f
;
1625 *descender
= font
->descender
*isize
/10.0f
;
1627 *lineh
= font
->lineh
*isize
/10.0f
;
1630 void fonsLineBounds(FONScontext
* stash
, float y
, float* miny
, float* maxy
)
1633 FONSstate
* state
= fons__getState(stash
);
1636 if (stash
== NULL
) return;
1637 if (state
->font
< 0 || state
->font
>= stash
->nfonts
) return;
1638 font
= stash
->fonts
[state
->font
];
1639 isize
= (short)(state
->size
*10.0f
);
1640 if (font
->data
== NULL
) return;
1642 y
+= fons__getVertAlign(stash
, font
, state
->align
, isize
);
1644 if (stash
->params
.flags
& FONS_ZERO_TOPLEFT
) {
1645 *miny
= y
- font
->ascender
* (float)isize
/10.0f
;
1646 *maxy
= *miny
+ font
->lineh
*isize
/10.0f
;
1648 *maxy
= y
+ font
->descender
* (float)isize
/10.0f
;
1649 *miny
= *maxy
- font
->lineh
*isize
/10.0f
;
1653 const unsigned char* fonsGetTextureData(FONScontext
* stash
, int* width
, int* height
)
1656 *width
= stash
->params
.width
;
1658 *height
= stash
->params
.height
;
1659 return stash
->texData
;
1662 int fonsValidateTexture(FONScontext
* stash
, int* dirty
)
1664 if (stash
->dirtyRect
[0] < stash
->dirtyRect
[2] && stash
->dirtyRect
[1] < stash
->dirtyRect
[3]) {
1665 dirty
[0] = stash
->dirtyRect
[0];
1666 dirty
[1] = stash
->dirtyRect
[1];
1667 dirty
[2] = stash
->dirtyRect
[2];
1668 dirty
[3] = stash
->dirtyRect
[3];
1670 stash
->dirtyRect
[0] = stash
->params
.width
;
1671 stash
->dirtyRect
[1] = stash
->params
.height
;
1672 stash
->dirtyRect
[2] = 0;
1673 stash
->dirtyRect
[3] = 0;
1679 void fonsDeleteInternal(FONScontext
* stash
)
1682 if (stash
== NULL
) return;
1684 if (stash
->params
.renderDelete
)
1685 stash
->params
.renderDelete(stash
->params
.userPtr
);
1687 for (i
= 0; i
< stash
->nfonts
; ++i
)
1688 fons__freeFont(stash
->fonts
[i
]);
1690 if (stash
->atlas
) fons__deleteAtlas(stash
->atlas
);
1691 if (stash
->fonts
) free(stash
->fonts
);
1692 if (stash
->texData
) free(stash
->texData
);
1693 if (stash
->scratch
) free(stash
->scratch
);
1694 fons__tt_done(stash
);
1698 void fonsSetErrorCallback(FONScontext
* stash
, void (*callback
)(void* uptr
, int error
, int val
), void* uptr
)
1700 if (stash
== NULL
) return;
1701 stash
->handleError
= callback
;
1702 stash
->errorUptr
= uptr
;
1705 void fonsGetAtlasSize(FONScontext
* stash
, int* width
, int* height
)
1707 if (stash
== NULL
) return;
1708 *width
= stash
->params
.width
;
1709 *height
= stash
->params
.height
;
1712 int fonsExpandAtlas(FONScontext
* stash
, int width
, int height
)
1715 unsigned char* data
= NULL
;
1716 if (stash
== NULL
) return 0;
1718 width
= fons__maxi(width
, stash
->params
.width
);
1719 height
= fons__maxi(height
, stash
->params
.height
);
1721 if (width
== stash
->params
.width
&& height
== stash
->params
.height
)
1724 // Flush pending glyphs.
1727 // Create new texture
1728 if (stash
->params
.renderResize
!= NULL
) {
1729 if (stash
->params
.renderResize(stash
->params
.userPtr
, width
, height
) == 0)
1732 // Copy old texture data over.
1733 data
= (unsigned char*)malloc(width
* height
);
1736 for (i
= 0; i
< stash
->params
.height
; i
++) {
1737 unsigned char* dst
= &data
[i
*width
];
1738 unsigned char* src
= &stash
->texData
[i
*stash
->params
.width
];
1739 memcpy(dst
, src
, stash
->params
.width
);
1740 if (width
> stash
->params
.width
)
1741 memset(dst
+stash
->params
.width
, 0, width
- stash
->params
.width
);
1743 if (height
> stash
->params
.height
)
1744 memset(&data
[stash
->params
.height
* width
], 0, (height
- stash
->params
.height
) * width
);
1746 free(stash
->texData
);
1747 stash
->texData
= data
;
1749 // Increase atlas size
1750 fons__atlasExpand(stash
->atlas
, width
, height
);
1752 // Add existing data as dirty.
1753 for (i
= 0; i
< stash
->atlas
->nnodes
; i
++)
1754 maxy
= fons__maxi(maxy
, stash
->atlas
->nodes
[i
].y
);
1755 stash
->dirtyRect
[0] = 0;
1756 stash
->dirtyRect
[1] = 0;
1757 stash
->dirtyRect
[2] = stash
->params
.width
;
1758 stash
->dirtyRect
[3] = maxy
;
1760 stash
->params
.width
= width
;
1761 stash
->params
.height
= height
;
1762 stash
->itw
= 1.0f
/stash
->params
.width
;
1763 stash
->ith
= 1.0f
/stash
->params
.height
;
1768 int fonsResetAtlas(FONScontext
* stash
, int width
, int height
)
1771 if (stash
== NULL
) return 0;
1773 // Flush pending glyphs.
1776 // Create new texture
1777 if (stash
->params
.renderResize
!= NULL
) {
1778 if (stash
->params
.renderResize(stash
->params
.userPtr
, width
, height
) == 0)
1783 fons__atlasReset(stash
->atlas
, width
, height
);
1785 // Clear texture data.
1786 stash
->texData
= (unsigned char*)realloc(stash
->texData
, width
* height
);
1787 if (stash
->texData
== NULL
) return 0;
1788 memset(stash
->texData
, 0, width
* height
);
1791 stash
->dirtyRect
[0] = width
;
1792 stash
->dirtyRect
[1] = height
;
1793 stash
->dirtyRect
[2] = 0;
1794 stash
->dirtyRect
[3] = 0;
1796 // Reset cached glyphs
1797 for (i
= 0; i
< stash
->nfonts
; i
++) {
1798 FONSfont
* font
= stash
->fonts
[i
];
1800 for (j
= 0; j
< FONS_HASH_LUT_SIZE
; j
++)
1804 stash
->params
.width
= width
;
1805 stash
->params
.height
= height
;
1806 stash
->itw
= 1.0f
/stash
->params
.width
;
1807 stash
->ith
= 1.0f
/stash
->params
.height
;
1809 // Add white rect at 0,0 for debug drawing.
1810 fons__addWhiteRect(stash
, 2,2);