Commit f8efbcfb8ef77797f5f2c7095a6030080e4c0855

Alexei Podtelezhnikov 2014-08-12T23:22:17

[base] Avoid undefined FT_MSB in `BBox_Cubic_Check'. * src/base/ftbbox.c (BBox_Cubic_Check): Update. (update_cubic_max): Repalce with... (cubic_peak): ... this, which now handles upscaling.

diff --git a/ChangeLog b/ChangeLog
index 48e3115..b6c5228 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2014-08-12  Alexei Podtelezhnikov  <apodtele@gmail.com>
+
+	[base] Avoid undefined FT_MSB in `BBox_Cubic_Check'.
+
+	* src/base/ftbbox.c (BBox_Cubic_Check): Update.
+	(update_cubic_max): Repalce with...
+	(cubic_peak): ... this, which now handles upscaling.
+
 2014-08-11  Alexei Podtelezhnikov  <apodtele@gmail.com>
 
 	[base] Handle collapsed outlines to avoid undefined FT_MSB.
diff --git a/src/base/ftbbox.c b/src/base/ftbbox.c
index 8d3f383..54531c6 100644
--- a/src/base/ftbbox.c
+++ b/src/base/ftbbox.c
@@ -203,15 +203,48 @@
   /*    max :: The address of the current maximum.                         */
   /*                                                                       */
   static FT_Pos
-  update_cubic_max( FT_Pos  q1,
-                    FT_Pos  q2,
-                    FT_Pos  q3,
-                    FT_Pos  q4,
-                    FT_Pos  max )
+  cubic_peak( FT_Pos  q1,
+              FT_Pos  q2,
+              FT_Pos  q3,
+              FT_Pos  q4 )
   {
+    FT_Pos  peak = 0;
+    FT_Int  shift;    
+
+    /* This function finds a peak of a cubic segment if it is above 0    */
+    /* using iterative bisection of the segment, or returns 0.           */
+    /* The fixed-point arithmetic 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.             */
+    /* It is called with either q2 or q3 positive, which is necessary    */
+    /* for the peak to exist and avoids undefined FT_MSB.                */
+
+    shift = 27 -
+      FT_MSB( FT_ABS( q1 ) | FT_ABS( q2 ) | FT_ABS( q3 ) | FT_ABS( q4 ) );
+
+    if ( shift > 0 )
+    {
+      /* upscaling too much just wastes time */
+      if ( shift > 2 )
+        shift = 2;
+
+      q1 <<=  shift;
+      q2 <<=  shift;
+      q3 <<=  shift;
+      q4 <<=  shift;
+    }
+    else
+    {
+      q1 >>= -shift;
+      q2 >>= -shift;
+      q3 >>= -shift;
+      q4 >>= -shift;
+    }
+
     /* 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 )
+    while ( q2 > 0 || q3 > 0 )
     {
       /* determine which half contains the maximum and split */
       if ( q1 + q2 > q3 + q4 ) /* first half */
@@ -240,17 +273,22 @@
       /* check whether either end reached the maximum */
       if ( q1 == q2 && q1 >= q3 )
       {
-        max = q1;
+        peak = q1;
         break;
       }
       if ( q3 == q4 && q2 <= q4 )
       {
-        max = q4;
+        peak = q4;
         break;
       }
     }
 
-    return max;
+    if ( shift > 0 )
+      peak >>=  shift;
+    else
+      peak <<= -shift;
+
+    return peak;
   }
 
 
@@ -262,65 +300,18 @@
                     FT_Pos*  min,
                     FT_Pos*  max )
   {
-    FT_Pos  nmin, nmax;
-    FT_Int  shift;
-
-
     /* 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 arithmetic 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.             */
-    /* 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 ) );
+    /* the bbox that contains all on-points.  So at least one of the     */
+    /* conditions below holds and cubic_peak is called with at least one */
+    /* non-zero argument.                                                */
 
-    if ( shift > 0 )
-    {
-      /* upscaling too much just wastes time */
-      if ( shift > 2 )
-        shift = 2;
-
-      p1 <<=  shift;
-      p2 <<=  shift;
-      p3 <<=  shift;
-      p4 <<=  shift;
-      nmin = *min << shift;
-      nmax = *max << shift;
-    }
-    else
-    {
-      p1 >>= -shift;
-      p2 >>= -shift;
-      p3 >>= -shift;
-      p4 >>= -shift;
-      nmin = *min >> -shift;
-      nmax = *max >> -shift;
-    }
-
-    nmax =  update_cubic_max(  p1,  p2,  p3,  p4,  nmax );
+    if ( p2 > *max || p3 > *max )
+      *max += cubic_peak( p1 - *max, p2 - *max, p3 - *max, p4 - *max );
 
     /* now flip the signs to update the minimum */
-    nmin = -update_cubic_max( -p1, -p2, -p3, -p4, -nmin );
-
-    if ( shift > 0 )
-    {
-      nmin >>=  shift;
-      nmax >>=  shift;
-    }
-    else
-    {
-      nmin <<= -shift;
-      nmax <<= -shift;
-    }
+    if ( p2 < *min || p3 < *min )
+      *min -= cubic_peak( *min - p1, *min - p2, *min - p3, *min - p4 );
 
-    if ( nmin < *min )
-      *min = nmin;
-    if ( nmax > *max )
-      *max = nmax;
   }