31#define FONS_DEF static
33#define FONS_DEF extern
36#define FONS_INVALID -1
79 void (*
renderUpdate)(
void* uptr,
int* rect,
const unsigned char* data);
136#ifndef FONS_USE_FREETYPE
184#ifndef FONS_SCRATCH_BUF_SIZE
185# define FONS_SCRATCH_BUF_SIZE 64000
187#ifndef FONS_HASH_LUT_SIZE
188# define FONS_HASH_LUT_SIZE 256
190#ifndef FONS_INIT_FONTS
191# define FONS_INIT_FONTS 4
193#ifndef FONS_INIT_GLYPHS
194# define FONS_INIT_GLYPHS 256
196#ifndef FONS_INIT_ATLAS_NODES
197# define FONS_INIT_ATLAS_NODES 256
199#ifndef FONS_VERTEX_COUNT
200# define FONS_VERTEX_COUNT 1024
202#ifndef FONS_MAX_STATES
203# define FONS_MAX_STATES 20
205#ifndef FONS_MAX_FALLBACKS
206# define FONS_MAX_FALLBACKS 20
213#ifdef FONTSTASH_IMPLEMENTATION
215#define FONS_NOTUSED(v) (void)sizeof(v)
217#ifdef FONS_USE_FREETYPE
220#include FT_FREETYPE_H
221#include FT_ADVANCES_H
224struct FONSttFontImpl {
227typedef struct FONSttFontImpl FONSttFontImpl;
229static FT_Library ftLibrary;
231static int fons__tt_init()
234 FONS_NOTUSED(context);
235 ftError = FT_Init_FreeType(&ftLibrary);
239static int fons__tt_loadFont(
FONScontext *context, FONSttFontImpl *font,
unsigned char *data,
int dataSize)
242 FONS_NOTUSED(context);
245 ftError = FT_New_Memory_Face(ftLibrary, (
const FT_Byte*)data, dataSize, 0, &font->font);
249static void fons__tt_getFontVMetrics(FONSttFontImpl *font,
int *ascent,
int *descent,
int *lineGap)
251 *ascent = font->font->ascender;
252 *descent = font->font->descender;
253 *lineGap = font->font->height - (*ascent - *descent);
256static float fons__tt_getPixelHeightScale(FONSttFontImpl *font,
float size)
258 return size / (font->font->ascender - font->font->descender);
261static int fons__tt_getGlyphIndex(FONSttFontImpl *font,
int codepoint)
263 return FT_Get_Char_Index(font->font, codepoint);
266static int fons__tt_buildGlyphBitmap(FONSttFontImpl *font,
int glyph,
float size,
float scale,
267 int *advance,
int *lsb,
int *x0,
int *y0,
int *x1,
int *y1,
const FONSsdfSettings* sdfSettings)
270 FT_GlyphSlot ftGlyph;
273 FONS_NOTUSED(sdfSettings);
275 ftError = FT_Set_Pixel_Sizes(font->font, 0, (FT_UInt)(size * (
float)font->font->units_per_EM / (
float)(font->font->ascender - font->font->descender)));
276 if (ftError)
return 0;
277 ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER);
278 if (ftError)
return 0;
279 ftError = FT_Get_Advance(font->font, glyph, FT_LOAD_NO_SCALE, &advFixed);
280 if (ftError)
return 0;
281 ftGlyph = font->font->glyph;
282 *advance = (int)advFixed;
283 *lsb = (int)ftGlyph->metrics.horiBearingX;
284 *x0 = ftGlyph->bitmap_left;
285 *x1 = *x0 + ftGlyph->bitmap.width;
286 *y0 = -ftGlyph->bitmap_top;
287 *y1 = *y0 + ftGlyph->bitmap.rows;
291static void fons__tt_renderGlyphBitmap(FONSttFontImpl *font,
unsigned char *output,
int outWidth,
int outHeight,
int outStride,
292 float scaleX,
float scaleY,
int glyph,
const FONSsdfSettings* sdfSettings)
294 FT_GlyphSlot ftGlyph = font->font->glyph;
295 int ftGlyphOffset = 0;
297 FONS_NOTUSED(outWidth);
298 FONS_NOTUSED(outHeight);
299 FONS_NOTUSED(scaleX);
300 FONS_NOTUSED(scaleY);
302 FONS_NOTUSED(sdfSettings);
304 for ( y = 0; y < ftGlyph->bitmap.rows; y++ ) {
305 for ( x = 0; x < ftGlyph->bitmap.width; x++ ) {
306 output[(y * outStride) + x] = ftGlyph->bitmap.buffer[ftGlyphOffset++];
311static int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font,
int glyph1,
int glyph2)
314 FT_Get_Kerning(font->font, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning);
315 return (
int)((ftKerning.x + 32) >> 6);
320#define STB_TRUETYPE_IMPLEMENTATION
322static void* fons__tmpalloc(
size_t size,
void* up);
323static void fons__tmpfree(
void* ptr,
void* up);
324#define STBTT_malloc(x,u) fons__tmpalloc(x,u)
325#define STBTT_free(x,u) fons__tmpfree(x,u)
330#pragma warning(disable:4505)
332#include <stb_truetype.h>
337struct FONSttFontImpl {
340typedef struct FONSttFontImpl FONSttFontImpl;
344 FONS_NOTUSED(context);
348static int fons__tt_loadFont(
FONScontext *context, FONSttFontImpl *font,
unsigned char *data,
int dataSize)
350 FONS_NOTUSED(dataSize);
352 font->font.userdata = context;
353 int stbError = stbtt_InitFont(&font->font, data, 0);
357static void fons__tt_getFontVMetrics(FONSttFontImpl *font,
int *ascent,
int *descent,
int *lineGap)
359 stbtt_GetFontVMetrics(&font->font, ascent, descent, lineGap);
362static float fons__tt_getPixelHeightScale(FONSttFontImpl *font,
float size)
364 return stbtt_ScaleForPixelHeight(&font->font, size);
367static int fons__tt_getGlyphIndex(FONSttFontImpl *font,
int codepoint)
369 return stbtt_FindGlyphIndex(&font->font, codepoint);
372static int fons__tt_buildGlyphBitmap(FONSttFontImpl *font,
int glyph,
float size,
float scale,
373 int *advance,
int *lsb,
int *x0,
int *y0,
int *x1,
int *y1,
const FONSsdfSettings* sdfSettings)
376 stbtt_GetGlyphHMetrics(&font->font, glyph, advance, lsb);
377 stbtt_GetGlyphBitmapBox(&font->font, glyph, scale, scale, x0, y0, x1, y1);
390static void fons__tt_renderGlyphBitmap(FONSttFontImpl *font,
unsigned char *output,
int outWidth,
int outHeight,
int outStride,
391 float scaleX,
float scaleY,
int glyph,
const FONSsdfSettings* sdfSettings)
395 stbtt_MakeGlyphBitmap(&font->font, output, outWidth, outHeight, outStride, scaleX, scaleY, glyph);
399 int w = 0, h = 0, xoff = 0, yoff = 0;
400 unsigned char* sdfData = stbtt_GetGlyphSDF(&font->font, scaleX, glyph, sdfSettings->
padding, sdfSettings->
onedgeValue, sdfSettings->
pixelDistScale, &w, &h, &xoff, &yoff);
402 for (
int y = 0; y < h; y++)
404 unsigned char* outRow = output + outStride*y;
405 unsigned char* inRow = sdfData + w*y;
407 memcpy(outRow, inRow, w);
411 stbtt_FreeSDF(sdfData,
nullptr);
415static int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font,
int glyph1,
int glyph2)
417 return stbtt_GetGlyphKernAdvance(&font->font, glyph1, glyph2);
422static unsigned int fons__hashint(
unsigned int a)
433static int fons__mini(
int a,
int b)
435 return a < b ? a : b;
438static int fons__maxi(
int a,
int b)
440 return a > b ? a : b;
445 unsigned int codepoint;
450 short xadv,xoff,yoff;
452typedef struct FONSglyph FONSglyph;
460 unsigned char freeData;
472typedef struct FONSfont FONSfont;
479 unsigned char colour[4];
483typedef struct FONSstate FONSstate;
485struct FONSatlasNode {
488typedef struct FONSatlasNode FONSatlasNode;
493 FONSatlasNode* nodes;
497typedef struct FONSatlas FONSatlas;
503 unsigned char* texData;
511 unsigned char* scratch;
515 void (*handleError)(
void*
uptr,
int error,
int val);
519#ifdef STB_TRUETYPE_IMPLEMENTATION
521static void* fons__tmpalloc(
size_t size,
void* up)
526 size = size + 0xf & ~0xf;
529 if (stash->handleError)
530 stash->handleError(stash->errorUptr,
FONS_SCRATCH_FULL, stash->nscratch+(
int)size);
533 unsigned char* ptr = stash->scratch + stash->nscratch;
534 stash->nscratch += (int)size;
538static void fons__tmpfree(
void* ptr,
void* up)
550#define FONS_UTF8_ACCEPT 0
551#define FONS_UTF8_REJECT 12
553static unsigned int fons__decutf8(
unsigned int* state,
unsigned int* codep,
unsigned int byte)
555 constexpr unsigned char utf8d[] = {
558 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,
559 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,
560 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,
561 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,
562 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,
563 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,
564 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,
565 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,
569 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
570 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
571 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
572 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
573 12,36,12,12,12,12,12,12,12,12,12,12,
576 const unsigned int type = utf8d[byte];
578 *codep = *state != FONS_UTF8_ACCEPT ?
579 (
byte & 0x3fu) | (*codep << 6) :
580 (0xff >> type) & byte;
582 *state = utf8d[256 + *state + type];
588static void fons__deleteAtlas(FONSatlas* atlas)
590 if (atlas ==
nullptr)
return;
591 if (atlas->nodes !=
nullptr) free(atlas->nodes);
595static FONSatlas* fons__allocAtlas(
int w,
int h,
int nnodes)
597 FONSatlas* atlas =
nullptr;
600 atlas = (FONSatlas*)malloc(
sizeof(FONSatlas));
601 if (atlas ==
nullptr)
goto error;
602 memset(atlas, 0,
sizeof(FONSatlas));
608 atlas->nodes = (FONSatlasNode*)malloc(
sizeof(FONSatlasNode) * nnodes);
609 if (atlas->nodes ==
nullptr)
goto error;
610 memset(atlas->nodes, 0,
sizeof(FONSatlasNode) * nnodes);
612 atlas->cnodes = nnodes;
615 atlas->nodes[0].x = 0;
616 atlas->nodes[0].y = 0;
617 atlas->nodes[0].width = (short)w;
623 if (atlas) fons__deleteAtlas(atlas);
627static int fons__atlasInsertNode(FONSatlas* atlas,
int idx,
int x,
int y,
int w)
630 if (atlas->nnodes+1 > atlas->cnodes) {
631 atlas->cnodes = atlas->cnodes == 0 ? 8 : atlas->cnodes * 2;
632 atlas->nodes = (FONSatlasNode*)realloc(atlas->nodes,
sizeof(FONSatlasNode) * atlas->cnodes);
633 if (atlas->nodes ==
nullptr)
637 for (
int i = atlas->nnodes; i > idx; i--)
639 atlas->nodes[i] = atlas->nodes[i-1];
642 atlas->nodes[idx].x = (short)x;
643 atlas->nodes[idx].y = (short)y;
644 atlas->nodes[idx].width = (short)w;
650static void fons__atlasRemoveNode(FONSatlas* atlas,
int idx)
652 if (atlas->nnodes == 0)
return;
653 for (
int i = idx; i < atlas->nnodes-1; i++)
654 atlas->nodes[i] = atlas->nodes[i+1];
658static void fons__atlasExpand(FONSatlas* atlas,
int w,
int h)
661 if (w > atlas->width)
662 fons__atlasInsertNode(atlas, atlas->nnodes, atlas->width, 0, w - atlas->width);
667static void fons__atlasReset(FONSatlas* atlas,
int w,
int h)
674 atlas->nodes[0].x = 0;
675 atlas->nodes[0].y = 0;
676 atlas->nodes[0].width = (short)w;
680static int fons__atlasAddSkylineLevel(FONSatlas* atlas,
int idx,
int x,
int y,
int w,
int h)
685 if (fons__atlasInsertNode(atlas, idx, x, y+h, w) == 0)
689 for (i = idx+1; i < atlas->nnodes; i++) {
690 if (atlas->nodes[i].x < atlas->nodes[i-1].x + atlas->nodes[i-1].width) {
691 const int shrink = atlas->nodes[i-1].x + atlas->nodes[i-1].width - atlas->nodes[i].x;
692 atlas->nodes[i].x += (short)shrink;
693 atlas->nodes[i].width -= (short)shrink;
694 if (atlas->nodes[i].width <= 0) {
695 fons__atlasRemoveNode(atlas, i);
706 for (i = 0; i < atlas->nnodes-1; i++) {
707 if (atlas->nodes[i].y == atlas->nodes[i+1].y) {
708 atlas->nodes[i].width += atlas->nodes[i+1].width;
709 fons__atlasRemoveNode(atlas, i+1);
717static int fons__atlasRectFits(FONSatlas* atlas,
int i,
int w,
int h)
722 const int x = atlas->nodes[i].x;
723 int y = atlas->nodes[i].y;
724 if (x + w > atlas->width)
730 while (spaceLeft > 0)
732 if (i == atlas->nnodes)
return -1;
733 y = fons__maxi(y, atlas->nodes[i].y);
734 if (y + h > atlas->height)
return -1;
735 spaceLeft -= atlas->nodes[i].width;
742static int fons__atlasAddRect(FONSatlas* atlas,
int rw,
int rh,
int* rx,
int* ry)
744 int besth = atlas->height, bestw = atlas->width, besti = -1;
745 int bestx = -1, besty = -1;
748 for (
int i = 0; i < atlas->nnodes; i++) {
749 const int y = fons__atlasRectFits(atlas, i, rw, rh);
751 if (y + rh < besth || (y + rh == besth && atlas->nodes[i].width < bestw))
754 bestw = atlas->nodes[i].width;
756 bestx = atlas->nodes[i].x;
766 if (fons__atlasAddSkylineLevel(atlas, besti, bestx, besty, rw, rh) == 0)
775static void fons__addWhiteRect(
FONScontext* stash,
int w,
int h)
778 if (fons__atlasAddRect(stash->atlas, w, h, &gx, &gy) == 0)
782 unsigned char* dst = &stash->texData[gx + gy * stash->params.width];
783 for (
int y = 0; y < h; y++) {
784 for (
int x = 0; x < w; x++)
786 dst += stash->params.width;
789 stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], gx);
790 stash->dirtyRect[1] = fons__mini(stash->dirtyRect[1], gy);
791 stash->dirtyRect[2] = fons__maxi(stash->dirtyRect[2], gx+w);
792 stash->dirtyRect[3] = fons__maxi(stash->dirtyRect[3], gy+h);
801 if (stash ==
nullptr)
goto error;
804 stash->params = *params;
808 if (stash->scratch ==
nullptr)
goto error;
811 if (!fons__tt_init(stash))
goto error;
813 if (stash->params.renderCreate !=
nullptr) {
814 if (stash->params.renderCreate(stash->params.userPtr, stash->params.width, stash->params.height) == 0)
819 if (stash->atlas ==
nullptr)
goto error;
822 stash->fonts = (FONSfont**)malloc(
sizeof(FONSfont*) *
FONS_INIT_FONTS);
823 if (stash->fonts ==
nullptr)
goto error;
829 stash->itw = 1.0f/stash->params.width;
830 stash->ith = 1.0f/stash->params.height;
831 stash->texData = (
unsigned char*)malloc(stash->params.width * stash->params.height);
832 if (stash->texData ==
nullptr)
goto error;
833 memset(stash->texData, 0, stash->params.width * stash->params.height);
835 stash->dirtyRect[0] = stash->params.width;
836 stash->dirtyRect[1] = stash->params.height;
837 stash->dirtyRect[2] = 0;
838 stash->dirtyRect[3] = 0;
841 fons__addWhiteRect(stash, 2,2);
853static FONSstate* fons__getState(
FONScontext* stash)
855 return &stash->states[stash->nstates-1];
860 FONSfont* baseFont = stash->fonts[base];
862 baseFont->fallbacks[baseFont->nfallbacks++] = fallback;
870 fons__getState(stash)->size = size;
873void fonsSetColour(
struct FONScontext* stash,
unsigned char colourR,
unsigned char colourG,
unsigned char colourB,
unsigned char colourA)
875 fons__getState(stash)->colour[0] = colourR;
876 fons__getState(stash)->colour[1] = colourG;
877 fons__getState(stash)->colour[2] = colourB;
878 fons__getState(stash)->colour[3] = colourA;
882 fons__getState(stash)->spacing = spacing;
887 fons__getState(stash)->blur = blur;
892 fons__getState(stash)->align = align;
897 fons__getState(stash)->font = font;
903 if (stash->handleError)
907 if (stash->nstates > 0)
908 memcpy(&stash->states[stash->nstates], &stash->states[stash->nstates-1],
sizeof(FONSstate));
914 if (stash->nstates <= 1) {
915 if (stash->handleError)
924 FONSstate* state = fons__getState(stash);
926 state->colour[0] = 0xff;
927 state->colour[1] = 0xff;
928 state->colour[2] = 0xff;
929 state->colour[3] = 0xff;
936static void fons__freeFont(FONSfont* font)
938 if (font ==
nullptr)
return;
939 if (font->glyphs) free(font->glyphs);
940 if (font->freeData && font->data) free(font->data);
946 FONSfont* font =
nullptr;
947 if (stash->nfonts+1 > stash->cfonts) {
948 stash->cfonts = stash->cfonts == 0 ? 8 : stash->cfonts * 2;
949 stash->fonts = (FONSfont**)realloc(stash->fonts,
sizeof(FONSfont*) * stash->cfonts);
950 if (stash->fonts ==
nullptr)
953 font = (FONSfont*)malloc(
sizeof(FONSfont));
954 if (font ==
nullptr)
goto error;
955 memset(font, 0,
sizeof(FONSfont));
958 if (font->glyphs ==
nullptr)
goto error;
962 stash->fonts[stash->nfonts++] = font;
963 return stash->nfonts-1;
966 fons__freeFont(font);
971static FILE* fons__fopen(
const char* filename,
const char* mode)
975 const int fileLen = (int)strlen(filename);
976 const int modeLen = (int)strlen(mode);
977 wchar_t wpath[MAX_PATH];
978 wchar_t wmode[MAX_PATH];
984 len = MultiByteToWideChar(CP_UTF8, 0, filename, fileLen, wpath, fileLen);
988 len = MultiByteToWideChar(CP_UTF8, 0, mode, modeLen, wmode, modeLen);
992 FILE* f = _wfopen(wpath, wmode);
995 return fopen(filename, mode);
1012 int dataSize = 0, readed = 0;
1013 unsigned char* data =
nullptr;
1016 fp = fons__fopen(path,
"rb");
1017 if (fp ==
nullptr)
goto error;
1019 dataSize = (int)ftell(fp);
1021 data = (
unsigned char*)malloc(dataSize);
1022 if (data ==
nullptr)
goto error;
1023 readed = (int)fread(data, 1, dataSize, fp);
1026 if (readed != dataSize)
goto error;
1031 if (data) free(data);
1047 int ascent, descent, lineGap, fh;
1049 const int idx = fons__allocFont(stash);
1053 FONSfont* font = stash->fonts[idx];
1054 font->sdfSettings = sdfSettings;
1056 strncpy(font->name, name,
sizeof font->name);
1057 font->name[
sizeof font->name-1] =
'\0';
1064 font->dataSize = dataSize;
1066 font->freeData = (
unsigned char)freeData;
1069 stash->nscratch = 0;
1070 if (!fons__tt_loadFont(stash, &font->font, data, dataSize))
goto error;
1074 fons__tt_getFontVMetrics( &font->font, &ascent, &descent, &lineGap);
1075 fh = ascent - descent;
1076 font->ascender = (float)ascent / (
float)fh;
1077 font->descender = (float)descent / (
float)fh;
1078 font->lineh = (float)(fh + lineGap) / (float)fh;
1083 fons__freeFont(font);
1090 for (
int i = 0; i < s->nfonts; i++) {
1091 if (strcmp(s->fonts[i]->name, name) == 0)
1098static FONSglyph* fons__allocGlyph(FONSfont* font)
1100 if (font->nglyphs+1 > font->cglyphs) {
1101 font->cglyphs = font->cglyphs == 0 ? 8 : font->cglyphs * 2;
1102 font->glyphs = (FONSglyph*)realloc(font->glyphs,
sizeof(FONSglyph) * font->cglyphs);
1103 if (font->glyphs ==
nullptr)
return nullptr;
1106 return &font->glyphs[font->nglyphs-1];
1115static void fons__blurCols(
unsigned char* dst,
int w,
int h,
int dstStride,
int alpha)
1118 for (
int y = 0; y < h; y++) {
1120 for (x = 1; x < w; x++) {
1121 z += alpha * (((int)dst[x] << ZPREC) - z) >> APREC;
1122 dst[x] = (
unsigned char)(z >> ZPREC);
1126 for (x = w-2; x >= 0; x--) {
1127 z += alpha * (((int)dst[x] << ZPREC) - z) >> APREC;
1128 dst[x] = (
unsigned char)(z >> ZPREC);
1135static void fons__blurRows(
unsigned char* dst,
int w,
int h,
int dstStride,
int alpha)
1138 for (
int x = 0; x < w; x++) {
1140 for (y = dstStride; y < h*dstStride; y += dstStride) {
1141 z += alpha * (((int)dst[y] << ZPREC) - z) >> APREC;
1142 dst[y] = (
unsigned char)(z >> ZPREC);
1144 dst[(h-1)*dstStride] = 0;
1146 for (y = (h-2)*dstStride; y >= 0; y -= dstStride) {
1147 z += alpha * (((int)dst[y] << ZPREC) - z) >> APREC;
1148 dst[y] = (
unsigned char)(z >> ZPREC);
1156static void fons__blur(
FONScontext* stash,
unsigned char* dst,
int w,
int h,
int dstStride,
int blur)
1163 float sigma = (float)blur * 0.57735f;
1164 int alpha = (int)((1 << APREC) * (1.0f - expf(-2.3f / (sigma + 1.0f))));
1165 fons__blurRows(dst, w, h, dstStride, alpha);
1166 fons__blurCols(dst, w, h, dstStride, alpha);
1167 fons__blurRows(dst, w, h, dstStride, alpha);
1168 fons__blurCols(dst, w, h, dstStride, alpha);
1173static FONSglyph* fons__getGlyph(
FONScontext* stash, FONSfont* font,
unsigned int codepoint,
1174 short isize,
short iblur)
1176 int advance, lsb, x0, y0, x1, y1, gx, gy;
1177 FONSglyph* glyph =
nullptr;
1178 const float size = isize/10.0f;
1179 FONSfont* renderFont = font;
1181 if (isize < 2)
return nullptr;
1182 if (iblur > 20) iblur = 20;
1183 int pad = iblur + 2;
1186 stash->nscratch = 0;
1190 int i = font->lut[h];
1192 if (font->glyphs[i].codepoint == codepoint && font->glyphs[i].size == isize && font->glyphs[i].blur == iblur)
1193 return &font->glyphs[i];
1194 i = font->glyphs[i].next;
1198 int g = fons__tt_getGlyphIndex(&font->font, codepoint);
1201 for (i = 0; i < font->nfallbacks; ++i) {
1202 FONSfont* fallbackFont = stash->fonts[font->fallbacks[i]];
1203 const int fallbackIndex = fons__tt_getGlyphIndex(&fallbackFont->font, codepoint);
1204 if (fallbackIndex != 0) {
1206 renderFont = fallbackFont;
1213 float scale = fons__tt_getPixelHeightScale(&renderFont->font, size);
1214 fons__tt_buildGlyphBitmap(&renderFont->font, g, size, scale, &advance, &lsb, &x0, &y0, &x1, &y1, &renderFont->sdfSettings);
1215 int gw = x1 - x0 + pad * 2;
1216 int gh = y1 - y0 + pad * 2;
1219 int added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy);
1220 if (added == 0 && stash->handleError !=
nullptr) {
1223 added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy);
1225 if (added == 0)
return nullptr;
1228 glyph = fons__allocGlyph(font);
1229 glyph->codepoint = codepoint;
1230 glyph->size = isize;
1231 glyph->blur = iblur;
1233 glyph->x0 = (short)gx;
1234 glyph->y0 = (short)gy;
1235 glyph->x1 = (short)(glyph->x0+gw);
1236 glyph->y1 = (short)(glyph->y0+gh);
1237 glyph->xadv = (short)(scale * advance * 10.0f);
1238 glyph->xoff = (short)(x0 - pad);
1239 glyph->yoff = (short)(y0 - pad);
1243 glyph->next = font->lut[h];
1244 font->lut[h] = font->nglyphs-1;
1247 unsigned char* dst = &stash->texData[glyph->x0 + pad + (glyph->y0 + pad) * stash->params.width];
1248 fons__tt_renderGlyphBitmap(&renderFont->font, dst, gw-pad*2,gh-pad*2, stash->params.width, scale,scale, g, &renderFont->sdfSettings);
1251 dst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width];
1252 for (
int y = 0; y < gh; y++) {
1253 dst[y*stash->params.width] = 0;
1254 dst[gw-1 + y*stash->params.width] = 0;
1256 for (
int x = 0; x < gw; x++) {
1258 dst[x + (gh-1)*stash->params.width] = 0;
1273 stash->nscratch = 0;
1274 unsigned char* bdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width];
1275 fons__blur(stash, bdst, gw,gh, stash->params.width, iblur);
1278 stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], glyph->x0);
1279 stash->dirtyRect[1] = fons__mini(stash->dirtyRect[1], glyph->y0);
1280 stash->dirtyRect[2] = fons__maxi(stash->dirtyRect[2], glyph->x1);
1281 stash->dirtyRect[3] = fons__maxi(stash->dirtyRect[3], glyph->y1);
1286static void fons__getQuad(
FONScontext* stash, FONSfont* font,
1287 int prevGlyphIndex, FONSglyph* glyph,
1288 float scale,
float spacing,
float* x,
float* y,
FONSquad* q)
1292 if (prevGlyphIndex != -1) {
1293 const float adv = fons__tt_getGlyphKernAdvance(&font->font, prevGlyphIndex, glyph->index) * scale;
1294 *x += (int)(adv + spacing + 0.5f);
1300 float xoff = (short)(glyph->xoff + 1);
1301 float yoff = (short)(glyph->yoff + 1);
1302 float x0 = (float)(glyph->x0 + 1);
1303 float y0 = (float)(glyph->y0 + 1);
1304 float x1 = (float)(glyph->x1 - 1);
1305 float y1 = (float)(glyph->y1 - 1);
1308 rx = (float)(
int)(*x + xoff);
1309 ry = (float)(
int)(*y + yoff);
1313 q->
x1 = rx + x1 - x0;
1314 q->
y1 = ry + y1 - y0;
1316 q->
s0 = x0 * stash->itw;
1317 q->
t0 = y0 * stash->ith;
1318 q->
s1 = x1 * stash->itw;
1319 q->
t1 = y1 * stash->ith;
1321 rx = (float)(
int)(*x + xoff);
1322 ry = (float)(
int)(*y - yoff);
1326 q->
x1 = rx + x1 - x0;
1327 q->
y1 = ry - y1 + y0;
1329 q->
s0 = x0 * stash->itw;
1330 q->
t0 = y0 * stash->ith;
1331 q->
s1 = x1 * stash->itw;
1332 q->
t1 = y1 * stash->ith;
1335 *x += (int)(glyph->xadv / 10.0f + 0.5f);
1341 if (stash->dirtyRect[0] < stash->dirtyRect[2] && stash->dirtyRect[1] < stash->dirtyRect[3]) {
1342 if (stash->params.renderUpdate !=
nullptr)
1343 stash->params.renderUpdate(stash->params.userPtr, stash->dirtyRect, stash->texData);
1345 stash->dirtyRect[0] = stash->params.width;
1346 stash->dirtyRect[1] = stash->params.height;
1347 stash->dirtyRect[2] = 0;
1348 stash->dirtyRect[3] = 0;
1352 if (stash->nverts > 0) {
1353 if (stash->params.renderDraw !=
nullptr)
1354 stash->params.renderDraw(stash->params.userPtr, stash->verts, stash->nverts);
1359static __inline
void fons__vertex(
FONScontext* stash,
float x,
float y,
float s,
float t,
unsigned char cR,
unsigned char cG,
unsigned char cB,
unsigned char cA)
1361 stash->verts[stash->nverts].pos[0] = x;
1362 stash->verts[stash->nverts].pos[1] = y;
1364 stash->verts[stash->nverts].tcoords[0] = s;
1365 stash->verts[stash->nverts].tcoords[1] = t;
1367 stash->verts[stash->nverts].colours[0] = cR;
1368 stash->verts[stash->nverts].colours[1] = cG;
1369 stash->verts[stash->nverts].colours[2] = cB;
1370 stash->verts[stash->nverts].colours[3] = cA;
1374static float fons__getVertAlign(
FONScontext* stash, FONSfont* font,
int align,
short isize)
1378 return font->ascender * (float)isize/10.0f;
1380 return (font->ascender + font->descender) / 2.0f * (float)isize/10.0f;
1384 return font->descender * (float)isize/10.0f;
1388 return -font->ascender * (float)isize/10.0f;
1390 return -(font->ascender + font->descender) / 2.0f * (
float)isize/10.0f;
1394 return -font->descender * (float)isize/10.0f;
1402 const char* str,
const char* end)
1404 FONSstate* state = fons__getState(stash);
1405 unsigned int codepoint;
1406 unsigned int utf8state = 0;
1407 FONSglyph* glyph =
nullptr;
1409 int prevGlyphIndex = -1;
1410 const short isize = (short)(state->size*10.0f);
1411 const short iblur = (short)state->blur;
1414 if (stash ==
nullptr)
return x;
1415 if (state->font < 0 || state->font >= stash->nfonts)
return x;
1416 FONSfont* font = stash->fonts[state->font];
1417 if (font->data ==
nullptr)
return x;
1419 float scale = fons__tt_getPixelHeightScale(&font->font, (
float)isize / 10.0f);
1422 end = str + strlen(str);
1435 y += fons__getVertAlign(stash, font, state->align, isize);
1437 for (; str != end; ++str) {
1438 if (fons__decutf8(&utf8state, &codepoint, *(
const unsigned char*)str))
1440 glyph = fons__getGlyph(stash, font, codepoint, isize, iblur);
1441 if (glyph !=
nullptr) {
1442 fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q);
1447 fons__vertex(stash, q.
x0, q.
y0, q.
s0, q.
t0, state->colour[0], state->colour[1], state->colour[2], state->colour[3]);
1448 fons__vertex(stash, q.
x1, q.
y1, q.
s1, q.
t1, state->colour[0], state->colour[1], state->colour[2], state->colour[3]);
1449 fons__vertex(stash, q.
x1, q.
y0, q.
s1, q.
t0, state->colour[0], state->colour[1], state->colour[2], state->colour[3]);
1451 fons__vertex(stash, q.
x0, q.
y0, q.
s0, q.
t0, state->colour[0], state->colour[1], state->colour[2], state->colour[3]);
1452 fons__vertex(stash, q.
x0, q.
y1, q.
s0, q.
t1, state->colour[0], state->colour[1], state->colour[2], state->colour[3]);
1453 fons__vertex(stash, q.
x1, q.
y1, q.
s1, q.
t1, state->colour[0], state->colour[1], state->colour[2], state->colour[3]);
1455 prevGlyphIndex = glyph !=
nullptr ? glyph->index : -1;
1463 float x,
float y,
const char* str,
const char* end)
1465 FONSstate* state = fons__getState(stash);
1468 memset(iter, 0,
sizeof*iter);
1470 if (stash ==
nullptr)
return 0;
1471 if (state->font < 0 || state->font >= stash->nfonts)
return 0;
1472 iter->
font = stash->fonts[state->font];
1473 if (iter->
font->data ==
nullptr)
return 0;
1475 iter->
isize = (short)(state->size*10.0f);
1476 iter->
iblur = (short)state->blur;
1477 iter->
scale = fons__tt_getPixelHeightScale(&iter->
font->font, (
float)iter->
isize/10.0f);
1490 y += fons__getVertAlign(stash, iter->
font, state->align, iter->
isize);
1493 end = str + strlen(str);
1495 iter->
x = iter->
nextx = x;
1496 iter->
y = iter->
nexty = y;
1497 iter->
spacing = state->spacing;
1509 FONSglyph* glyph =
nullptr;
1510 const char* str = iter->
next;
1513 if (str == iter->
end)
1516 for (; str != iter->
end; str++) {
1524 if (glyph !=
nullptr)
1536 const int w = stash->params.width;
1537 const int h = stash->params.height;
1538 const float u = w == 0 ? 0 : 1.0f / w;
1539 const float v = h == 0 ? 0 : 1.0f / h;
1545 fons__vertex(stash, x+0, y+0, u, v, 0xff, 0xff, 0xff, 0xff);
1546 fons__vertex(stash, x+w, y+h, u, v, 0xff, 0xff, 0xff, 0xff);
1547 fons__vertex(stash, x+w, y+0, u, v, 0xff, 0xff, 0xff, 0xff);
1549 fons__vertex(stash, x+0, y+0, u, v, 0xff, 0xff, 0xff, 0xff);
1550 fons__vertex(stash, x+0, y+h, u, v, 0xff, 0xff, 0xff, 0xff);
1551 fons__vertex(stash, x+w, y+h, u, v, 0xff, 0xff, 0xff, 0xff);
1554 fons__vertex(stash, x+0, y+0, 0, 0, 0xff, 0xff, 0xff, 0xff);
1555 fons__vertex(stash, x+w, y+h, 1, 1, 0xff, 0xff, 0xff, 0xff);
1556 fons__vertex(stash, x+w, y+0, 1, 0, 0xff, 0xff, 0xff, 0xff);
1558 fons__vertex(stash, x+0, y+0, 0, 0, 0xff, 0xff, 0xff, 0xff);
1559 fons__vertex(stash, x+0, y+h, 0, 1, 0xff, 0xff, 0xff, 0xff);
1560 fons__vertex(stash, x+w, y+h, 1, 1, 0xff, 0xff, 0xff, 0xff);
1563 for (
int i = 0; i < stash->atlas->nnodes; i++) {
1564 FONSatlasNode* n = &stash->atlas->nodes[i];
1569 fons__vertex(stash, x+n->x+0, y+n->y+0, u, v, 0xc0, 0x00, 0x00, 0xff);
1570 fons__vertex(stash, x+n->x+n->width, y+n->y+1, u, v, 0xc0, 0x00, 0x00, 0xff);
1571 fons__vertex(stash, x+n->x+n->width, y+n->y+0, u, v, 0xc0, 0x00, 0x00, 0xff);
1573 fons__vertex(stash, x+n->x+0, y+n->y+0, u, v, 0xc0, 0x00, 0x00, 0xff);
1574 fons__vertex(stash, x+n->x+0, y+n->y+1, u, v, 0xc0, 0x00, 0x00, 0xff);
1575 fons__vertex(stash, x+n->x+n->width, y+n->y+1, u, v, 0xc0, 0x00, 0x00, 0xff);
1583 const char* str,
const char* end,
1586 FONSstate* state = fons__getState(stash);
1587 unsigned int codepoint;
1588 unsigned int utf8state = 0;
1590 FONSglyph* glyph =
nullptr;
1591 int prevGlyphIndex = -1;
1592 const short isize = (short)(state->size*10.0f);
1593 const short iblur = (short)state->blur;
1596 if (stash ==
nullptr)
return 0;
1597 if (state->font < 0 || state->font >= stash->nfonts)
return 0;
1598 FONSfont* font = stash->fonts[state->font];
1599 if (font->data ==
nullptr)
return 0;
1601 float scale = fons__tt_getPixelHeightScale(&font->font, (
float)isize / 10.0f);
1604 y += fons__getVertAlign(stash, font, state->align, isize);
1606 float minx = maxx = x;
1607 float miny = maxy = y;
1611 end = str + strlen(str);
1613 for (; str != end; ++str) {
1614 if (fons__decutf8(&utf8state, &codepoint, *(
const unsigned char*)str))
1616 glyph = fons__getGlyph(stash, font, codepoint, isize, iblur);
1617 if (glyph !=
nullptr) {
1618 fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q);
1619 if (q.
x0 < minx) minx = q.
x0;
1620 if (q.
x1 > maxx) maxx = q.
x1;
1622 if (q.
y0 < miny) miny = q.
y0;
1623 if (q.
y1 > maxy) maxy = q.
y1;
1625 if (q.
y1 < miny) miny = q.
y1;
1626 if (q.
y0 > maxy) maxy = q.
y0;
1629 prevGlyphIndex = glyph !=
nullptr ? glyph->index : -1;
1632 float advance = x - startx;
1641 minx -= advance * 0.5f;
1642 maxx -= advance * 0.5f;
1656 float* ascender,
float* descender,
float* lineh)
1658 FONSstate* state = fons__getState(stash);
1660 if (stash ==
nullptr)
return;
1661 if (state->font < 0 || state->font >= stash->nfonts)
return;
1662 FONSfont* font = stash->fonts[state->font];
1663 short isize = (short)(state->size * 10.0f);
1664 if (font->data ==
nullptr)
return;
1667 *ascender = font->ascender*isize/10.0f;
1669 *descender = font->descender*isize/10.0f;
1671 *lineh = font->lineh*isize/10.0f;
1676 FONSstate* state = fons__getState(stash);
1678 if (stash ==
nullptr)
return;
1679 if (state->font < 0 || state->font >= stash->nfonts)
return;
1680 FONSfont* font = stash->fonts[state->font];
1681 short isize = (short)(state->size * 10.0f);
1682 if (font->data ==
nullptr)
return;
1684 y += fons__getVertAlign(stash, font, state->align, isize);
1687 *miny = y - font->ascender * (float)isize/10.0f;
1688 *maxy = *miny + font->lineh*isize/10.0f;
1690 *maxy = y + font->descender * (float)isize/10.0f;
1691 *miny = *maxy - font->lineh*isize/10.0f;
1697 if (width !=
nullptr)
1698 *width = stash->params.width;
1699 if (height !=
nullptr)
1700 *height = stash->params.height;
1701 return stash->texData;
1706 if (stash->dirtyRect[0] < stash->dirtyRect[2] && stash->dirtyRect[1] < stash->dirtyRect[3]) {
1707 dirty[0] = stash->dirtyRect[0];
1708 dirty[1] = stash->dirtyRect[1];
1709 dirty[2] = stash->dirtyRect[2];
1710 dirty[3] = stash->dirtyRect[3];
1712 stash->dirtyRect[0] = stash->params.width;
1713 stash->dirtyRect[1] = stash->params.height;
1714 stash->dirtyRect[2] = 0;
1715 stash->dirtyRect[3] = 0;
1723 if (stash ==
nullptr)
return;
1725 if (stash->params.renderDelete)
1726 stash->params.renderDelete(stash->params.userPtr);
1728 for (
int i = 0; i < stash->nfonts; ++i)
1729 fons__freeFont(stash->fonts[i]);
1731 if (stash->atlas) fons__deleteAtlas(stash->atlas);
1732 if (stash->fonts) free(stash->fonts);
1733 if (stash->texData) free(stash->texData);
1734 if (stash->scratch) free(stash->scratch);
1740 if (stash ==
nullptr)
return;
1741 stash->handleError = callback;
1742 stash->errorUptr =
uptr;
1747 if (stash ==
nullptr)
return;
1748 *width = stash->params.width;
1749 *height = stash->params.height;
1755 unsigned char* data =
nullptr;
1756 if (stash ==
nullptr)
return 0;
1758 width = fons__maxi(width, stash->params.width);
1759 height = fons__maxi(height, stash->params.height);
1761 if (width == stash->params.width && height == stash->params.height)
1768 if (stash->params.renderResize !=
nullptr) {
1769 if (stash->params.renderResize(stash->params.userPtr, width, height) == 0)
1773 data = (
unsigned char*)malloc(width * height);
1774 if (data ==
nullptr)
1776 for (i = 0; i < stash->params.height; i++) {
1777 unsigned char* dst = &data[i*width];
1778 unsigned char* src = &stash->texData[i*stash->params.width];
1779 memcpy(dst, src, stash->params.width);
1780 if (width > stash->params.width)
1781 memset(dst+stash->params.width, 0, width - stash->params.width);
1783 if (height > stash->params.height)
1784 memset(&data[stash->params.height * width], 0, (height - stash->params.height) * width);
1786 free(stash->texData);
1787 stash->texData = data;
1790 fons__atlasExpand(stash->atlas, width, height);
1793 for (i = 0; i < stash->atlas->nnodes; i++)
1794 maxy = fons__maxi(maxy, stash->atlas->nodes[i].y);
1795 stash->dirtyRect[0] = 0;
1796 stash->dirtyRect[1] = 0;
1797 stash->dirtyRect[2] = stash->params.width;
1798 stash->dirtyRect[3] = maxy;
1800 stash->params.width = width;
1801 stash->params.height = height;
1802 stash->itw = 1.0f/stash->params.width;
1803 stash->ith = 1.0f/stash->params.height;
1810 if (stash ==
nullptr)
return 0;
1816 if (stash->params.renderResize !=
nullptr) {
1817 if (stash->params.renderResize(stash->params.userPtr, width, height) == 0)
1822 fons__atlasReset(stash->atlas, width, height);
1825 stash->texData = (
unsigned char*)realloc(stash->texData, width * height);
1826 if (stash->texData ==
nullptr)
return 0;
1827 memset(stash->texData, 0, width * height);
1830 stash->dirtyRect[0] = width;
1831 stash->dirtyRect[1] = height;
1832 stash->dirtyRect[2] = 0;
1833 stash->dirtyRect[3] = 0;
1836 for (
int i = 0; i < stash->nfonts; i++) {
1837 FONSfont* font = stash->fonts[i];
1843 stash->params.width = width;
1844 stash->params.height = height;
1845 stash->itw = 1.0f/stash->params.width;
1846 stash->ith = 1.0f/stash->params.height;
1849 fons__addWhiteRect(stash, 2,2);
int fonsGetFontByName(FONScontext *s, const char *name)
int fonsAddFontSdfMem(FONScontext *s, const char *name, unsigned char *data, int ndata, int freeData, FONSsdfSettings sdfSettings)
int fonsValidateTexture(FONScontext *s, int *dirty)
int fonsAddFallbackFont(FONScontext *stash, int base, int fallback)
void fonsLineBounds(FONScontext *s, float y, float *miny, float *maxy)
void fonsSetAlign(FONScontext *s, int align)
void fonsSetSpacing(FONScontext *s, float spacing)
void fonsDrawDebug(FONScontext *s, float x, float y)
int fonsExpandAtlas(FONScontext *s, int width, int height)
#define FONS_INIT_ATLAS_NODES
void fonsGetAtlasSize(FONScontext *s, int *width, int *height)
int fonsTextIterInit(FONScontext *stash, FONStextIter *iter, float x, float y, const char *str, const char *end)
int fonsTextIterNext(FONScontext *stash, FONStextIter *iter, struct FONSquad *quad)
void fonsVertMetrics(FONScontext *s, float *ascender, float *descender, float *lineh)
void fonsSetBlur(FONScontext *s, float blur)
float fonsTextBounds(FONScontext *s, float x, float y, const char *string, const char *end, float *bounds)
void fonsPopState(FONScontext *s)
#define FONS_HASH_LUT_SIZE
void fonsSetColor(FONScontext *s, unsigned int color)
void fonsSetColour(FONScontext *s, unsigned char colourR, unsigned char colourG, unsigned char colourB, unsigned char colourA)
#define FONS_VERTEX_COUNT
const unsigned char * fonsGetTextureData(FONScontext *stash, int *width, int *height)
FONScontext * fonsCreateInternal(FONSparams *params)
FONScontext * dummyfonsCreate(int width, int height, int flags)
int fonsResetAtlas(FONScontext *stash, int width, int height)
void fonsPushState(FONScontext *s)
int fonsAddFontMem(FONScontext *s, const char *name, unsigned char *data, int ndata, int freeData)
int fonsAddFont(FONScontext *s, const char *name, const char *path)
void fonsSetErrorCallback(FONScontext *s, void(*callback)(void *uptr, int error, int val), void *uptr)
void fonsSetSize(FONScontext *s, float size)
float fonsDrawText(FONScontext *s, float x, float y, const char *string, const char *end)
void fonsDeleteInternal(FONScontext *s)
void fonsClearState(FONScontext *s)
struct FONScontext FONScontext
#define FONS_SCRATCH_BUF_SIZE
void fonsSetFont(FONScontext *s, int font)
int fonsAddFontSdf(FONScontext *s, const char *name, const char *path, FONSsdfSettings sdfSettings)
void dummyfonsDelete(FONScontext *ctx)
#define FONS_MAX_FALLBACKS
int(* renderCreate)(void *uptr, int width, int height)
void(* renderDelete)(void *uptr)
int(* renderResize)(void *uptr, int width, int height)
void(* renderDraw)(void *uptr, const FONSvert *verts, int nverts)
void(* renderUpdate)(void *uptr, int *rect, const unsigned char *data)
unsigned char onedgeValue