| 1 | /**************************************************************************** | 
| 2 |  * | 
| 3 |  * fttrigon.c | 
| 4 |  * | 
| 5 |  *   FreeType trigonometric functions (body). | 
| 6 |  * | 
| 7 |  * Copyright (C) 2001-2023 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 |  */ | 
| 17 |  | 
| 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 |    */ | 
| 31 |  | 
| 32 | #include <freetype/internal/ftobjs.h> | 
| 33 | #include <freetype/internal/ftcalc.h> | 
| 34 | #include <freetype/fttrigon.h> | 
| 35 |  | 
| 36 |  | 
| 37 |   /* the Cordic shrink factor 0.858785336480436 * 2^32 */ | 
| 38 | #define FT_TRIG_SCALE      0xDBD95B16UL | 
| 39 |  | 
| 40 |   /* the highest bit in overflow-safe vector components, */ | 
| 41 |   /* MSB of 0.858785336480436 * sqrt(0.5) * 2^30         */ | 
| 42 | #define FT_TRIG_SAFE_MSB   29 | 
| 43 |  | 
| 44 |   /* this table was generated for FT_PI = 180L << 16, i.e. degrees */ | 
| 45 | #define FT_TRIG_MAX_ITERS  23 | 
| 46 |  | 
| 47 |   static const FT_Angle | 
| 48 |   ft_trig_arctan_table[] = | 
| 49 |   { | 
| 50 |     1740967L, 919879L, 466945L, 234379L, 117304L, 58666L, 29335L, | 
| 51 |     14668L, 7334L, 3667L, 1833L, 917L, 458L, 229L, 115L, | 
| 52 |     57L, 29L, 14L, 7L, 4L, 2L, 1L | 
| 53 |   }; | 
| 54 |  | 
| 55 |  | 
| 56 | #ifdef FT_INT64 | 
| 57 |  | 
| 58 |   /* multiply a given value by the CORDIC shrink factor */ | 
| 59 |   static FT_Fixed | 
| 60 |   ft_trig_downscale( FT_Fixed  val ) | 
| 61 |   { | 
| 62 |     FT_Int  s = 1; | 
| 63 |  | 
| 64 |  | 
| 65 |     if ( val < 0 ) | 
| 66 |     { | 
| 67 |        val = -val; | 
| 68 |        s = -1; | 
| 69 |     } | 
| 70 |  | 
| 71 |     /* 0x40000000 comes from regression analysis between true */ | 
| 72 |     /* and CORDIC hypotenuse, so it minimizes the error       */ | 
| 73 |     val = (FT_Fixed)( | 
| 74 |             ( (FT_UInt64)val * FT_TRIG_SCALE + 0x40000000UL ) >> 32 ); | 
| 75 |  | 
| 76 |     return s < 0 ? -val : val; | 
| 77 |   } | 
| 78 |  | 
| 79 | #else /* !FT_INT64 */ | 
| 80 |  | 
| 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; | 
| 87 |  | 
| 88 |  | 
| 89 |     if ( val < 0 ) | 
| 90 |     { | 
| 91 |        val = -val; | 
| 92 |        s = -1; | 
| 93 |     } | 
| 94 |  | 
| 95 |     lo1 = (FT_UInt32)val & 0x0000FFFFU; | 
| 96 |     hi1 = (FT_UInt32)val >> 16; | 
| 97 |     lo2 = FT_TRIG_SCALE & 0x0000FFFFU; | 
| 98 |     hi2 = FT_TRIG_SCALE >> 16; | 
| 99 |  | 
| 100 |     lo = lo1 * lo2; | 
| 101 |     i1 = lo1 * hi2; | 
| 102 |     i2 = lo2 * hi1; | 
| 103 |     hi = hi1 * hi2; | 
| 104 |  | 
| 105 |     /* Check carry overflow of i1 + i2 */ | 
| 106 |     i1 += i2; | 
| 107 |     hi += (FT_UInt32)( i1 < i2 ) << 16; | 
| 108 |  | 
| 109 |     hi += i1 >> 16; | 
| 110 |     i1  = i1 << 16; | 
| 111 |  | 
| 112 |     /* Check carry overflow of i1 + lo */ | 
| 113 |     lo += i1; | 
| 114 |     hi += ( lo < i1 ); | 
| 115 |  | 
| 116 |     /* 0x40000000 comes from regression analysis between true */ | 
| 117 |     /* and CORDIC hypotenuse, so it minimizes the error       */ | 
| 118 |  | 
| 119 |     /* Check carry overflow of lo + 0x40000000 */ | 
| 120 |     lo += 0x40000000UL; | 
| 121 |     hi += ( lo < 0x40000000UL ); | 
| 122 |  | 
| 123 |     val = (FT_Fixed)hi; | 
| 124 |  | 
| 125 |     return s < 0 ? -val : val; | 
| 126 |   } | 
| 127 |  | 
| 128 | #endif /* !FT_INT64 */ | 
| 129 |  | 
| 130 |  | 
| 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; | 
| 137 |  | 
| 138 |  | 
| 139 |     x = vec->x; | 
| 140 |     y = vec->y; | 
| 141 |  | 
| 142 |     shift = FT_MSB( (FT_UInt32)( FT_ABS( x ) | FT_ABS( y ) ) ); | 
| 143 |  | 
| 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 |     } | 
| 157 |  | 
| 158 |     return shift; | 
| 159 |   } | 
| 160 |  | 
| 161 |  | 
| 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; | 
| 169 |  | 
| 170 |  | 
| 171 |     x = vec->x; | 
| 172 |     y = vec->y; | 
| 173 |  | 
| 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 |     } | 
| 182 |  | 
| 183 |     while ( theta > FT_ANGLE_PI4 ) | 
| 184 |     { | 
| 185 |       xtemp  = -y; | 
| 186 |       y      =  x; | 
| 187 |       x      =  xtemp; | 
| 188 |       theta -=  FT_ANGLE_PI2; | 
| 189 |     } | 
| 190 |  | 
| 191 |     arctanptr = ft_trig_arctan_table; | 
| 192 |  | 
| 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 |     } | 
| 211 |  | 
| 212 |     vec->x = x; | 
| 213 |     vec->y = y; | 
| 214 |   } | 
| 215 |  | 
| 216 |  | 
| 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; | 
| 224 |  | 
| 225 |  | 
| 226 |     x = vec->x; | 
| 227 |     y = vec->y; | 
| 228 |  | 
| 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 |     } | 
| 260 |  | 
| 261 |     arctanptr = ft_trig_arctan_table; | 
| 262 |  | 
| 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 |     } | 
| 281 |  | 
| 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 ); | 
| 288 |  | 
| 289 |     vec->x = x; | 
| 290 |     vec->y = theta; | 
| 291 |   } | 
| 292 |  | 
| 293 |  | 
| 294 |   /* documentation is in fttrigon.h */ | 
| 295 |  | 
| 296 |   FT_EXPORT_DEF( FT_Fixed ) | 
| 297 |   FT_Cos( FT_Angle  angle ) | 
| 298 |   { | 
| 299 |     FT_Vector  v; | 
| 300 |  | 
| 301 |  | 
| 302 |     FT_Vector_Unit( &v, angle ); | 
| 303 |  | 
| 304 |     return v.x; | 
| 305 |   } | 
| 306 |  | 
| 307 |  | 
| 308 |   /* documentation is in fttrigon.h */ | 
| 309 |  | 
| 310 |   FT_EXPORT_DEF( FT_Fixed ) | 
| 311 |   FT_Sin( FT_Angle  angle ) | 
| 312 |   { | 
| 313 |     FT_Vector  v; | 
| 314 |  | 
| 315 |  | 
| 316 |     FT_Vector_Unit( &v, angle ); | 
| 317 |  | 
| 318 |     return v.y; | 
| 319 |   } | 
| 320 |  | 
| 321 |  | 
| 322 |   /* documentation is in fttrigon.h */ | 
| 323 |  | 
| 324 |   FT_EXPORT_DEF( FT_Fixed ) | 
| 325 |   FT_Tan( FT_Angle  angle ) | 
| 326 |   { | 
| 327 |     FT_Vector  v = { 1 << 24, 0 }; | 
| 328 |  | 
| 329 |  | 
| 330 |     ft_trig_pseudo_rotate( &v, angle ); | 
| 331 |  | 
| 332 |     return FT_DivFix( v.y, v.x ); | 
| 333 |   } | 
| 334 |  | 
| 335 |  | 
| 336 |   /* documentation is in fttrigon.h */ | 
| 337 |  | 
| 338 |   FT_EXPORT_DEF( FT_Angle ) | 
| 339 |   FT_Atan2( FT_Fixed  dx, | 
| 340 |             FT_Fixed  dy ) | 
| 341 |   { | 
| 342 |     FT_Vector  v; | 
| 343 |  | 
| 344 |  | 
| 345 |     if ( dx == 0 && dy == 0 ) | 
| 346 |       return 0; | 
| 347 |  | 
| 348 |     v.x = dx; | 
| 349 |     v.y = dy; | 
| 350 |     ft_trig_prenorm( &v ); | 
| 351 |     ft_trig_pseudo_polarize( &v ); | 
| 352 |  | 
| 353 |     return v.y; | 
| 354 |   } | 
| 355 |  | 
| 356 |  | 
| 357 |   /* documentation is in fttrigon.h */ | 
| 358 |  | 
| 359 |   FT_EXPORT_DEF( void ) | 
| 360 |   FT_Vector_Unit( FT_Vector*  vec, | 
| 361 |                   FT_Angle    angle ) | 
| 362 |   { | 
| 363 |     if ( !vec ) | 
| 364 |       return; | 
| 365 |  | 
| 366 |     vec->x = FT_TRIG_SCALE >> 8; | 
| 367 |     vec->y = 0; | 
| 368 |     ft_trig_pseudo_rotate( vec, angle ); | 
| 369 |     vec->x = ( vec->x + 0x80L ) >> 8; | 
| 370 |     vec->y = ( vec->y + 0x80L ) >> 8; | 
| 371 |   } | 
| 372 |  | 
| 373 |  | 
| 374 |   /* documentation is in fttrigon.h */ | 
| 375 |  | 
| 376 |   FT_EXPORT_DEF( void ) | 
| 377 |   FT_Vector_Rotate( FT_Vector*  vec, | 
| 378 |                     FT_Angle    angle ) | 
| 379 |   { | 
| 380 |     FT_Int     shift; | 
| 381 |     FT_Vector  v; | 
| 382 |  | 
| 383 |  | 
| 384 |     if ( !vec || !angle ) | 
| 385 |       return; | 
| 386 |  | 
| 387 |     v = *vec; | 
| 388 |  | 
| 389 |     if ( v.x == 0 && v.y == 0 ) | 
| 390 |       return; | 
| 391 |  | 
| 392 |     shift = ft_trig_prenorm( &v ); | 
| 393 |     ft_trig_pseudo_rotate( &v, angle ); | 
| 394 |     v.x = ft_trig_downscale( v.x ); | 
| 395 |     v.y = ft_trig_downscale( v.y ); | 
| 396 |  | 
| 397 |     if ( shift > 0 ) | 
| 398 |     { | 
| 399 |       FT_Int32  half = (FT_Int32)1L << ( shift - 1 ); | 
| 400 |  | 
| 401 |  | 
| 402 |       vec->x = ( v.x + half - ( v.x < 0 ) ) >> shift; | 
| 403 |       vec->y = ( v.y + half - ( v.y < 0 ) ) >> shift; | 
| 404 |     } | 
| 405 |     else | 
| 406 |     { | 
| 407 |       shift  = -shift; | 
| 408 |       vec->x = (FT_Pos)( (FT_ULong)v.x << shift ); | 
| 409 |       vec->y = (FT_Pos)( (FT_ULong)v.y << shift ); | 
| 410 |     } | 
| 411 |   } | 
| 412 |  | 
| 413 |  | 
| 414 |   /* documentation is in fttrigon.h */ | 
| 415 |  | 
| 416 |   FT_EXPORT_DEF( FT_Fixed ) | 
| 417 |   FT_Vector_Length( FT_Vector*  vec ) | 
| 418 |   { | 
| 419 |     FT_Int     shift; | 
| 420 |     FT_Vector  v; | 
| 421 |  | 
| 422 |  | 
| 423 |     if ( !vec ) | 
| 424 |       return 0; | 
| 425 |  | 
| 426 |     v = *vec; | 
| 427 |  | 
| 428 |     /* handle trivial cases */ | 
| 429 |     if ( v.x == 0 ) | 
| 430 |     { | 
| 431 |       return FT_ABS( v.y ); | 
| 432 |     } | 
| 433 |     else if ( v.y == 0 ) | 
| 434 |     { | 
| 435 |       return FT_ABS( v.x ); | 
| 436 |     } | 
| 437 |  | 
| 438 |     /* general case */ | 
| 439 |     shift = ft_trig_prenorm( &v ); | 
| 440 |     ft_trig_pseudo_polarize( &v ); | 
| 441 |  | 
| 442 |     v.x = ft_trig_downscale( v.x ); | 
| 443 |  | 
| 444 |     if ( shift > 0 ) | 
| 445 |       return ( v.x + ( 1L << ( shift - 1 ) ) ) >> shift; | 
| 446 |  | 
| 447 |     return (FT_Fixed)( (FT_UInt32)v.x << -shift ); | 
| 448 |   } | 
| 449 |  | 
| 450 |  | 
| 451 |   /* documentation is in fttrigon.h */ | 
| 452 |  | 
| 453 |   FT_EXPORT_DEF( void ) | 
| 454 |   FT_Vector_Polarize( FT_Vector*  vec, | 
| 455 |                       FT_Fixed   *length, | 
| 456 |                       FT_Angle   *angle ) | 
| 457 |   { | 
| 458 |     FT_Int     shift; | 
| 459 |     FT_Vector  v; | 
| 460 |  | 
| 461 |  | 
| 462 |     if ( !vec || !length || !angle ) | 
| 463 |       return; | 
| 464 |  | 
| 465 |     v = *vec; | 
| 466 |  | 
| 467 |     if ( v.x == 0 && v.y == 0 ) | 
| 468 |       return; | 
| 469 |  | 
| 470 |     shift = ft_trig_prenorm( &v ); | 
| 471 |     ft_trig_pseudo_polarize( &v ); | 
| 472 |  | 
| 473 |     v.x = ft_trig_downscale( v.x ); | 
| 474 |  | 
| 475 |     *length = shift >= 0 ?                      ( v.x >>  shift ) | 
| 476 |                          : (FT_Fixed)( (FT_UInt32)v.x << -shift ); | 
| 477 |     *angle  = v.y; | 
| 478 |   } | 
| 479 |  | 
| 480 |  | 
| 481 |   /* documentation is in fttrigon.h */ | 
| 482 |  | 
| 483 |   FT_EXPORT_DEF( void ) | 
| 484 |   FT_Vector_From_Polar( FT_Vector*  vec, | 
| 485 |                         FT_Fixed    length, | 
| 486 |                         FT_Angle    angle ) | 
| 487 |   { | 
| 488 |     if ( !vec ) | 
| 489 |       return; | 
| 490 |  | 
| 491 |     vec->x = length; | 
| 492 |     vec->y = 0; | 
| 493 |  | 
| 494 |     FT_Vector_Rotate( vec, angle ); | 
| 495 |   } | 
| 496 |  | 
| 497 |  | 
| 498 |   /* documentation is in fttrigon.h */ | 
| 499 |  | 
| 500 |   FT_EXPORT_DEF( FT_Angle ) | 
| 501 |   FT_Angle_Diff( FT_Angle  angle1, | 
| 502 |                  FT_Angle  angle2 ) | 
| 503 |   { | 
| 504 |     FT_Angle  delta = angle2 - angle1; | 
| 505 |  | 
| 506 |  | 
| 507 |     while ( delta <= -FT_ANGLE_PI ) | 
| 508 |       delta += FT_ANGLE_2PI; | 
| 509 |  | 
| 510 |     while ( delta > FT_ANGLE_PI ) | 
| 511 |       delta -= FT_ANGLE_2PI; | 
| 512 |  | 
| 513 |     return delta; | 
| 514 |   } | 
| 515 |  | 
| 516 |  | 
| 517 | /* END */ | 
| 518 |  |