| @@ -1,5 +1,5 @@ |
| 1 | | −// stb_truetype.h - v1.24 - public domain |
| 2 | | −// authored from 2009-2020 by Sean Barrett / RAD Game Tools |
| 1 | +// stb_truetype.h - v1.26 - public domain |
| 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 | 53 | // Johan Duparc Thomas Fields |
| 54 | 54 | // Hou Qiming Derek Vinyard |
| 55 | 55 | // Rob Loach Cort Stratton |
| 56 | | −// Kenney Phillis Jr. Brian Costabile |
| 57 | | −// Ken Voskuil (kaesve) |
| 56 | +// Kenney Phillis Jr. Brian Costabile |
| 57 | +// Ken Voskuil (kaesve) |
| 58 | 58 | // |
| 59 | 59 | // VERSION HISTORY |
| 60 | 60 | // |
| 61 | +// 1.26 (2021-08-28) fix broken rasterizer |
| 62 | +// 1.25 (2021-07-11) many fixes |
| 61 | 63 | // 1.24 (2020-02-05) fix warning |
| 62 | 64 | // 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) |
| 63 | 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 | 272 | //// SAMPLE PROGRAMS |
| 271 | 273 | //// |
| 272 | 274 | // |
| 273 | | −// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless |
| 274 | | −// |
| 275 | +// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless. |
| 276 | +// See "tests/truetype_demo_win32.c" for a complete version. |
| 275 | 277 | #if 0 |
| 276 | 278 | #define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation |
| 277 | 279 | #include "stb_truetype.h" |
| @@ -297,6 +299,8 @@ void my_stbtt_initfont(void) · 19 unmodified lines |
| 297 | 299 | void my_stbtt_print(float x, float y, char *text) |
| 298 | 300 | { |
| 299 | 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 | 304 | glEnable(GL_TEXTURE_2D); |
| 301 | 305 | glBindTexture(GL_TEXTURE_2D, ftex); |
| 302 | 306 | glBegin(GL_QUADS); |
| @@ -304,10 +308,10 @@ void my_stbtt_print(float x, float y, char *text) · 1 unmodified line |
| 304 | 308 | if (*text >= 32 && *text < 128) { |
| 305 | 309 | stbtt_aligned_quad q; |
| 306 | 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); |
| 308 | | − glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0); |
| 309 | | − glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1); |
| 310 | | − glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1); |
| 311 | + glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y0); |
| 312 | + glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y0); |
| 313 | + glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y1); |
| 314 | + glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y1); |
| 311 | 315 | } |
| 312 | 316 | ++text; |
| 313 | 317 | } |
| @@ -717,10 +721,6 @@ struct stbtt_fontinfo · 403 unmodified lines |
| 717 | 721 | int loca,head,glyf,hhea,hmtx,kern,gpos,svg; // table locations as offset from start of .ttf |
| 718 | 722 | int index_map; // a cmap mapping for our chosen character encoding |
| 719 | 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 | 725 | stbtt__buf cff; // cff font data |
| 726 | 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 | 857 | STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); |
| 858 | 858 | // frees the data allocated above |
| 859 | 859 | |
| 860 | +STBTT_DEF unsigned char *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl); |
| 860 | 861 | STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg); |
| 861 | 862 | STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg); |
| 862 | 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 | 1383 | static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart) |
| 1383 | 1384 | { |
| 1384 | 1385 | stbtt_uint32 cmap, t; |
| 1385 | | − stbtt_int32 i, numTables; |
| 1386 | | − |
| 1387 | | − memset(info, 0, sizeof(*info)); |
| 1386 | + stbtt_int32 i,numTables; |
| 1388 | 1387 | |
| 1389 | 1388 | info->data = data; |
| 1390 | 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 | 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 | 1464 | // find a cmap encoding table we understand *now* to avoid searching |
| 1503 | 1465 | // later. (todo: could make this installable) |
| 1504 | 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 | 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 | 1496 | STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) |
| 1593 | 1497 | { |
| 1594 | 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 | 1544 | search += 2; |
| 1641 | 1545 | |
| 1642 | 1546 | { |
| 1643 | | − stbtt_uint16 offset, start; |
| 1547 | + stbtt_uint16 offset, start, last; |
| 1644 | 1548 | stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); |
| 1645 | 1549 | |
| 1646 | | − STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); |
| 1647 | 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 | 1553 | return 0; |
| 1650 | 1554 | |
| 1651 | 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 | 1876 | if (comp_verts) STBTT_free(comp_verts, info->userdata); |
| 1973 | 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 | 1880 | STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); |
| 1977 | 1881 | if (vertices) STBTT_free(vertices, info->userdata); |
| 1978 | 1882 | vertices = tmp; |
| @@ -2235,7 +2139,7 @@ static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, st · 256 unmodified lines |
| 2235 | 2139 | subrs = stbtt__cid_get_glyph_subrs(info, glyph_index); |
| 2236 | 2140 | has_subrs = 1; |
| 2237 | 2141 | } |
| 2238 | | − // fallthrough |
| 2142 | + // FALLTHROUGH |
| 2239 | 2143 | case 0x1D: // callgsubr |
| 2240 | 2144 | if (sp < 1) return STBTT__CSERR("call(g|)subr stack"); |
| 2241 | 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 | 2244 | } break; |
| 2341 | 2245 | |
| 2342 | 2246 | default: |
| 2343 | | − if (b0 != 255 && b0 != 28 && (b0 < 32 || b0 > 254)) |
| 2247 | + if (b0 != 255 && b0 != 28 && b0 < 32) |
| 2344 | 2248 | return STBTT__CSERR("reserved operator"); |
| 2345 | 2249 | |
| 2346 | 2250 | // push immediate |
| @@ -2452,7 +2356,7 @@ STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningent · 105 unmodified lines |
| 2452 | 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 | 2361 | stbtt_uint8 *data = info->data + info->kern; |
| 2458 | 2362 | stbtt_uint32 needle, straw; |
| @@ -2482,247 +2386,225 @@ static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph · 23 unmodified lines |
| 2482 | 2386 | return 0; |
| 2483 | 2387 | } |
| 2484 | 2388 | |
| 2485 | | −static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph) |
| 2486 | | −{ |
| 2487 | | − stbtt_uint16 coverageFormat = ttUSHORT(coverageTable); |
| 2488 | | − switch(coverageFormat) { |
| 2489 | | − case 1: { |
| 2490 | | − stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2); |
| 2491 | | − |
| 2492 | | − // Binary search. |
| 2493 | | − stbtt_int32 l=0, r=glyphCount-1, m; |
| 2494 | | − int straw, needle=glyph; |
| 2495 | | − while (l <= r) { |
| 2496 | | − stbtt_uint8 *glyphArray = coverageTable + 4; |
| 2497 | | − stbtt_uint16 glyphID; |
| 2498 | | − m = (l + r) >> 1; |
| 2499 | | − glyphID = ttUSHORT(glyphArray + 2 * m); |
| 2500 | | − straw = glyphID; |
| 2501 | | − if (needle < straw) |
| 2502 | | − r = m - 1; |
| 2503 | | − else if (needle > straw) |
| 2504 | | − l = m + 1; |
| 2505 | | − else { |
| 2506 | | − return m; |
| 2507 | | − } |
| 2389 | +static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph) |
| 2390 | +{ |
| 2391 | + stbtt_uint16 coverageFormat = ttUSHORT(coverageTable); |
| 2392 | + switch (coverageFormat) { |
| 2393 | + case 1: { |
| 2394 | + stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2); |
| 2395 | + |
| 2396 | + // Binary search. |
| 2397 | + stbtt_int32 l=0, r=glyphCount-1, m; |
| 2398 | + int straw, needle=glyph; |
| 2399 | + while (l <= r) { |
| 2400 | + stbtt_uint8 *glyphArray = coverageTable + 4; |
| 2401 | + stbtt_uint16 glyphID; |
| 2402 | + m = (l + r) >> 1; |
| 2403 | + glyphID = ttUSHORT(glyphArray + 2 * m); |
| 2404 | + straw = glyphID; |
| 2405 | + if (needle < straw) |
| 2406 | + r = m - 1; |
| 2407 | + else if (needle > straw) |
| 2408 | + l = m + 1; |
| 2409 | + else { |
| 2410 | + return m; |
| 2508 | 2411 | } |
| 2509 | | − } break; |
| 2510 | | − |
| 2511 | | − case 2: { |
| 2512 | | − stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2); |
| 2513 | | − stbtt_uint8 *rangeArray = coverageTable + 4; |
| 2514 | | − |
| 2515 | | − // Binary search. |
| 2516 | | − stbtt_int32 l=0, r=rangeCount-1, m; |
| 2517 | | − int strawStart, strawEnd, needle=glyph; |
| 2518 | | − while (l <= r) { |
| 2519 | | − stbtt_uint8 *rangeRecord; |
| 2520 | | − m = (l + r) >> 1; |
| 2521 | | − rangeRecord = rangeArray + 6 * m; |
| 2522 | | − strawStart = ttUSHORT(rangeRecord); |
| 2523 | | − strawEnd = ttUSHORT(rangeRecord + 2); |
| 2524 | | − if (needle < strawStart) |
| 2525 | | − r = m - 1; |
| 2526 | | − else if (needle > strawEnd) |
| 2527 | | − l = m + 1; |
| 2528 | | − else { |
| 2529 | | − stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4); |
| 2530 | | − return startCoverageIndex + glyph - strawStart; |
| 2531 | | − } |
| 2412 | + } |
| 2413 | + break; |
| 2414 | + } |
| 2415 | + |
| 2416 | + case 2: { |
| 2417 | + stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2); |
| 2418 | + stbtt_uint8 *rangeArray = coverageTable + 4; |
| 2419 | + |
| 2420 | + // Binary search. |
| 2421 | + stbtt_int32 l=0, r=rangeCount-1, m; |
| 2422 | + int strawStart, strawEnd, needle=glyph; |
| 2423 | + while (l <= r) { |
| 2424 | + stbtt_uint8 *rangeRecord; |
| 2425 | + m = (l + r) >> 1; |
| 2426 | + rangeRecord = rangeArray + 6 * m; |
| 2427 | + strawStart = ttUSHORT(rangeRecord); |
| 2428 | + strawEnd = ttUSHORT(rangeRecord + 2); |
| 2429 | + if (needle < strawStart) |
| 2430 | + r = m - 1; |
| 2431 | + else if (needle > strawEnd) |
| 2432 | + l = m + 1; |
| 2433 | + else { |
| 2434 | + stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4); |
| 2435 | + return startCoverageIndex + glyph - strawStart; |
| 2532 | 2436 | } |
| 2533 | | − } break; |
| 2437 | + } |
| 2438 | + break; |
| 2439 | + } |
| 2534 | 2440 | |
| 2535 | | − default: { |
| 2536 | | − // There are no other cases. |
| 2537 | | − STBTT_assert(0); |
| 2538 | | − } break; |
| 2539 | | − } |
| 2441 | + default: return -1; // unsupported |
| 2442 | + } |
| 2540 | 2443 | |
| 2541 | | − return -1; |
| 2444 | + return -1; |
| 2542 | 2445 | } |
| 2543 | 2446 | |
| 2544 | 2447 | static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph) |
| 2545 | 2448 | { |
| 2546 | | − stbtt_uint16 classDefFormat = ttUSHORT(classDefTable); |
| 2547 | | − switch(classDefFormat) |
| 2548 | | − { |
| 2549 | | − case 1: { |
| 2550 | | − stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2); |
| 2551 | | − stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4); |
| 2552 | | − 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 | | − } |
| 2449 | + stbtt_uint16 classDefFormat = ttUSHORT(classDefTable); |
| 2450 | + switch (classDefFormat) |
| 2451 | + { |
| 2452 | + case 1: { |
| 2453 | + stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2); |
| 2454 | + stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4); |
| 2455 | + stbtt_uint8 *classDef1ValueArray = classDefTable + 6; |
| 2581 | 2456 | |
| 2582 | | − classDefTable = classRangeRecords + 6 * classRangeCount; |
| 2583 | | − USED(classDefTable); |
| 2584 | | − } break; |
| 2457 | + if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount) |
| 2458 | + return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID)); |
| 2459 | + break; |
| 2460 | + } |
| 2585 | 2461 | |
| 2586 | | − default: { |
| 2587 | | − // There are no other cases. |
| 2588 | | − STBTT_assert(0); |
| 2589 | | − } break; |
| 2590 | | − } |
| 2462 | + case 2: { |
| 2463 | + stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2); |
| 2464 | + stbtt_uint8 *classRangeRecords = classDefTable + 4; |
| 2465 | + |
| 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 | 2493 | // Define to STBTT_assert(x) if you want to break on unimplemented formats. |
| 2596 | 2494 | #define STBTT_GPOS_TODO_assert(x) |
| 2597 | 2495 | |
| 2598 | | −static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) |
| 2599 | | −{ |
| 2600 | | − stbtt_uint16 lookupListOffset; |
| 2601 | | − stbtt_uint8 *lookupList; |
| 2602 | | − stbtt_uint16 lookupCount; |
| 2603 | | − stbtt_uint8 *data; |
| 2604 | | − stbtt_int32 i; |
| 2605 | | − |
| 2606 | | − if (!info->gpos) return 0; |
| 2607 | | − |
| 2608 | | − data = info->data + info->gpos; |
| 2609 | | − |
| 2610 | | − if (ttUSHORT(data+0) != 1) return 0; // Major version 1 |
| 2611 | | − if (ttUSHORT(data+2) != 0) return 0; // Minor version 0 |
| 2612 | | − |
| 2613 | | − lookupListOffset = ttUSHORT(data+8); |
| 2614 | | − lookupList = data + lookupListOffset; |
| 2615 | | − lookupCount = ttUSHORT(lookupList); |
| 2616 | | − |
| 2617 | | − for (i=0; i<lookupCount; ++i) { |
| 2618 | | − stbtt_uint16 lookupOffset = ttUSHORT(lookupList + 2 + 2 * i); |
| 2619 | | − stbtt_uint8 *lookupTable = lookupList + lookupOffset; |
| 2620 | | − |
| 2621 | | − stbtt_uint16 lookupType = ttUSHORT(lookupTable); |
| 2622 | | − stbtt_uint16 subTableCount = ttUSHORT(lookupTable + 4); |
| 2623 | | − stbtt_uint8 *subTableOffsets = lookupTable + 6; |
| 2624 | | − switch(lookupType) { |
| 2625 | | − case 2: { // Pair Adjustment Positioning Subtable |
| 2626 | | − stbtt_int32 sti; |
| 2627 | | − for (sti=0; sti<subTableCount; sti++) { |
| 2628 | | − stbtt_uint16 subtableOffset = ttUSHORT(subTableOffsets + 2 * sti); |
| 2629 | | − stbtt_uint8 *table = lookupTable + subtableOffset; |
| 2630 | | − stbtt_uint16 posFormat = ttUSHORT(table); |
| 2631 | | − stbtt_uint16 coverageOffset = ttUSHORT(table + 2); |
| 2632 | | − stbtt_int32 coverageIndex = stbtt__GetCoverageIndex(table + coverageOffset, glyph1); |
| 2633 | | − if (coverageIndex == -1) continue; |
| 2634 | | − |
| 2635 | | − switch (posFormat) { |
| 2636 | | − case 1: { |
| 2637 | | − stbtt_int32 l, r, m; |
| 2638 | | − int straw, needle; |
| 2639 | | − stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); |
| 2640 | | − stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); |
| 2641 | | − stbtt_int32 valueRecordPairSizeInBytes = 2; |
| 2642 | | − stbtt_uint16 pairSetCount = ttUSHORT(table + 8); |
| 2643 | | − stbtt_uint16 pairPosOffset = ttUSHORT(table + 10 + 2 * coverageIndex); |
| 2644 | | − stbtt_uint8 *pairValueTable = table + pairPosOffset; |
| 2645 | | − stbtt_uint16 pairValueCount = ttUSHORT(pairValueTable); |
| 2646 | | − stbtt_uint8 *pairValueArray = pairValueTable + 2; |
| 2647 | | − // TODO: Support more formats. |
| 2648 | | − STBTT_GPOS_TODO_assert(valueFormat1 == 4); |
| 2649 | | − if (valueFormat1 != 4) return 0; |
| 2650 | | − STBTT_GPOS_TODO_assert(valueFormat2 == 0); |
| 2651 | | − if (valueFormat2 != 0) return 0; |
| 2652 | | − |
| 2653 | | − STBTT_assert(coverageIndex < pairSetCount); |
| 2654 | | − STBTT__NOTUSED(pairSetCount); |
| 2655 | | − |
| 2656 | | − needle=glyph2; |
| 2657 | | − r=pairValueCount-1; |
| 2658 | | − l=0; |
| 2659 | | − |
| 2660 | | − // Binary search. |
| 2661 | | − while (l <= r) { |
| 2662 | | − stbtt_uint16 secondGlyph; |
| 2663 | | − stbtt_uint8 *pairValue; |
| 2664 | | − m = (l + r) >> 1; |
| 2665 | | − pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m; |
| 2666 | | − secondGlyph = ttUSHORT(pairValue); |
| 2667 | | − straw = secondGlyph; |
| 2668 | | − if (needle < straw) |
| 2669 | | − r = m - 1; |
| 2670 | | − else if (needle > straw) |
| 2671 | | − l = m + 1; |
| 2672 | | − else { |
| 2673 | | − stbtt_int16 xAdvance = ttSHORT(pairValue + 2); |
| 2674 | | − return xAdvance; |
| 2675 | | − } |
| 2676 | | − } |
| 2677 | | − } break; |
| 2678 | | − |
| 2679 | | − case 2: { |
| 2680 | | − stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); |
| 2681 | | − stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); |
| 2682 | | − |
| 2683 | | − stbtt_uint16 classDef1Offset = ttUSHORT(table + 8); |
| 2684 | | − stbtt_uint16 classDef2Offset = ttUSHORT(table + 10); |
| 2685 | | − int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1); |
| 2686 | | − int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2); |
| 2687 | | − |
| 2688 | | − stbtt_uint16 class1Count = ttUSHORT(table + 12); |
| 2689 | | − stbtt_uint16 class2Count = ttUSHORT(table + 14); |
| 2690 | | − if(glyph1class >= class1Count || glyph2class >= class2Count) |
| 2691 | | − return 0; |
| 2692 | | − STBTT_assert(glyph1class < class1Count); |
| 2693 | | − STBTT_assert(glyph2class < class2Count); |
| 2694 | | − |
| 2695 | | − // TODO: Support more formats. |
| 2696 | | − STBTT_GPOS_TODO_assert(valueFormat1 == 4); |
| 2697 | | − if (valueFormat1 != 4) return 0; |
| 2698 | | − STBTT_GPOS_TODO_assert(valueFormat2 == 0); |
| 2699 | | − if (valueFormat2 != 0) return 0; |
| 2700 | | − |
| 2701 | | − if (glyph1class >= 0 && glyph1class < class1Count && glyph2class >= 0 && glyph2class < class2Count) { |
| 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 | | − }; |
| 2496 | +static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) |
| 2497 | +{ |
| 2498 | + stbtt_uint16 lookupListOffset; |
| 2499 | + stbtt_uint8 *lookupList; |
| 2500 | + stbtt_uint16 lookupCount; |
| 2501 | + stbtt_uint8 *data; |
| 2502 | + stbtt_int32 i, sti; |
| 2503 | + |
| 2504 | + if (!info->gpos) return 0; |
| 2505 | + |
| 2506 | + data = info->data + info->gpos; |
| 2507 | + |
| 2508 | + if (ttUSHORT(data+0) != 1) return 0; // Major version 1 |
| 2509 | + if (ttUSHORT(data+2) != 0) return 0; // Minor version 0 |
| 2510 | + |
| 2511 | + lookupListOffset = ttUSHORT(data+8); |
| 2512 | + lookupList = data + lookupListOffset; |
| 2513 | + lookupCount = ttUSHORT(lookupList); |
| 2514 | + |
| 2515 | + for (i=0; i<lookupCount; ++i) { |
| 2516 | + stbtt_uint16 lookupOffset = ttUSHORT(lookupList + 2 + 2 * i); |
| 2517 | + stbtt_uint8 *lookupTable = lookupList + lookupOffset; |
| 2518 | + |
| 2519 | + stbtt_uint16 lookupType = ttUSHORT(lookupTable); |
| 2520 | + stbtt_uint16 subTableCount = ttUSHORT(lookupTable + 4); |
| 2521 | + stbtt_uint8 *subTableOffsets = lookupTable + 6; |
| 2522 | + if (lookupType != 2) // Pair Adjustment Positioning Subtable |
| 2523 | + continue; |
| 2524 | + |
| 2525 | + for (sti=0; sti<subTableCount; sti++) { |
| 2526 | + stbtt_uint16 subtableOffset = ttUSHORT(subTableOffsets + 2 * sti); |
| 2527 | + stbtt_uint8 *table = lookupTable + subtableOffset; |
| 2528 | + stbtt_uint16 posFormat = ttUSHORT(table); |
| 2529 | + stbtt_uint16 coverageOffset = ttUSHORT(table + 2); |
| 2530 | + stbtt_int32 coverageIndex = stbtt__GetCoverageIndex(table + coverageOffset, glyph1); |
| 2531 | + if (coverageIndex == -1) continue; |
| 2532 | + |
| 2533 | + switch (posFormat) { |
| 2534 | + case 1: { |
| 2535 | + stbtt_int32 l, r, m; |
| 2536 | + int straw, needle; |
| 2537 | + stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); |
| 2538 | + stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); |
| 2539 | + if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats? |
| 2540 | + stbtt_int32 valueRecordPairSizeInBytes = 2; |
| 2541 | + stbtt_uint16 pairSetCount = ttUSHORT(table + 8); |
| 2542 | + stbtt_uint16 pairPosOffset = ttUSHORT(table + 10 + 2 * coverageIndex); |
| 2543 | + stbtt_uint8 *pairValueTable = table + pairPosOffset; |
| 2544 | + stbtt_uint16 pairValueCount = ttUSHORT(pairValueTable); |
| 2545 | + stbtt_uint8 *pairValueArray = pairValueTable + 2; |
| 2546 | + |
| 2547 | + if (coverageIndex >= pairSetCount) return 0; |
| 2548 | + |
| 2549 | + needle=glyph2; |
| 2550 | + r=pairValueCount-1; |
| 2551 | + l=0; |
| 2552 | + |
| 2553 | + // Binary search. |
| 2554 | + while (l <= r) { |
| 2555 | + stbtt_uint16 secondGlyph; |
| 2556 | + stbtt_uint8 *pairValue; |
| 2557 | + m = (l + r) >> 1; |
| 2558 | + pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m; |
| 2559 | + secondGlyph = ttUSHORT(pairValue); |
| 2560 | + straw = secondGlyph; |
| 2561 | + if (needle < straw) |
| 2562 | + r = m - 1; |
| 2563 | + else if (needle > straw) |
| 2564 | + l = m + 1; |
| 2565 | + else { |
| 2566 | + stbtt_int16 xAdvance = ttSHORT(pairValue + 2); |
| 2567 | + return xAdvance; |
| 2568 | + } |
| 2569 | + } |
| 2570 | + } else |
| 2571 | + return 0; |
| 2572 | + break; |
| 2573 | + } |
| 2574 | + |
| 2575 | + case 2: { |
| 2576 | + stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); |
| 2577 | + stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); |
| 2578 | + if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats? |
| 2579 | + stbtt_uint16 classDef1Offset = ttUSHORT(table + 8); |
| 2580 | + stbtt_uint16 classDef2Offset = ttUSHORT(table + 10); |
| 2581 | + int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1); |
| 2582 | + int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2); |
| 2583 | + |
| 2584 | + stbtt_uint16 class1Count = ttUSHORT(table + 12); |
| 2585 | + stbtt_uint16 class2Count = ttUSHORT(table + 14); |
| 2586 | + stbtt_uint8 *class1Records, *class2Records; |
| 2587 | + stbtt_int16 xAdvance; |
| 2588 | + |
| 2589 | + if (glyph1class < 0 || glyph1class >= class1Count) return 0; // malformed |
| 2590 | + if (glyph2class < 0 || glyph2class >= class2Count) return 0; // malformed |
| 2591 | + |
| 2592 | + class1Records = table + 16; |
| 2593 | + class2Records = class1Records + 2 * (glyph1class * class2Count); |
| 2594 | + xAdvance = ttSHORT(class2Records + 2 * glyph2class); |
| 2595 | + return xAdvance; |
| 2596 | + } else |
| 2597 | + return 0; |
| 2598 | + break; |
| 2599 | + } |
| 2718 | 2600 | |
| 2719 | 2601 | default: |
| 2720 | | − // TODO: Implement other stuff. |
| 2721 | | − break; |
| 2722 | | − } |
| 2723 | | − } |
| 2602 | + return 0; // Unsupported position format |
| 2603 | + } |
| 2604 | + } |
| 2605 | + } |
| 2724 | 2606 | |
| 2725 | | − return 0; |
| 2607 | + return 0; |
| 2726 | 2608 | } |
| 2727 | 2609 | |
| 2728 | 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 | 2614 | if (info->gpos) |
| 2733 | 2615 | xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2); |
| 2734 | | − if (info->kern && xAdvance == 0) |
| 2616 | + else if (info->kern) |
| 2735 | 2617 | xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2); |
| 2736 | 2618 | |
| 2737 | 2619 | return xAdvance; |
| @@ -2789,7 +2671,6 @@ STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, floa · 51 unmodified lines |
| 2789 | 2671 | |
| 2790 | 2672 | STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) |
| 2791 | 2673 | { |
| 2792 | | − USED(info); |
| 2793 | 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 | 3055 | if (x0 <= x && x1 <= x) |
| 3175 | 3056 | scanline[x] += e->direction * (y1-y0); |
| 3176 | | − else if (x0 >= x+1 && x1 >= x+1) |
| 3177 | | − {} |
| 3178 | | − else { |
| 3057 | + else if (x0 >= x+1 && x1 >= x+1) { |
| 3058 | + } else { |
| 3179 | 3059 | STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); |
| 3180 | 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 | 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 | 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 | 3132 | float height; |
| 3236 | 3133 | // simple case, only spans one pixel |
| 3237 | 3134 | int x = (int) x_top; |
| 3238 | | − height = sy1 - sy0; |
| 3135 | + height = (sy1 - sy0) * e->direction; |
| 3239 | 3136 | STBTT_assert(x >= 0 && x < len); |
| 3240 | | − scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height; |
| 3241 | | − scanline_fill[x] += e->direction * height; // everything right of this pixel is filled |
| 3137 | + scanline[x] += stbtt__position_trapezoid_area(height, x_top, x+1.0f, x_bottom, x+1.0f); |
| 3138 | + scanline_fill[x] += height; // everything right of this pixel is filled |
| 3242 | 3139 | } else { |
| 3243 | 3140 | int x,x1,x2; |
| 3244 | | − float y_crossing, step, sign, area; |
| 3141 | + float y_crossing, y_final, step, sign, area; |
| 3245 | 3142 | // covers 2+ pixels |
| 3246 | 3143 | if (x_top > x_bottom) { |
| 3247 | 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 | 3149 | t = x_bottom, x_bottom = x_top, x_top = t; |
| 3253 | 3150 | dx = -dx; |
| 3254 | 3151 | dy = -dy; |
| 3255 | | − t = x0, x0 = xb, xb = t; |
| 3256 | | − USED(dx); USED(xb); |
| 3152 | + x0 = xb; |
| 3257 | 3153 | } |
| 3154 | + STBTT_assert(dy >= 0); |
| 3155 | + STBTT_assert(dx >= 0); |
| 3258 | 3156 | |
| 3259 | 3157 | x1 = (int) x_top; |
| 3260 | 3158 | x2 = (int) x_bottom; |
| 3261 | 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 | 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 | 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 | 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 | 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); |
| 3278 | | − |
| 3279 | | − scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (sy1-y_crossing); |
| 3222 | + // area covered in the last pixel is the rectangle from all the pixels to the left, |
| 3223 | + // plus the trapezoid filled by the line segment in this pixel all the way to the right edge |
| 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 | 3227 | scanline_fill[x2] += sign * (sy1-sy0); |
| 3282 | 3228 | } |
| 3283 | 3229 | } else { |
| @@ -3285,6 +3231,9 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, · 1 unmodified line |
| 3285 | 3231 | // clipping logic. since this does not match the intended use |
| 3286 | 3232 | // of this library, we use a different, very slow brute |
| 3287 | 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 | 3237 | int x; |
| 3289 | 3238 | for (x=0; x < len; ++x) { |
| 3290 | 3239 | // cases: |
| @@ -4521,15 +4470,14 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex · 1230 unmodified lines |
| 4521 | 4470 | float y_frac; |
| 4522 | 4471 | int winding = 0; |
| 4523 | 4472 | |
| 4524 | | − orig[0] = x; |
| 4525 | | − orig[1] = y; |
| 4526 | | − |
| 4527 | 4473 | // make sure y never passes through a vertex of the shape |
| 4528 | 4474 | y_frac = (float) STBTT_fmod(y, 1.0f); |
| 4529 | 4475 | if (y_frac < 0.01f) |
| 4530 | 4476 | y += 0.01f; |
| 4531 | 4477 | else if (y_frac > 0.99f) |
| 4532 | 4478 | y -= 0.01f; |
| 4479 | + |
| 4480 | + orig[0] = x; |
| 4533 | 4481 | orig[1] = y; |
| 4534 | 4482 | |
| 4535 | 4483 | // test a ray from (-infinity,y) to (x,y) |
| @@ -4591,35 +4539,35 @@ static float stbtt__cuberoot( float x ) · 55 unmodified lines |
| 4591 | 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 | 4543 | static int stbtt__solve_cubic(float a, float b, float c, float* r) |
| 4596 | 4544 | { |
| 4597 | | − float s = -a / 3; |
| 4598 | | − float p = b - a*a / 3; |
| 4599 | | − float q = a * (2*a*a - 9*b) / 27 + c; |
| 4545 | + float s = -a / 3; |
| 4546 | + float p = b - a*a / 3; |
| 4547 | + float q = a * (2*a*a - 9*b) / 27 + c; |
| 4600 | 4548 | float p3 = p*p*p; |
| 4601 | | − float d = q*q + 4*p3 / 27; |
| 4602 | | − if (d >= 0) { |
| 4603 | | − float z = (float) STBTT_sqrt(d); |
| 4604 | | − float u = (-q + z) / 2; |
| 4605 | | − float v = (-q - z) / 2; |
| 4606 | | − u = stbtt__cuberoot(u); |
| 4607 | | − v = stbtt__cuberoot(v); |
| 4608 | | − r[0] = s + u + v; |
| 4609 | | − return 1; |
| 4610 | | − } else { |
| 4611 | | − 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 |
| 4613 | | − float m = (float) STBTT_cos(v); |
| 4549 | + float d = q*q + 4*p3 / 27; |
| 4550 | + if (d >= 0) { |
| 4551 | + float z = (float) STBTT_sqrt(d); |
| 4552 | + float u = (-q + z) / 2; |
| 4553 | + float v = (-q - z) / 2; |
| 4554 | + u = stbtt__cuberoot(u); |
| 4555 | + v = stbtt__cuberoot(v); |
| 4556 | + r[0] = s + u + v; |
| 4557 | + return 1; |
| 4558 | + } else { |
| 4559 | + float u = (float) STBTT_sqrt(-p/3); |
| 4560 | + float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative |
| 4561 | + float m = (float) STBTT_cos(v); |
| 4614 | 4562 | float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f; |
| 4615 | | − r[0] = s + u * 2 * m; |
| 4616 | | − r[1] = s - u * (m + n); |
| 4617 | | − r[2] = s - u * (m - n); |
| 4563 | + r[0] = s + u * 2 * m; |
| 4564 | + r[1] = s - u * (m + n); |
| 4565 | + r[2] = s - u * (m - n); |
| 4618 | 4566 | |
| 4619 | 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 | 4568 | //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f); |
| 4621 | 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 | 4644 | for (i=0; i < num_verts; ++i) { |
| 4697 | 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 |
| 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) { |
| 4647 | + if (verts[i].type == STBTT_vline && precompute[i] != 0.0f) { |
| 4705 | 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 | 4654 | // coarse culling against bbox |
| 4708 | 4655 | //if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist && |
| 4709 | 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 | 4658 | STBTT_assert(i != 0); |
| 4712 | 4659 | if (dist < min_dist) { |
| 4713 | 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 | 4681 | float ax = x1-x0, ay = y1-y0; |
| 4735 | 4682 | float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; |
| 4736 | 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 | 4686 | float a_inv = precompute[i]; |
| 4739 | 4687 | if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula |
| 4740 | 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 | 4709 | float d = (mx*ax+my*ay) * a_inv; |
| 4762 | 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 | 4716 | if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) { |
| 4765 | 4717 | t = res[0], it = 1.0f - t; |
| 4766 | 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 | 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 | 4981 | // 1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod |
| 5024 | 4982 | // 1.18 (2018-01-29) add missing function |
| 5025 | 4983 | // 1.17 (2017-07-23) make more arguments const; doc fix |