Skip to content

Commit c0c9826

Browse files
author
Sean Barrett
committed
stb_truetype 1.26: fix rendering glitches
1 parent ef86142 commit c0c9826

File tree

4 files changed

+80
-23
lines changed

4 files changed

+80
-23
lines changed

README.md

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ library | lastest version | category | LoC | description
2222
--------------------- | ---- | -------- | --- | --------------------------------
2323
**[stb_vorbis.c](stb_vorbis.c)** | 1.22 | audio | 5584 | decode ogg vorbis files from file/memory to float/16-bit signed output
2424
**[stb_hexwave.h](stb_hexwave.h)** | 0.5 | audio | 680 | audio waveform synthesizer
25-
**[stb_image.h](stb_image.h)** | 2.27 | graphics | 7890 | image loading/decoding from file/memory: JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC
26-
**[stb_truetype.h](stb_truetype.h)** | 1.25 | graphics | 5017 | parse, decode, and rasterize characters from truetype fonts
25+
**[stb_image.h](stb_image.h)** | 2.27 | graphics | 7897 | image loading/decoding from file/memory: JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC
26+
**[stb_truetype.h](stb_truetype.h)** | 1.26 | graphics | 5077 | parse, decode, and rasterize characters from truetype fonts
2727
**[stb_image_write.h](stb_image_write.h)** | 1.16 | graphics | 1724 | image writing to disk: PNG, TGA, BMP
2828
**[stb_image_resize.h](stb_image_resize.h)** | 0.97 | graphics | 2634 | resize images larger/smaller with good quality
2929
**[stb_rect_pack.h](stb_rect_pack.h)** | 1.01 | graphics | 623 | simple 2D rectangle packer with decent quality
@@ -32,19 +32,17 @@ library | lastest version | category | LoC | description
3232
**[stb_textedit.h](stb_textedit.h)** | 1.14 | user interface | 1429 | guts of a text editor for games etc implementing them from scratch
3333
**[stb_voxel_render.h](stb_voxel_render.h)** | 0.89 | 3D graphics | 3807 | Minecraft-esque voxel rendering "engine" with many more features
3434
**[stb_dxt.h](stb_dxt.h)** | 1.12 | 3D graphics | 719 | Fabian "ryg" Giesen's real-time DXT compressor
35-
**[stb_perlin.h](stb_perlin.h)** | 0.5 | 3D graphics | 428 | revised Perlin noise (3D input, 1D output)
3635
**[stb_easy_font.h](stb_easy_font.h)** | 1.1 | 3D graphics | 305 | quick-and-dirty easy-to-deploy bitmap font for printing frame rate, etc
3736
**[stb_tilemap_editor.h](stb_tilemap_editor.h)** | 0.42 | game dev | 4187 | embeddable tilemap editor
3837
**[stb_herringbone_wa...](stb_herringbone_wang_tile.h)** | 0.7 | game dev | 1221 | herringbone Wang tile map generator
3938
**[stb_c_lexer.h](stb_c_lexer.h)** | 0.12 | parsing | 940 | simplify writing parsers for C-like languages
4039
**[stb_divide.h](stb_divide.h)** | 0.94 | math | 433 | more useful 32-bit modulus e.g. "euclidean divide"
4140
**[stb_connected_comp...](stb_connected_components.h)** | 0.96 | misc | 1049 | incrementally compute reachability on grids
42-
**[stb.h](stb.h)** | 2.37 | misc | 13105 | _deprecated_ helper functions for C, mostly redundant in C++; basically author's personal stuff
4341
**[stb_leakcheck.h](stb_leakcheck.h)** | 0.6 | misc | 194 | quick-and-dirty malloc/free leak-checking
4442
**[stb_include.h](stb_include.h)** | 0.02 | misc | 295 | implement recursive #include support, particularly for GLSL
4543

46-
Total libraries: 22
47-
Total lines of C code: 56065
44+
Total libraries: 20
45+
Total lines of C code: 42599
4846

4947

5048
FAQ

stb_truetype.h

Lines changed: 75 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// stb_truetype.h - v1.25 - public domain
1+
// stb_truetype.h - v1.26 - public domain
22
// authored from 2009-2021 by Sean Barrett / RAD Game Tools
33
//
44
// =======================================================================
@@ -58,6 +58,7 @@
5858
//
5959
// VERSION HISTORY
6060
//
61+
// 1.26 (2021-08-28) fix broken rasterizer
6162
// 1.25 (2021-07-11) many fixes
6263
// 1.24 (2020-02-05) fix warning
6364
// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS)
@@ -3061,6 +3062,23 @@ static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edg
30613062
}
30623063
}
30633064

3065+
static float stbtt__sized_trapezoid_area(float height, float top_width, float bottom_width)
3066+
{
3067+
STBTT_assert(top_width >= 0);
3068+
STBTT_assert(bottom_width >= 0);
3069+
return (top_width + bottom_width) / 2.0f * height;
3070+
}
3071+
3072+
static float stbtt__position_trapezoid_area(float height, float tx0, float tx1, float bx0, float bx1)
3073+
{
3074+
return stbtt__sized_trapezoid_area(height, tx1 - tx0, bx1 - bx0);
3075+
}
3076+
3077+
static float stbtt__sized_triangle_area(float height, float width)
3078+
{
3079+
return height * width / 2;
3080+
}
3081+
30643082
static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top)
30653083
{
30663084
float y_bottom = y_top+1;
@@ -3115,10 +3133,10 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
31153133
float height;
31163134
// simple case, only spans one pixel
31173135
int x = (int) x_top;
3118-
height = sy1 - sy0;
3136+
height = (sy1 - sy0) * e->direction;
31193137
STBTT_assert(x >= 0 && x < len);
3120-
scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height;
3121-
scanline_fill[x] += e->direction * height; // everything right of this pixel is filled
3138+
scanline[x] += stbtt__position_trapezoid_area(height, x_top, x+1.0f, x_bottom, x+1.0f);
3139+
scanline_fill[x] += height; // everything right of this pixel is filled
31223140
} else {
31233141
int x,x1,x2;
31243142
float y_crossing, y_final, step, sign, area;
@@ -3134,47 +3152,89 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
31343152
dy = -dy;
31353153
t = x0, x0 = xb, xb = t;
31363154
}
3137-
assert(dy >= 0);
3138-
assert(dx >= 0);
3155+
STBTT_assert(dy >= 0);
3156+
STBTT_assert(dx >= 0);
31393157

31403158
x1 = (int) x_top;
31413159
x2 = (int) x_bottom;
31423160
// compute intersection with y axis at x1+1
3143-
y_crossing = (x1+1 - x0) * dy + y_top;
3161+
y_crossing = y_top + dy * (x1+1 - x0);
3162+
3163+
// compute intersection with y axis at x2
3164+
y_final = y_top + dy * (x2 - x0);
3165+
3166+
// x1 x_top x2 x_bottom
3167+
// y_top +------|-----+------------+------------+--------|---+------------+
3168+
// | | | | | |
3169+
// | | | | | |
3170+
// sy0 | Txxxxx|............|............|............|............|
3171+
// y_crossing | *xxxxx.......|............|............|............|
3172+
// | | xxxxx..|............|............|............|
3173+
// | | /- xx*xxxx........|............|............|
3174+
// | | dy < | xxxxxx..|............|............|
3175+
// y_final | | \- | xx*xxx.........|............|
3176+
// sy1 | | | | xxxxxB...|............|
3177+
// | | | | | |
3178+
// | | | | | |
3179+
// y_bottom +------------+------------+------------+------------+------------+
3180+
//
3181+
// goal is to measure the area covered by '.' in each pixel
3182+
31443183
// if x2 is right at the right edge of x1, y_crossing can blow up, github #1057
3184+
// @TODO: maybe test against sy1 rather than y_bottom?
31453185
if (y_crossing > y_bottom)
31463186
y_crossing = y_bottom;
31473187

31483188
sign = e->direction;
3149-
// area of the rectangle covered from y0..y_crossing
3189+
3190+
// area of the rectangle covered from sy0..y_crossing
31503191
area = sign * (y_crossing-sy0);
3151-
// area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing)
3152-
scanline[x1] += area * (x1+1 - x_top)/2;
3192+
3193+
// area of the triangle (x_top,sy0), (x1+1,sy0), (x1+1,y_crossing)
3194+
scanline[x1] += stbtt__sized_triangle_area(area, x1+1 - x_top);
31533195

31543196
// check if final y_crossing is blown up; no test case for this
3155-
y_final = y_crossing + dy * (x2 - (x1+1)); // advance y by number of steps taken below
31563197
if (y_final > y_bottom) {
31573198
y_final = y_bottom;
31583199
dy = (y_final - y_crossing ) / (x2 - (x1+1)); // if denom=0, y_final = y_crossing, so y_final <= y_bottom
31593200
}
31603201

3161-
step = sign * dy * 1; // dy is dy/dx, change in y for every 1 change in x, which is also how much pixel area changes for each step in x
3202+
// in second pixel, area covered by line segment found in first pixel
3203+
// is always a rectangle 1 wide * the height of that line segment; this
3204+
// is exactly what the variable 'area' stores. it also gets a contribution
3205+
// from the line segment within it. the THIRD pixel will get the first
3206+
// pixel's rectangle contribution, the second pixel's rectangle contribution,
3207+
// and its own contribution. the 'own contribution' is the same in every pixel except
3208+
// the leftmost and rightmost, a trapezoid that slides down in each pixel.
3209+
// the second pixel's contribution to the third pixel will be the
3210+
// rectangle 1 wide times the height change in the second pixel, which is dy.
3211+
3212+
step = sign * dy * 1; // dy is dy/dx, change in y for every 1 change in x,
3213+
// which multiplied by 1-pixel-width is how much pixel area changes for each step in x
3214+
// so the area advances by 'step' every time
3215+
31623216
for (x = x1+1; x < x2; ++x) {
3163-
scanline[x] += area + step/2; // area of parallelogram is step/2
3217+
scanline[x] += area + step/2; // area of trapezoid is 1*step/2
31643218
area += step;
31653219
}
31663220
STBTT_assert(STBTT_fabs(area) <= 1.01f); // accumulated error from area += step unless we round step down
3221+
STBTT_assert(sy1 > y_final-0.01f);
31673222

3168-
// area of the triangle (x2,y_crossing), (x_bottom,y1), (x2,y1)
3169-
scanline[x2] += area + sign * (x_bottom - x2)/2 * (sy1-y_crossing);
3223+
// area covered in the last pixel is the rectangle from all the pixels to the left,
3224+
// plus the trapezoid filled by the line segment in this pixel all the way to the right edge
3225+
scanline[x2] += area + sign * stbtt__position_trapezoid_area(sy1-y_final, (float) x2, x2+1.0f, x_bottom, x2+1.0f);
31703226

3227+
// the rest of the line is filled based on the total height of the line segment in this pixel
31713228
scanline_fill[x2] += sign * (sy1-sy0);
31723229
}
31733230
} else {
31743231
// if edge goes outside of box we're drawing, we require
31753232
// clipping logic. since this does not match the intended use
31763233
// of this library, we use a different, very slow brute
31773234
// force implementation
3235+
// note though that this does happen some of the time because
3236+
// x_top and x_bottom can be extrapolated at the top & bottom of
3237+
// the shape and actually lie outside the bounding box
31783238
int x;
31793239
for (x=0; x < len; ++x) {
31803240
// cases:

tools/README.list

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,11 @@ stb_sprintf.h | utility | fast sprintf, snprintf for C/C+
1010
stb_textedit.h | user interface | guts of a text editor for games etc implementing them from scratch
1111
stb_voxel_render.h | 3D graphics | Minecraft-esque voxel rendering "engine" with many more features
1212
stb_dxt.h | 3D graphics | Fabian "ryg" Giesen's real-time DXT compressor
13-
stb_perlin.h | 3D graphics | revised Perlin noise (3D input, 1D output)
1413
stb_easy_font.h | 3D graphics | quick-and-dirty easy-to-deploy bitmap font for printing frame rate, etc
1514
stb_tilemap_editor.h | game dev | embeddable tilemap editor
1615
stb_herringbone_wang_tile.h | game dev | herringbone Wang tile map generator
1716
stb_c_lexer.h | parsing | simplify writing parsers for C-like languages
1817
stb_divide.h | math | more useful 32-bit modulus e.g. "euclidean divide"
1918
stb_connected_components.h | misc | incrementally compute reachability on grids
20-
stb.h | misc | _deprecated_ helper functions for C, mostly redundant in C++; basically author's personal stuff
2119
stb_leakcheck.h | misc | quick-and-dirty malloc/free leak-checking
2220
stb_include.h | misc | implement recursive #include support, particularly for GLSL

tools/make_readme.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ int main(int argc, char **argv)
2020
int num_lines;
2121
char **lines = stb_stringfile(stb_sprintf("../%s", tokens[0]), &num_lines);
2222
char *s1, *s2,*s3;
23+
if (lines == NULL) stb_fatal("Couldn't open '%s'", tokens[0]);
2324
s1 = strchr(lines[0], '-');
2425
if (!s1) stb_fatal("Couldn't find '-' before version number in %s", tokens[0]); // stb_fatal -- print error message & exit
2526
s2 = strchr(s1+2, '-');

0 commit comments

Comments
 (0)