1. updated Rubik font:

1) numerals are now monospace, e.g. '1' has the same width as '0',
    2) '0' is different from capital 'o',
    3) new glyphs added
2. stb_truetype upgraded from 1.24 to 1.26 with some fixes in rendering.
This commit is contained in:
Vadim Pisarevsky 2024-10-13 03:25:24 +03:00
parent 5aa45e9053
commit 08c6d00d96
4 changed files with 271 additions and 218 deletions

Binary file not shown.

View File

@ -40,8 +40,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------------------------------
*/
// stb_truetype.cpp - v1.24 - public domain
// authored from 2009-2020 by Sean Barrett / RAD Game Tools
// stb_truetype.h - v1.26 - public domain
// authored from 2009-2021 by Sean Barrett / RAD Game Tools
//
// =======================================================================
//
@ -100,6 +100,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// VERSION HISTORY
//
// 1.26 (2021-08-28) fix broken rasterizer
// 1.25 (2021-07-11) many fixes
// 1.24 (2020-02-05) fix warning
// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS)
// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined
@ -1693,7 +1695,7 @@ static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index,
if (comp_verts) STBTT_free(comp_verts, info->userdata);
return 0;
}
if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex));
if (num_vertices > 0 && vertices) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex));
STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex));
if (vertices) STBTT_free(vertices, info->userdata);
vertices = tmp;
@ -1956,7 +1958,7 @@ static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, st
subrs = stbtt__cid_get_glyph_subrs(info, glyph_index);
has_subrs = 1;
}
// fallthrough
// FALLTHROUGH
case 0x1D: // callgsubr
if (sp < 1) return STBTT__CSERR("call(g|)subr stack");
v = (int) s[--sp];
@ -2061,7 +2063,7 @@ static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, st
} break;
default:
if (b0 != 255 && b0 != 28 && (b0 < 32 || b0 > 254))
if (b0 != 255 && b0 != 28 && b0 < 32)
return STBTT__CSERR("reserved operator");
// push immediate
@ -2211,245 +2213,226 @@ static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph
return 0;
}
static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph)
static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph)
{
stbtt_uint16 coverageFormat = ttUSHORT(coverageTable);
switch(coverageFormat) {
case 1: {
stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2);
stbtt_uint16 coverageFormat = ttUSHORT(coverageTable);
switch (coverageFormat) {
case 1: {
stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2);
// Binary search.
stbtt_int32 l=0, r=glyphCount-1, m;
int straw, needle=glyph;
while (l <= r) {
stbtt_uint8 *glyphArray = coverageTable + 4;
stbtt_uint16 glyphID;
m = (l + r) >> 1;
glyphID = ttUSHORT(glyphArray + 2 * m);
straw = glyphID;
if (needle < straw)
r = m - 1;
else if (needle > straw)
l = m + 1;
else {
return m;
}
// Binary search.
stbtt_int32 l=0, r=glyphCount-1, m;
int straw, needle=glyph;
while (l <= r) {
stbtt_uint8 *glyphArray = coverageTable + 4;
stbtt_uint16 glyphID;
m = (l + r) >> 1;
glyphID = ttUSHORT(glyphArray + 2 * m);
straw = glyphID;
if (needle < straw)
r = m - 1;
else if (needle > straw)
l = m + 1;
else {
return m;
}
} break;
}
break;
}
case 2: {
stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2);
stbtt_uint8 *rangeArray = coverageTable + 4;
case 2: {
stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2);
stbtt_uint8 *rangeArray = coverageTable + 4;
// Binary search.
stbtt_int32 l=0, r=rangeCount-1, m;
int strawStart, strawEnd, needle=glyph;
while (l <= r) {
stbtt_uint8 *rangeRecord;
m = (l + r) >> 1;
rangeRecord = rangeArray + 6 * m;
strawStart = ttUSHORT(rangeRecord);
strawEnd = ttUSHORT(rangeRecord + 2);
if (needle < strawStart)
r = m - 1;
else if (needle > strawEnd)
l = m + 1;
else {
stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4);
return startCoverageIndex + glyph - strawStart;
}
// Binary search.
stbtt_int32 l=0, r=rangeCount-1, m;
int strawStart, strawEnd, needle=glyph;
while (l <= r) {
stbtt_uint8 *rangeRecord;
m = (l + r) >> 1;
rangeRecord = rangeArray + 6 * m;
strawStart = ttUSHORT(rangeRecord);
strawEnd = ttUSHORT(rangeRecord + 2);
if (needle < strawStart)
r = m - 1;
else if (needle > strawEnd)
l = m + 1;
else {
stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4);
return startCoverageIndex + glyph - strawStart;
}
} break;
}
break;
}
default: {
// There are no other cases.
STBTT_assert(0);
} break;
}
default: return -1; // unsupported
}
return -1;
return -1;
}
static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph)
{
stbtt_uint16 classDefFormat = ttUSHORT(classDefTable);
switch(classDefFormat)
{
case 1: {
stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2);
stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4);
stbtt_uint8 *classDef1ValueArray = classDefTable + 6;
stbtt_uint16 classDefFormat = ttUSHORT(classDefTable);
switch (classDefFormat)
{
case 1: {
stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2);
stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4);
stbtt_uint8 *classDef1ValueArray = classDefTable + 6;
if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount)
return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID));
if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount)
return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID));
break;
}
classDefTable = classDef1ValueArray + 2 * glyphCount;
} break;
case 2: {
stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2);
stbtt_uint8 *classRangeRecords = classDefTable + 4;
case 2: {
stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2);
stbtt_uint8 *classRangeRecords = classDefTable + 4;
// Binary search.
stbtt_int32 l=0, r=classRangeCount-1, m;
int strawStart, strawEnd, needle=glyph;
while (l <= r) {
stbtt_uint8 *classRangeRecord;
m = (l + r) >> 1;
classRangeRecord = classRangeRecords + 6 * m;
strawStart = ttUSHORT(classRangeRecord);
strawEnd = ttUSHORT(classRangeRecord + 2);
if (needle < strawStart)
r = m - 1;
else if (needle > strawEnd)
l = m + 1;
else
return (stbtt_int32)ttUSHORT(classRangeRecord + 4);
}
break;
}
// Binary search.
stbtt_int32 l=0, r=classRangeCount-1, m;
int strawStart, strawEnd, needle=glyph;
while (l <= r) {
stbtt_uint8 *classRangeRecord;
m = (l + r) >> 1;
classRangeRecord = classRangeRecords + 6 * m;
strawStart = ttUSHORT(classRangeRecord);
strawEnd = ttUSHORT(classRangeRecord + 2);
if (needle < strawStart)
r = m - 1;
else if (needle > strawEnd)
l = m + 1;
else
return (stbtt_int32)ttUSHORT(classRangeRecord + 4);
}
default:
return -1; // Unsupported definition type, return an error.
}
classDefTable = classRangeRecords + 6 * classRangeCount;
} break;
default: {
// There are no other cases.
STBTT_assert(0);
} break;
}
return -1;
// "All glyphs not assigned to a class fall into class 0". (OpenType spec)
return 0;
}
// Define to STBTT_assert(x) if you want to break on unimplemented formats.
#define STBTT_GPOS_TODO_assert(x)
static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
{
stbtt_uint16 lookupListOffset;
stbtt_uint8 *lookupList;
stbtt_uint16 lookupCount;
stbtt_uint8 *data;
stbtt_int32 i;
stbtt_uint16 lookupListOffset;
stbtt_uint8 *lookupList;
stbtt_uint16 lookupCount;
stbtt_uint8 *data;
stbtt_int32 i, sti;
if (!info->gpos) return 0;
if (!info->gpos) return 0;
data = info->data + info->gpos;
data = info->data + info->gpos;
if (ttUSHORT(data+0) != 1) return 0; // Major version 1
if (ttUSHORT(data+2) != 0) return 0; // Minor version 0
if (ttUSHORT(data+0) != 1) return 0; // Major version 1
if (ttUSHORT(data+2) != 0) return 0; // Minor version 0
lookupListOffset = ttUSHORT(data+8);
lookupList = data + lookupListOffset;
lookupCount = ttUSHORT(lookupList);
lookupListOffset = ttUSHORT(data+8);
lookupList = data + lookupListOffset;
lookupCount = ttUSHORT(lookupList);
for (i=0; i<lookupCount; ++i) {
stbtt_uint16 lookupOffset = ttUSHORT(lookupList + 2 + 2 * i);
stbtt_uint8 *lookupTable = lookupList + lookupOffset;
for (i=0; i<lookupCount; ++i) {
stbtt_uint16 lookupOffset = ttUSHORT(lookupList + 2 + 2 * i);
stbtt_uint8 *lookupTable = lookupList + lookupOffset;
stbtt_uint16 lookupType = ttUSHORT(lookupTable);
stbtt_uint16 subTableCount = ttUSHORT(lookupTable + 4);
stbtt_uint8 *subTableOffsets = lookupTable + 6;
switch(lookupType) {
case 2: { // Pair Adjustment Positioning Subtable
stbtt_int32 sti;
for (sti=0; sti<subTableCount; sti++) {
stbtt_uint16 subtableOffset = ttUSHORT(subTableOffsets + 2 * sti);
stbtt_uint8 *table = lookupTable + subtableOffset;
stbtt_uint16 posFormat = ttUSHORT(table);
stbtt_uint16 coverageOffset = ttUSHORT(table + 2);
stbtt_int32 coverageIndex = stbtt__GetCoverageIndex(table + coverageOffset, glyph1);
if (coverageIndex == -1) continue;
stbtt_uint16 lookupType = ttUSHORT(lookupTable);
stbtt_uint16 subTableCount = ttUSHORT(lookupTable + 4);
stbtt_uint8 *subTableOffsets = lookupTable + 6;
if (lookupType != 2) // Pair Adjustment Positioning Subtable
continue;
switch (posFormat) {
case 1: {
stbtt_int32 l, r, m;
int straw, needle;
stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);
stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);
stbtt_int32 valueRecordPairSizeInBytes = 2;
stbtt_uint16 pairSetCount = ttUSHORT(table + 8);
stbtt_uint16 pairPosOffset = ttUSHORT(table + 10 + 2 * coverageIndex);
stbtt_uint8 *pairValueTable = table + pairPosOffset;
stbtt_uint16 pairValueCount = ttUSHORT(pairValueTable);
stbtt_uint8 *pairValueArray = pairValueTable + 2;
// TODO: Support more formats.
STBTT_GPOS_TODO_assert(valueFormat1 == 4);
if (valueFormat1 != 4) return 0;
STBTT_GPOS_TODO_assert(valueFormat2 == 0);
if (valueFormat2 != 0) return 0;
for (sti=0; sti<subTableCount; sti++) {
stbtt_uint16 subtableOffset = ttUSHORT(subTableOffsets + 2 * sti);
stbtt_uint8 *table = lookupTable + subtableOffset;
stbtt_uint16 posFormat = ttUSHORT(table);
stbtt_uint16 coverageOffset = ttUSHORT(table + 2);
stbtt_int32 coverageIndex = stbtt__GetCoverageIndex(table + coverageOffset, glyph1);
if (coverageIndex == -1) continue;
STBTT_assert(coverageIndex < pairSetCount);
STBTT__NOTUSED(pairSetCount);
switch (posFormat) {
case 1: {
stbtt_int32 l, r, m;
int straw, needle;
stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);
stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);
if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats?
stbtt_int32 valueRecordPairSizeInBytes = 2;
stbtt_uint16 pairSetCount = ttUSHORT(table + 8);
stbtt_uint16 pairPosOffset = ttUSHORT(table + 10 + 2 * coverageIndex);
stbtt_uint8 *pairValueTable = table + pairPosOffset;
stbtt_uint16 pairValueCount = ttUSHORT(pairValueTable);
stbtt_uint8 *pairValueArray = pairValueTable + 2;
needle=glyph2;
r=pairValueCount-1;
l=0;
if (coverageIndex >= pairSetCount) return 0;
// Binary search.
while (l <= r) {
stbtt_uint16 secondGlyph;
stbtt_uint8 *pairValue;
m = (l + r) >> 1;
pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m;
secondGlyph = ttUSHORT(pairValue);
straw = secondGlyph;
if (needle < straw)
r = m - 1;
else if (needle > straw)
l = m + 1;
else {
stbtt_int16 xAdvance = ttSHORT(pairValue + 2);
return xAdvance;
}
}
} break;
needle=glyph2;
r=pairValueCount-1;
l=0;
case 2: {
stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);
stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);
// Binary search.
while (l <= r) {
stbtt_uint16 secondGlyph;
stbtt_uint8 *pairValue;
m = (l + r) >> 1;
pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m;
secondGlyph = ttUSHORT(pairValue);
straw = secondGlyph;
if (needle < straw)
r = m - 1;
else if (needle > straw)
l = m + 1;
else {
stbtt_int16 xAdvance = ttSHORT(pairValue + 2);
return xAdvance;
}
}
} else
return 0;
break;
}
stbtt_uint16 classDef1Offset = ttUSHORT(table + 8);
stbtt_uint16 classDef2Offset = ttUSHORT(table + 10);
int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1);
int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2);
case 2: {
stbtt_uint16 valueFormat1 = ttUSHORT(table + 4);
stbtt_uint16 valueFormat2 = ttUSHORT(table + 6);
if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats?
stbtt_uint16 classDef1Offset = ttUSHORT(table + 8);
stbtt_uint16 classDef2Offset = ttUSHORT(table + 10);
int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1);
int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2);
stbtt_uint16 class1Count = ttUSHORT(table + 12);
stbtt_uint16 class2Count = ttUSHORT(table + 14);
STBTT_assert(glyph1class < class1Count);
STBTT_assert(glyph2class < class2Count);
stbtt_uint16 class1Count = ttUSHORT(table + 12);
stbtt_uint16 class2Count = ttUSHORT(table + 14);
stbtt_uint8 *class1Records, *class2Records;
stbtt_int16 xAdvance;
// TODO: Support more formats.
STBTT_GPOS_TODO_assert(valueFormat1 == 4);
if (valueFormat1 != 4) return 0;
STBTT_GPOS_TODO_assert(valueFormat2 == 0);
if (valueFormat2 != 0) return 0;
if (glyph1class < 0 || glyph1class >= class1Count) return 0; // malformed
if (glyph2class < 0 || glyph2class >= class2Count) return 0; // malformed
if (glyph1class >= 0 && glyph1class < class1Count && glyph2class >= 0 && glyph2class < class2Count) {
stbtt_uint8 *class1Records = table + 16;
stbtt_uint8 *class2Records = class1Records + 2 * (glyph1class * class2Count);
stbtt_int16 xAdvance = ttSHORT(class2Records + 2 * glyph2class);
return xAdvance;
}
} break;
default: {
// There are no other cases.
STBTT_assert(0);
break;
};
}
}
break;
};
class1Records = table + 16;
class2Records = class1Records + 2 * (glyph1class * class2Count);
xAdvance = ttSHORT(class2Records + 2 * glyph2class);
return xAdvance;
} else
return 0;
break;
}
default:
// TODO: Implement other stuff.
break;
}
}
return 0; // Unsupported position format
}
}
}
return 0;
return 0;
}
STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2)
{
int xAdvance = 0;
@ -2873,6 +2856,23 @@ static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edg
}
}
static float stbtt__sized_trapezoid_area(float height, float top_width, float bottom_width)
{
STBTT_assert(top_width >= 0);
STBTT_assert(bottom_width >= 0);
return (top_width + bottom_width) / 2.0f * height;
}
static float stbtt__position_trapezoid_area(float height, float tx0, float tx1, float bx0, float bx1)
{
return stbtt__sized_trapezoid_area(height, tx1 - tx0, bx1 - bx0);
}
static float stbtt__sized_triangle_area(float height, float width)
{
return height * width / 2;
}
static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top)
{
float y_bottom = y_top+1;
@ -2927,13 +2927,13 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
float height;
// simple case, only spans one pixel
int x = (int) x_top;
height = sy1 - sy0;
height = (sy1 - sy0) * e->direction;
STBTT_assert(x >= 0 && x < len);
scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height;
scanline_fill[x] += e->direction * height; // everything right of this pixel is filled
scanline[x] += stbtt__position_trapezoid_area(height, x_top, x+1.0f, x_bottom, x+1.0f);
scanline_fill[x] += height; // everything right of this pixel is filled
} else {
int x,x1,x2;
float y_crossing, step, sign, area;
float y_crossing, y_final, step, sign, area;
// covers 2+ pixels
if (x_top > x_bottom) {
// flip scanline vertically; signed area is the same
@ -2946,29 +2946,79 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
dy = -dy;
t = x0, x0 = xb, xb = t;
}
STBTT_assert(dy >= 0);
STBTT_assert(dx >= 0);
x1 = (int) x_top;
x2 = (int) x_bottom;
// compute intersection with y axis at x1+1
y_crossing = (x1+1 - x0) * dy + y_top;
y_crossing = y_top + dy * (x1+1 - x0);
// compute intersection with y axis at x2
y_final = y_top + dy * (x2 - x0);
// x1 x_top x2 x_bottom
// y_top +------|-----+------------+------------+--------|---+------------+
// | | | | | |
// | | | | | |
// sy0 | Txxxxx|............|............|............|............|
// y_crossing | *xxxxx.......|............|............|............|
// | | xxxxx..|............|............|............|
// | | /- xx*xxxx........|............|............|
// | | dy < | xxxxxx..|............|............|
// y_final | | \- | xx*xxx.........|............|
// sy1 | | | | xxxxxB...|............|
// | | | | | |
// | | | | | |
// y_bottom +------------+------------+------------+------------+------------+
//
// goal is to measure the area covered by '.' in each pixel
// if x2 is right at the right edge of x1, y_crossing can blow up, github #1057
// @TODO: maybe test against sy1 rather than y_bottom?
if (y_crossing > y_bottom)
y_crossing = y_bottom;
sign = e->direction;
// area of the rectangle covered from y0..y_crossing
area = sign * (y_crossing-sy0);
// area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing)
scanline[x1] += area * (1-((x_top - x1)+(x1+1-x1))/2);
step = sign * dy;
// area of the rectangle covered from sy0..y_crossing
area = sign * (y_crossing-sy0);
// area of the triangle (x_top,sy0), (x1+1,sy0), (x1+1,y_crossing)
scanline[x1] += stbtt__sized_triangle_area(area, x1+1 - x_top);
// check if final y_crossing is blown up; no test case for this
if (y_final > y_bottom) {
y_final = y_bottom;
dy = (y_final - y_crossing ) / (x2 - (x1+1)); // if denom=0, y_final = y_crossing, so y_final <= y_bottom
}
// in second pixel, area covered by line segment found in first pixel
// is always a rectangle 1 wide * the height of that line segment; this
// is exactly what the variable 'area' stores. it also gets a contribution
// from the line segment within it. the THIRD pixel will get the first
// pixel's rectangle contribution, the second pixel's rectangle contribution,
// and its own contribution. the 'own contribution' is the same in every pixel except
// the leftmost and rightmost, a trapezoid that slides down in each pixel.
// the second pixel's contribution to the third pixel will be the
// rectangle 1 wide times the height change in the second pixel, which is dy.
step = sign * dy * 1; // dy is dy/dx, change in y for every 1 change in x,
// which multiplied by 1-pixel-width is how much pixel area changes for each step in x
// so the area advances by 'step' every time
for (x = x1+1; x < x2; ++x) {
scanline[x] += area + step/2;
scanline[x] += area + step/2; // area of trapezoid is 1*step/2
area += step;
}
y_crossing += dy * (x2 - (x1+1));
STBTT_assert(STBTT_fabs(area) <= 1.01f); // accumulated error from area += step unless we round step down
STBTT_assert(sy1 > y_final-0.01f);
STBTT_assert(STBTT_fabs(area) <= 1.01f);
scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (sy1-y_crossing);
// area covered in the last pixel is the rectangle from all the pixels to the left,
// plus the trapezoid filled by the line segment in this pixel all the way to the right edge
scanline[x2] += area + sign * stbtt__position_trapezoid_area(sy1-y_final, (float) x2, x2+1.0f, x_bottom, x2+1.0f);
// the rest of the line is filled based on the total height of the line segment in this pixel
scanline_fill[x2] += sign * (sy1-sy0);
}
} else {
@ -2976,6 +3026,9 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
// clipping logic. since this does not match the intended use
// of this library, we use a different, very slow brute
// force implementation
// note though that this does happen some of the time because
// x_top and x_bottom can be extrapolated at the top & bottom of
// the shape and actually lie outside the bounding box
int x;
for (x=0; x < len; ++x) {
// cases:

View File

@ -769,7 +769,7 @@ TEST(Drawing, ttf_text)
TextProp text[] =
{
{"The quick brown fox jumps over lazy dog. Fly, start, finish, shuffle shuttle.", false, 400, false},
{"vechicle #5 detected; fps=123.45.\n", false, 600, false},
{"vechicle #5 detected; fps=103.45.\n", false, 600, false},
{"Съешь же ещё этих мягких французских булок, да выпей чаю!\n"
"Dès Noël où un zéphyr haï me vêt de glaçons würmiens je dîne\ndexquis rôtis de bœuf au kir à laÿ dâge mûr & cætera!\n"
"“Falsches Üben von Xylophonmusik quält jeden größeren Zwerg”.", false, 400, true},