Commit fc32e1c8cc67b0481d3e563e39dd11f120585b7f

Alexei Podtelezhnikov 2013-08-19T22:57:05

[base] Enable new algorithm for BBox_Cubic_Check. * src/base/ftbbox.c: Enable new BBox_Cubic_Check algorithm, remove the old one. Improve comments.

diff --git a/ChangeLog b/ChangeLog
index eb13f32..051ef3b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2013-08-19  Alexei Podtelezhnikov  <apodtele@gmail.com>
+
+	[base] Enable new algorithm for BBox_Cubic_Check.
+
+	* src/base/ftbbox.c: Enable new BBox_Cubic_Check algorithm, remove the
+	old one. Improve comments.
+
 2013-08-18  Werner Lemberg  <wl@gnu.org>
 
 	* builds/unix/unix-def.in (freetype2.pc): Don't set executable bit.
diff --git a/src/base/ftbbox.c b/src/base/ftbbox.c
index c4bd027..ebbfb1a 100644
--- a/src/base/ftbbox.c
+++ b/src/base/ftbbox.c
@@ -109,9 +109,9 @@
                     FT_Pos*  max )
   {
     /* This function is only called when a control off-point is outside  */
-    /* the bbox. This also means there must be a local extremum within   */
-    /* the segment with the value of (y1*y3 - y2*y2)/(y1 - 2*y2 + y3).   */
-    /* Offsetting from the closest point to the extermum, y2, we get     */
+    /* the bbox that contains all on-points. It finds a local extremum   */
+    /* within the segment, equal to (y1*y3 - y2*y2)/(y1 - 2*y2 + y3).    */
+    /* Or, offsetting from y2, we get                                    */
 
     y1 -= y2;
     y3 -= y2;
@@ -185,8 +185,8 @@
   /*                                                                       */
   /* <Description>                                                         */
   /*    Finds the extrema of a 1-dimensional cubic Bezier curve and        */
-  /*    updates a bounding range.  This version uses splitting because we  */
-  /*    don't want to use square roots and extra accuracy.                 */
+  /*    updates a bounding range.  This version uses iterative splitting   */
+  /*    because it is faster than the exact solution with square roots.    */
   /*                                                                       */
   /* <Input>                                                               */
   /*    p1  :: The start coordinate.                                       */
@@ -203,17 +203,15 @@
   /*    max :: The address of the current maximum.                         */
   /*                                                                       */
 
-#if 0
-
   static FT_Pos
-  update_max( FT_Pos   q1,
-              FT_Pos   q2,
-              FT_Pos   q3,
-              FT_Pos   q4,
-              FT_Pos   max )
+  update_cubic_max( FT_Pos   q1,
+                    FT_Pos   q2,
+                    FT_Pos   q3,
+                    FT_Pos   q4,
+                    FT_Pos   max )
   {
-    /* for a conic segment to possibly reach new maximum     */
-    /* one of its off-points must be above the current value */
+    /* for a cubic segment to possibly reach new maximum, at least */
+    /* one of its off-points must stay above the current value     */
     while ( q2 > max || q3 > max )
     {
       /* determine which half contains the maximum and split */
@@ -267,13 +265,15 @@
     FT_Pos nmin, nmax;
     FT_Int shift;
 
-    /* This implementation relies on iterative bisection of the segment. */
-    /* The fixed-point arithmentic of bisection is inherently stable but */
-    /* may loose accuracy in the two lowest bits. To compensate, we      */
-    /* upscale the segment if there is room. Large values may need to be */
-    /* downscaled to avoid overflows during bisection bisection. This    */
-    /* function is only called when a control off-point is outside the   */
-    /* the bbox and, thus, has the top absolute value among arguments.   */
+    /* This function is only called when a control off-point is outside  */
+    /* the bbox that contains all on-points. It finds a local extremum   */
+    /* within the segment using iterative bisection of the segment.      */
+    /* The fixed-point arithmentic of bisection is inherently stable     */
+    /* but may loose accuracy in the two lowest bits. To compensate,     */
+    /* we upscale the segment if there is room. Large values may need    */
+    /* to be downscaled to avoid overflows during bisection bisection.   */
+    /* The control off-point outside the bbox is likely to have the top  */
+    /* absolute value among arguments.                                   */
 
     shift = 27 - FT_MSB( FT_ABS( p2 ) | FT_ABS( p3 ) );
 
@@ -282,7 +282,7 @@
       /* upscaling too much just wastes time */
       if ( shift > 2 )
         shift = 2;
-  
+
       p1 <<=  shift;
       p2 <<=  shift;
       p3 <<=  shift;
@@ -290,7 +290,7 @@
       nmin = *min << shift;
       nmax = *max << shift;
     }
-    else 
+    else
     {
       p1 >>= -shift;
       p2 >>= -shift;
@@ -300,10 +300,10 @@
       nmax = *max >> -shift;
     }
 
-    nmax =  update_max(  p1,  p2,  p3,  p4,  nmax ); 
+    nmax =  update_cubic_max(  p1,  p2,  p3,  p4,  nmax );
 
     /* now flip the signs to update the minimum */
-    nmin = -update_max( -p1, -p2, -p3, -p4, -nmin );
+    nmin = -update_cubic_max( -p1, -p2, -p3, -p4, -nmin );
 
     if ( shift > 0 )
     {
@@ -322,172 +322,6 @@
       *max = nmax;
   }
 
-#else
-
-  static void
-  test_cubic_extrema( FT_Pos    y1,
-                      FT_Pos    y2,
-                      FT_Pos    y3,
-                      FT_Pos    y4,
-                      FT_Fixed  u,
-                      FT_Pos*   min,
-                      FT_Pos*   max )
-  {
- /* FT_Pos    a = y4 - 3*y3 + 3*y2 - y1; */
-    FT_Pos    b = y3 - 2*y2 + y1;
-    FT_Pos    c = y2 - y1;
-    FT_Pos    d = y1;
-    FT_Pos    y;
-    FT_Fixed  uu;
-
-    FT_UNUSED ( y4 );
-
-
-    /* The polynomial is                      */
-    /*                                        */
-    /*    P(x) = a*x^3 + 3b*x^2 + 3c*x + d  , */
-    /*                                        */
-    /*   dP/dx = 3a*x^2 + 6b*x + 3c         . */
-    /*                                        */
-    /* However, we also have                  */
-    /*                                        */
-    /*   dP/dx(u) = 0                       , */
-    /*                                        */
-    /* which implies by subtraction that      */
-    /*                                        */
-    /*   P(u) = b*u^2 + 2c*u + d            . */
-
-    if ( u > 0 && u < 0x10000L )
-    {
-      uu = FT_MulFix( u, u );
-      y  = d + FT_MulFix( c, 2*u ) + FT_MulFix( b, uu );
-
-      if ( y < *min ) *min = y;
-      if ( y > *max ) *max = y;
-    }
-  }
-
-
-  static void
-  BBox_Cubic_Check( FT_Pos   y1,
-                    FT_Pos   y2,
-                    FT_Pos   y3,
-                    FT_Pos   y4,
-                    FT_Pos*  min,
-                    FT_Pos*  max )
-  {
-    /* always compare first and last points */
-    if      ( y1 < *min )  *min = y1;
-    else if ( y1 > *max )  *max = y1;
-
-    if      ( y4 < *min )  *min = y4;
-    else if ( y4 > *max )  *max = y4;
-
-    /* now, try to see if there are split points here */
-    if ( y1 <= y4 )
-    {
-      /* flat or ascending arc test */
-      if ( y1 <= y2 && y2 <= y4 && y1 <= y3 && y3 <= y4 )
-        return;
-    }
-    else /* y1 > y4 */
-    {
-      /* descending arc test */
-      if ( y1 >= y2 && y2 >= y4 && y1 >= y3 && y3 >= y4 )
-        return;
-    }
-
-    /* There are some split points.  Find them.                        */
-    /* We already made sure that a, b, and c below cannot be all zero. */
-    {
-      FT_Pos    a = y4 - 3*y3 + 3*y2 - y1;
-      FT_Pos    b = y3 - 2*y2 + y1;
-      FT_Pos    c = y2 - y1;
-      FT_Pos    d;
-      FT_Fixed  t;
-      FT_Int    shift;
-
-
-      /* We need to solve `ax^2+2bx+c' here, without floating points!      */
-      /* The trick is to normalize to a different representation in order  */
-      /* to use our 16.16 fixed-point routines.                            */
-      /*                                                                   */
-      /* We compute FT_MulFix(b,b) and FT_MulFix(a,c) after normalization. */
-      /* These values must fit into a single 16.16 value.                  */
-      /*                                                                   */
-      /* We normalize a, b, and c to `8.16' fixed-point values to ensure   */
-      /* that their product is held in a `16.16' value including the sign. */
-      /* Necessarily, we need to shift `a', `b', and `c' so that the most  */
-      /* significant bit of their absolute values is at position 22.       */
-      /*                                                                   */
-      /* This also means that we are using 23 bits of precision to compute */
-      /* the zeros, independently of the range of the original polynomial  */
-      /* coefficients.                                                     */
-      /*                                                                   */
-      /* This algorithm should ensure reasonably accurate values for the   */
-      /* zeros.  Note that they are only expressed with 16 bits when       */
-      /* computing the extrema (the zeros need to be in 0..1 exclusive     */
-      /* to be considered part of the arc).                                */
-
-      shift = FT_MSB( FT_ABS( a ) | FT_ABS( b ) | FT_ABS( c ) );
-
-      if ( shift > 22 )
-      {
-        shift -= 22;
-
-        /* this loses some bits of precision, but we use 23 of them */
-        /* for the computation anyway                               */
-        a >>= shift;
-        b >>= shift;
-        c >>= shift;
-      }
-      else
-      {
-        shift = 22 - shift;
-
-        a <<= shift;
-        b <<= shift;
-        c <<= shift;
-      }
-
-      /* handle a == 0 */
-      if ( a == 0 )
-      {
-        if ( b != 0 )
-        {
-          t = - FT_DivFix( c, b ) / 2;
-          test_cubic_extrema( y1, y2, y3, y4, t, min, max );
-        }
-      }
-      else
-      {
-        /* solve the equation now */
-        d = FT_MulFix( b, b ) - FT_MulFix( a, c );
-        if ( d < 0 )
-          return;
-
-        if ( d == 0 )
-        {
-          /* there is a single split point at -b/a */
-          t = - FT_DivFix( b, a );
-          test_cubic_extrema( y1, y2, y3, y4, t, min, max );
-        }
-        else
-        {
-          /* there are two solutions; we need to filter them */
-          d = FT_SqrtFixed( (FT_Int32)d );
-          t = - FT_DivFix( b - d, a );
-          test_cubic_extrema( y1, y2, y3, y4, t, min, max );
-
-          t = - FT_DivFix( b + d, a );
-          test_cubic_extrema( y1, y2, y3, y4, t, min, max );
-        }
-      }
-    }
-  }
-
-#endif
-
 
   /*************************************************************************/
   /*                                                                       */