tizen 2.3.1 release
[framework/graphics/freetype.git] / src / base / fttrigon.c
index fdf433a..22b7ecf 100644 (file)
@@ -4,7 +4,7 @@
 /*                                                                         */
 /*    FreeType trigonometric functions (body).                             */
 /*                                                                         */
-/*  Copyright 2001, 2002, 2003, 2004, 2005 by                              */
+/*  Copyright 2001-2005, 2012-2014 by                                      */
 /*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
 /*                                                                         */
 /*  This file is part of the FreeType project, and may only be used,       */
 /*                                                                         */
 /***************************************************************************/
 
+  /*************************************************************************/
+  /*                                                                       */
+  /* This is a fixed-point CORDIC implementation of trigonometric          */
+  /* functions as well as transformations between Cartesian and polar      */
+  /* coordinates.  The angles are represented as 16.16 fixed-point values  */
+  /* in degrees, i.e., the angular resolution is 2^-16 degrees.  Note that */
+  /* only vectors longer than 2^16*180/pi (or at least 22 bits) on a       */
+  /* discrete Cartesian grid can have the same or better angular           */
+  /* resolution.  Therefore, to maintain this precision, some functions    */
+  /* require an interim upscaling of the vectors, whereas others operate   */
+  /* with 24-bit long vectors directly.                                    */
+  /*                                                                       */
+  /*************************************************************************/
 
 #include <ft2build.h>
 #include FT_INTERNAL_OBJECTS_H
+#include FT_INTERNAL_CALC_H
 #include FT_TRIGONOMETRY_H
 
 
-  /* the following is 0.2715717684432231 * 2^30 */
-#define FT_TRIG_COSCALE  0x11616E8EUL
+  /* the Cordic shrink factor 0.858785336480436 * 2^32 */
+#define FT_TRIG_SCALE      0xDBD95B16UL
+
+  /* the highest bit in overflow-safe vector components, */
+  /* MSB of 0.858785336480436 * sqrt(0.5) * 2^30         */
+#define FT_TRIG_SAFE_MSB   29
 
   /* this table was generated for FT_PI = 180L << 16, i.e. degrees */
 #define FT_TRIG_MAX_ITERS  23
 
-  static const FT_Fixed
-  ft_trig_arctan_table[24] =
+  static const FT_Angle
+  ft_trig_arctan_table[] =
   {
-    4157273L, 2949120L, 1740967L, 919879L, 466945L, 234379L, 117304L,
-    58666L, 29335L, 14668L, 7334L, 3667L, 1833L, 917L, 458L, 229L, 115L,
+    1740967L, 919879L, 466945L, 234379L, 117304L, 58666L, 29335L,
+    14668L, 7334L, 3667L, 1833L, 917L, 458L, 229L, 115L,
     57L, 29L, 14L, 7L, 4L, 2L, 1L
   };
 
-  /* the Cordic shrink factor, multiplied by 2^32 */
-#define FT_TRIG_SCALE    1166391785UL  /* 0x4585BA38UL */
-
 
-#ifdef FT_CONFIG_HAS_INT64
+#ifdef FT_LONG64
 
   /* multiply a given value by the CORDIC shrink factor */
   static FT_Fixed
   ft_trig_downscale( FT_Fixed  val )
   {
-    FT_Fixed  s;
-    FT_Int64  v;
+    FT_Int  s = 1;
 
 
-    s   = val;
-    val = ( val >= 0 ) ? val : -val;
+    if ( val < 0 )
+    {
+       val = -val;
+       s = -1;
+    }
 
-    v   = ( val * (FT_Int64)FT_TRIG_SCALE ) + 0x100000000UL;
-    val = (FT_Fixed)( v >> 32 );
+    /* 0x40000000 comes from regression analysis between true */
+    /* and CORDIC hypotenuse, so it minimizes the error       */
+    val = (FT_Fixed)( ( (FT_Int64)val * FT_TRIG_SCALE + 0x40000000UL ) >> 32 );
 
-    return ( s >= 0 ) ? val : -val;
+    return s < 0 ? -val : val;
   }
 
-#else /* !FT_CONFIG_HAS_INT64 */
+#else /* !FT_LONG64 */
 
   /* multiply a given value by the CORDIC shrink factor */
   static FT_Fixed
   ft_trig_downscale( FT_Fixed  val )
   {
-    FT_Fixed   s;
-    FT_UInt32  v1, v2, k1, k2, hi, lo1, lo2, lo3;
+    FT_Int     s = 1;
+    FT_UInt32  lo1, hi1, lo2, hi2, lo, hi, i1, i2;
+
 
+    if ( val < 0 )
+    {
+       val = -val;
+       s = -1;
+    }
+
+    lo1 = val & 0x0000FFFFU;
+    hi1 = val >> 16;
+    lo2 = FT_TRIG_SCALE & 0x0000FFFFU;
+    hi2 = FT_TRIG_SCALE >> 16;
 
-    s   = val;
-    val = ( val >= 0 ) ? val : -val;
+    lo = lo1 * lo2;
+    i1 = lo1 * hi2;
+    i2 = lo2 * hi1;
+    hi = hi1 * hi2;
 
-    v1 = (FT_UInt32)val >> 16;
-    v2 = (FT_UInt32)(val & 0xFFFFL);
+    /* Check carry overflow of i1 + i2 */
+    i1 += i2;
+    hi += (FT_UInt32)( i1 < i2 ) << 16;
 
-    k1 = (FT_UInt32)FT_TRIG_SCALE >> 16;       /* constant */
-    k2 = (FT_UInt32)(FT_TRIG_SCALE & 0xFFFFL);   /* constant */
+    hi += i1 >> 16;
+    i1  = i1 << 16;
 
-    hi   = k1 * v1;
-    lo1  = k1 * v2 + k2 * v1;       /* can't overflow */
+    /* Check carry overflow of i1 + lo */
+    lo += i1;
+    hi += ( lo < i1 );
 
-    lo2  = ( k2 * v2 ) >> 16;
-    lo3  = ( lo1 >= lo2 ) ? lo1 : lo2;
-    lo1 += lo2;
+    /* 0x40000000 comes from regression analysis between true */
+    /* and CORDIC hypotenuse, so it minimizes the error       */
 
-    hi  += lo1 >> 16;
-    if ( lo1 < lo3 )
-      hi += (FT_UInt32)0x10000UL;
+    /* Check carry overflow of lo + 0x40000000 */
+    lo += 0x40000000UL;
+    hi += ( lo < 0x40000000UL );
 
     val  = (FT_Fixed)hi;
 
-    return ( s >= 0 ) ? val : -val;
+    return s < 0 ? -val : val;
   }
 
-#endif /* !FT_CONFIG_HAS_INT64 */
+#endif /* !FT_LONG64 */
 
 
+  /* undefined and never called for zero vector */
   static FT_Int
   ft_trig_prenorm( FT_Vector*  vec )
   {
-    FT_Fixed  x, y, z;
-    FT_Int    shift;
+    FT_Pos  x, y;
+    FT_Int  shift;
 
 
     x = vec->x;
     y = vec->y;
 
-    z     = ( ( x >= 0 ) ? x : - x ) | ( (y >= 0) ? y : -y );
-    shift = 0;
-
-#if 1
-    /* determine msb bit index in `shift' */
-    if ( z >= ( 1L << 16 ) )
-    {
-      z     >>= 16;
-      shift  += 16;
-    }
-    if ( z >= ( 1L << 8 ) )
-    {
-      z     >>= 8;
-      shift  += 8;
-    }
-    if ( z >= ( 1L << 4 ) )
-    {
-      z     >>= 4;
-      shift  += 4;
-    }
-    if ( z >= ( 1L << 2 ) )
-    {
-      z     >>= 2;
-      shift  += 2;
-    }
-    if ( z >= ( 1L << 1 ) )
-    {
-      z    >>= 1;
-      shift += 1;
-    }
+    shift = FT_MSB( FT_ABS( x ) | FT_ABS( y ) );
 
-    if ( shift <= 27 )
+    if ( shift <= FT_TRIG_SAFE_MSB )
     {
-      shift  = 27 - shift;
-      vec->x = x << shift;
-      vec->y = y << shift;
+      shift  = FT_TRIG_SAFE_MSB - shift;
+      vec->x = (FT_Pos)( (FT_ULong)x << shift );
+      vec->y = (FT_Pos)( (FT_ULong)y << shift );
     }
     else
     {
-      shift -= 27;
+      shift -= FT_TRIG_SAFE_MSB;
       vec->x = x >> shift;
       vec->y = y >> shift;
       shift  = -shift;
     }
 
-#else /* 0 */
-
-    if ( z < ( 1L << 27 ) )
-    {
-      do
-      {
-        shift++;
-        z <<= 1;
-      } while ( z < ( 1L << 27 ) );
-      vec->x = x << shift;
-      vec->y = y << shift;
-    }
-    else if ( z > ( 1L << 28 ) )
-    {
-      do
-      {
-        shift++;
-        z >>= 1;
-      } while ( z > ( 1L << 28 ) );
-
-      vec->x = x >> shift;
-      vec->y = y >> shift;
-      shift  = -shift;
-    }
-
-#endif /* 0 */
-
     return shift;
   }
 
                          FT_Angle    theta )
   {
     FT_Int           i;
-    FT_Fixed         x, y, xtemp;
-    const FT_Fixed  *arctanptr;
+    FT_Fixed         x, y, xtemp, b;
+    const FT_Angle  *arctanptr;
 
 
     x = vec->x;
     y = vec->y;
 
-    /* Get angle between -90 and 90 degrees */
-    while ( theta <= -FT_ANGLE_PI2 )
+    /* Rotate inside [-PI/4,PI/4] sector */
+    while ( theta < -FT_ANGLE_PI4 )
     {
-      x = -x;
-      y = -y;
-      theta += FT_ANGLE_PI;
+      xtemp  =  y;
+      y      = -x;
+      x      =  xtemp;
+      theta +=  FT_ANGLE_PI2;
     }
 
-    while ( theta > FT_ANGLE_PI2 )
+    while ( theta > FT_ANGLE_PI4 )
     {
-      x = -x;
-      y = -y;
-      theta -= FT_ANGLE_PI;
+      xtemp  = -y;
+      y      =  x;
+      x      =  xtemp;
+      theta -=  FT_ANGLE_PI2;
     }
 
-    /* Initial pseudorotation, with left shift */
     arctanptr = ft_trig_arctan_table;
 
-    if ( theta < 0 )
-    {
-      xtemp  = x + ( y << 1 );
-      y      = y - ( x << 1 );
-      x      = xtemp;
-      theta += *arctanptr++;
-    }
-    else
-    {
-      xtemp  = x - ( y << 1 );
-      y      = y + ( x << 1 );
-      x      = xtemp;
-      theta -= *arctanptr++;
-    }
-
-    /* Subsequent pseudorotations, with right shifts */
-    i = 0;
-    do
+    /* Pseudorotations, with right shifts */
+    for ( i = 1, b = 1; i < FT_TRIG_MAX_ITERS; b <<= 1, i++ )
     {
       if ( theta < 0 )
       {
-        xtemp  = x + ( y >> i );
-        y      = y - ( x >> i );
+        xtemp  = x + ( ( y + b ) >> i );
+        y      = y - ( ( x + b ) >> i );
         x      = xtemp;
         theta += *arctanptr++;
       }
       else
       {
-        xtemp  = x - ( y >> i );
-        y      = y + ( x >> i );
+        xtemp  = x - ( ( y + b ) >> i );
+        y      = y + ( ( x + b ) >> i );
         x      = xtemp;
         theta -= *arctanptr++;
       }
-    } while ( ++i < FT_TRIG_MAX_ITERS );
+    }
 
     vec->x = x;
     vec->y = y;
   static void
   ft_trig_pseudo_polarize( FT_Vector*  vec )
   {
-    FT_Fixed         theta;
-    FT_Fixed         yi, i;
-    FT_Fixed         x, y;
-    const FT_Fixed  *arctanptr;
+    FT_Angle         theta;
+    FT_Int           i;
+    FT_Fixed         x, y, xtemp, b;
+    const FT_Angle  *arctanptr;
 
 
     x = vec->x;
     y = vec->y;
 
-    /* Get the vector into the right half plane */
-    theta = 0;
-    if ( x < 0 )
-    {
-      x = -x;
-      y = -y;
-      theta = 2 * FT_ANGLE_PI2;
-    }
-
-    if ( y > 0 )
-      theta = - theta;
-
-    arctanptr = ft_trig_arctan_table;
-
-    if ( y < 0 )
+    /* Get the vector into [-PI/4,PI/4] sector */
+    if ( y > x )
     {
-      /* Rotate positive */
-      yi     = y + ( x << 1 );
-      x      = x - ( y << 1 );
-      y      = yi;
-      theta -= *arctanptr++;  /* Subtract angle */
+      if ( y > -x )
+      {
+        theta =  FT_ANGLE_PI2;
+        xtemp =  y;
+        y     = -x;
+        x     =  xtemp;
+      }
+      else
+      {
+        theta =  y > 0 ? FT_ANGLE_PI : -FT_ANGLE_PI;
+        x     = -x;
+        y     = -y;
+      }
     }
     else
     {
-      /* Rotate negative */
-      yi     = y - ( x << 1 );
-      x      = x + ( y << 1 );
-      y      = yi;
-      theta += *arctanptr++;  /* Add angle */
+      if ( y < -x )
+      {
+        theta = -FT_ANGLE_PI2;
+        xtemp = -y;
+        y     =  x;
+        x     =  xtemp;
+      }
+      else
+      {
+        theta = 0;
+      }
     }
 
-    i = 0;
-    do
+    arctanptr = ft_trig_arctan_table;
+
+    /* Pseudorotations, with right shifts */
+    for ( i = 1, b = 1; i < FT_TRIG_MAX_ITERS; b <<= 1, i++ )
     {
-      if ( y < 0 )
+      if ( y > 0 )
       {
-        /* Rotate positive */
-        yi     = y + ( x >> i );
-        x      = x - ( y >> i );
-        y      = yi;
-        theta -= *arctanptr++;
+        xtemp  = x + ( ( y + b ) >> i );
+        y      = y - ( ( x + b ) >> i );
+        x      = xtemp;
+        theta += *arctanptr++;
       }
       else
       {
-        /* Rotate negative */
-        yi     = y - ( x >> i );
-        x      = x + ( y >> i );
-        y      = yi;
-        theta += *arctanptr++;
+        xtemp  = x - ( ( y + b ) >> i );
+        y      = y + ( ( x + b ) >> i );
+        x      = xtemp;
+        theta -= *arctanptr++;
       }
-    } while ( ++i < FT_TRIG_MAX_ITERS );
+    }
 
-    /* round theta */
+    /* round theta to acknowledge its error that mostly comes */
+    /* from accumulated rounding errors in the arctan table   */
     if ( theta >= 0 )
-      theta = FT_PAD_ROUND( theta, 32 );
+      theta = FT_PAD_ROUND( theta, 16 );
     else
-      theta = -FT_PAD_ROUND( -theta, 32 );
+      theta = -FT_PAD_ROUND( -theta, 16 );
 
     vec->x = x;
     vec->y = theta;
     FT_Vector  v;
 
 
-    v.x = FT_TRIG_COSCALE >> 2;
+    v.x = FT_TRIG_SCALE >> 8;
     v.y = 0;
     ft_trig_pseudo_rotate( &v, angle );
 
-    return v.x / ( 1 << 12 );
+    return ( v.x + 0x80L ) >> 8;
   }
 
 
     FT_Vector  v;
 
 
-    v.x = FT_TRIG_COSCALE >> 2;
+    v.x = FT_TRIG_SCALE >> 8;
     v.y = 0;
     ft_trig_pseudo_rotate( &v, angle );
 
   FT_Vector_Unit( FT_Vector*  vec,
                   FT_Angle    angle )
   {
-    vec->x = FT_TRIG_COSCALE >> 2;
+    if ( !vec )
+      return;
+
+    vec->x = FT_TRIG_SCALE >> 8;
     vec->y = 0;
     ft_trig_pseudo_rotate( vec, angle );
-    vec->x >>= 12;
-    vec->y >>= 12;
+    vec->x = ( vec->x + 0x80L ) >> 8;
+    vec->y = ( vec->y + 0x80L ) >> 8;
   }
 
 
     FT_Vector  v;
 
 
+    if ( !vec )
+      return;
+
     v.x   = vec->x;
     v.y   = vec->y;
 
       else
       {
         shift  = -shift;
-        vec->x = v.x << shift;
-        vec->y = v.y << shift;
+        vec->x = (FT_Pos)( (FT_ULong)v.x << shift );
+        vec->y = (FT_Pos)( (FT_ULong)v.y << shift );
       }
     }
   }
     FT_Vector  v;
 
 
+    if ( !vec )
+      return 0;
+
     v = *vec;
 
     /* handle trivial cases */
     if ( v.x == 0 )
     {
-      return ( v.y >= 0 ) ? v.y : -v.y;
+      return FT_ABS( v.y );
     }
     else if ( v.y == 0 )
     {
-      return ( v.x >= 0 ) ? v.x : -v.x;
+      return FT_ABS( v.x );
     }
 
     /* general case */
     if ( shift > 0 )
       return ( v.x + ( 1 << ( shift - 1 ) ) ) >> shift;
 
-    return v.x << -shift;
+    return (FT_Fixed)( (FT_UInt32)v.x << -shift );
   }
 
 
     FT_Vector  v;
 
 
+    if ( !vec || !length || !angle )
+      return;
+
     v = *vec;
 
     if ( v.x == 0 && v.y == 0 )
 
     v.x = ft_trig_downscale( v.x );
 
-    *length = ( shift >= 0 ) ? ( v.x >> shift ) : ( v.x << -shift );
+    *length = shift >= 0 ?                      ( v.x >>  shift )
+                         : (FT_Fixed)( (FT_UInt32)v.x << -shift );
     *angle  = v.y;
   }
 
                         FT_Fixed    length,
                         FT_Angle    angle )
   {
+    if ( !vec )
+      return;
+
     vec->x = length;
     vec->y = 0;