| /**************************************************************************** |
| * |
| * fttrigon.c |
| * |
| * FreeType trigonometric functions (body). |
| * |
| * Copyright (C) 2001-2023 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. |
| * |
| */ |
| |
| /************************************************************************** |
| * |
| * 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 <freetype/internal/ftobjs.h> |
| #include <freetype/internal/ftcalc.h> |
| #include <freetype/fttrigon.h> |
| |
| |
| /* 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_Angle |
| ft_trig_arctan_table[] = |
| { |
| 1740967L, 919879L, 466945L, 234379L, 117304L, 58666L, 29335L, |
| 14668L, 7334L, 3667L, 1833L, 917L, 458L, 229L, 115L, |
| 57L, 29L, 14L, 7L, 4L, 2L, 1L |
| }; |
| |
| |
| #ifdef FT_INT64 |
| |
| /* multiply a given value by the CORDIC shrink factor */ |
| static FT_Fixed |
| ft_trig_downscale( FT_Fixed val ) |
| { |
| FT_Int s = 1; |
| |
| |
| if ( val < 0 ) |
| { |
| val = -val; |
| s = -1; |
| } |
| |
| /* 0x40000000 comes from regression analysis between true */ |
| /* and CORDIC hypotenuse, so it minimizes the error */ |
| val = (FT_Fixed)( |
| ( (FT_UInt64)val * FT_TRIG_SCALE + 0x40000000UL ) >> 32 ); |
| |
| return s < 0 ? -val : val; |
| } |
| |
| #else /* !FT_INT64 */ |
| |
| /* multiply a given value by the CORDIC shrink factor */ |
| static FT_Fixed |
| ft_trig_downscale( FT_Fixed val ) |
| { |
| FT_Int s = 1; |
| FT_UInt32 lo1, hi1, lo2, hi2, lo, hi, i1, i2; |
| |
| |
| if ( val < 0 ) |
| { |
| val = -val; |
| s = -1; |
| } |
| |
| lo1 = (FT_UInt32)val & 0x0000FFFFU; |
| hi1 = (FT_UInt32)val >> 16; |
| lo2 = FT_TRIG_SCALE & 0x0000FFFFU; |
| hi2 = FT_TRIG_SCALE >> 16; |
| |
| lo = lo1 * lo2; |
| i1 = lo1 * hi2; |
| i2 = lo2 * hi1; |
| hi = hi1 * hi2; |
| |
| /* Check carry overflow of i1 + i2 */ |
| i1 += i2; |
| hi += (FT_UInt32)( i1 < i2 ) << 16; |
| |
| hi += i1 >> 16; |
| i1 = i1 << 16; |
| |
| /* Check carry overflow of i1 + lo */ |
| lo += i1; |
| hi += ( lo < i1 ); |
| |
| /* 0x40000000 comes from regression analysis between true */ |
| /* and CORDIC hypotenuse, so it minimizes the error */ |
| |
| /* Check carry overflow of lo + 0x40000000 */ |
| lo += 0x40000000UL; |
| hi += ( lo < 0x40000000UL ); |
| |
| val = (FT_Fixed)hi; |
| |
| return s < 0 ? -val : val; |
| } |
| |
| #endif /* !FT_INT64 */ |
| |
| |
| /* undefined and never called for zero vector */ |
| static FT_Int |
| ft_trig_prenorm( FT_Vector* vec ) |
| { |
| FT_Pos x, y; |
| FT_Int shift; |
| |
| |
| x = vec->x; |
| y = vec->y; |
| |
| shift = FT_MSB( (FT_UInt32)( FT_ABS( x ) | FT_ABS( y ) ) ); |
| |
| if ( shift <= FT_TRIG_SAFE_MSB ) |
| { |
| shift = FT_TRIG_SAFE_MSB - shift; |
| vec->x = (FT_Pos)( (FT_ULong)x << shift ); |
| vec->y = (FT_Pos)( (FT_ULong)y << shift ); |
| } |
| else |
| { |
| shift -= FT_TRIG_SAFE_MSB; |
| vec->x = x >> shift; |
| vec->y = y >> shift; |
| shift = -shift; |
| } |
| |
| return shift; |
| } |
| |
| |
| static void |
| ft_trig_pseudo_rotate( FT_Vector* vec, |
| FT_Angle theta ) |
| { |
| FT_Int i; |
| FT_Fixed x, y, xtemp, b; |
| const FT_Angle *arctanptr; |
| |
| |
| x = vec->x; |
| y = vec->y; |
| |
| /* Rotate inside [-PI/4,PI/4] sector */ |
| while ( theta < -FT_ANGLE_PI4 ) |
| { |
| xtemp = y; |
| y = -x; |
| x = xtemp; |
| theta += FT_ANGLE_PI2; |
| } |
| |
| while ( theta > FT_ANGLE_PI4 ) |
| { |
| xtemp = -y; |
| y = x; |
| x = xtemp; |
| theta -= FT_ANGLE_PI2; |
| } |
| |
| arctanptr = ft_trig_arctan_table; |
| |
| /* Pseudorotations, with right shifts */ |
| for ( i = 1, b = 1; i < FT_TRIG_MAX_ITERS; b <<= 1, i++ ) |
| { |
| if ( theta < 0 ) |
| { |
| xtemp = x + ( ( y + b ) >> i ); |
| y = y - ( ( x + b ) >> i ); |
| x = xtemp; |
| theta += *arctanptr++; |
| } |
| else |
| { |
| xtemp = x - ( ( y + b ) >> i ); |
| y = y + ( ( x + b ) >> i ); |
| x = xtemp; |
| theta -= *arctanptr++; |
| } |
| } |
| |
| 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, b; |
| const FT_Angle *arctanptr; |
| |
| |
| x = vec->x; |
| y = vec->y; |
| |
| /* Get the vector into [-PI/4,PI/4] sector */ |
| if ( y > x ) |
| { |
| 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 |
| { |
| if ( y < -x ) |
| { |
| theta = -FT_ANGLE_PI2; |
| xtemp = -y; |
| y = x; |
| x = xtemp; |
| } |
| else |
| { |
| theta = 0; |
| } |
| } |
| |
| 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 ) |
| { |
| xtemp = x + ( ( y + b ) >> i ); |
| y = y - ( ( x + b ) >> i ); |
| x = xtemp; |
| theta += *arctanptr++; |
| } |
| else |
| { |
| xtemp = x - ( ( y + b ) >> i ); |
| y = y + ( ( x + b ) >> i ); |
| x = xtemp; |
| theta -= *arctanptr++; |
| } |
| } |
| |
| /* 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, 16 ); |
| else |
| theta = -FT_PAD_ROUND( -theta, 16 ); |
| |
| vec->x = x; |
| vec->y = theta; |
| } |
| |
| |
| /* documentation is in fttrigon.h */ |
| |
| FT_EXPORT_DEF( FT_Fixed ) |
| FT_Cos( FT_Angle angle ) |
| { |
| FT_Vector v; |
| |
| |
| FT_Vector_Unit( &v, angle ); |
| |
| return v.x; |
| } |
| |
| |
| /* documentation is in fttrigon.h */ |
| |
| FT_EXPORT_DEF( FT_Fixed ) |
| FT_Sin( FT_Angle angle ) |
| { |
| FT_Vector v; |
| |
| |
| FT_Vector_Unit( &v, angle ); |
| |
| return v.y; |
| } |
| |
| |
| /* documentation is in fttrigon.h */ |
| |
| FT_EXPORT_DEF( FT_Fixed ) |
| FT_Tan( FT_Angle angle ) |
| { |
| FT_Vector v = { 1 << 24, 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 ) |
| { |
| if ( !vec ) |
| return; |
| |
| vec->x = FT_TRIG_SCALE >> 8; |
| vec->y = 0; |
| ft_trig_pseudo_rotate( vec, angle ); |
| vec->x = ( vec->x + 0x80L ) >> 8; |
| vec->y = ( vec->y + 0x80L ) >> 8; |
| } |
| |
| |
| /* documentation is in fttrigon.h */ |
| |
| FT_EXPORT_DEF( void ) |
| FT_Vector_Rotate( FT_Vector* vec, |
| FT_Angle angle ) |
| { |
| FT_Int shift; |
| FT_Vector v; |
| |
| |
| if ( !vec || !angle ) |
| return; |
| |
| v = *vec; |
| |
| if ( v.x == 0 && v.y == 0 ) |
| return; |
| |
| 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 - ( v.x < 0 ) ) >> shift; |
| vec->y = ( v.y + half - ( v.y < 0 ) ) >> shift; |
| } |
| else |
| { |
| shift = -shift; |
| vec->x = (FT_Pos)( (FT_ULong)v.x << shift ); |
| vec->y = (FT_Pos)( (FT_ULong)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; |
| |
| |
| if ( !vec ) |
| return 0; |
| |
| v = *vec; |
| |
| /* handle trivial cases */ |
| if ( v.x == 0 ) |
| { |
| return FT_ABS( v.y ); |
| } |
| else if ( v.y == 0 ) |
| { |
| return FT_ABS( 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 + ( 1L << ( shift - 1 ) ) ) >> shift; |
| |
| return (FT_Fixed)( (FT_UInt32)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; |
| |
| |
| if ( !vec || !length || !angle ) |
| return; |
| |
| 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 ) |
| : (FT_Fixed)( (FT_UInt32)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 ) |
| { |
| if ( !vec ) |
| return; |
| |
| 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; |
| |
| |
| while ( delta <= -FT_ANGLE_PI ) |
| delta += FT_ANGLE_2PI; |
| |
| while ( delta > FT_ANGLE_PI ) |
| delta -= FT_ANGLE_2PI; |
| |
| return delta; |
| } |
| |
| |
| /* END */ |