2
0
Эх сурвалжийг харах

stb_truetype 1.26: fix rendering glitches

Sean Barrett 3 жил өмнө
parent
commit
c0c982601f

+ 4 - 6
README.md

@@ -22,8 +22,8 @@ library    | lastest version | category | LoC | description
 --------------------- | ---- | -------- | --- | --------------------------------
 --------------------- | ---- | -------- | --- | --------------------------------
 **[stb_vorbis.c](stb_vorbis.c)** | 1.22 | audio | 5584 | decode ogg vorbis files from file/memory to float/16-bit signed output
 **[stb_vorbis.c](stb_vorbis.c)** | 1.22 | audio | 5584 | decode ogg vorbis files from file/memory to float/16-bit signed output
 **[stb_hexwave.h](stb_hexwave.h)** | 0.5 | audio | 680 | audio waveform synthesizer
 **[stb_hexwave.h](stb_hexwave.h)** | 0.5 | audio | 680 | audio waveform synthesizer
-**[stb_image.h](stb_image.h)** | 2.27 | graphics | 7890 | image loading/decoding from file/memory: JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC
-**[stb_truetype.h](stb_truetype.h)** | 1.25 | graphics | 5017 | parse, decode, and rasterize characters from truetype fonts
+**[stb_image.h](stb_image.h)** | 2.27 | graphics | 7897 | image loading/decoding from file/memory: JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC
+**[stb_truetype.h](stb_truetype.h)** | 1.26 | graphics | 5077 | parse, decode, and rasterize characters from truetype fonts
 **[stb_image_write.h](stb_image_write.h)** | 1.16 | graphics | 1724 | image writing to disk: PNG, TGA, BMP
 **[stb_image_write.h](stb_image_write.h)** | 1.16 | graphics | 1724 | image writing to disk: PNG, TGA, BMP
 **[stb_image_resize.h](stb_image_resize.h)** | 0.97 | graphics | 2634 | resize images larger/smaller with good quality
 **[stb_image_resize.h](stb_image_resize.h)** | 0.97 | graphics | 2634 | resize images larger/smaller with good quality
 **[stb_rect_pack.h](stb_rect_pack.h)** | 1.01 | graphics | 623 | simple 2D rectangle packer with decent quality
 **[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
 **[stb_textedit.h](stb_textedit.h)** | 1.14 | user interface | 1429 | guts of a text editor for games etc implementing them from scratch
 **[stb_textedit.h](stb_textedit.h)** | 1.14 | user interface | 1429 | guts of a text editor for games etc implementing them from scratch
 **[stb_voxel_render.h](stb_voxel_render.h)** | 0.89 | 3D graphics | 3807 | Minecraft-esque voxel rendering "engine" with many more features
 **[stb_voxel_render.h](stb_voxel_render.h)** | 0.89 | 3D graphics | 3807 | Minecraft-esque voxel rendering "engine" with many more features
 **[stb_dxt.h](stb_dxt.h)** | 1.12 | 3D graphics | 719 | Fabian "ryg" Giesen's real-time DXT compressor
 **[stb_dxt.h](stb_dxt.h)** | 1.12 | 3D graphics | 719 | Fabian "ryg" Giesen's real-time DXT compressor
-**[stb_perlin.h](stb_perlin.h)** | 0.5 | 3D graphics | 428 | revised Perlin noise (3D input, 1D output)
 **[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
 **[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
 **[stb_tilemap_editor.h](stb_tilemap_editor.h)** | 0.42 | game dev | 4187 | embeddable tilemap editor
 **[stb_tilemap_editor.h](stb_tilemap_editor.h)** | 0.42 | game dev | 4187 | embeddable tilemap editor
 **[stb_herringbone_wa...](stb_herringbone_wang_tile.h)** | 0.7 | game dev | 1221 | herringbone Wang tile map generator
 **[stb_herringbone_wa...](stb_herringbone_wang_tile.h)** | 0.7 | game dev | 1221 | herringbone Wang tile map generator
 **[stb_c_lexer.h](stb_c_lexer.h)** | 0.12 | parsing | 940 | simplify writing parsers for C-like languages
 **[stb_c_lexer.h](stb_c_lexer.h)** | 0.12 | parsing | 940 | simplify writing parsers for C-like languages
 **[stb_divide.h](stb_divide.h)** | 0.94 | math | 433 | more useful 32-bit modulus e.g. "euclidean divide"
 **[stb_divide.h](stb_divide.h)** | 0.94 | math | 433 | more useful 32-bit modulus e.g. "euclidean divide"
 **[stb_connected_comp...](stb_connected_components.h)** | 0.96 | misc | 1049 | incrementally compute reachability on grids
 **[stb_connected_comp...](stb_connected_components.h)** | 0.96 | misc | 1049 | incrementally compute reachability on grids
-**[stb.h](stb.h)** | 2.37 | misc | 13105 | _deprecated_ helper functions for C, mostly redundant in C++; basically author's personal stuff
 **[stb_leakcheck.h](stb_leakcheck.h)** | 0.6 | misc | 194 | quick-and-dirty malloc/free leak-checking
 **[stb_leakcheck.h](stb_leakcheck.h)** | 0.6 | misc | 194 | quick-and-dirty malloc/free leak-checking
 **[stb_include.h](stb_include.h)** | 0.02 | misc | 295 | implement recursive #include support, particularly for GLSL
 **[stb_include.h](stb_include.h)** | 0.02 | misc | 295 | implement recursive #include support, particularly for GLSL
 
 
-Total libraries: 22
-Total lines of C code: 56065
+Total libraries: 20
+Total lines of C code: 42599
 
 
 
 
 FAQ
 FAQ

+ 75 - 15
stb_truetype.h

@@ -1,4 +1,4 @@
-// stb_truetype.h - v1.25 - public domain
+// stb_truetype.h - v1.26 - public domain
 // authored from 2009-2021 by Sean Barrett / RAD Game Tools
 // authored from 2009-2021 by Sean Barrett / RAD Game Tools
 //
 //
 // =======================================================================
 // =======================================================================
@@ -58,6 +58,7 @@
 //
 //
 // VERSION HISTORY
 // VERSION HISTORY
 //
 //
+//   1.26 (2021-08-28) fix broken rasterizer
 //   1.25 (2021-07-11) many fixes
 //   1.25 (2021-07-11) many fixes
 //   1.24 (2020-02-05) fix warning
 //   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.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
    }
    }
 }
 }
 
 
+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)
 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;
    float y_bottom = y_top+1;
@@ -3115,10 +3133,10 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
                float height;
                float height;
                // simple case, only spans one pixel
                // simple case, only spans one pixel
                int x = (int) x_top;
                int x = (int) x_top;
-               height = sy1 - sy0;
+               height = (sy1 - sy0) * e->direction;
                STBTT_assert(x >= 0 && x < len);
                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 {
             } else {
                int x,x1,x2;
                int x,x1,x2;
                float y_crossing, y_final, step, sign, area;
                float y_crossing, y_final, step, sign, area;
@@ -3134,40 +3152,79 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
                   dy = -dy;
                   dy = -dy;
                   t = x0, x0 = xb, xb = t;
                   t = x0, x0 = xb, xb = t;
                }
                }
-               assert(dy >= 0);
-               assert(dx >= 0);
+               STBTT_assert(dy >= 0);
+               STBTT_assert(dx >= 0);
 
 
                x1 = (int) x_top;
                x1 = (int) x_top;
                x2 = (int) x_bottom;
                x2 = (int) x_bottom;
                // compute intersection with y axis at x1+1
                // 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
                // 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)
                if (y_crossing > y_bottom)
                   y_crossing = y_bottom;
                   y_crossing = y_bottom;
 
 
                sign = e->direction;
                sign = e->direction;
-               // area of the rectangle covered from y0..y_crossing
+
+               // area of the rectangle covered from sy0..y_crossing
                area = sign * (y_crossing-sy0);
                area = sign * (y_crossing-sy0);
-               // area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing)
-               scanline[x1] += area * (x1+1 - x_top)/2;
+
+               // 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
                // check if final y_crossing is blown up; no test case for this
-               y_final = y_crossing + dy * (x2 - (x1+1)); // advance y by number of steps taken below
                if (y_final > y_bottom) {
                if (y_final > y_bottom) {
                   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
                   dy = (y_final - y_crossing ) / (x2 - (x1+1)); // if denom=0, y_final = y_crossing, so y_final <= y_bottom
                }
                }
 
 
-               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
+               // 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) {
                for (x = x1+1; x < x2; ++x) {
-                  scanline[x] += area + step/2; // area of parallelogram is step/2
+                  scanline[x] += area + step/2; // area of trapezoid is 1*step/2
                   area += step;
                   area += step;
                }
                }
                STBTT_assert(STBTT_fabs(area) <= 1.01f); // accumulated error from area += step unless we round step down
                STBTT_assert(STBTT_fabs(area) <= 1.01f); // accumulated error from area += step unless we round step down
+               STBTT_assert(sy1 > y_final-0.01f);
 
 
-               // area of the triangle (x2,y_crossing), (x_bottom,y1), (x2,y1)
-               scanline[x2] += area + sign * (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);
                scanline_fill[x2] += sign * (sy1-sy0);
             }
             }
          } else {
          } else {
@@ -3175,6 +3232,9 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill,
             // clipping logic. since this does not match the intended use
             // clipping logic. since this does not match the intended use
             // of this library, we use a different, very slow brute
             // of this library, we use a different, very slow brute
             // force implementation
             // 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;
             int x;
             for (x=0; x < len; ++x) {
             for (x=0; x < len; ++x) {
                // cases:
                // cases:

+ 0 - 2
tools/README.list

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

+ 1 - 0
tools/make_readme.c

@@ -20,6 +20,7 @@ int main(int argc, char  **argv)
       int num_lines;
       int num_lines;
       char **lines = stb_stringfile(stb_sprintf("../%s", tokens[0]), &num_lines);
       char **lines = stb_stringfile(stb_sprintf("../%s", tokens[0]), &num_lines);
       char *s1, *s2,*s3;
       char *s1, *s2,*s3;
+      if (lines == NULL) stb_fatal("Couldn't open '%s'", tokens[0]);
       s1 = strchr(lines[0], '-');
       s1 = strchr(lines[0], '-');
       if (!s1) stb_fatal("Couldn't find '-' before version number in %s", tokens[0]); // stb_fatal -- print error message & exit
       if (!s1) stb_fatal("Couldn't find '-' before version number in %s", tokens[0]); // stb_fatal -- print error message & exit
       s2 = strchr(s1+2, '-');
       s2 = strchr(s1+2, '-');