| @@ -1,5 +1,5 @@ |
| 1 | // stb_truetype.h - v1.24 - public domain | 1 | // stb_truetype.h - v1.26 - public domain |
| 2 | // authored from 2009-2020 by Sean Barrett / RAD Game Tools | 2 | // authored from 2009-2021 by Sean Barrett / RAD Game Tools |
| 3 | // | 3 | // |
| 4 | // ======================================================================= | 4 | // ======================================================================= |
| 5 | // | 5 | // |
| @@ -53,11 +53,13 @@ · 47 unmodified lines |
| 53 | // Johan Duparc Thomas Fields | 53 | // Johan Duparc Thomas Fields |
| 54 | // Hou Qiming Derek Vinyard | 54 | // Hou Qiming Derek Vinyard |
| 55 | // Rob Loach Cort Stratton | 55 | // Rob Loach Cort Stratton |
| 56 | // Kenney Phillis Jr. Brian Costabile | 56 | // Kenney Phillis Jr. Brian Costabile |
| 57 | // Ken Voskuil (kaesve) | 57 | // Ken Voskuil (kaesve) |
| 58 | // | 58 | // |
| 59 | // VERSION HISTORY | 59 | // VERSION HISTORY |
| 60 | // | 60 | // |
| | 61 | // 1.26 (2021-08-28) fix broken rasterizer |
| | 62 | // 1.25 (2021-07-11) many fixes |
| 61 | // 1.24 (2020-02-05) fix warning | 63 | // 1.24 (2020-02-05) fix warning |
| 62 | // 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) | 64 | // 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) |
| 63 | // 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined | 65 | // 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined |
| @@ -270,8 +272,8 @@ · 206 unmodified lines |
| 270 | //// SAMPLE PROGRAMS | 272 | //// SAMPLE PROGRAMS |
| 271 | //// | 273 | //// |
| 272 | // | 274 | // |
| 273 | // Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless | 275 | // Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless. |
| 274 | // | 276 | // See "tests/truetype_demo_win32.c" for a complete version. |
| 275 | #if 0 | 277 | #if 0 |
| 276 | #define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation | 278 | #define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation |
| 277 | #include "stb_truetype.h" | 279 | #include "stb_truetype.h" |
| @@ -297,6 +299,8 @@ void my_stbtt_initfont(void) · 19 unmodified lines |
| 297 | void my_stbtt_print(float x, float y, char *text) | 299 | void my_stbtt_print(float x, float y, char *text) |
| 298 | { | 300 | { |
| 299 | // assume orthographic projection with units = screen pixels, origin at top left | 301 | // assume orthographic projection with units = screen pixels, origin at top left |
| | 302 | glEnable(GL_BLEND); |
| | 303 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
| 300 | glEnable(GL_TEXTURE_2D); | 304 | glEnable(GL_TEXTURE_2D); |
| 301 | glBindTexture(GL_TEXTURE_2D, ftex); | 305 | glBindTexture(GL_TEXTURE_2D, ftex); |
| 302 | glBegin(GL_QUADS); | 306 | glBegin(GL_QUADS); |
| @@ -304,10 +308,10 @@ void my_stbtt_print(float x, float y, char *text) · 1 unmodified line |
| 304 | if (*text >= 32 && *text < 128) { | 308 | if (*text >= 32 && *text < 128) { |
| 305 | stbtt_aligned_quad q; | 309 | stbtt_aligned_quad q; |
| 306 | stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 | 310 | stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 |
| 307 | glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0); | 311 | glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y0); |
| 308 | glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0); | 312 | glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y0); |
| 309 | glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1); | 313 | glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y1); |
| 310 | glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1); | 314 | glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y1); |
| 311 | } | 315 | } |
| 312 | ++text; | 316 | ++text; |
| 313 | } | 317 | } |
| @@ -717,10 +721,6 @@ struct stbtt_fontinfo · 403 unmodified lines |
| 717 | int loca,head,glyf,hhea,hmtx,kern,gpos,svg; // table locations as offset from start of .ttf | 721 | int loca,head,glyf,hhea,hmtx,kern,gpos,svg; // table locations as offset from start of .ttf |
| 718 | int index_map; // a cmap mapping for our chosen character encoding | 722 | int index_map; // a cmap mapping for our chosen character encoding |
| 719 | int indexToLocFormat; // format needed to map from glyph index to glyph | 723 | int indexToLocFormat; // format needed to map from glyph index to glyph |
| 720 | int underlinePosition; | | |
| 721 | int underlineThickness; | | |
| 722 | char family[128]; | | |
| 723 | char subfamily[128]; | | |
| 724 | | 724 | |
| 725 | stbtt__buf cff; // cff font data | 725 | stbtt__buf cff; // cff font data |
| 726 | stbtt__buf charstrings; // the charstring index | 726 | stbtt__buf charstrings; // the charstring index |
| @@ -857,6 +857,7 @@ STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, s · 130 unmodified lines |
| 857 | STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); | 857 | STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); |
| 858 | // frees the data allocated above | 858 | // frees the data allocated above |
| 859 | | 859 | |
| | 860 | STBTT_DEF unsigned char *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl); |
| 860 | STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg); | 861 | STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg); |
| 861 | STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg); | 862 | STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg); |
| 862 | // fills svg with the character's SVG data. | 863 | // fills svg with the character's SVG data. |
| @@ -1382,9 +1383,7 @@ static int stbtt__get_svg(stbtt_fontinfo *info) · 519 unmodified lines |
| 1382 | static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart) | 1383 | static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart) |
| 1383 | { | 1384 | { |
| 1384 | stbtt_uint32 cmap, t; | 1385 | stbtt_uint32 cmap, t; |
| 1385 | stbtt_int32 i, numTables; | 1386 | stbtt_int32 i,numTables; |
| 1386 | | | |
| 1387 | memset(info, 0, sizeof(*info)); | | |
| 1388 | | 1387 | |
| 1389 | info->data = data; | 1388 | info->data = data; |
| 1390 | info->fontstart = fontstart; | 1389 | info->fontstart = fontstart; |
| @@ -1462,43 +1461,6 @@ static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, in · 71 unmodified lines |
| 1462 | | 1461 | |
| 1463 | info->svg = -1; | 1462 | info->svg = -1; |
| 1464 | | 1463 | |
| 1465 | t = stbtt__find_table(data, fontstart, "post"); | | |
| 1466 | if (t) { | | |
| 1467 | info->underlinePosition = ttSHORT(data+t+8); | | |
| 1468 | info->underlineThickness = ttUSHORT(data+t+10); | | |
| 1469 | } | | |
| 1470 | | | |
| 1471 | t = stbtt__find_table(data, fontstart, "name"); | | |
| 1472 | if (t) { | | |
| 1473 | stbtt_int32 i, platform, name, count, stringOffset, length; | | |
| 1474 | stbtt_uint8 *fc = data; | | |
| 1475 | stbtt_uint32 offset = fontstart; | | |
| 1476 | stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); | | |
| 1477 | char *o, tmp[256]; | | |
| 1478 | | | |
| 1479 | count = ttUSHORT(fc+nm+2); | | |
| 1480 | stringOffset = nm + ttUSHORT(fc+nm+4); | | |
| 1481 | for (i=0; i < count; ++i) { | | |
| 1482 | stbtt_uint32 loc = nm + 6 + 12 * i; | | |
| 1483 | platform = ttUSHORT(fc+loc+0); | | |
| 1484 | if (platform != 0 && platform != 1) | | |
| 1485 | continue; | | |
| 1486 | name = ttUSHORT(fc+loc+6); | | |
| 1487 | if (name != 1 && name != 2) /* FIXME use 16 and 17? */ | | |
| 1488 | continue; | | |
| 1489 | length = ttUSHORT(fc+loc+8); | | |
| 1490 | o = (char*)fc+stringOffset+ttUSHORT(fc+loc+10); | | |
| 1491 | if (platform == 0) { | | |
| 1492 | length = utf16to8((uchar*)tmp, sizeof(tmp), (uchar*)o, length); | | |
| 1493 | o = tmp; | | |
| 1494 | } | | |
| 1495 | if (name == 1) | | |
| 1496 | snprint(info->family, sizeof(info->family), "%.*s", length, o); | | |
| 1497 | else if (name == 2) | | |
| 1498 | snprint(info->subfamily, sizeof(info->subfamily), "%.*s", length, o); | | |
| 1499 | } | | |
| 1500 | } | | |
| 1501 | | | |
| 1502 | // find a cmap encoding table we understand *now* to avoid searching | 1464 | // find a cmap encoding table we understand *now* to avoid searching |
| 1503 | // later. (todo: could make this installable) | 1465 | // later. (todo: could make this installable) |
| 1504 | // the same regardless of glyph. | 1466 | // the same regardless of glyph. |
| @@ -1531,64 +1493,6 @@ static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, in · 26 unmodified lines |
| 1531 | return 1; | 1493 | return 1; |
| 1532 | } | 1494 | } |
| 1533 | | 1495 | |
| 1534 | typedef struct stbtt_code2glyph stbtt_code2glyph; | | |
| 1535 | | | |
| 1536 | struct stbtt_code2glyph { | | |
| 1537 | u32int *code; | | |
| 1538 | u16int *glyph; | | |
| 1539 | int num; | | |
| 1540 | }; | | |
| 1541 | | | |
| 1542 | STBTT_DEF stbtt_code2glyph *stbtt_Code2Glyph(const stbtt_fontinfo *info) { | | |
| 1543 | stbtt_uint8 *data = info->data; | | |
| 1544 | stbtt_uint32 index_map = info->index_map; | | |
| 1545 | stbtt_code2glyph *c2g; | | |
| 1546 | int i; | | |
| 1547 | stbtt_uint16 format = ttUSHORT(data + index_map + 0); | | |
| 1548 | | | |
| 1549 | if(format == 4) // FIXME | | |
| 1550 | return NULL; | | |
| 1551 | | | |
| 1552 | c2g = mallocz(sizeof(*c2g) + info->numGlyphs*6, 1); | | |
| 1553 | c2g->code = (u32int*)(c2g + 1); | | |
| 1554 | c2g->glyph = (u16int*)(c2g->code + info->numGlyphs); | | |
| 1555 | c2g->num = info->numGlyphs; | | |
| 1556 | | | |
| 1557 | if (format == 0) { // apple byte encoding | | |
| 1558 | stbtt_int32 bytes = ttUSHORT(data + index_map + 2); | | |
| 1559 | for(i = 0; i < bytes-6; i++){ | | |
| 1560 | c2g->code[i] = i; | | |
| 1561 | c2g->glyph[i] = ttBYTE(data + index_map + 6 + i); | | |
| 1562 | } | | |
| 1563 | } else if (format == 6) { | | |
| 1564 | stbtt_int32 first = ttUSHORT(data + index_map + 6); | | |
| 1565 | stbtt_int32 count = ttUSHORT(data + index_map + 8); | | |
| 1566 | for(i = 0; i < count; i++){ | | |
| 1567 | c2g->code[i] = first + i; | | |
| 1568 | c2g->glyph[i] = ttUSHORT(data + index_map + 10 + i*2); | | |
| 1569 | } | | |
| 1570 | } else if (format == 2) { | | |
| 1571 | STBTT_assert(format == 2 && 0); // @TODO: high-byte mapping for japanese/chinese/korean | | |
| 1572 | } else if (format == 12 || format == 13) { | | |
| 1573 | stbtt_uint32 ngroups = ttULONG(data+index_map+12); | | |
| 1574 | stbtt_uint32 g, ch; | | |
| 1575 | for(i = 0, g = 0; g < ngroups; g++){ | | |
| 1576 | stbtt_uint32 start_char = ttULONG(data+index_map+16+g*12); | | |
| 1577 | stbtt_uint32 end_char = ttULONG(data+index_map+16+g*12+4); | | |
| 1578 | stbtt_uint32 start_glyph = ttULONG(data+index_map+16+g*12+8); | | |
| 1579 | for(ch = start_char; ch <= end_char; ch++, i++){ | | |
| 1580 | c2g->code[i] = ch; | | |
| 1581 | c2g->glyph[i] = format == 12 ? start_glyph+ch-start_char : start_glyph; | | |
| 1582 | } | | |
| 1583 | } | | |
| 1584 | } else { | | |
| 1585 | // @TODO | | |
| 1586 | STBTT_assert(0); | | |
| 1587 | } | | |
| 1588 | | | |
| 1589 | return c2g; | | |
| 1590 | } | | |
| 1591 | | | |
| 1592 | STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) | 1496 | STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) |
| 1593 | { | 1497 | { |
| 1594 | stbtt_uint8 *data = info->data; | 1498 | stbtt_uint8 *data = info->data; |
| @@ -1640,12 +1544,12 @@ STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codep · 45 unmodified lines |
| 1640 | search += 2; | 1544 | search += 2; |
| 1641 | | 1545 | |
| 1642 | { | 1546 | { |
| 1643 | stbtt_uint16 offset, start; | 1547 | stbtt_uint16 offset, start, last; |
| 1644 | stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); | 1548 | stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); |
| 1645 | | 1549 | |
| 1646 | STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); | | |
| 1647 | start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); | 1550 | start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); |
| 1648 | if (unicode_codepoint < start) | 1551 | last = ttUSHORT(data + endCount + 2*item); |
| | 1552 | if (unicode_codepoint < start || unicode_codepoint > last) |
| 1649 | return 0; | 1553 | return 0; |
| 1650 | | 1554 | |
| 1651 | offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); | 1555 | offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); |
| @@ -1972,7 +1876,7 @@ static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, s · 320 unmodified lines |
| 1972 | if (comp_verts) STBTT_free(comp_verts, info->userdata); | 1876 | if (comp_verts) STBTT_free(comp_verts, info->userdata); |
| 1973 | return 0; | 1877 | return 0; |
| 1974 | } | 1878 | } |
| 1975 | if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); | 1879 | if (num_vertices > 0 && vertices) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); |
| 1976 | STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); | 1880 | STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); |
| 1977 | if (vertices) STBTT_free(vertices, info->userdata); | 1881 | if (vertices) STBTT_free(vertices, info->userdata); |
| 1978 | vertices = tmp; | 1882 | vertices = tmp; |
| @@ -2235,7 +2139,7 @@ static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, st · 256 unmodified lines |
| 2235 | subrs = stbtt__cid_get_glyph_subrs(info, glyph_index); | 2139 | subrs = stbtt__cid_get_glyph_subrs(info, glyph_index); |
| 2236 | has_subrs = 1; | 2140 | has_subrs = 1; |
| 2237 | } | 2141 | } |
| 2238 | // fallthrough | 2142 | // FALLTHROUGH |
| 2239 | case 0x1D: // callgsubr | 2143 | case 0x1D: // callgsubr |
| 2240 | if (sp < 1) return STBTT__CSERR("call(g|)subr stack"); | 2144 | if (sp < 1) return STBTT__CSERR("call(g|)subr stack"); |
| 2241 | v = (int) s[--sp]; | 2145 | v = (int) s[--sp]; |
| @@ -2340,7 +2244,7 @@ static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, st · 98 unmodified lines |
| 2340 | } break; | 2244 | } break; |
| 2341 | | 2245 | |
| 2342 | default: | 2246 | default: |
| 2343 | if (b0 != 255 && b0 != 28 && (b0 < 32 || b0 > 254)) | 2247 | if (b0 != 255 && b0 != 28 && b0 < 32) |
| 2344 | return STBTT__CSERR("reserved operator"); | 2248 | return STBTT__CSERR("reserved operator"); |
| 2345 | | 2249 | |
| 2346 | // push immediate | 2250 | // push immediate |
| @@ -2452,7 +2356,7 @@ STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningent · 105 unmodified lines |
| 2452 | return length; | 2356 | return length; |
| 2453 | } | 2357 | } |
| 2454 | | 2358 | |
| 2455 | static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) | 2359 | static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) |
| 2456 | { | 2360 | { |
| 2457 | stbtt_uint8 *data = info->data + info->kern; | 2361 | stbtt_uint8 *data = info->data + info->kern; |
| 2458 | stbtt_uint32 needle, straw; | 2362 | stbtt_uint32 needle, straw; |
| @@ -2482,247 +2386,225 @@ static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph · 23 unmodified lines |
| 2482 | return 0; | 2386 | return 0; |
| 2483 | } | 2387 | } |
| 2484 | | 2388 | |
| 2485 | static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph) | 2389 | static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph) |
| 2486 | { | 2390 | { |
| 2487 | stbtt_uint16 coverageFormat = ttUSHORT(coverageTable); | 2391 | stbtt_uint16 coverageFormat = ttUSHORT(coverageTable); |
| 2488 | switch(coverageFormat) { | 2392 | switch (coverageFormat) { |
| 2489 | case 1: { | 2393 | case 1: { |
| 2490 | stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2); | 2394 | stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2); |
| 2491 | | 2395 | |
| 2492 | // Binary search. | 2396 | // Binary search. |
| 2493 | stbtt_int32 l=0, r=glyphCount-1, m; | 2397 | stbtt_int32 l=0, r=glyphCount-1, m; |
| 2494 | int straw, needle=glyph; | 2398 | int straw, needle=glyph; |
| 2495 | while (l <= r) { | 2399 | while (l <= r) { |
| 2496 | stbtt_uint8 *glyphArray = coverageTable + 4; | 2400 | stbtt_uint8 *glyphArray = coverageTable + 4; |
| 2497 | stbtt_uint16 glyphID; | 2401 | stbtt_uint16 glyphID; |
| 2498 | m = (l + r) >> 1; | 2402 | m = (l + r) >> 1; |
| 2499 | glyphID = ttUSHORT(glyphArray + 2 * m); | 2403 | glyphID = ttUSHORT(glyphArray + 2 * m); |
| 2500 | straw = glyphID; | 2404 | straw = glyphID; |
| 2501 | if (needle < straw) | 2405 | if (needle < straw) |
| 2502 | r = m - 1; | 2406 | r = m - 1; |
| 2503 | else if (needle > straw) | 2407 | else if (needle > straw) |
| 2504 | l = m + 1; | 2408 | l = m + 1; |
| 2505 | else { | 2409 | else { |
| 2506 | return m; | 2410 | return m; |
| 2507 | } | | |
| 2508 | } | 2411 | } |
| 2509 | } break; | 2412 | } |
| 2510 | | 2413 | break; |
| 2511 | case 2: { | 2414 | } |
| 2512 | stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2); | 2415 | |
| 2513 | stbtt_uint8 *rangeArray = coverageTable + 4; | 2416 | case 2: { |
| 2514 | | 2417 | stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2); |
| 2515 | // Binary search. | 2418 | stbtt_uint8 *rangeArray = coverageTable + 4; |
| 2516 | stbtt_int32 l=0, r=rangeCount-1, m; | 2419 | |
| 2517 | int strawStart, strawEnd, needle=glyph; | 2420 | // Binary search. |
| 2518 | while (l <= r) { | 2421 | stbtt_int32 l=0, r=rangeCount-1, m; |
| 2519 | stbtt_uint8 *rangeRecord; | 2422 | int strawStart, strawEnd, needle=glyph; |
| 2520 | m = (l + r) >> 1; | 2423 | while (l <= r) { |
| 2521 | rangeRecord = rangeArray + 6 * m; | 2424 | stbtt_uint8 *rangeRecord; |
| 2522 | strawStart = ttUSHORT(rangeRecord); | 2425 | m = (l + r) >> 1; |
| 2523 | strawEnd = ttUSHORT(rangeRecord + 2); | 2426 | rangeRecord = rangeArray + 6 * m; |
| 2524 | if (needle < strawStart) | 2427 | strawStart = ttUSHORT(rangeRecord); |
| 2525 | r = m - 1; | 2428 | strawEnd = ttUSHORT(rangeRecord + 2); |
| 2526 | else if (needle > strawEnd) | 2429 | if (needle < strawStart) |
| 2527 | l = m + 1; | 2430 | r = m - 1; |
| 2528 | else { | 2431 | else if (needle > strawEnd) |
| 2529 | stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4); | 2432 | l = m + 1; |
| 2530 | return startCoverageIndex + glyph - strawStart; | 2433 | else { |
| 2531 | } | 2434 | stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4); |
| | 2435 | return startCoverageIndex + glyph - strawStart; |
| 2532 | } | 2436 | } |
| 2533 | } break; | 2437 | } |
| | 2438 | break; |
| | 2439 | } |
| 2534 | | 2440 | |
| 2535 | default: { | 2441 | default: return -1; // unsupported |
| 2536 | // There are no other cases. | 2442 | } |
| 2537 | STBTT_assert(0); | | |
| 2538 | } break; | | |
| 2539 | } | | |
| 2540 | | 2443 | |
| 2541 | return -1; | 2444 | return -1; |
| 2542 | } | 2445 | } |
| 2543 | | 2446 | |
| 2544 | static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph) | 2447 | static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph) |
| 2545 | { | 2448 | { |
| 2546 | stbtt_uint16 classDefFormat = ttUSHORT(classDefTable); | 2449 | stbtt_uint16 classDefFormat = ttUSHORT(classDefTable); |
| 2547 | switch(classDefFormat) | 2450 | switch (classDefFormat) |
| 2548 | { | 2451 | { |
| 2549 | case 1: { | 2452 | case 1: { |
| 2550 | stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2); | 2453 | stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2); |
| 2551 | stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4); | 2454 | stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4); |
| 2552 | stbtt_uint8 *classDef1ValueArray = classDefTable + 6; | 2455 | stbtt_uint8 *classDef1ValueArray = classDefTable + 6; |
| 2553 | | | |
| 2554 | if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount) | | |
| 2555 | return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID)); | | |
| 2556 | | | |
| 2557 | classDefTable = classDef1ValueArray + 2 * glyphCount; | | |
| 2558 | USED(classDefTable); | | |
| 2559 | } break; | | |
| 2560 | | | |
| 2561 | case 2: { | | |
| 2562 | stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2); | | |
| 2563 | stbtt_uint8 *classRangeRecords = classDefTable + 4; | | |
| 2564 | | | |
| 2565 | // Binary search. | | |
| 2566 | stbtt_int32 l=0, r=classRangeCount-1, m; | | |
| 2567 | int strawStart, strawEnd, needle=glyph; | | |
| 2568 | while (l <= r) { | | |
| 2569 | stbtt_uint8 *classRangeRecord; | | |
| 2570 | m = (l + r) >> 1; | | |
| 2571 | classRangeRecord = classRangeRecords + 6 * m; | | |
| 2572 | strawStart = ttUSHORT(classRangeRecord); | | |
| 2573 | strawEnd = ttUSHORT(classRangeRecord + 2); | | |
| 2574 | if (needle < strawStart) | | |
| 2575 | r = m - 1; | | |
| 2576 | else if (needle > strawEnd) | | |
| 2577 | l = m + 1; | | |
| 2578 | else | | |
| 2579 | return (stbtt_int32)ttUSHORT(classRangeRecord + 4); | | |
| 2580 | } | | |
| 2581 | | 2456 | |
| 2582 | classDefTable = classRangeRecords + 6 * classRangeCount; | 2457 | if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount) |
| 2583 | USED(classDefTable); | 2458 | return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID)); |
| 2584 | } break; | 2459 | break; |
| | 2460 | } |
| 2585 | | 2461 | |
| 2586 | default: { | 2462 | case 2: { |
| 2587 | // There are no other cases. | 2463 | stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2); |
| 2588 | STBTT_assert(0); | 2464 | stbtt_uint8 *classRangeRecords = classDefTable + 4; |
| 2589 | } break; | 2465 | |
| 2590 | } | 2466 | // Binary search. |
| | 2467 | stbtt_int32 l=0, r=classRangeCount-1, m; |
| | 2468 | int strawStart, strawEnd, needle=glyph; |
| | 2469 | while (l <= r) { |
| | 2470 | stbtt_uint8 *classRangeRecord; |
| | 2471 | m = (l + r) >> 1; |
| | 2472 | classRangeRecord = classRangeRecords + 6 * m; |
| | 2473 | strawStart = ttUSHORT(classRangeRecord); |
| | 2474 | strawEnd = ttUSHORT(classRangeRecord + 2); |
| | 2475 | if (needle < strawStart) |
| | 2476 | r = m - 1; |
| | 2477 | else if (needle > strawEnd) |
| | 2478 | l = m + 1; |
| | 2479 | else |
| | 2480 | return (stbtt_int32)ttUSHORT(classRangeRecord + 4); |
| | 2481 | } |
| | 2482 | break; |
| | 2483 | } |
| 2591 | | 2484 | |
| 2592 | return -1; | 2485 | default: |
| | 2486 | return -1; // Unsupported definition type, return an error. |
| | 2487 | } |
| | 2488 | |
| | 2489 | // "All glyphs not assigned to a class fall into class 0". (OpenType spec) |
| | 2490 | return 0; |
| 2593 | } | 2491 | } |
| 2594 | | 2492 | |
| 2595 | // Define to STBTT_assert(x) if you want to break on unimplemented formats. | 2493 | // Define to STBTT_assert(x) if you want to break on unimplemented formats. |
| 2596 | #define STBTT_GPOS_TODO_assert(x) | 2494 | #define STBTT_GPOS_TODO_assert(x) |
| 2597 | | 2495 | |
| 2598 | static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) | 2496 | static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) |
| 2599 | { | 2497 | { |
| 2600 | stbtt_uint16 lookupListOffset; | 2498 | stbtt_uint16 lookupListOffset; |
| 2601 | stbtt_uint8 *lookupList; | 2499 | stbtt_uint8 *lookupList; |
| 2602 | stbtt_uint16 lookupCount; | 2500 | stbtt_uint16 lookupCount; |
| 2603 | stbtt_uint8 *data; | 2501 | stbtt_uint8 *data; |
| 2604 | stbtt_int32 i; | 2502 | stbtt_int32 i, sti; |
| 2605 | | 2503 | |
| 2606 | if (!info->gpos) return 0; | 2504 | if (!info->gpos) return 0; |
| 2607 | | 2505 | |
| 2608 | data = info->data + info->gpos; | 2506 | data = info->data + info->gpos; |
| 2609 | | 2507 | |
| 2610 | if (ttUSHORT(data+0) != 1) return 0; // Major version 1 | 2508 | if (ttUSHORT(data+0) != 1) return 0; // Major version 1 |
| 2611 | if (ttUSHORT(data+2) != 0) return 0; // Minor version 0 | 2509 | if (ttUSHORT(data+2) != 0) return 0; // Minor version 0 |
| 2612 | | 2510 | |
| 2613 | lookupListOffset = ttUSHORT(data+8); | 2511 | lookupListOffset = ttUSHORT(data+8); |
| 2614 | lookupList = data + lookupListOffset; | 2512 | lookupList = data + lookupListOffset; |
| 2615 | lookupCount = ttUSHORT(lookupList); | 2513 | lookupCount = ttUSHORT(lookupList); |
| 2616 | | 2514 | |
| 2617 | for (i=0; i<lookupCount; ++i) { | 2515 | for (i=0; i<lookupCount; ++i) { |
| 2618 | stbtt_uint16 lookupOffset = ttUSHORT(lookupList + 2 + 2 * i); | 2516 | stbtt_uint16 lookupOffset = ttUSHORT(lookupList + 2 + 2 * i); |
| 2619 | stbtt_uint8 *lookupTable = lookupList + lookupOffset; | 2517 | stbtt_uint8 *lookupTable = lookupList + lookupOffset; |
| 2620 | | 2518 | |
| 2621 | stbtt_uint16 lookupType = ttUSHORT(lookupTable); | 2519 | stbtt_uint16 lookupType = ttUSHORT(lookupTable); |
| 2622 | stbtt_uint16 subTableCount = ttUSHORT(lookupTable + 4); | 2520 | stbtt_uint16 subTableCount = ttUSHORT(lookupTable + 4); |
| 2623 | stbtt_uint8 *subTableOffsets = lookupTable + 6; | 2521 | stbtt_uint8 *subTableOffsets = lookupTable + 6; |
| 2624 | switch(lookupType) { | 2522 | if (lookupType != 2) // Pair Adjustment Positioning Subtable |
| 2625 | case 2: { // Pair Adjustment Positioning Subtable | 2523 | continue; |
| 2626 | stbtt_int32 sti; | 2524 | |
| 2627 | for (sti=0; sti<subTableCount; sti++) { | 2525 | for (sti=0; sti<subTableCount; sti++) { |
| 2628 | stbtt_uint16 subtableOffset = ttUSHORT(subTableOffsets + 2 * sti); | 2526 | stbtt_uint16 subtableOffset = ttUSHORT(subTableOffsets + 2 * sti); |
| 2629 | stbtt_uint8 *table = lookupTable + subtableOffset; | 2527 | stbtt_uint8 *table = lookupTable + subtableOffset; |
| 2630 | stbtt_uint16 posFormat = ttUSHORT(table); | 2528 | stbtt_uint16 posFormat = ttUSHORT(table); |
| 2631 | stbtt_uint16 coverageOffset = ttUSHORT(table + 2); | 2529 | stbtt_uint16 coverageOffset = ttUSHORT(table + 2); |
| 2632 | stbtt_int32 coverageIndex = stbtt__GetCoverageIndex(table + coverageOffset, glyph1); | 2530 | stbtt_int32 coverageIndex = stbtt__GetCoverageIndex(table + coverageOffset, glyph1); |
| 2633 | if (coverageIndex == -1) continue; | 2531 | if (coverageIndex == -1) continue; |
| 2634 | | 2532 | |
| 2635 | switch (posFormat) { | 2533 | switch (posFormat) { |
| 2636 | case 1: { | 2534 | case 1: { |
| 2637 | stbtt_int32 l, r, m; | 2535 | stbtt_int32 l, r, m; |
| 2638 | int straw, needle; | 2536 | int straw, needle; |
| 2639 | stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); | 2537 | stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); |
| 2640 | stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); | 2538 | stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); |
| 2641 | stbtt_int32 valueRecordPairSizeInBytes = 2; | 2539 | if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats? |
| 2642 | stbtt_uint16 pairSetCount = ttUSHORT(table + 8); | 2540 | stbtt_int32 valueRecordPairSizeInBytes = 2; |
| 2643 | stbtt_uint16 pairPosOffset = ttUSHORT(table + 10 + 2 * coverageIndex); | 2541 | stbtt_uint16 pairSetCount = ttUSHORT(table + 8); |
| 2644 | stbtt_uint8 *pairValueTable = table + pairPosOffset; | 2542 | stbtt_uint16 pairPosOffset = ttUSHORT(table + 10 + 2 * coverageIndex); |
| 2645 | stbtt_uint16 pairValueCount = ttUSHORT(pairValueTable); | 2543 | stbtt_uint8 *pairValueTable = table + pairPosOffset; |
| 2646 | stbtt_uint8 *pairValueArray = pairValueTable + 2; | 2544 | stbtt_uint16 pairValueCount = ttUSHORT(pairValueTable); |
| 2647 | // TODO: Support more formats. | 2545 | stbtt_uint8 *pairValueArray = pairValueTable + 2; |
| 2648 | STBTT_GPOS_TODO_assert(valueFormat1 == 4); | 2546 | |
| 2649 | if (valueFormat1 != 4) return 0; | 2547 | if (coverageIndex >= pairSetCount) return 0; |
| 2650 | STBTT_GPOS_TODO_assert(valueFormat2 == 0); | 2548 | |
| 2651 | if (valueFormat2 != 0) return 0; | 2549 | needle=glyph2; |
| 2652 | | 2550 | r=pairValueCount-1; |
| 2653 | STBTT_assert(coverageIndex < pairSetCount); | 2551 | l=0; |
| 2654 | STBTT__NOTUSED(pairSetCount); | 2552 | |
| 2655 | | 2553 | // Binary search. |
| 2656 | needle=glyph2; | 2554 | while (l <= r) { |
| 2657 | r=pairValueCount-1; | 2555 | stbtt_uint16 secondGlyph; |
| 2658 | l=0; | 2556 | stbtt_uint8 *pairValue; |
| 2659 | | 2557 | m = (l + r) >> 1; |
| 2660 | // Binary search. | 2558 | pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m; |
| 2661 | while (l <= r) { | 2559 | secondGlyph = ttUSHORT(pairValue); |
| 2662 | stbtt_uint16 secondGlyph; | 2560 | straw = secondGlyph; |
| 2663 | stbtt_uint8 *pairValue; | 2561 | if (needle < straw) |
| 2664 | m = (l + r) >> 1; | 2562 | r = m - 1; |
| 2665 | pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m; | 2563 | else if (needle > straw) |
| 2666 | secondGlyph = ttUSHORT(pairValue); | 2564 | l = m + 1; |
| 2667 | straw = secondGlyph; | 2565 | else { |
| 2668 | if (needle < straw) | 2566 | stbtt_int16 xAdvance = ttSHORT(pairValue + 2); |
| 2669 | r = m - 1; | 2567 | return xAdvance; |
| 2670 | else if (needle > straw) | 2568 | } |
| 2671 | l = m + 1; | 2569 | } |
| 2672 | else { | 2570 | } else |
| 2673 | stbtt_int16 xAdvance = ttSHORT(pairValue + 2); | 2571 | return 0; |
| 2674 | return xAdvance; | 2572 | break; |
| 2675 | } | 2573 | } |
| 2676 | } | 2574 | |
| 2677 | } break; | 2575 | case 2: { |
| 2678 | | 2576 | stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); |
| 2679 | case 2: { | 2577 | stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); |
| 2680 | stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); | 2578 | if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats? |
| 2681 | stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); | 2579 | stbtt_uint16 classDef1Offset = ttUSHORT(table + 8); |
| 2682 | | 2580 | stbtt_uint16 classDef2Offset = ttUSHORT(table + 10); |
| 2683 | stbtt_uint16 classDef1Offset = ttUSHORT(table + 8); | 2581 | int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1); |
| 2684 | stbtt_uint16 classDef2Offset = ttUSHORT(table + 10); | 2582 | int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2); |
| 2685 | int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1); | 2583 | |
| 2686 | int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2); | 2584 | stbtt_uint16 class1Count = ttUSHORT(table + 12); |
| 2687 | | 2585 | stbtt_uint16 class2Count = ttUSHORT(table + 14); |
| 2688 | stbtt_uint16 class1Count = ttUSHORT(table + 12); | 2586 | stbtt_uint8 *class1Records, *class2Records; |
| 2689 | stbtt_uint16 class2Count = ttUSHORT(table + 14); | 2587 | stbtt_int16 xAdvance; |
| 2690 | if(glyph1class >= class1Count || glyph2class >= class2Count) | 2588 | |
| 2691 | return 0; | 2589 | if (glyph1class < 0 || glyph1class >= class1Count) return 0; // malformed |
| 2692 | STBTT_assert(glyph1class < class1Count); | 2590 | if (glyph2class < 0 || glyph2class >= class2Count) return 0; // malformed |
| 2693 | STBTT_assert(glyph2class < class2Count); | 2591 | |
| 2694 | | 2592 | class1Records = table + 16; |
| 2695 | // TODO: Support more formats. | 2593 | class2Records = class1Records + 2 * (glyph1class * class2Count); |
| 2696 | STBTT_GPOS_TODO_assert(valueFormat1 == 4); | 2594 | xAdvance = ttSHORT(class2Records + 2 * glyph2class); |
| 2697 | if (valueFormat1 != 4) return 0; | 2595 | return xAdvance; |
| 2698 | STBTT_GPOS_TODO_assert(valueFormat2 == 0); | 2596 | } else |
| 2699 | if (valueFormat2 != 0) return 0; | 2597 | return 0; |
| 2700 | | 2598 | break; |
| 2701 | if (glyph1class >= 0 && glyph1class < class1Count && glyph2class >= 0 && glyph2class < class2Count) { | 2599 | } |
| 2702 | stbtt_uint8 *class1Records = table + 16; | | |
| 2703 | stbtt_uint8 *class2Records = class1Records + 2 * (glyph1class * class2Count); | | |
| 2704 | stbtt_int16 xAdvance = ttSHORT(class2Records + 2 * glyph2class); | | |
| 2705 | return xAdvance; | | |
| 2706 | } | | |
| 2707 | } break; | | |
| 2708 | | | |
| 2709 | default: { | | |
| 2710 | // There are no other cases. | | |
| 2711 | STBTT_assert(0); | | |
| 2712 | break; | | |
| 2713 | }; | | |
| 2714 | } | | |
| 2715 | } | | |
| 2716 | break; | | |
| 2717 | }; | | |
| 2718 | | 2600 | |
| 2719 | default: | 2601 | default: |
| 2720 | // TODO: Implement other stuff. | 2602 | return 0; // Unsupported position format |
| 2721 | break; | 2603 | } |
| 2722 | } | 2604 | } |
| 2723 | } | 2605 | } |
| 2724 | | 2606 | |
| 2725 | return 0; | 2607 | return 0; |
| 2726 | } | 2608 | } |
| 2727 | | 2609 | |
| 2728 | STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2) | 2610 | STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2) |
| @@ -2731,7 +2613,7 @@ STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int · 2 unmodified lines |
| 2731 | | 2613 | |
| 2732 | if (info->gpos) | 2614 | if (info->gpos) |
| 2733 | xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2); | 2615 | xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2); |
| 2734 | if (info->kern && xAdvance == 0) | 2616 | else if (info->kern) |
| 2735 | xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2); | 2617 | xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2); |
| 2736 | | 2618 | |
| 2737 | return xAdvance; | 2619 | return xAdvance; |
| @@ -2789,7 +2671,6 @@ STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, floa · 51 unmodified lines |
| 2789 | | 2671 | |
| 2790 | STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) | 2672 | STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) |
| 2791 | { | 2673 | { |
| 2792 | USED(info); | | |
| 2793 | STBTT_free(v, info->userdata); | 2674 | STBTT_free(v, info->userdata); |
| 2794 | } | 2675 | } |
| 2795 | | 2676 | |
| @@ -3173,14 +3054,30 @@ static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edg · 377 unmodified lines |
| 3173 | | 3054 | |
| 3174 | if (x0 <= x && x1 <= x) | 3055 | if (x0 <= x && x1 <= x) |
| 3175 | scanline[x] += e->direction * (y1-y0); | 3056 | scanline[x] += e->direction * (y1-y0); |
| 3176 | else if (x0 >= x+1 && x1 >= x+1) | 3057 | else if (x0 >= x+1 && x1 >= x+1) { |
| 3177 | {} | 3058 | } else { |
| 3178 | else { | | |
| 3179 | STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); | 3059 | STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); |
| 3180 | scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position | 3060 | scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position |
| 3181 | } | 3061 | } |
| 3182 | } | 3062 | } |
| 3183 | | 3063 | |
| | 3064 | static float stbtt__sized_trapezoid_area(float height, float top_width, float bottom_width) |
| | 3065 | { |
| | 3066 | STBTT_assert(top_width >= 0); |
| | 3067 | STBTT_assert(bottom_width >= 0); |
| | 3068 | return (top_width + bottom_width) / 2.0f * height; |
| | 3069 | } |
| | 3070 | |
| | 3071 | static float stbtt__position_trapezoid_area(float height, float tx0, float tx1, float bx0, float bx1) |
| | 3072 | { |
| | 3073 | return stbtt__sized_trapezoid_area(height, tx1 - tx0, bx1 - bx0); |
| | 3074 | } |
| | 3075 | |
| | 3076 | static float stbtt__sized_triangle_area(float height, float width) |
| | 3077 | { |
| | 3078 | return height * width / 2; |
| | 3079 | } |
| | 3080 | |
| 3184 | static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top) | 3081 | static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top) |
| 3185 | { | 3082 | { |
| 3186 | float y_bottom = y_top+1; | 3083 | float y_bottom = y_top+1; |
| @@ -3235,13 +3132,13 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, · 48 unmodified lines |
| 3235 | float height; | 3132 | float height; |
| 3236 | // simple case, only spans one pixel | 3133 | // simple case, only spans one pixel |
| 3237 | int x = (int) x_top; | 3134 | int x = (int) x_top; |
| 3238 | height = sy1 - sy0; | 3135 | height = (sy1 - sy0) * e->direction; |
| 3239 | STBTT_assert(x >= 0 && x < len); | 3136 | STBTT_assert(x >= 0 && x < len); |
| 3240 | scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height; | 3137 | scanline[x] += stbtt__position_trapezoid_area(height, x_top, x+1.0f, x_bottom, x+1.0f); |
| 3241 | scanline_fill[x] += e->direction * height; // everything right of this pixel is filled | 3138 | scanline_fill[x] += height; // everything right of this pixel is filled |
| 3242 | } else { | 3139 | } else { |
| 3243 | int x,x1,x2; | 3140 | int x,x1,x2; |
| 3244 | float y_crossing, step, sign, area; | 3141 | float y_crossing, y_final, step, sign, area; |
| 3245 | // covers 2+ pixels | 3142 | // covers 2+ pixels |
| 3246 | if (x_top > x_bottom) { | 3143 | if (x_top > x_bottom) { |
| 3247 | // flip scanline vertically; signed area is the same | 3144 | // flip scanline vertically; signed area is the same |
| @@ -3252,32 +3149,81 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, · 4 unmodified lines |
| 3252 | t = x_bottom, x_bottom = x_top, x_top = t; | 3149 | t = x_bottom, x_bottom = x_top, x_top = t; |
| 3253 | dx = -dx; | 3150 | dx = -dx; |
| 3254 | dy = -dy; | 3151 | dy = -dy; |
| 3255 | t = x0, x0 = xb, xb = t; | 3152 | x0 = xb; |
| 3256 | USED(dx); USED(xb); | | |
| 3257 | } | 3153 | } |
| | 3154 | STBTT_assert(dy >= 0); |
| | 3155 | STBTT_assert(dx >= 0); |
| 3258 | | 3156 | |
| 3259 | x1 = (int) x_top; | 3157 | x1 = (int) x_top; |
| 3260 | x2 = (int) x_bottom; | 3158 | x2 = (int) x_bottom; |
| 3261 | // compute intersection with y axis at x1+1 | 3159 | // compute intersection with y axis at x1+1 |
| 3262 | y_crossing = (x1+1 - x0) * dy + y_top; | 3160 | y_crossing = y_top + dy * (x1+1 - x0); |
| | 3161 | |
| | 3162 | // compute intersection with y axis at x2 |
| | 3163 | y_final = y_top + dy * (x2 - x0); |
| | 3164 | |
| | 3165 | // x1 x_top x2 x_bottom |
| | 3166 | // y_top +------|-----+------------+------------+--------|---+------------+ |
| | 3167 | // | | | | | | |
| | 3168 | // | | | | | | |
| | 3169 | // sy0 | Txxxxx|............|............|............|............| |
| | 3170 | // y_crossing | *xxxxx.......|............|............|............| |
| | 3171 | // | | xxxxx..|............|............|............| |
| | 3172 | // | | /- xx*xxxx........|............|............| |
| | 3173 | // | | dy < | xxxxxx..|............|............| |
| | 3174 | // y_final | | \- | xx*xxx.........|............| |
| | 3175 | // sy1 | | | | xxxxxB...|............| |
| | 3176 | // | | | | | | |
| | 3177 | // | | | | | | |
| | 3178 | // y_bottom +------------+------------+------------+------------+------------+ |
| | 3179 | // |
| | 3180 | // goal is to measure the area covered by '.' in each pixel |
| | 3181 | |
| | 3182 | // if x2 is right at the right edge of x1, y_crossing can blow up, github #1057 |
| | 3183 | // @TODO: maybe test against sy1 rather than y_bottom? |
| | 3184 | if (y_crossing > y_bottom) |
| | 3185 | y_crossing = y_bottom; |
| 3263 | | 3186 | |
| 3264 | sign = e->direction; | 3187 | sign = e->direction; |
| 3265 | // area of the rectangle covered from y0..y_crossing | 3188 | |
| | 3189 | // area of the rectangle covered from sy0..y_crossing |
| 3266 | area = sign * (y_crossing-sy0); | 3190 | area = sign * (y_crossing-sy0); |
| 3267 | // area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing) | | |
| 3268 | scanline[x1] += area * (1-((x_top - x1)+(x1+1-x1))/2); | | |
| 3269 | | 3191 | |
| 3270 | step = sign * dy; | 3192 | // area of the triangle (x_top,sy0), (x1+1,sy0), (x1+1,y_crossing) |
| | 3193 | scanline[x1] += stbtt__sized_triangle_area(area, x1+1 - x_top); |
| | 3194 | |
| | 3195 | // check if final y_crossing is blown up; no test case for this |
| | 3196 | if (y_final > y_bottom) { |
| | 3197 | y_final = y_bottom; |
| | 3198 | dy = (y_final - y_crossing ) / (x2 - (x1+1)); // if denom=0, y_final = y_crossing, so y_final <= y_bottom |
| | 3199 | } |
| | 3200 | |
| | 3201 | // in second pixel, area covered by line segment found in first pixel |
| | 3202 | // is always a rectangle 1 wide * the height of that line segment; this |
| | 3203 | // is exactly what the variable 'area' stores. it also gets a contribution |
| | 3204 | // from the line segment within it. the THIRD pixel will get the first |
| | 3205 | // pixel's rectangle contribution, the second pixel's rectangle contribution, |
| | 3206 | // and its own contribution. the 'own contribution' is the same in every pixel except |
| | 3207 | // the leftmost and rightmost, a trapezoid that slides down in each pixel. |
| | 3208 | // the second pixel's contribution to the third pixel will be the |
| | 3209 | // rectangle 1 wide times the height change in the second pixel, which is dy. |
| | 3210 | |
| | 3211 | step = sign * dy * 1; // dy is dy/dx, change in y for every 1 change in x, |
| | 3212 | // which multiplied by 1-pixel-width is how much pixel area changes for each step in x |
| | 3213 | // so the area advances by 'step' every time |
| | 3214 | |
| 3271 | for (x = x1+1; x < x2; ++x) { | 3215 | for (x = x1+1; x < x2; ++x) { |
| 3272 | scanline[x] += area + step/2; | 3216 | scanline[x] += area + step/2; // area of trapezoid is 1*step/2 |
| 3273 | area += step; | 3217 | area += step; |
| 3274 | } | 3218 | } |
| 3275 | y_crossing += dy * (x2 - (x1+1)); | 3219 | STBTT_assert(STBTT_fabs(area) <= 1.01f); // accumulated error from area += step unless we round step down |
| | 3220 | STBTT_assert(sy1 > y_final-0.01f); |
| 3276 | | 3221 | |
| 3277 | STBTT_assert(STBTT_fabs(area) <= 1.01f); | 3222 | // area covered in the last pixel is the rectangle from all the pixels to the left, |
| 3278 | | 3223 | // plus the trapezoid filled by the line segment in this pixel all the way to the right edge |
| 3279 | scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (sy1-y_crossing); | 3224 | scanline[x2] += area + sign * stbtt__position_trapezoid_area(sy1-y_final, (float) x2, x2+1.0f, x_bottom, x2+1.0f); |
| 3280 | | 3225 | |
| | 3226 | // the rest of the line is filled based on the total height of the line segment in this pixel |
| 3281 | scanline_fill[x2] += sign * (sy1-sy0); | 3227 | scanline_fill[x2] += sign * (sy1-sy0); |
| 3282 | } | 3228 | } |
| 3283 | } else { | 3229 | } else { |
| @@ -3285,6 +3231,9 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, · 1 unmodified line |
| 3285 | // clipping logic. since this does not match the intended use | 3231 | // clipping logic. since this does not match the intended use |
| 3286 | // of this library, we use a different, very slow brute | 3232 | // of this library, we use a different, very slow brute |
| 3287 | // force implementation | 3233 | // force implementation |
| | 3234 | // note though that this does happen some of the time because |
| | 3235 | // x_top and x_bottom can be extrapolated at the top & bottom of |
| | 3236 | // the shape and actually lie outside the bounding box |
| 3288 | int x; | 3237 | int x; |
| 3289 | for (x=0; x < len; ++x) { | 3238 | for (x=0; x < len; ++x) { |
| 3290 | // cases: | 3239 | // cases: |
| @@ -4521,15 +4470,14 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex · 1230 unmodified lines |
| 4521 | float y_frac; | 4470 | float y_frac; |
| 4522 | int winding = 0; | 4471 | int winding = 0; |
| 4523 | | 4472 | |
| 4524 | orig[0] = x; | | |
| 4525 | orig[1] = y; | | |
| 4526 | | | |
| 4527 | // make sure y never passes through a vertex of the shape | 4473 | // make sure y never passes through a vertex of the shape |
| 4528 | y_frac = (float) STBTT_fmod(y, 1.0f); | 4474 | y_frac = (float) STBTT_fmod(y, 1.0f); |
| 4529 | if (y_frac < 0.01f) | 4475 | if (y_frac < 0.01f) |
| 4530 | y += 0.01f; | 4476 | y += 0.01f; |
| 4531 | else if (y_frac > 0.99f) | 4477 | else if (y_frac > 0.99f) |
| 4532 | y -= 0.01f; | 4478 | y -= 0.01f; |
| | 4479 | |
| | 4480 | orig[0] = x; |
| 4533 | orig[1] = y; | 4481 | orig[1] = y; |
| 4534 | | 4482 | |
| 4535 | // test a ray from (-infinity,y) to (x,y) | 4483 | // test a ray from (-infinity,y) to (x,y) |
| @@ -4591,35 +4539,35 @@ static float stbtt__cuberoot( float x ) · 55 unmodified lines |
| 4591 | return (float) STBTT_pow( x,1.0f/3.0f); | 4539 | return (float) STBTT_pow( x,1.0f/3.0f); |
| 4592 | } | 4540 | } |
| 4593 | | 4541 | |
| 4594 | // x^3 + c*x^2 + b*x + a = 0 | 4542 | // x^3 + a*x^2 + b*x + c = 0 |
| 4595 | static int stbtt__solve_cubic(float a, float b, float c, float* r) | 4543 | static int stbtt__solve_cubic(float a, float b, float c, float* r) |
| 4596 | { | 4544 | { |
| 4597 | float s = -a / 3; | 4545 | float s = -a / 3; |
| 4598 | float p = b - a*a / 3; | 4546 | float p = b - a*a / 3; |
| 4599 | float q = a * (2*a*a - 9*b) / 27 + c; | 4547 | float q = a * (2*a*a - 9*b) / 27 + c; |
| 4600 | float p3 = p*p*p; | 4548 | float p3 = p*p*p; |
| 4601 | float d = q*q + 4*p3 / 27; | 4549 | float d = q*q + 4*p3 / 27; |
| 4602 | if (d >= 0) { | 4550 | if (d >= 0) { |
| 4603 | float z = (float) STBTT_sqrt(d); | 4551 | float z = (float) STBTT_sqrt(d); |
| 4604 | float u = (-q + z) / 2; | 4552 | float u = (-q + z) / 2; |
| 4605 | float v = (-q - z) / 2; | 4553 | float v = (-q - z) / 2; |
| 4606 | u = stbtt__cuberoot(u); | 4554 | u = stbtt__cuberoot(u); |
| 4607 | v = stbtt__cuberoot(v); | 4555 | v = stbtt__cuberoot(v); |
| 4608 | r[0] = s + u + v; | 4556 | r[0] = s + u + v; |
| 4609 | return 1; | 4557 | return 1; |
| 4610 | } else { | 4558 | } else { |
| 4611 | float u = (float) STBTT_sqrt(-p/3); | 4559 | float u = (float) STBTT_sqrt(-p/3); |
| 4612 | float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative | 4560 | float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative |
| 4613 | float m = (float) STBTT_cos(v); | 4561 | float m = (float) STBTT_cos(v); |
| 4614 | float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f; | 4562 | float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f; |
| 4615 | r[0] = s + u * 2 * m; | 4563 | r[0] = s + u * 2 * m; |
| 4616 | r[1] = s - u * (m + n); | 4564 | r[1] = s - u * (m + n); |
| 4617 | r[2] = s - u * (m - n); | 4565 | r[2] = s - u * (m - n); |
| 4618 | | 4566 | |
| 4619 | //STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f); // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe? | 4567 | //STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f); // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe? |
| 4620 | //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f); | 4568 | //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f); |
| 4621 | //STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f); | 4569 | //STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f); |
| 4622 | return 3; | 4570 | return 3; |
| 4623 | } | 4571 | } |
| 4624 | } | 4572 | } |
| 4625 | | 4573 | |
| @@ -4696,18 +4644,17 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc · 70 unmodified lines |
| 4696 | for (i=0; i < num_verts; ++i) { | 4644 | for (i=0; i < num_verts; ++i) { |
| 4697 | float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; | 4645 | float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; |
| 4698 | | 4646 | |
| 4699 | // check against every point here rather than inside line/curve primitives -- @TODO: wrong if multiple 'moves' in a row produce a garbage point, and given culling, probably more efficient to do within line/curve | 4647 | if (verts[i].type == STBTT_vline && precompute[i] != 0.0f) { |
| 4700 | float dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); | | |
| 4701 | if (dist2 < min_dist*min_dist) | | |
| 4702 | min_dist = (float) STBTT_sqrt(dist2); | | |
| 4703 | | | |
| 4704 | if (verts[i].type == STBTT_vline) { | | |
| 4705 | float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y; | 4648 | float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y; |
| 4706 | | 4649 | |
| | 4650 | float dist,dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); |
| | 4651 | if (dist2 < min_dist*min_dist) |
| | 4652 | min_dist = (float) STBTT_sqrt(dist2); |
| | 4653 | |
| 4707 | // coarse culling against bbox | 4654 | // coarse culling against bbox |
| 4708 | //if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist && | 4655 | //if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist && |
| 4709 | // sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist) | 4656 | // sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist) |
| 4710 | float dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i]; | 4657 | dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i]; |
| 4711 | STBTT_assert(i != 0); | 4658 | STBTT_assert(i != 0); |
| 4712 | if (dist < min_dist) { | 4659 | if (dist < min_dist) { |
| 4713 | // check position along line | 4660 | // check position along line |
| @@ -4734,7 +4681,8 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc · 20 unmodified lines |
| 4734 | float ax = x1-x0, ay = y1-y0; | 4681 | float ax = x1-x0, ay = y1-y0; |
| 4735 | float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; | 4682 | float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; |
| 4736 | float mx = x0 - sx, my = y0 - sy; | 4683 | float mx = x0 - sx, my = y0 - sy; |
| 4737 | float res[3],px,py,t,it; | 4684 | float res[3] = {0.f,0.f,0.f}; |
| | 4685 | float px,py,t,it,dist2; |
| 4738 | float a_inv = precompute[i]; | 4686 | float a_inv = precompute[i]; |
| 4739 | if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula | 4687 | if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula |
| 4740 | float a = 3*(ax*bx + ay*by); | 4688 | float a = 3*(ax*bx + ay*by); |
| @@ -4761,6 +4709,10 @@ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float sc · 20 unmodified lines |
| 4761 | float d = (mx*ax+my*ay) * a_inv; | 4709 | float d = (mx*ax+my*ay) * a_inv; |
| 4762 | num = stbtt__solve_cubic(b, c, d, res); | 4710 | num = stbtt__solve_cubic(b, c, d, res); |
| 4763 | } | 4711 | } |
| | 4712 | dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); |
| | 4713 | if (dist2 < min_dist*min_dist) |
| | 4714 | min_dist = (float) STBTT_sqrt(dist2); |
| | 4715 | |
| 4764 | if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) { | 4716 | if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) { |
| 4765 | t = res[0], it = 1.0f - t; | 4717 | t = res[0], it = 1.0f - t; |
| 4766 | px = it*it*x0 + 2*t*it*x1 + t*t*x2; | 4718 | px = it*it*x0 + 2*t*it*x1 + t*t*x2; |
| @@ -5020,6 +4972,12 @@ STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const · 253 unmodified lines |
| 5020 | | 4972 | |
| 5021 | // FULL VERSION HISTORY | 4973 | // FULL VERSION HISTORY |
| 5022 | // | 4974 | // |
| | 4975 | // 1.25 (2021-07-11) many fixes |
| | 4976 | // 1.24 (2020-02-05) fix warning |
| | 4977 | // 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) |
| | 4978 | // 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined |
| | 4979 | // 1.21 (2019-02-25) fix warning |
| | 4980 | // 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() |
| 5023 | // 1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod | 4981 | // 1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod |
| 5024 | // 1.18 (2018-01-29) add missing function | 4982 | // 1.18 (2018-01-29) add missing function |
| 5025 | // 1.17 (2017-07-23) make more arguments const; doc fix | 4983 | // 1.17 (2017-07-23) make more arguments const; doc fix |