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