Here is a third and I hope final version of the spline flattening patch, incorporating the latest version of David Bevan's cubic spline flattening routine.

Notes:

1. As with the previous versions, it solves the bug in flattening s-shaped curves (cubic with control points on both sides of the chord).

2. I have incorporated David's latest code as is. However, I have expanded his comments, most importantly (i) by giving the reference to Hain's paper in full rather than as a tinyurl, so as to make it easier to find elsewhere if it is moved or if the tinyurl service is discontinued, and (ii) by incorporating a detailed explanation he provided of the calculation of L.

3. It may be thought advisable to split the patch into two: one for cubics, which is essential because it fixes a bug, and one for conics, which is not essential but gives some speed improvement and simpler code. I leave that up to Werner.


Graham

diff --git "a/C:\\DOCUME~1\\Graham\\LOCALS~1\\Temp\\ftgrays_c8f5b9.c" 
"b/C:\\DOCUME~1\\Graham\\LOCALS~1\\Temp\\ftgrays_f4f03b.c"
index 0b94143..a3a7a31 100644
--- "a/C:\\DOCUME~1\\Graham\\LOCALS~1\\Temp\\ftgrays_c8f5b9.c"
+++ "b/C:\\DOCUME~1\\Graham\\LOCALS~1\\Temp\\ftgrays_f4f03b.c"
@@ -90,6 +90,9 @@
 #undef  FT_COMPONENT
 #define FT_COMPONENT  trace_smooth
 
+/* The maximum distance of a curve from the chord, in 64ths of a pixel; */
+/* used when flattening curves. */
+#define FT_MAX_CURVE_DEVIATION 16
 
 #ifdef _STANDALONE_
 
@@ -354,8 +357,6 @@ typedef ptrdiff_t  FT_PtrDist;
 
     int  band_size;
     int  band_shoot;
-    int  conic_level;
-    int  cubic_level;
 
     ft_jmp_buf  jump_buffer;
 
@@ -888,31 +889,18 @@ typedef ptrdiff_t  FT_PtrDist;
     if ( dx < dy )
       dx = dy;
 
-    level = 1;
-    dx = dx / ras.conic_level;
-    while ( dx > 0 )
+    if ( dx <= FT_MAX_CURVE_DEVIATION )
     {
-      dx >>= 2;
-      level++;
+      gray_render_line( RAS_VAR_ UPSCALE( to->x ), UPSCALE( to->y ) );
+      return;
     }
 
-    /* a shortcut to speed things up */
-    if ( level <= 1 )
+    level = 1;
+    dx /= FT_MAX_CURVE_DEVIATION;
+    while ( dx > 1 )
     {
-      /* we compute the mid-point directly in order to avoid */
-      /* calling gray_split_conic()                          */
-      TPos  to_x, to_y, mid_x, mid_y;
-
-
-      to_x  = UPSCALE( to->x );
-      to_y  = UPSCALE( to->y );
-      mid_x = ( ras.x + to_x + 2 * UPSCALE( control->x ) ) / 4;
-      mid_y = ( ras.y + to_y + 2 * UPSCALE( control->y ) ) / 4;
-
-      gray_render_line( RAS_VAR_ mid_x, mid_y );
-      gray_render_line( RAS_VAR_ to_x, to_y );
-
-      return;
+      dx >>= 2;
+      level++;
     }
 
     arc       = ras.bez_stack;
@@ -957,21 +945,9 @@ typedef ptrdiff_t  FT_PtrDist;
       }
 
     Draw:
-      {
-        TPos  to_x, to_y, mid_x, mid_y;
-
-
-        to_x  = arc[0].x;
-        to_y  = arc[0].y;
-        mid_x = ( ras.x + to_x + 2 * arc[1].x ) / 4;
-        mid_y = ( ras.y + to_y + 2 * arc[1].y ) / 4;
-
-        gray_render_line( RAS_VAR_ mid_x, mid_y );
-        gray_render_line( RAS_VAR_ to_x, to_y );
-
-        top--;
-        arc -= 2;
-      }
+      gray_render_line( RAS_VAR_ arc[0].x, arc[0].y );
+      top--;
+      arc -= 2;
     }
 
     return;
@@ -1011,55 +987,8 @@ typedef ptrdiff_t  FT_PtrDist;
                               const FT_Vector*  control2,
                               const FT_Vector*  to )
   {
-    int         top, level;
-    int*        levels;
     FT_Vector*  arc;
-    int         mid_x = ( DOWNSCALE( ras.x ) + to->x +
-                          3 * (control1->x + control2->x ) ) / 8;
-    int         mid_y = ( DOWNSCALE( ras.y ) + to->y +
-                          3 * (control1->y + control2->y ) ) / 8;
-    TPos        dx = DOWNSCALE( ras.x ) + to->x - ( mid_x << 1 );
-    TPos        dy = DOWNSCALE( ras.y ) + to->y - ( mid_y << 1 );
-
 
-    if ( dx < 0 )
-      dx = -dx;
-    if ( dy < 0 )
-      dy = -dy;
-    if ( dx < dy )
-      dx = dy;
-
-    level = 1;
-    dx /= ras.cubic_level;
-    while ( dx > 0 )
-    {
-      dx >>= 2;
-      level++;
-    }
-
-    if ( level <= 1 )
-    {
-      TPos  to_x, to_y;
-
-
-      to_x  = UPSCALE( to->x );
-      to_y  = UPSCALE( to->y );
-
-      /* Recalculation of midpoint is needed only if */
-      /* UPSCALE and DOWNSCALE have any effect.      */
-
-#if ( PIXEL_BITS != 6 )
-      mid_x = ( ras.x + to_x +
-                3 * UPSCALE( control1->x + control2->x ) ) / 8;
-      mid_y = ( ras.y + to_y +
-                3 * UPSCALE( control1->y + control2->y ) ) / 8;
-#endif
-
-      gray_render_line( RAS_VAR_ mid_x, mid_y );
-      gray_render_line( RAS_VAR_ to_x, to_y );
-
-      return;
-    }
 
     arc      = ras.bez_stack;
     arc[0].x = UPSCALE( to->x );
@@ -1071,60 +1000,98 @@ typedef ptrdiff_t  FT_PtrDist;
     arc[3].x = ras.x;
     arc[3].y = ras.y;
 
-    levels    = ras.lev_stack;
-    top       = 0;
-    levels[0] = level;
-
-    while ( top >= 0 )
+    for (;;)
     {
-      level = levels[top];
-      if ( level > 1 )
-      {
-        /* check that the arc crosses the current band */
-        TPos  min, max, y;
+    /* Check that the arc crosses the current band. */
+    TPos  min, max, y;
+
+
+    min = max = arc[0].y;
+    y = arc[1].y;
+    if ( y < min ) min = y;
+    if ( y > max ) max = y;
+    y = arc[2].y;
+    if ( y < min ) min = y;
+    if ( y > max ) max = y;
+    y = arc[3].y;
+    if ( y < min ) min = y;
+    if ( y > max ) max = y;
+    if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < 0 )
+      goto Draw;
+
+    /* Decide whether to split or draw. See "Rapid Termination Evaluation    */
+    /* for Recursive Subdivision of Bezier Curves" by Thomas F. Hain, at     */
+    /* 
http://www.cis.southalabama.edu/~hain/general/Publications/Bezier/Camera-ready%20CISST02%202.pdf.
 */
+    {
+       TPos  dx, dy, L, dx1, dy1, dx2, dy2, s, s_limit;
+
+
+       /* dx and dy are x- and y- components of the P0-P3 chord vector. */
+       dx = arc[3].x - arc[0].x;
+       dy = arc[3].y - arc[0].y; 
+
+       /* L is an (under)estimate of the Euclidean distance P0-P3.           */
+       /* If dx >= dy, then r = sqrt(dx2 + dy2) can be overestimated with    */
+       /* least maximum error by r_upperbound = dx + (sqrt(2) - 1) * dy      */
+       /* where sqrt(2) - 1 can be (over)estimated by 107/256 - giving an    */
+       /* error of no more than 8.4%. Similarly, some elementary calculus    */
+       /* shows that r can be underestimated with least maximum error by     */
+       /* r_lowerbound = sqrt(2+sqrt(2))/2 * dx + sqrt(2-sqrt(2))/2 * dy.    */
+       /* 236/256 and 97/256 are (under)estimates of the two algebraic       */
+       /* numbers - giving an error of no more than 8.1%.                    */
+       L = (  236 * FT_MAX(labs(dx), labs(dy)) 
+            +  97 * FT_MIN(labs(dx), labs(dy))) >> 8;
+
+       /* Avoid possible arithmetic overflow below by splitting. */
+       if (L > 32767)
+          goto Split;
+
+       /* s is L * the perpendicular distance from P1 to the line P0-P3. */
+       s = labs(  dy * (dx1 = arc[1].x - arc[0].x) 
+                - dx * (dy1 = arc[1].y - arc[0].y));
+
+       /* Max deviation may be as much as (s/L) * 3/4 (if Hain's v = 1). */
+       if (s > (s_limit = L * (TPos)(FT_MAX_CURVE_DEVIATION / 0.75)))
+          goto Split;
+
+       /* s is L * the perpendicular distance from P2 to the line P0-P3. */
+       s = labs(  dy * (dx2 = arc[2].x - arc[0].x) 
+                - dx * (dy2 = arc[2].y - arc[0].y));
+
+       /* Max deviation may be as much as (s/L) * 3/4 (if Hain's v = 1). */
+       if (s > s_limit)
+          goto Split;
+
+       /* If P1 or P2 is outside P0-P3, split the curve. */
+       if (   dy * dy1 + dx * dx1 < 0
+           || dy * dy2 + dx * dx2 < 0
+           || dy * (arc[3].y - arc[1].y) + dx * (arc[3].x - arc[1].x) < 0
+           || dy * (arc[3].y - arc[2].y) + dx * (arc[3].x - arc[2].x) < 0
+          )
+          goto Split;
+
+       /* No reason to split. */
+       goto Draw;
+    }
 
+    Split:
 
-        min = max = arc[0].y;
-        y = arc[1].y;
-        if ( y < min ) min = y;
-        if ( y > max ) max = y;
-        y = arc[2].y;
-        if ( y < min ) min = y;
-        if ( y > max ) max = y;
-        y = arc[3].y;
-        if ( y < min ) min = y;
-        if ( y > max ) max = y;
-        if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < 0 )
-          goto Draw;
-        gray_split_cubic( arc );
-        arc += 3;
-        top ++;
-        levels[top] = levels[top - 1] = level - 1;
-        continue;
-      }
+    gray_split_cubic( arc );
+    arc += 3;
+    continue;
 
     Draw:
-      {
-        TPos  to_x, to_y;
 
+    gray_render_line( RAS_VAR_ arc[0].x, arc[0].y );
 
-        to_x  = arc[0].x;
-        to_y  = arc[0].y;
-        mid_x = ( ras.x + to_x + 3 * ( arc[1].x + arc[2].x ) ) / 8;
-        mid_y = ( ras.y + to_y + 3 * ( arc[1].y + arc[2].y ) ) / 8;
+    if (arc == ras.bez_stack)
+      return;
 
-        gray_render_line( RAS_VAR_ mid_x, mid_y );
-        gray_render_line( RAS_VAR_ to_x, to_y );
-        top --;
-        arc -= 3;
-      }
+    arc -= 3;
     }
-
-    return;
   }
 
 
-
   static int
   gray_move_to( const FT_Vector*  to,
                 PWorker           worker )
@@ -1760,25 +1727,6 @@ typedef ptrdiff_t  FT_PtrDist;
     ras.count_ex = ras.max_ex - ras.min_ex;
     ras.count_ey = ras.max_ey - ras.min_ey;
 
-    /* simple heuristic used to speed up the bezier decomposition -- see */
-    /* the code in gray_render_conic() and gray_render_cubic() for more  */
-    /* details                                                           */
-    ras.conic_level = 32;
-    ras.cubic_level = 16;
-
-    {
-      int  level = 0;
-
-
-      if ( ras.count_ex > 24 || ras.count_ey > 24 )
-        level++;
-      if ( ras.count_ex > 120 || ras.count_ey > 120 )
-        level++;
-
-      ras.conic_level <<= level;
-      ras.cubic_level <<= level;
-    }
-
     /* set up vertical bands */
     num_bands = (int)( ( ras.max_ey - ras.min_ey ) / ras.band_size );
     if ( num_bands == 0 )
_______________________________________________
Freetype-devel mailing list
[email protected]
http://lists.nongnu.org/mailman/listinfo/freetype-devel

Reply via email to