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