Home | History | Annotate | Download | only in core
      1 
      2 /*
      3  * Copyright 2006 The Android Open Source Project
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 
     10 #include "SkCordic.h"
     11 #include "SkMath.h"
     12 #include "Sk64.h"
     13 
     14 // 0x20000000 equals pi / 4
     15 const int32_t kATanDegrees[] = { 0x20000000,
     16     0x12E4051D, 0x9FB385B, 0x51111D4, 0x28B0D43, 0x145D7E1, 0xA2F61E, 0x517C55,
     17     0x28BE53, 0x145F2E, 0xA2F98, 0x517CC, 0x28BE6, 0x145F3, 0xA2F9, 0x517C,
     18     0x28BE, 0x145F, 0xA2F, 0x517, 0x28B, 0x145, 0xA2, 0x51, 0x28, 0x14,
     19     0xA, 0x5, 0x2, 0x1 };
     20 
     21 const int32_t kFixedInvGain1 = 0x18bde0bb;  // 0.607252935
     22 
     23 static void SkCircularRotation(int32_t* x0, int32_t* y0, int32_t* z0)
     24 {
     25     int32_t t = 0;
     26     int32_t x = *x0;
     27     int32_t y = *y0;
     28     int32_t z = *z0;
     29     const int32_t* tanPtr = kATanDegrees;
     30    do {
     31         int32_t x1 = y >> t;
     32         int32_t y1 = x >> t;
     33         int32_t tan = *tanPtr++;
     34         if (z >= 0) {
     35             x -= x1;
     36             y += y1;
     37             z -= tan;
     38         } else {
     39             x += x1;
     40             y -= y1;
     41             z += tan;
     42         }
     43    } while (++t < 16); // 30);
     44     *x0 = x;
     45     *y0 = y;
     46     *z0 = z;
     47 }
     48 
     49 SkFixed SkCordicSinCos(SkFixed radians, SkFixed* cosp)
     50 {
     51     int32_t scaledRadians = radians * 0x28be;   // scale radians to 65536 / PI()
     52     int quadrant = scaledRadians >> 30;
     53     quadrant += 1;
     54     if (quadrant & 2)
     55         scaledRadians = -scaledRadians + 0x80000000;
     56     /* |a| <= 90 degrees as a 1.31 number */
     57     SkFixed sin = 0;
     58     SkFixed cos = kFixedInvGain1;
     59     SkCircularRotation(&cos, &sin, &scaledRadians);
     60     Sk64 scaled;
     61     scaled.setMul(sin, 0x6488d);
     62     sin = scaled.fHi;
     63     scaled.setMul(cos, 0x6488d);
     64     if (quadrant & 2)
     65         scaled.fHi = - scaled.fHi;
     66     *cosp = scaled.fHi;
     67     return sin;
     68 }
     69 
     70 SkFixed SkCordicTan(SkFixed a)
     71 {
     72     int32_t cos;
     73     int32_t sin = SkCordicSinCos(a, &cos);
     74     return SkFixedDiv(sin, cos);
     75 }
     76 
     77 static int32_t SkCircularVector(int32_t* y0, int32_t* x0, int32_t vecMode)
     78 {
     79     int32_t x = *x0;
     80     int32_t y = *y0;
     81     int32_t z = 0;
     82     int32_t t = 0;
     83     const int32_t* tanPtr = kATanDegrees;
     84    do {
     85         int32_t x1 = y >> t;
     86         int32_t y1 = x >> t;
     87         int32_t tan = *tanPtr++;
     88         if (y < vecMode) {
     89             x -= x1;
     90             y += y1;
     91             z -= tan;
     92         } else {
     93             x += x1;
     94             y -= y1;
     95             z += tan;
     96         }
     97    } while (++t < 16);  // 30
     98     Sk64 scaled;
     99     scaled.setMul(z, 0x6488d); // scale back into the SkScalar space (0x100000000/0x28be)
    100    return scaled.fHi;
    101 }
    102 
    103 SkFixed SkCordicASin(SkFixed a) {
    104     int32_t sign = SkExtractSign(a);
    105     int32_t z = SkFixedAbs(a);
    106     if (z >= SK_Fixed1)
    107         return SkApplySign(SK_FixedPI>>1, sign);
    108     int32_t x = kFixedInvGain1;
    109     int32_t y = 0;
    110     z *= 0x28be;
    111     z = SkCircularVector(&y, &x, z);
    112     z = SkApplySign(z, ~sign);
    113     return z;
    114 }
    115 
    116 SkFixed SkCordicACos(SkFixed a) {
    117     int32_t z = SkCordicASin(a);
    118     z = (SK_FixedPI>>1) - z;
    119     return z;
    120 }
    121 
    122 SkFixed SkCordicATan2(SkFixed y, SkFixed x) {
    123     if ((x | y) == 0)
    124         return 0;
    125     int32_t xsign = SkExtractSign(x);
    126     x = SkFixedAbs(x);
    127     int32_t result = SkCircularVector(&y, &x, 0);
    128     if (xsign) {
    129         int32_t rsign = SkExtractSign(result);
    130         if (y == 0)
    131             rsign = 0;
    132         SkFixed pi = SkApplySign(SK_FixedPI, rsign);
    133         result = pi - result;
    134     }
    135     return result;
    136 }
    137 
    138 const int32_t kATanHDegrees[] = {
    139     0x1661788D, 0xA680D61, 0x51EA6FC, 0x28CBFDD, 0x1460E34,
    140     0xA2FCE8, 0x517D2E, 0x28BE6E, 0x145F32,
    141     0xA2F98, 0x517CC, 0x28BE6, 0x145F3, 0xA2F9, 0x517C,
    142     0x28BE, 0x145F, 0xA2F, 0x517, 0x28B, 0x145, 0xA2, 0x51, 0x28, 0x14,
    143     0xA, 0x5, 0x2, 0x1 };
    144 
    145 const int32_t kFixedInvGain2 = 0x31330AAA;  // 1.207534495
    146 
    147 static void SkHyperbolic(int32_t* x0, int32_t* y0, int32_t* z0, int mode)
    148 {
    149     int32_t t = 1;
    150     int32_t x = *x0;
    151     int32_t y = *y0;
    152     int32_t z = *z0;
    153     const int32_t* tanPtr = kATanHDegrees;
    154     int k = -3;
    155     do {
    156         int32_t x1 = y >> t;
    157         int32_t y1 = x >> t;
    158         int32_t tan = *tanPtr++;
    159         int count = 2 + (k >> 31);
    160         if (++k == 1)
    161             k = -2;
    162         do {
    163             if (((y >> 31) & mode) | ~((z >> 31) | mode)) {
    164                 x += x1;
    165                 y += y1;
    166                 z -= tan;
    167             } else {
    168                 x -= x1;
    169                 y -= y1;
    170                 z += tan;
    171             }
    172         } while (--count);
    173     } while (++t < 30);
    174     *x0 = x;
    175     *y0 = y;
    176     *z0 = z;
    177 }
    178 
    179 SkFixed SkCordicLog(SkFixed a) {
    180     a *= 0x28be;
    181     int32_t x = a + 0x28BE60DB; // 1.0
    182     int32_t y = a - 0x28BE60DB;
    183     int32_t z = 0;
    184     SkHyperbolic(&x, &y, &z, -1);
    185     Sk64 scaled;
    186     scaled.setMul(z, 0x6488d);
    187     z = scaled.fHi;
    188     return z << 1;
    189 }
    190 
    191 SkFixed SkCordicExp(SkFixed a) {
    192     int32_t cosh = kFixedInvGain2;
    193     int32_t sinh = 0;
    194     SkHyperbolic(&cosh, &sinh, &a, 0);
    195     return cosh + sinh;
    196 }
    197 
    198 #ifdef SK_DEBUG
    199 
    200 #ifdef SK_CAN_USE_FLOAT
    201     #include "SkFloatingPoint.h"
    202 #endif
    203 
    204 void SkCordic_UnitTest()
    205 {
    206 #if defined(SK_SUPPORT_UNITTEST) && defined(SK_CAN_USE_FLOAT)
    207     float val;
    208     for (float angle = -720; angle < 720; angle += 30) {
    209         float radian = angle * 3.1415925358f / 180.0f;
    210         SkFixed f_angle = (int) (radian * 65536.0f);
    211     // sincos
    212         float sine = sinf(radian);
    213         float cosine = cosf(radian);
    214         SkFixed f_cosine;
    215         SkFixed f_sine = SkCordicSinCos(f_angle, &f_cosine);
    216         float sine2 = (float) f_sine / 65536.0f;
    217         float cosine2 = (float) f_cosine / 65536.0f;
    218         float error = fabsf(sine - sine2);
    219         if (error > 0.001)
    220             SkDebugf("sin error : angle = %g ; sin = %g ; cordic = %g\n", angle, sine, sine2);
    221         error = fabsf(cosine - cosine2);
    222         if (error > 0.001)
    223             SkDebugf("cos error : angle = %g ; cos = %g ; cordic = %g\n", angle, cosine, cosine2);
    224     // tan
    225         float _tan = tanf(radian);
    226         SkFixed f_tan = SkCordicTan(f_angle);
    227         float tan2 = (float) f_tan / 65536.0f;
    228         error = fabsf(_tan - tan2);
    229         if (error > 0.05 && fabsf(_tan) < 1e6)
    230             SkDebugf("tan error : angle = %g ; tan = %g ; cordic = %g\n", angle, _tan, tan2);
    231     }
    232     for (val = -1; val <= 1; val += .1f) {
    233         SkFixed f_val = (int) (val * 65536.0f);
    234     // asin
    235         float arcsine = asinf(val);
    236         SkFixed f_arcsine = SkCordicASin(f_val);
    237         float arcsine2 = (float) f_arcsine / 65536.0f;
    238         float error = fabsf(arcsine - arcsine2);
    239         if (error > 0.001)
    240             SkDebugf("asin error : val = %g ; asin = %g ; cordic = %g\n", val, arcsine, arcsine2);
    241     }
    242 #if 1
    243     for (val = -1; val <= 1; val += .1f) {
    244 #else
    245     val = .5; {
    246 #endif
    247         SkFixed f_val = (int) (val * 65536.0f);
    248     // acos
    249         float arccos = acosf(val);
    250         SkFixed f_arccos = SkCordicACos(f_val);
    251         float arccos2 = (float) f_arccos / 65536.0f;
    252         float error = fabsf(arccos - arccos2);
    253         if (error > 0.001)
    254             SkDebugf("acos error : val = %g ; acos = %g ; cordic = %g\n", val, arccos, arccos2);
    255     }
    256     // atan2
    257 #if 1
    258     for (val = -1000; val <= 1000; val += 500.f) {
    259         for (float val2 = -1000; val2 <= 1000; val2 += 500.f) {
    260 #else
    261             val = 0; {
    262             float val2 = -1000; {
    263 #endif
    264             SkFixed f_val = (int) (val * 65536.0f);
    265             SkFixed f_val2 = (int) (val2 * 65536.0f);
    266             float arctan = atan2f(val, val2);
    267             SkFixed f_arctan = SkCordicATan2(f_val, f_val2);
    268             float arctan2 = (float) f_arctan / 65536.0f;
    269             float error = fabsf(arctan - arctan2);
    270             if (error > 0.001)
    271                 SkDebugf("atan2 error : val = %g ; val2 = %g ; atan2 = %g ; cordic = %g\n", val, val2, arctan, arctan2);
    272         }
    273     }
    274     // log
    275 #if 1
    276     for (val = 0.125f; val <= 8.f; val *= 2.0f) {
    277 #else
    278     val = .5; {
    279 #endif
    280         SkFixed f_val = (int) (val * 65536.0f);
    281     // acos
    282         float log = logf(val);
    283         SkFixed f_log = SkCordicLog(f_val);
    284         float log2 = (float) f_log / 65536.0f;
    285         float error = fabsf(log - log2);
    286         if (error > 0.001)
    287             SkDebugf("log error : val = %g ; log = %g ; cordic = %g\n", val, log, log2);
    288     }
    289     // exp
    290 #endif
    291 }
    292 
    293 #endif
    294