Edit

kc3-lang/freetype/src/base/fttrigon.c

Branch :

  • Show log

    Commit

  • Author : Alexei Podtelezhnikov
    Date : 2012-12-20 01:03:22
    Hash : d7383b7b
    Message : [base] Improve trigonometric core. FreeType used to rely on a 24-step iteration CORDIC algorithm to calculate trigonometric functions and rotate vectors. It turns out that once the vector is in the right half-plane, the initial rotation by 63 degrees is not necessary. The algorithm is perfectly capable to converge to any angle starting from the second 45 degree rotation. This patch removes the first rotation and makes it a 23-step CORDIC algorithm. * src/base/fttrigon.c (FT_TRIG_SCALE, FT_TRIG_COSCALE): Update macro values. (ft_trig_pseudo_rotate, ft_trig_pseudo_polarize): Remove initial rotation.

  • src/base/fttrigon.c
  • /***************************************************************************/
    /*                                                                         */
    /*  fttrigon.c                                                             */
    /*                                                                         */
    /*    FreeType trigonometric functions (body).                             */
    /*                                                                         */
    /*  Copyright 2001-2005, 2012 by                                           */
    /*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
    /*                                                                         */
    /*  This file is part of the FreeType project, and may only be used,       */
    /*  modified, and distributed under the terms of the FreeType project      */
    /*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
    /*  this file you indicate that you have read the license and              */
    /*  understand and accept it fully.                                        */
    /*                                                                         */
    /***************************************************************************/
    
    
    #include <ft2build.h>
    #include FT_INTERNAL_OBJECTS_H
    #include FT_TRIGONOMETRY_H
    
    
    #ifdef FT_LONG64
      typedef FT_INT64  FT_Int64;
    #endif
    
    
      /* the Cordic shrink factor 0.607252935008887 * 2^32 */
    #define FT_TRIG_SCALE    0x9B74EDA8UL
    
      /* the following is 0.607252935008887 * 2^30 */
    #define FT_TRIG_COSCALE  0x26DD3B6AUL
    
      /* 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[23] =
      {
        2949120L, 1740967L, 919879L, 466945L, 234379L, 117304L, 58666L,
        29335L, 14668L, 7334L, 3667L, 1833L, 917L, 458L, 229L, 115L,
        57L, 29L, 14L, 7L, 4L, 2L, 1L
      };
    
    
    #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;
    
    
        s   = val;
        val = ( val >= 0 ) ? val : -val;
    
        v   = ( val * (FT_Int64)FT_TRIG_SCALE ) + 0x100000000UL;
        val = (FT_Fixed)( v >> 32 );
    
        return ( s >= 0 ) ? val : -val;
      }
    
    #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;
    
    
        s   = val;
        val = ( val >= 0 ) ? val : -val;
    
        v1 = (FT_UInt32)val >> 16;
        v2 = (FT_UInt32)( val & 0xFFFFL );
    
        k1 = (FT_UInt32)FT_TRIG_SCALE >> 16;           /* constant */
        k2 = (FT_UInt32)( FT_TRIG_SCALE & 0xFFFFL );   /* constant */
    
        hi   = k1 * v1;
        lo1  = k1 * v2 + k2 * v1;       /* can't overflow */
    
        lo2  = ( k2 * v2 ) >> 16;
        lo3  = ( lo1 >= lo2 ) ? lo1 : lo2;
        lo1 += lo2;
    
        hi  += lo1 >> 16;
        if ( lo1 < lo3 )
          hi += (FT_UInt32)0x10000UL;
    
        val  = (FT_Fixed)hi;
    
        return ( s >= 0 ) ? val : -val;
      }
    
    #endif /* !FT_LONG64 */
    
    
      static FT_Int
      ft_trig_prenorm( FT_Vector*  vec )
      {
        FT_Fixed  x, y, z;
        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;
        }
    
        if ( shift <= 27 )
        {
          shift  = 27 - shift;
          vec->x = x << shift;
          vec->y = y << shift;
        }
        else
        {
          shift -= 27;
          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;
      }
    
    
      static void
      ft_trig_pseudo_rotate( FT_Vector*  vec,
                             FT_Angle    theta )
      {
        FT_Int           i;
        FT_Fixed         x, y, xtemp;
        const FT_Fixed  *arctanptr;
    
    
        x = vec->x;
        y = vec->y;
    
        /* Get angle between -90 and 90 degrees */
        while ( theta <= -FT_ANGLE_PI2 )
        {
          x = -x;
          y = -y;
          theta += FT_ANGLE_PI;
        }
    
        while ( theta > FT_ANGLE_PI2 )
        {
          x = -x;
          y = -y;
          theta -= FT_ANGLE_PI;
        }
    
        arctanptr = ft_trig_arctan_table;
    
        /* Pseudorotations, with right shifts */
        i = 0;
        do
        {
          if ( theta < 0 )
          {
            xtemp  = x + ( y >> i );
            y      = y - ( x >> i );
            x      = xtemp;
            theta += *arctanptr++;
          }
          else
          {
            xtemp  = x - ( y >> i );
            y      = y + ( x >> 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_Angle         theta;
        FT_Int           i;
        FT_Fixed         x, y, xtemp;
        const FT_Fixed  *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;
    
        /* Pseudorotations, with right shifts */
        i = 0;
        do
        {
          if ( y > 0 )
          {
            xtemp  = x + ( y >> i );
            y      = y - ( x >> i );
            x      = xtemp;
            theta += *arctanptr++;
          }
          else
          {
            xtemp  = x - ( y >> i );
            y      = y + ( x >> i );
            x      = xtemp;
            theta -= *arctanptr++;
          }
        } while ( ++i < FT_TRIG_MAX_ITERS );
    
        /* round theta */
        if ( theta >= 0 )
          theta = FT_PAD_ROUND( theta, 32 );
        else
          theta = -FT_PAD_ROUND( -theta, 32 );
    
        vec->x = x;
        vec->y = theta;
      }
    
    
      /* documentation is in fttrigon.h */
    
      FT_EXPORT_DEF( FT_Fixed )
      FT_Cos( FT_Angle  angle )
      {
        FT_Vector  v;
    
    
        v.x = FT_TRIG_COSCALE >> 2;
        v.y = 0;
        ft_trig_pseudo_rotate( &v, angle );
    
        return v.x / ( 1 << 12 );
      }
    
    
      /* documentation is in fttrigon.h */
    
      FT_EXPORT_DEF( FT_Fixed )
      FT_Sin( FT_Angle  angle )
      {
        return FT_Cos( FT_ANGLE_PI2 - angle );
      }
    
    
      /* documentation is in fttrigon.h */
    
      FT_EXPORT_DEF( FT_Fixed )
      FT_Tan( FT_Angle  angle )
      {
        FT_Vector  v;
    
    
        v.x = FT_TRIG_COSCALE >> 2;
        v.y = 0;
        ft_trig_pseudo_rotate( &v, angle );
    
        return FT_DivFix( v.y, v.x );
      }
    
    
      /* documentation is in fttrigon.h */
    
      FT_EXPORT_DEF( FT_Angle )
      FT_Atan2( FT_Fixed  dx,
                FT_Fixed  dy )
      {
        FT_Vector  v;
    
    
        if ( dx == 0 && dy == 0 )
          return 0;
    
        v.x = dx;
        v.y = dy;
        ft_trig_prenorm( &v );
        ft_trig_pseudo_polarize( &v );
    
        return v.y;
      }
    
    
      /* documentation is in fttrigon.h */
    
      FT_EXPORT_DEF( void )
      FT_Vector_Unit( FT_Vector*  vec,
                      FT_Angle    angle )
      {
        vec->x = FT_TRIG_COSCALE >> 2;
        vec->y = 0;
        ft_trig_pseudo_rotate( vec, angle );
        vec->x >>= 12;
        vec->y >>= 12;
      }
    
    
      /* these macros return 0 for positive numbers,
         and -1 for negative ones */
    #define FT_SIGN_LONG( x )   ( (x) >> ( FT_SIZEOF_LONG * 8 - 1 ) )
    #define FT_SIGN_INT( x )    ( (x) >> ( FT_SIZEOF_INT * 8 - 1 ) )
    #define FT_SIGN_INT32( x )  ( (x) >> 31 )
    #define FT_SIGN_INT16( x )  ( (x) >> 15 )
    
    
      /* documentation is in fttrigon.h */
    
      FT_EXPORT_DEF( void )
      FT_Vector_Rotate( FT_Vector*  vec,
                        FT_Angle    angle )
      {
        FT_Int     shift;
        FT_Vector  v;
    
    
        v.x   = vec->x;
        v.y   = vec->y;
    
        if ( angle && ( v.x != 0 || v.y != 0 ) )
        {
          shift = ft_trig_prenorm( &v );
          ft_trig_pseudo_rotate( &v, angle );
          v.x = ft_trig_downscale( v.x );
          v.y = ft_trig_downscale( v.y );
    
          if ( shift > 0 )
          {
            FT_Int32  half = (FT_Int32)1L << ( shift - 1 );
    
    
            vec->x = ( v.x + half + FT_SIGN_LONG( v.x ) ) >> shift;
            vec->y = ( v.y + half + FT_SIGN_LONG( v.y ) ) >> shift;
          }
          else
          {
            shift  = -shift;
            vec->x = v.x << shift;
            vec->y = v.y << shift;
          }
        }
      }
    
    
      /* documentation is in fttrigon.h */
    
      FT_EXPORT_DEF( FT_Fixed )
      FT_Vector_Length( FT_Vector*  vec )
      {
        FT_Int     shift;
        FT_Vector  v;
    
    
        v = *vec;
    
        /* handle trivial cases */
        if ( v.x == 0 )
        {
          return ( v.y >= 0 ) ? v.y : -v.y;
        }
        else if ( v.y == 0 )
        {
          return ( v.x >= 0 ) ? v.x : -v.x;
        }
    
        /* general case */
        shift = ft_trig_prenorm( &v );
        ft_trig_pseudo_polarize( &v );
    
        v.x = ft_trig_downscale( v.x );
    
        if ( shift > 0 )
          return ( v.x + ( 1 << ( shift - 1 ) ) ) >> shift;
    
        return v.x << -shift;
      }
    
    
      /* documentation is in fttrigon.h */
    
      FT_EXPORT_DEF( void )
      FT_Vector_Polarize( FT_Vector*  vec,
                          FT_Fixed   *length,
                          FT_Angle   *angle )
      {
        FT_Int     shift;
        FT_Vector  v;
    
    
        v = *vec;
    
        if ( v.x == 0 && v.y == 0 )
          return;
    
        shift = ft_trig_prenorm( &v );
        ft_trig_pseudo_polarize( &v );
    
        v.x = ft_trig_downscale( v.x );
    
        *length = ( shift >= 0 ) ? ( v.x >> shift ) : ( v.x << -shift );
        *angle  = v.y;
      }
    
    
      /* documentation is in fttrigon.h */
    
      FT_EXPORT_DEF( void )
      FT_Vector_From_Polar( FT_Vector*  vec,
                            FT_Fixed    length,
                            FT_Angle    angle )
      {
        vec->x = length;
        vec->y = 0;
    
        FT_Vector_Rotate( vec, angle );
      }
    
    
      /* documentation is in fttrigon.h */
    
      FT_EXPORT_DEF( FT_Angle )
      FT_Angle_Diff( FT_Angle  angle1,
                     FT_Angle  angle2 )
      {
        FT_Angle  delta = angle2 - angle1;
    
    
        delta %= FT_ANGLE_2PI;
        if ( delta < 0 )
          delta += FT_ANGLE_2PI;
    
        if ( delta > FT_ANGLE_PI )
          delta -= FT_ANGLE_2PI;
    
        return delta;
      }
    
    
    /* END */