fontstash.h (49832B)
1 // 2 // Copyright (c) 2009-2013 Mikko Mononen memon@inside.org 3 // 4 // This software is provided 'as-is', without any express or implied 5 // warranty. In no event will the authors be held liable for any damages 6 // arising from the use of this software. 7 // Permission is granted to anyone to use this software for any purpose, 8 // including commercial applications, and to alter it and redistribute it 9 // freely, subject to the following restrictions: 10 // 1. The origin of this software must not be misrepresented; you must not 11 // claim that you wrote the original software. If you use this software 12 // in a product, an acknowledgment in the product documentation would be 13 // appreciated but is not required. 14 // 2. Altered source versions must be plainly marked as such, and must not be 15 // misrepresented as being the original software. 16 // 3. This notice may not be removed or altered from any source distribution. 17 // 18 19 #ifndef FONS_H 20 #define FONS_H 21 22 #define FONS_INVALID -1 23 24 enum FONSflags { 25 FONS_ZERO_TOPLEFT = 1, 26 FONS_ZERO_BOTTOMLEFT = 2, 27 }; 28 29 enum FONSalign { 30 // Horizontal align 31 FONS_ALIGN_LEFT = 1<<0, // Default 32 FONS_ALIGN_CENTER = 1<<1, 33 FONS_ALIGN_RIGHT = 1<<2, 34 // Vertical align 35 FONS_ALIGN_TOP = 1<<3, 36 FONS_ALIGN_MIDDLE = 1<<4, 37 FONS_ALIGN_BOTTOM = 1<<5, 38 FONS_ALIGN_BASELINE = 1<<6, // Default 39 }; 40 41 enum FONSglyphBitmap { 42 FONS_GLYPH_BITMAP_OPTIONAL = 1, 43 FONS_GLYPH_BITMAP_REQUIRED = 2, 44 }; 45 46 enum FONSerrorCode { 47 // Font atlas is full. 48 FONS_ATLAS_FULL = 1, 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, 55 }; 56 57 struct FONSparams { 58 int width, height; 59 unsigned char flags; 60 void* userPtr; 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); 66 }; 67 typedef struct FONSparams FONSparams; 68 69 struct FONSquad 70 { 71 float x0,y0,s0,t0; 72 float x1,y1,s1,t1; 73 }; 74 typedef struct FONSquad FONSquad; 75 76 struct FONStextIter { 77 float x, y, nextx, nexty, scale, spacing; 78 unsigned int codepoint; 79 short isize, iblur; 80 struct FONSfont* font; 81 int prevGlyphIndex; 82 const char* str; 83 const char* next; 84 const char* end; 85 unsigned int utf8state; 86 int bitmapOption; 87 }; 88 typedef struct FONStextIter FONStextIter; 89 90 typedef struct FONScontext FONScontext; 91 92 // Constructor and destructor. 93 FONScontext* fonsCreateInternal(FONSparams* params); 94 void fonsDeleteInternal(FONScontext* s); 95 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); 103 104 // Add fonts 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); 108 109 // State handling 110 void fonsPushState(FONScontext* s); 111 void fonsPopState(FONScontext* s); 112 void fonsClearState(FONScontext* s); 113 114 // State setting 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); 121 122 // Draw text 123 float fonsDrawText(FONScontext* s, float x, float y, const char* string, const char* end); 124 125 // Measure text 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); 129 130 // Text iterator 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); 133 134 // Pull texture changes 135 const unsigned char* fonsGetTextureData(FONScontext* stash, int* width, int* height); 136 int fonsValidateTexture(FONScontext* s, int* dirty); 137 138 // Draws the stash texture for debugging 139 void fonsDrawDebug(FONScontext* s, float x, float y); 140 141 #endif // FONTSTASH_H 142 143 144 #ifdef FONTSTASH_IMPLEMENTATION 145 146 #define FONS_NOTUSED(v) (void)sizeof(v) 147 148 #ifdef FONS_USE_FREETYPE 149 150 #include <ft2build.h> 151 #include FT_FREETYPE_H 152 #include FT_ADVANCES_H 153 #include <math.h> 154 155 struct FONSttFontImpl { 156 FT_Face font; 157 }; 158 typedef struct FONSttFontImpl FONSttFontImpl; 159 160 #else 161 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" 168 169 struct FONSttFontImpl { 170 stbtt_fontinfo font; 171 }; 172 typedef struct FONSttFontImpl FONSttFontImpl; 173 174 #endif 175 176 #ifndef FONS_SCRATCH_BUF_SIZE 177 # define FONS_SCRATCH_BUF_SIZE 96000 178 #endif 179 #ifndef FONS_HASH_LUT_SIZE 180 # define FONS_HASH_LUT_SIZE 256 181 #endif 182 #ifndef FONS_INIT_FONTS 183 # define FONS_INIT_FONTS 4 184 #endif 185 #ifndef FONS_INIT_GLYPHS 186 # define FONS_INIT_GLYPHS 256 187 #endif 188 #ifndef FONS_INIT_ATLAS_NODES 189 # define FONS_INIT_ATLAS_NODES 256 190 #endif 191 #ifndef FONS_VERTEX_COUNT 192 # define FONS_VERTEX_COUNT 1024 193 #endif 194 #ifndef FONS_MAX_STATES 195 # define FONS_MAX_STATES 20 196 #endif 197 #ifndef FONS_MAX_FALLBACKS 198 # define FONS_MAX_FALLBACKS 20 199 #endif 200 201 static unsigned int fons__hashint(unsigned int a) 202 { 203 a += ~(a<<15); 204 a ^= (a>>10); 205 a += (a<<3); 206 a ^= (a>>6); 207 a += ~(a<<11); 208 a ^= (a>>16); 209 return a; 210 } 211 212 static int fons__mini(int a, int b) 213 { 214 return a < b ? a : b; 215 } 216 217 static int fons__maxi(int a, int b) 218 { 219 return a > b ? a : b; 220 } 221 222 struct FONSglyph 223 { 224 unsigned int codepoint; 225 int index; 226 int next; 227 short size, blur; 228 short x0,y0,x1,y1; 229 short xadv,xoff,yoff; 230 }; 231 typedef struct FONSglyph FONSglyph; 232 233 struct FONSfont 234 { 235 FONSttFontImpl font; 236 char name[64]; 237 unsigned char* data; 238 int dataSize; 239 unsigned char freeData; 240 float ascender; 241 float descender; 242 float lineh; 243 FONSglyph* glyphs; 244 int cglyphs; 245 int nglyphs; 246 int lut[FONS_HASH_LUT_SIZE]; 247 int fallbacks[FONS_MAX_FALLBACKS]; 248 int nfallbacks; 249 }; 250 typedef struct FONSfont FONSfont; 251 252 struct FONSstate 253 { 254 int font; 255 int align; 256 float size; 257 unsigned int color; 258 float blur; 259 float spacing; 260 }; 261 typedef struct FONSstate FONSstate; 262 263 struct FONSatlasNode { 264 short x, y, width; 265 }; 266 typedef struct FONSatlasNode FONSatlasNode; 267 268 struct FONSatlas 269 { 270 int width, height; 271 FONSatlasNode* nodes; 272 int nnodes; 273 int cnodes; 274 }; 275 typedef struct FONSatlas FONSatlas; 276 277 struct FONScontext 278 { 279 FONSparams params; 280 float itw,ith; 281 unsigned char* texData; 282 int dirtyRect[4]; 283 FONSfont** fonts; 284 FONSatlas* atlas; 285 int cfonts; 286 int nfonts; 287 float verts[FONS_VERTEX_COUNT*2]; 288 float tcoords[FONS_VERTEX_COUNT*2]; 289 unsigned int colors[FONS_VERTEX_COUNT]; 290 int nverts; 291 unsigned char* scratch; 292 int nscratch; 293 FONSstate states[FONS_MAX_STATES]; 294 int nstates; 295 void (*handleError)(void* uptr, int error, int val); 296 void* errorUptr; 297 #ifdef FONS_USE_FREETYPE 298 FT_Library ftLibrary; 299 #endif 300 }; 301 302 #ifdef FONS_USE_FREETYPE 303 304 int fons__tt_init(FONScontext *context) 305 { 306 FT_Error ftError; 307 ftError = FT_Init_FreeType(&context->ftLibrary); 308 return ftError == 0; 309 } 310 311 int fons__tt_done(FONScontext *context) 312 { 313 FT_Error ftError; 314 ftError = FT_Done_FreeType(context->ftLibrary); 315 return ftError == 0; 316 } 317 318 int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize, int fontIndex) 319 { 320 FT_Error ftError; 321 322 //font->font.userdata = stash; 323 ftError = FT_New_Memory_Face(context->ftLibrary, (const FT_Byte*)data, dataSize, fontIndex, &font->font); 324 return ftError == 0; 325 } 326 327 void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, int *lineGap) 328 { 329 *ascent = font->font->ascender; 330 *descent = font->font->descender; 331 *lineGap = font->font->height - (*ascent - *descent); 332 } 333 334 float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) 335 { 336 #if 1 337 // Note(DPF) maintain pixel-based units for compat after nanovg update 338 return size / (font->font->ascender - font->font->descender); 339 #else 340 return size / font->font->units_per_EM; 341 #endif 342 } 343 344 int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) 345 { 346 return FT_Get_Char_Index(font->font, codepoint); 347 } 348 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) 351 { 352 FT_Error ftError; 353 FT_GlyphSlot ftGlyph; 354 FT_Fixed advFixed; 355 FONS_NOTUSED(scale); 356 357 #if 1 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))); 360 #else 361 ftError = FT_Set_Pixel_Sizes(font->font, 0, size); 362 #endif 363 if (ftError) return 0; 364 #if 1 365 // Note(DPF) maintain pixel-based units for compat after nanovg update 366 ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER); 367 #else 368 ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT); 369 #endif 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; 380 return 1; 381 } 382 383 void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int outWidth, int outHeight, int outStride, 384 float scaleX, float scaleY, int glyph) 385 { 386 FT_GlyphSlot ftGlyph = font->font->glyph; 387 int ftGlyphOffset = 0; 388 unsigned int x, y; 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 394 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++]; 398 } 399 } 400 } 401 402 int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) 403 { 404 FT_Vector ftKerning; 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 407 } 408 409 #else 410 411 int fons__tt_init(FONScontext *context) 412 { 413 FONS_NOTUSED(context); 414 return 1; 415 } 416 417 int fons__tt_done(FONScontext *context) 418 { 419 FONS_NOTUSED(context); 420 return 1; 421 } 422 423 int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize, int fontIndex) 424 { 425 int offset, stbError; 426 FONS_NOTUSED(dataSize); 427 428 font->font.userdata = context; 429 offset = stbtt_GetFontOffsetForIndex(data, fontIndex); 430 if (offset == -1) { 431 stbError = 0; 432 } else { 433 stbError = stbtt_InitFont(&font->font, data, offset); 434 } 435 return stbError; 436 } 437 438 void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, int *lineGap) 439 { 440 stbtt_GetFontVMetrics(&font->font, ascent, descent, lineGap); 441 } 442 443 float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) 444 { 445 #if 1 446 // Note(DPF) maintain pixel-based units for compat after nanovg update 447 return stbtt_ScaleForPixelHeight(&font->font, size); 448 #else 449 return stbtt_ScaleForMappingEmToPixels(&font->font, size); 450 #endif 451 } 452 453 int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) 454 { 455 return stbtt_FindGlyphIndex(&font->font, codepoint); 456 } 457 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) 460 { 461 FONS_NOTUSED(size); 462 stbtt_GetGlyphHMetrics(&font->font, glyph, advance, lsb); 463 stbtt_GetGlyphBitmapBox(&font->font, glyph, scale, scale, x0, y0, x1, y1); 464 return 1; 465 } 466 467 void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int outWidth, int outHeight, int outStride, 468 float scaleX, float scaleY, int glyph) 469 { 470 stbtt_MakeGlyphBitmap(&font->font, output, outWidth, outHeight, outStride, scaleX, scaleY, glyph); 471 } 472 473 int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) 474 { 475 return stbtt_GetGlyphKernAdvance(&font->font, glyph1, glyph2); 476 } 477 478 #endif 479 480 #ifdef STB_TRUETYPE_IMPLEMENTATION 481 482 static void* fons__tmpalloc(size_t size, void* up) 483 { 484 unsigned char* ptr; 485 FONScontext* stash = (FONScontext*)up; 486 487 // 16-byte align the returned pointer 488 size = (size + 0xf) & ~0xf; 489 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); 493 return NULL; 494 } 495 ptr = stash->scratch + stash->nscratch; 496 stash->nscratch += (int)size; 497 return ptr; 498 } 499 500 static void fons__tmpfree(void* ptr, void* up) 501 { 502 (void)ptr; 503 (void)up; 504 // empty 505 } 506 507 #endif // STB_TRUETYPE_IMPLEMENTATION 508 509 // Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de> 510 // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. 511 512 #define FONS_UTF8_ACCEPT 0 513 #define FONS_UTF8_REJECT 12 514 515 static unsigned int fons__decutf8(unsigned int* state, unsigned int* codep, unsigned int byte) 516 { 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, 528 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, 536 }; 537 538 unsigned int type = utf8d[byte]; 539 540 *codep = (*state != FONS_UTF8_ACCEPT) ? 541 (byte & 0x3fu) | (*codep << 6) : 542 (0xff >> type) & (byte); 543 544 *state = utf8d[256 + *state + type]; 545 return *state; 546 } 547 548 // Atlas based on Skyline Bin Packer by Jukka Jylänki 549 550 static void fons__deleteAtlas(FONSatlas* atlas) 551 { 552 if (atlas == NULL) return; 553 if (atlas->nodes != NULL) free(atlas->nodes); 554 free(atlas); 555 } 556 557 static FONSatlas* fons__allocAtlas(int w, int h, int nnodes) 558 { 559 FONSatlas* atlas = NULL; 560 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)); 565 566 atlas->width = w; 567 atlas->height = h; 568 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); 573 atlas->nnodes = 0; 574 atlas->cnodes = nnodes; 575 576 // Init root node. 577 atlas->nodes[0].x = 0; 578 atlas->nodes[0].y = 0; 579 atlas->nodes[0].width = (short)w; 580 atlas->nnodes++; 581 582 return atlas; 583 584 error: 585 if (atlas) fons__deleteAtlas(atlas); 586 return NULL; 587 } 588 589 static int fons__atlasInsertNode(FONSatlas* atlas, int idx, int x, int y, int w) 590 { 591 int i; 592 // Insert node 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) 597 return 0; 598 } 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; 604 atlas->nnodes++; 605 606 return 1; 607 } 608 609 static void fons__atlasRemoveNode(FONSatlas* atlas, int idx) 610 { 611 int i; 612 if (atlas->nnodes == 0) return; 613 for (i = idx; i < atlas->nnodes-1; i++) 614 atlas->nodes[i] = atlas->nodes[i+1]; 615 atlas->nnodes--; 616 } 617 618 static void fons__atlasExpand(FONSatlas* atlas, int w, int h) 619 { 620 // Insert node for empty space 621 if (w > atlas->width) 622 fons__atlasInsertNode(atlas, atlas->nnodes, atlas->width, 0, w - atlas->width); 623 atlas->width = w; 624 atlas->height = h; 625 } 626 627 static void fons__atlasReset(FONSatlas* atlas, int w, int h) 628 { 629 atlas->width = w; 630 atlas->height = h; 631 atlas->nnodes = 0; 632 633 // Init root node. 634 atlas->nodes[0].x = 0; 635 atlas->nodes[0].y = 0; 636 atlas->nodes[0].width = (short)w; 637 atlas->nnodes++; 638 } 639 640 static int fons__atlasAddSkylineLevel(FONSatlas* atlas, int idx, int x, int y, int w, int h) 641 { 642 int i; 643 644 // Insert new node 645 if (fons__atlasInsertNode(atlas, idx, x, y+h, w) == 0) 646 return 0; 647 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); 656 i--; 657 } else { 658 break; 659 } 660 } else { 661 break; 662 } 663 } 664 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); 670 i--; 671 } 672 } 673 674 return 1; 675 } 676 677 static int fons__atlasRectFits(FONSatlas* atlas, int i, int w, int h) 678 { 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; 684 int spaceLeft; 685 if (x + w > atlas->width) 686 return -1; 687 spaceLeft = w; 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; 693 ++i; 694 } 695 return y; 696 } 697 698 static int fons__atlasAddRect(FONSatlas* atlas, int rw, int rh, int* rx, int* ry) 699 { 700 int besth = atlas->height, bestw = atlas->width, besti = -1; 701 int bestx = -1, besty = -1, i; 702 703 // Bottom left fit heuristic. 704 for (i = 0; i < atlas->nnodes; i++) { 705 int y = fons__atlasRectFits(atlas, i, rw, rh); 706 if (y != -1) { 707 if (y + rh < besth || (y + rh == besth && atlas->nodes[i].width < bestw)) { 708 besti = i; 709 bestw = atlas->nodes[i].width; 710 besth = y + rh; 711 bestx = atlas->nodes[i].x; 712 besty = y; 713 } 714 } 715 } 716 717 if (besti == -1) 718 return 0; 719 720 // Perform the actual packing. 721 if (fons__atlasAddSkylineLevel(atlas, besti, bestx, besty, rw, rh) == 0) 722 return 0; 723 724 *rx = bestx; 725 *ry = besty; 726 727 return 1; 728 } 729 730 static void fons__addWhiteRect(FONScontext* stash, int w, int h) 731 { 732 int x, y, gx, gy; 733 unsigned char* dst; 734 if (fons__atlasAddRect(stash->atlas, w, h, &gx, &gy) == 0) 735 return; 736 737 // Rasterize 738 dst = &stash->texData[gx + gy * stash->params.width]; 739 for (y = 0; y < h; y++) { 740 for (x = 0; x < w; x++) 741 dst[x] = 0xff; 742 dst += stash->params.width; 743 } 744 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); 749 } 750 751 FONScontext* fonsCreateInternal(FONSparams* params) 752 { 753 FONScontext* stash = NULL; 754 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)); 759 760 stash->params = *params; 761 762 // Allocate scratch buffer. 763 stash->scratch = (unsigned char*)malloc(FONS_SCRATCH_BUF_SIZE); 764 if (stash->scratch == NULL) goto error; 765 766 // Initialize implementation library 767 if (!fons__tt_init(stash)) goto error; 768 769 if (stash->params.renderCreate != NULL) { 770 if (stash->params.renderCreate(stash->params.userPtr, stash->params.width, stash->params.height) == 0) 771 goto error; 772 } 773 774 stash->atlas = fons__allocAtlas(stash->params.width, stash->params.height, FONS_INIT_ATLAS_NODES); 775 if (stash->atlas == NULL) goto error; 776 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; 782 stash->nfonts = 0; 783 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); 790 791 stash->dirtyRect[0] = stash->params.width; 792 stash->dirtyRect[1] = stash->params.height; 793 stash->dirtyRect[2] = 0; 794 stash->dirtyRect[3] = 0; 795 796 // Add white rect at 0,0 for debug drawing. 797 fons__addWhiteRect(stash, 2,2); 798 799 fonsPushState(stash); 800 fonsClearState(stash); 801 802 return stash; 803 804 error: 805 fonsDeleteInternal(stash); 806 return NULL; 807 } 808 809 static FONSstate* fons__getState(FONScontext* stash) 810 { 811 return &stash->states[stash->nstates-1]; 812 } 813 814 int fonsAddFallbackFont(FONScontext* stash, int base, int fallback) 815 { 816 FONSfont* baseFont = stash->fonts[base]; 817 if (baseFont->nfallbacks < FONS_MAX_FALLBACKS) { 818 baseFont->fallbacks[baseFont->nfallbacks++] = fallback; 819 return 1; 820 } 821 return 0; 822 } 823 824 void fonsResetFallbackFont(FONScontext* stash, int base) 825 { 826 int i; 827 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; 833 } 834 835 void fonsSetSize(FONScontext* stash, float size) 836 { 837 fons__getState(stash)->size = size; 838 } 839 840 void fonsSetColor(FONScontext* stash, unsigned int color) 841 { 842 fons__getState(stash)->color = color; 843 } 844 845 void fonsSetSpacing(FONScontext* stash, float spacing) 846 { 847 fons__getState(stash)->spacing = spacing; 848 } 849 850 void fonsSetBlur(FONScontext* stash, float blur) 851 { 852 fons__getState(stash)->blur = blur; 853 } 854 855 void fonsSetAlign(FONScontext* stash, int align) 856 { 857 fons__getState(stash)->align = align; 858 } 859 860 void fonsSetFont(FONScontext* stash, int font) 861 { 862 fons__getState(stash)->font = font; 863 } 864 865 void fonsPushState(FONScontext* stash) 866 { 867 if (stash->nstates >= FONS_MAX_STATES) { 868 if (stash->handleError) 869 stash->handleError(stash->errorUptr, FONS_STATES_OVERFLOW, 0); 870 return; 871 } 872 if (stash->nstates > 0) 873 memcpy(&stash->states[stash->nstates], &stash->states[stash->nstates-1], sizeof(FONSstate)); 874 stash->nstates++; 875 } 876 877 void fonsPopState(FONScontext* stash) 878 { 879 if (stash->nstates <= 1) { 880 if (stash->handleError) 881 stash->handleError(stash->errorUptr, FONS_STATES_UNDERFLOW, 0); 882 return; 883 } 884 stash->nstates--; 885 } 886 887 void fonsClearState(FONScontext* stash) 888 { 889 FONSstate* state = fons__getState(stash); 890 state->size = 12.0f; 891 state->color = 0xffffffff; 892 state->font = 0; 893 state->blur = 0; 894 state->spacing = 0; 895 state->align = FONS_ALIGN_LEFT | FONS_ALIGN_BASELINE; 896 } 897 898 static void fons__freeFont(FONSfont* font) 899 { 900 if (font == NULL) return; 901 if (font->glyphs) free(font->glyphs); 902 if (font->freeData && font->data) free(font->data); 903 free(font); 904 } 905 906 static int fons__allocFont(FONScontext* stash) 907 { 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) 913 return -1; 914 for (int i=stash->nfonts; i<stash->cfonts; ++i) 915 stash->fonts[i] = NULL; 916 } 917 font = (FONSfont*)malloc(sizeof(FONSfont)); 918 if (font == NULL) goto error; 919 memset(font, 0, sizeof(FONSfont)); 920 921 font->glyphs = (FONSglyph*)malloc(sizeof(FONSglyph) * FONS_INIT_GLYPHS); 922 if (font->glyphs == NULL) goto error; 923 font->cglyphs = FONS_INIT_GLYPHS; 924 font->nglyphs = 0; 925 926 stash->fonts[stash->nfonts++] = font; 927 return stash->nfonts-1; 928 929 error: 930 fons__freeFont(font); 931 932 return FONS_INVALID; 933 } 934 935 int fonsAddFont(FONScontext* stash, const char* name, const char* path, int fontIndex) 936 { 937 FILE* fp = 0; 938 int dataSize = 0; 939 size_t readed; 940 unsigned char* data = NULL; 941 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); 951 fclose(fp); 952 fp = 0; 953 if (readed != (size_t)dataSize) goto error; 954 955 return fonsAddFontMem(stash, name, data, dataSize, 1, fontIndex); 956 957 error: 958 if (data) free(data); 959 if (fp) fclose(fp); 960 return FONS_INVALID; 961 } 962 963 int fonsAddFontMem(FONScontext* stash, const char* name, unsigned char* data, int dataSize, int freeData, int fontIndex) 964 { 965 int i, ascent, descent, fh, lineGap; 966 FONSfont* font; 967 968 int idx = fons__allocFont(stash); 969 if (idx == FONS_INVALID) 970 { 971 if (freeData && data) free(data); 972 return FONS_INVALID; 973 } 974 975 font = stash->fonts[idx]; 976 977 strncpy(font->name, name, sizeof(font->name)); 978 font->name[sizeof(font->name)-1] = '\0'; 979 980 // Init hash lookup. 981 for (i = 0; i < FONS_HASH_LUT_SIZE; ++i) 982 font->lut[i] = -1; 983 984 // Read in the font data. 985 font->dataSize = dataSize; 986 font->data = data; 987 font->freeData = (unsigned char)freeData; 988 989 // Init font 990 stash->nscratch = 0; 991 if (!fons__tt_loadFont(stash, &font->font, data, dataSize, fontIndex)) goto error; 992 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); 996 ascent += 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; 1001 1002 return idx; 1003 1004 error: 1005 fons__freeFont(font); 1006 stash->nfonts--; 1007 return FONS_INVALID; 1008 } 1009 1010 int fonsGetFontByName(FONScontext* s, const char* name) 1011 { 1012 int i; 1013 for (i = 0; i < s->nfonts; i++) { 1014 if (strcmp(s->fonts[i]->name, name) == 0) 1015 return i; 1016 } 1017 return FONS_INVALID; 1018 } 1019 1020 1021 static FONSglyph* fons__allocGlyph(FONSfont* font) 1022 { 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)); 1029 } 1030 font->nglyphs++; 1031 return &font->glyphs[font->nglyphs-1]; 1032 } 1033 1034 1035 // Based on Exponential blur, Jani Huhtanen, 2006 1036 1037 #define APREC 16 1038 #define ZPREC 7 1039 1040 static void fons__blurCols(unsigned char* dst, int w, int h, int dstStride, int alpha) 1041 { 1042 int x, y; 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); 1048 } 1049 dst[w-1] = 0; // force zero border 1050 z = 0; 1051 for (x = w-2; x >= 0; x--) { 1052 z += (alpha * (((int)(dst[x]) << ZPREC) - z)) >> APREC; 1053 dst[x] = (unsigned char)(z >> ZPREC); 1054 } 1055 dst[0] = 0; // force zero border 1056 dst += dstStride; 1057 } 1058 } 1059 1060 static void fons__blurRows(unsigned char* dst, int w, int h, int dstStride, int alpha) 1061 { 1062 int x, y; 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); 1068 } 1069 dst[(h-1)*dstStride] = 0; // force zero border 1070 z = 0; 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); 1074 } 1075 dst[0] = 0; // force zero border 1076 dst++; 1077 } 1078 } 1079 1080 1081 static void fons__blur(FONScontext* stash, unsigned char* dst, int w, int h, int dstStride, int blur) 1082 { 1083 int alpha; 1084 float sigma; 1085 (void)stash; 1086 1087 if (blur < 1) 1088 return; 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); 1098 } 1099 1100 static FONSglyph* fons__getGlyph(FONScontext* stash, FONSfont* font, unsigned int codepoint, 1101 short isize, short iblur, int bitmapOption) 1102 { 1103 int i, g, advance, lsb, x0, y0, x1, y1, gw, gh, gx, gy, x, y; 1104 float scale; 1105 FONSglyph* glyph = NULL; 1106 unsigned int h; 1107 float size = isize/10.0f; 1108 int pad, added; 1109 unsigned char* bdst; 1110 unsigned char* dst; 1111 FONSfont* renderFont = font; 1112 1113 if (isize < 2) return NULL; 1114 if (iblur > 20) iblur = 20; 1115 pad = iblur+2; 1116 1117 // Reset allocator. 1118 stash->nscratch = 0; 1119 1120 // Find code point and size. 1121 h = fons__hashint(codepoint) & (FONS_HASH_LUT_SIZE-1); 1122 i = font->lut[h]; 1123 while (i != -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)) { 1127 return glyph; 1128 } 1129 // At this point, glyph exists but the bitmap data is not yet created. 1130 break; 1131 } 1132 i = font->glyphs[i].next; 1133 } 1134 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. 1138 if (g == 0) { 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) { 1143 g = fallbackIndex; 1144 renderFont = fallbackFont; 1145 break; 1146 } 1147 } 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. 1150 } 1151 scale = fons__tt_getPixelHeightScale(&renderFont->font, size); 1152 fons__tt_buildGlyphBitmap(&renderFont->font, g, size, scale, &advance, &lsb, &x0, &y0, &x1, &y1); 1153 gw = x1-x0 + pad*2; 1154 gh = y1-y0 + pad*2; 1155 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); 1164 } 1165 if (added == 0) return NULL; 1166 } else { 1167 // Negative coordinate indicates there is no bitmap data created. 1168 gx = -1; 1169 gy = -1; 1170 } 1171 1172 // Init glyph. 1173 if (glyph == NULL) { 1174 glyph = fons__allocGlyph(font); 1175 glyph->codepoint = codepoint; 1176 glyph->size = isize; 1177 glyph->blur = iblur; 1178 glyph->next = 0; 1179 1180 // Insert char to hash lookup. 1181 glyph->next = font->lut[h]; 1182 font->lut[h] = font->nglyphs-1; 1183 } 1184 glyph->index = g; 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); 1192 1193 if (bitmapOption == FONS_GLYPH_BITMAP_OPTIONAL) { 1194 return glyph; 1195 } 1196 1197 // Rasterize 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); 1200 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; 1206 } 1207 for (x = 0; x < gw; x++) { 1208 dst[x] = 0; 1209 dst[x + (gh-1)*stash->params.width] = 0; 1210 } 1211 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; 1219 } 1220 }*/ 1221 1222 // Blur 1223 if (iblur > 0) { 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); 1227 } 1228 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); 1233 1234 return glyph; 1235 } 1236 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) 1240 { 1241 float rx,ry,xoff,yoff,x0,y0,x1,y1; 1242 1243 if (prevGlyphIndex != -1) { 1244 float adv = fons__tt_getGlyphKernAdvance(&font->font, prevGlyphIndex, glyph->index) * scale; 1245 *x += (int)(adv + spacing + 0.5f); 1246 } 1247 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); 1257 1258 if (stash->params.flags & FONS_ZERO_TOPLEFT) { 1259 rx = floorf(*x + xoff); 1260 ry = floorf(*y + yoff); 1261 1262 q->x0 = rx; 1263 q->y0 = ry; 1264 q->x1 = rx + x1 - x0; 1265 q->y1 = ry + y1 - y0; 1266 1267 q->s0 = x0 * stash->itw; 1268 q->t0 = y0 * stash->ith; 1269 q->s1 = x1 * stash->itw; 1270 q->t1 = y1 * stash->ith; 1271 } else { 1272 rx = floorf(*x + xoff); 1273 ry = floorf(*y - yoff); 1274 1275 q->x0 = rx; 1276 q->y0 = ry; 1277 q->x1 = rx + x1 - x0; 1278 q->y1 = ry - y1 + y0; 1279 1280 q->s0 = x0 * stash->itw; 1281 q->t0 = y0 * stash->ith; 1282 q->s1 = x1 * stash->itw; 1283 q->t1 = y1 * stash->ith; 1284 } 1285 1286 *x += (int)(glyph->xadv / 10.0f + 0.5f); 1287 } 1288 1289 static void fons__flush(FONScontext* stash) 1290 { 1291 // Flush texture 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); 1295 // Reset dirty rect 1296 stash->dirtyRect[0] = stash->params.width; 1297 stash->dirtyRect[1] = stash->params.height; 1298 stash->dirtyRect[2] = 0; 1299 stash->dirtyRect[3] = 0; 1300 } 1301 1302 // Flush triangles 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); 1306 stash->nverts = 0; 1307 } 1308 } 1309 1310 static __inline void fons__vertex(FONScontext* stash, float x, float y, float s, float t, unsigned int c) 1311 { 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; 1317 stash->nverts++; 1318 } 1319 1320 static float fons__getVertAlign(FONScontext* stash, FONSfont* font, int align, short isize) 1321 { 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) { 1328 return 0.0f; 1329 } else if (align & FONS_ALIGN_BOTTOM) { 1330 return font->descender * (float)isize/10.0f; 1331 } 1332 } else { 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) { 1338 return 0.0f; 1339 } else if (align & FONS_ALIGN_BOTTOM) { 1340 return -font->descender * (float)isize/10.0f; 1341 } 1342 } 1343 return 0.0; 1344 } 1345 1346 float fonsDrawText(FONScontext* stash, 1347 float x, float y, 1348 const char* str, const char* end) 1349 { 1350 FONSstate* state = fons__getState(stash); 1351 unsigned int codepoint; 1352 unsigned int utf8state = 0; 1353 FONSglyph* glyph = NULL; 1354 FONSquad q; 1355 int prevGlyphIndex = -1; 1356 short isize = (short)(state->size*10.0f); 1357 short iblur = (short)state->blur; 1358 float scale; 1359 FONSfont* font; 1360 float width; 1361 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; 1366 1367 scale = fons__tt_getPixelHeightScale(&font->font, (float)isize/10.0f); 1368 1369 if (end == NULL) 1370 end = str + strlen(str); 1371 1372 // Align horizontally 1373 if (state->align & FONS_ALIGN_LEFT) { 1374 // empty 1375 } else if (state->align & FONS_ALIGN_RIGHT) { 1376 width = fonsTextBounds(stash, x,y, str, end, NULL); 1377 x -= width; 1378 } else if (state->align & FONS_ALIGN_CENTER) { 1379 width = fonsTextBounds(stash, x,y, str, end, NULL); 1380 x -= width * 0.5f; 1381 } 1382 // Align vertically. 1383 y += fons__getVertAlign(stash, font, state->align, isize); 1384 1385 for (; str != end; ++str) { 1386 if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str)) 1387 continue; 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); 1391 1392 if (stash->nverts+6 > FONS_VERTEX_COUNT) 1393 fons__flush(stash); 1394 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); 1398 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); 1402 } 1403 prevGlyphIndex = glyph != NULL ? glyph->index : -1; 1404 } 1405 fons__flush(stash); 1406 1407 return x; 1408 } 1409 1410 int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, 1411 float x, float y, const char* str, const char* end, int bitmapOption) 1412 { 1413 FONSstate* state = fons__getState(stash); 1414 float width; 1415 1416 memset(iter, 0, sizeof(*iter)); 1417 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; 1422 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); 1426 1427 // Align horizontally 1428 if (state->align & FONS_ALIGN_LEFT) { 1429 // empty 1430 } else if (state->align & FONS_ALIGN_RIGHT) { 1431 width = fonsTextBounds(stash, x,y, str, end, NULL); 1432 x -= width; 1433 } else if (state->align & FONS_ALIGN_CENTER) { 1434 width = fonsTextBounds(stash, x,y, str, end, NULL); 1435 x -= width * 0.5f; 1436 } 1437 // Align vertically. 1438 y += fons__getVertAlign(stash, iter->font, state->align, iter->isize); 1439 1440 if (end == NULL) 1441 end = str + strlen(str); 1442 1443 iter->x = iter->nextx = x; 1444 iter->y = iter->nexty = y; 1445 iter->spacing = state->spacing; 1446 iter->str = str; 1447 iter->next = str; 1448 iter->end = end; 1449 iter->codepoint = 0; 1450 iter->prevGlyphIndex = -1; 1451 iter->bitmapOption = bitmapOption; 1452 1453 return 1; 1454 } 1455 1456 int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, FONSquad* quad) 1457 { 1458 FONSglyph* glyph = NULL; 1459 const char* str = iter->next; 1460 iter->str = iter->next; 1461 1462 if (str == iter->end) 1463 return 0; 1464 1465 for (; str != iter->end; str++) { 1466 if (fons__decutf8(&iter->utf8state, &iter->codepoint, *(const unsigned char*)str)) 1467 continue; 1468 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. 1474 if (glyph != NULL) 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; 1477 break; 1478 } 1479 iter->next = str; 1480 1481 return 1; 1482 } 1483 1484 void fonsDrawDebug(FONScontext* stash, float x, float y) 1485 { 1486 int i; 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); 1491 1492 if (stash->nverts+6+6 > FONS_VERTEX_COUNT) 1493 fons__flush(stash); 1494 1495 // Draw background 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); 1499 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); 1503 1504 // Draw texture 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); 1508 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); 1512 1513 // Drawbug draw atlas 1514 for (i = 0; i < stash->atlas->nnodes; i++) { 1515 FONSatlasNode* n = &stash->atlas->nodes[i]; 1516 1517 if (stash->nverts+6 > FONS_VERTEX_COUNT) 1518 fons__flush(stash); 1519 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); 1523 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); 1527 } 1528 1529 fons__flush(stash); 1530 } 1531 1532 float fonsTextBounds(FONScontext* stash, 1533 float x, float y, 1534 const char* str, const char* end, 1535 float* bounds) 1536 { 1537 FONSstate* state = fons__getState(stash); 1538 unsigned int codepoint; 1539 unsigned int utf8state = 0; 1540 FONSquad q; 1541 FONSglyph* glyph = NULL; 1542 int prevGlyphIndex = -1; 1543 short isize = (short)(state->size*10.0f); 1544 short iblur = (short)state->blur; 1545 float scale; 1546 FONSfont* font; 1547 float startx, advance; 1548 float minx, miny, maxx, maxy; 1549 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; 1554 1555 scale = fons__tt_getPixelHeightScale(&font->font, (float)isize/10.0f); 1556 1557 // Align vertically. 1558 y += fons__getVertAlign(stash, font, state->align, isize); 1559 1560 minx = maxx = x; 1561 miny = maxy = y; 1562 startx = x; 1563 1564 if (end == NULL) 1565 end = str + strlen(str); 1566 1567 for (; str != end; ++str) { 1568 if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str)) 1569 continue; 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; 1578 } else { 1579 if (q.y1 < miny) miny = q.y1; 1580 if (q.y0 > maxy) maxy = q.y0; 1581 } 1582 } 1583 prevGlyphIndex = glyph != NULL ? glyph->index : -1; 1584 } 1585 1586 advance = x - startx; 1587 1588 // Align horizontally 1589 if (state->align & FONS_ALIGN_LEFT) { 1590 // empty 1591 } else if (state->align & FONS_ALIGN_RIGHT) { 1592 minx -= advance; 1593 maxx -= advance; 1594 } else if (state->align & FONS_ALIGN_CENTER) { 1595 minx -= advance * 0.5f; 1596 maxx -= advance * 0.5f; 1597 } 1598 1599 if (bounds) { 1600 bounds[0] = minx; 1601 bounds[1] = miny; 1602 bounds[2] = maxx; 1603 bounds[3] = maxy; 1604 } 1605 1606 return advance; 1607 } 1608 1609 void fonsVertMetrics(FONScontext* stash, 1610 float* ascender, float* descender, float* lineh) 1611 { 1612 FONSfont* font; 1613 FONSstate* state = fons__getState(stash); 1614 short isize; 1615 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; 1621 1622 if (ascender) 1623 *ascender = font->ascender*isize/10.0f; 1624 if (descender) 1625 *descender = font->descender*isize/10.0f; 1626 if (lineh) 1627 *lineh = font->lineh*isize/10.0f; 1628 } 1629 1630 void fonsLineBounds(FONScontext* stash, float y, float* miny, float* maxy) 1631 { 1632 FONSfont* font; 1633 FONSstate* state = fons__getState(stash); 1634 short isize; 1635 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; 1641 1642 y += fons__getVertAlign(stash, font, state->align, isize); 1643 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; 1647 } else { 1648 *maxy = y + font->descender * (float)isize/10.0f; 1649 *miny = *maxy - font->lineh*isize/10.0f; 1650 } 1651 } 1652 1653 const unsigned char* fonsGetTextureData(FONScontext* stash, int* width, int* height) 1654 { 1655 if (width != NULL) 1656 *width = stash->params.width; 1657 if (height != NULL) 1658 *height = stash->params.height; 1659 return stash->texData; 1660 } 1661 1662 int fonsValidateTexture(FONScontext* stash, int* dirty) 1663 { 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]; 1669 // Reset dirty rect 1670 stash->dirtyRect[0] = stash->params.width; 1671 stash->dirtyRect[1] = stash->params.height; 1672 stash->dirtyRect[2] = 0; 1673 stash->dirtyRect[3] = 0; 1674 return 1; 1675 } 1676 return 0; 1677 } 1678 1679 void fonsDeleteInternal(FONScontext* stash) 1680 { 1681 int i; 1682 if (stash == NULL) return; 1683 1684 if (stash->params.renderDelete) 1685 stash->params.renderDelete(stash->params.userPtr); 1686 1687 for (i = 0; i < stash->nfonts; ++i) 1688 fons__freeFont(stash->fonts[i]); 1689 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); 1695 free(stash); 1696 } 1697 1698 void fonsSetErrorCallback(FONScontext* stash, void (*callback)(void* uptr, int error, int val), void* uptr) 1699 { 1700 if (stash == NULL) return; 1701 stash->handleError = callback; 1702 stash->errorUptr = uptr; 1703 } 1704 1705 void fonsGetAtlasSize(FONScontext* stash, int* width, int* height) 1706 { 1707 if (stash == NULL) return; 1708 *width = stash->params.width; 1709 *height = stash->params.height; 1710 } 1711 1712 int fonsExpandAtlas(FONScontext* stash, int width, int height) 1713 { 1714 int i, maxy = 0; 1715 unsigned char* data = NULL; 1716 if (stash == NULL) return 0; 1717 1718 width = fons__maxi(width, stash->params.width); 1719 height = fons__maxi(height, stash->params.height); 1720 1721 if (width == stash->params.width && height == stash->params.height) 1722 return 1; 1723 1724 // Flush pending glyphs. 1725 fons__flush(stash); 1726 1727 // Create new texture 1728 if (stash->params.renderResize != NULL) { 1729 if (stash->params.renderResize(stash->params.userPtr, width, height) == 0) 1730 return 0; 1731 } 1732 // Copy old texture data over. 1733 data = (unsigned char*)malloc(width * height); 1734 if (data == NULL) 1735 return 0; 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); 1742 } 1743 if (height > stash->params.height) 1744 memset(&data[stash->params.height * width], 0, (height - stash->params.height) * width); 1745 1746 free(stash->texData); 1747 stash->texData = data; 1748 1749 // Increase atlas size 1750 fons__atlasExpand(stash->atlas, width, height); 1751 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; 1759 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; 1764 1765 return 1; 1766 } 1767 1768 int fonsResetAtlas(FONScontext* stash, int width, int height) 1769 { 1770 int i, j; 1771 if (stash == NULL) return 0; 1772 1773 // Flush pending glyphs. 1774 fons__flush(stash); 1775 1776 // Create new texture 1777 if (stash->params.renderResize != NULL) { 1778 if (stash->params.renderResize(stash->params.userPtr, width, height) == 0) 1779 return 0; 1780 } 1781 1782 // Reset atlas 1783 fons__atlasReset(stash->atlas, width, height); 1784 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); 1789 1790 // Reset dirty rect 1791 stash->dirtyRect[0] = width; 1792 stash->dirtyRect[1] = height; 1793 stash->dirtyRect[2] = 0; 1794 stash->dirtyRect[3] = 0; 1795 1796 // Reset cached glyphs 1797 for (i = 0; i < stash->nfonts; i++) { 1798 FONSfont* font = stash->fonts[i]; 1799 font->nglyphs = 0; 1800 for (j = 0; j < FONS_HASH_LUT_SIZE; j++) 1801 font->lut[j] = -1; 1802 } 1803 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; 1808 1809 // Add white rect at 0,0 for debug drawing. 1810 fons__addWhiteRect(stash, 2,2); 1811 1812 return 1; 1813 } 1814 1815 1816 #endif