1 /***************************************************************************/
2 /*                                                                         */
3 /*  fttrigon.c                                                             */
4 /*                                                                         */
5 /*    FreeType trigonometric functions (body).                             */
6 /*                                                                         */
7 /*  Copyright 2001-2005, 2012-2014 by                                      */
8 /*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
9 /*                                                                         */
10 /*  This file is part of the FreeType project, and may only be used,       */
11 /*  modified, and distributed under the terms of the FreeType project      */
12 /*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
13 /*  this file you indicate that you have read the license and              */
14 /*  understand and accept it fully.                                        */
15 /*                                                                         */
16 /***************************************************************************/
18   /*************************************************************************/
19   /*                                                                       */
20   /* This is a fixed-point CORDIC implementation of trigonometric          */
21   /* functions as well as transformations between Cartesian and polar      */
22   /* coordinates.  The angles are represented as 16.16 fixed-point values  */
23   /* in degrees, i.e., the angular resolution is 2^-16 degrees.  Note that */
24   /* only vectors longer than 2^16*180/pi (or at least 22 bits) on a       */
25   /* discrete Cartesian grid can have the same or better angular           */
26   /* resolution.  Therefore, to maintain this precision, some functions    */
27   /* require an interim upscaling of the vectors, whereas others operate   */
28   /* with 24-bit long vectors directly.                                    */
29   /*                                                                       */
30   /*************************************************************************/
32 #include <ft2build.h>
33 #include FT_INTERNAL_OBJECTS_H
34 #include FT_INTERNAL_CALC_H
35 #include FT_TRIGONOMETRY_H
38   /* the Cordic shrink factor 0.858785336480436 * 2^32 */
39 #define FT_TRIG_SCALE      0xDBD95B16UL
41   /* the highest bit in overflow-safe vector components, */
42   /* MSB of 0.858785336480436 * sqrt(0.5) * 2^30         */
43 #define FT_TRIG_SAFE_MSB   29
45   /* this table was generated for FT_PI = 180L << 16, i.e. degrees */
46 #define FT_TRIG_MAX_ITERS  23
48   static const FT_Angle
49   ft_trig_arctan_table[] =
50   {
51     1740967L, 919879L, 466945L, 234379L, 117304L, 58666L, 29335L,
52     14668L, 7334L, 3667L, 1833L, 917L, 458L, 229L, 115L,
53     57L, 29L, 14L, 7L, 4L, 2L, 1L
54   };
57 #ifdef FT_LONG64
59   /* multiply a given value by the CORDIC shrink factor */
60   static FT_Fixed
61   ft_trig_downscale( FT_Fixed  val )
62   {
63     FT_Int  s = 1;
66     if ( val < 0 )
67     {
68        val = -val;
69        s = -1;
70     }
72     /* 0x40000000 comes from regression analysis between true */
73     /* and CORDIC hypotenuse, so it minimizes the error       */
74     val = (FT_Fixed)( ( (FT_Int64)val * FT_TRIG_SCALE + 0x40000000UL ) >> 32 );
76     return ( s >= 0 ) ? val : -val;
77   }
79 #else /* !FT_LONG64 */
81   /* multiply a given value by the CORDIC shrink factor */
82   static FT_Fixed
83   ft_trig_downscale( FT_Fixed  val )
84   {
85     FT_Int     s = 1;
86     FT_UInt32  lo1, hi1, lo2, hi2, lo, hi, i1, i2;
89     if ( val < 0 )
90     {
91        val = -val;
92        s = -1;
93     }
95     lo1 = val & 0x0000FFFFU;
96     hi1 = val >> 16;
97     lo2 = FT_TRIG_SCALE & 0x0000FFFFU;
98     hi2 = FT_TRIG_SCALE >> 16;
100     lo = lo1 * lo2;
101     i1 = lo1 * hi2;
102     i2 = lo2 * hi1;
103     hi = hi1 * hi2;
105     /* Check carry overflow of i1 + i2 */
106     i1 += i2;
107     hi += (FT_UInt32)( i1 < i2 ) << 16;
109     hi += i1 >> 16;
110     i1  = i1 << 16;
112     /* Check carry overflow of i1 + lo */
113     lo += i1;
114     hi += ( lo < i1 );
116     /* 0x40000000 comes from regression analysis between true */
117     /* and CORDIC hypotenuse, so it minimizes the error       */
119     /* Check carry overflow of lo + 0x40000000 */
120     lo += 0x40000000U;
121     hi += ( lo < 0x40000000U );
123     val  = (FT_Fixed)hi;
125     return ( s >= 0 ) ? val : -val;
126   }
128 #endif /* !FT_LONG64 */
131   /* undefined and never called for zero vector */
132   static FT_Int
133   ft_trig_prenorm( FT_Vector*  vec )
134   {
135     FT_Pos  x, y;
136     FT_Int  shift;
139     x = vec->x;
140     y = vec->y;
142     shift = FT_MSB( FT_ABS( x ) | FT_ABS( y ) );
144     if ( shift <= FT_TRIG_SAFE_MSB )
145     {
146       shift  = FT_TRIG_SAFE_MSB - shift;
147       vec->x = (FT_Pos)( (FT_ULong)x << shift );
148       vec->y = (FT_Pos)( (FT_ULong)y << shift );
149     }
150     else
151     {
152       shift -= FT_TRIG_SAFE_MSB;
153       vec->x = x >> shift;
154       vec->y = y >> shift;
155       shift  = -shift;
156     }
158     return shift;
159   }
162   static void
163   ft_trig_pseudo_rotate( FT_Vector*  vec,
164                          FT_Angle    theta )
165   {
166     FT_Int           i;
167     FT_Fixed         x, y, xtemp, b;
168     const FT_Angle  *arctanptr;
171     x = vec->x;
172     y = vec->y;
174     /* Rotate inside [-PI/4,PI/4] sector */
175     while ( theta < -FT_ANGLE_PI4 )
176     {
177       xtemp  =  y;
178       y      = -x;
179       x      =  xtemp;
180       theta +=  FT_ANGLE_PI2;
181     }
183     while ( theta > FT_ANGLE_PI4 )
184     {
185       xtemp  = -y;
186       y      =  x;
187       x      =  xtemp;
188       theta -=  FT_ANGLE_PI2;
189     }
191     arctanptr = ft_trig_arctan_table;
193     /* Pseudorotations, with right shifts */
194     for ( i = 1, b = 1; i < FT_TRIG_MAX_ITERS; b <<= 1, i++ )
195     {
196       if ( theta < 0 )
197       {
198         xtemp  = x + ( ( y + b ) >> i );
199         y      = y - ( ( x + b ) >> i );
200         x      = xtemp;
201         theta += *arctanptr++;
202       }
203       else
204       {
205         xtemp  = x - ( ( y + b ) >> i );
206         y      = y + ( ( x + b ) >> i );
207         x      = xtemp;
208         theta -= *arctanptr++;
209       }
210     }
212     vec->x = x;
213     vec->y = y;
214   }
217   static void
218   ft_trig_pseudo_polarize( FT_Vector*  vec )
219   {
220     FT_Angle         theta;
221     FT_Int           i;
222     FT_Fixed         x, y, xtemp, b;
223     const FT_Angle  *arctanptr;
226     x = vec->x;
227     y = vec->y;
229     /* Get the vector into [-PI/4,PI/4] sector */
230     if ( y > x )
231     {
232       if ( y > -x )
233       {
234         theta =  FT_ANGLE_PI2;
235         xtemp =  y;
236         y     = -x;
237         x     =  xtemp;
238       }
239       else
240       {
241         theta =  y > 0 ? FT_ANGLE_PI : -FT_ANGLE_PI;
242         x     = -x;
243         y     = -y;
244       }
245     }
246     else
247     {
248       if ( y < -x )
249       {
250         theta = -FT_ANGLE_PI2;
251         xtemp = -y;
252         y     =  x;
253         x     =  xtemp;
254       }
255       else
256       {
257         theta = 0;
258       }
259     }
261     arctanptr = ft_trig_arctan_table;
263     /* Pseudorotations, with right shifts */
264     for ( i = 1, b = 1; i < FT_TRIG_MAX_ITERS; b <<= 1, i++ )
265     {
266       if ( y > 0 )
267       {
268         xtemp  = x + ( ( y + b ) >> i );
269         y      = y - ( ( x + b ) >> i );
270         x      = xtemp;
271         theta += *arctanptr++;
272       }
273       else
274       {
275         xtemp  = x - ( ( y + b ) >> i );
276         y      = y + ( ( x + b ) >> i );
277         x      = xtemp;
278         theta -= *arctanptr++;
279       }
280     }
282     /* round theta to acknowledge its error that mostly comes */
283     /* from accumulated rounding errors in the arctan table   */
284     if ( theta >= 0 )
285       theta = FT_PAD_ROUND( theta, 16 );
286     else
287       theta = -FT_PAD_ROUND( -theta, 16 );
289     vec->x = x;
290     vec->y = theta;
291   }
294   /* documentation is in fttrigon.h */
296   FT_EXPORT_DEF( FT_Fixed )
297   FT_Cos( FT_Angle  angle )
298   {
299     FT_Vector  v;
302     v.x = FT_TRIG_SCALE >> 8;
303     v.y = 0;
304     ft_trig_pseudo_rotate( &v, angle );
306     return ( v.x + 0x80L ) >> 8;
307   }
310   /* documentation is in fttrigon.h */
312   FT_EXPORT_DEF( FT_Fixed )
313   FT_Sin( FT_Angle  angle )
314   {
315     return FT_Cos( FT_ANGLE_PI2 - angle );
316   }
319   /* documentation is in fttrigon.h */
321   FT_EXPORT_DEF( FT_Fixed )
322   FT_Tan( FT_Angle  angle )
323   {
324     FT_Vector  v;
327     v.x = FT_TRIG_SCALE >> 8;
328     v.y = 0;
329     ft_trig_pseudo_rotate( &v, angle );
331     return FT_DivFix( v.y, v.x );
332   }
335   /* documentation is in fttrigon.h */
337   FT_EXPORT_DEF( FT_Angle )
338   FT_Atan2( FT_Fixed  dx,
339             FT_Fixed  dy )
340   {
341     FT_Vector  v;
344     if ( dx == 0 && dy == 0 )
345       return 0;
347     v.x = dx;
348     v.y = dy;
349     ft_trig_prenorm( &v );
350     ft_trig_pseudo_polarize( &v );
352     return v.y;
353   }
356   /* documentation is in fttrigon.h */
358   FT_EXPORT_DEF( void )
359   FT_Vector_Unit( FT_Vector*  vec,
360                   FT_Angle    angle )
361   {
362     if ( !vec )
363       return;
365     vec->x = FT_TRIG_SCALE >> 8;
366     vec->y = 0;
367     ft_trig_pseudo_rotate( vec, angle );
368     vec->x = ( vec->x + 0x80L ) >> 8;
369     vec->y = ( vec->y + 0x80L ) >> 8;
370   }
373   /* these macros return 0 for positive numbers,
374      and -1 for negative ones */
375 #define FT_SIGN_LONG( x )   ( (x) >> ( FT_SIZEOF_LONG * 8 - 1 ) )
376 #define FT_SIGN_INT( x )    ( (x) >> ( FT_SIZEOF_INT * 8 - 1 ) )
377 #define FT_SIGN_INT32( x )  ( (x) >> 31 )
378 #define FT_SIGN_INT16( x )  ( (x) >> 15 )
381   /* documentation is in fttrigon.h */
383   FT_EXPORT_DEF( void )
384   FT_Vector_Rotate( FT_Vector*  vec,
385                     FT_Angle    angle )
386   {
387     FT_Int     shift;
388     FT_Vector  v;
391     if ( !vec )
392       return;
394     v.x   = vec->x;
395     v.y   = vec->y;
397     if ( angle && ( v.x != 0 || v.y != 0 ) )
398     {
399       shift = ft_trig_prenorm( &v );
400       ft_trig_pseudo_rotate( &v, angle );
401       v.x = ft_trig_downscale( v.x );
402       v.y = ft_trig_downscale( v.y );
404       if ( shift > 0 )
405       {
406         FT_Int32  half = (FT_Int32)1L << ( shift - 1 );
409         vec->x = ( v.x + half + FT_SIGN_LONG( v.x ) ) >> shift;
410         vec->y = ( v.y + half + FT_SIGN_LONG( v.y ) ) >> shift;
411       }
412       else
413       {
414         shift  = -shift;
415         vec->x = (FT_Pos)( (FT_ULong)v.x << shift );
416         vec->y = (FT_Pos)( (FT_ULong)v.y << shift );
417       }
418     }
419   }
422   /* documentation is in fttrigon.h */
424   FT_EXPORT_DEF( FT_Fixed )
425   FT_Vector_Length( FT_Vector*  vec )
426   {
427     FT_Int     shift;
428     FT_Vector  v;
431     if ( !vec )
432       return 0;
434     v = *vec;
436     /* handle trivial cases */
437     if ( v.x == 0 )
438     {
439       return FT_ABS( v.y );
440     }
441     else if ( v.y == 0 )
442     {
443       return FT_ABS( v.x );
444     }
446     /* general case */
447     shift = ft_trig_prenorm( &v );
448     ft_trig_pseudo_polarize( &v );
450     v.x = ft_trig_downscale( v.x );
452     if ( shift > 0 )
453       return ( v.x + ( 1 << ( shift - 1 ) ) ) >> shift;
455     return (FT_Fixed)( (FT_UInt32)v.x << -shift );
456   }
459   /* documentation is in fttrigon.h */
461   FT_EXPORT_DEF( void )
462   FT_Vector_Polarize( FT_Vector*  vec,
463                       FT_Fixed   *length,
464                       FT_Angle   *angle )
465   {
466     FT_Int     shift;
467     FT_Vector  v;
470     if ( !vec || !length || !angle )
471       return;
473     v = *vec;
475     if ( v.x == 0 && v.y == 0 )
476       return;
478     shift = ft_trig_prenorm( &v );
479     ft_trig_pseudo_polarize( &v );
481     v.x = ft_trig_downscale( v.x );
483     *length = ( shift >= 0 ) ?                      ( v.x >>  shift )
484                              : (FT_Fixed)( (FT_UInt32)v.x << -shift );
485     *angle  = v.y;
486   }
489   /* documentation is in fttrigon.h */
491   FT_EXPORT_DEF( void )
492   FT_Vector_From_Polar( FT_Vector*  vec,
493                         FT_Fixed    length,
494                         FT_Angle    angle )
495   {
496     if ( !vec )
497       return;
499     vec->x = length;
500     vec->y = 0;
502     FT_Vector_Rotate( vec, angle );
503   }
506   /* documentation is in fttrigon.h */
508   FT_EXPORT_DEF( FT_Angle )
509   FT_Angle_Diff( FT_Angle  angle1,
510                  FT_Angle  angle2 )
511   {
512     FT_Angle  delta = angle2 - angle1;
515     delta %= FT_ANGLE_2PI;
516     if ( delta < 0 )
517       delta += FT_ANGLE_2PI;
519     if ( delta > FT_ANGLE_PI )
520       delta -= FT_ANGLE_2PI;
522     return delta;
523   }
526 /* END */