SkPoint.cpp revision 8a1c16ff38322f0210116fa7293eb8817c7e477e
1/*
2 * Copyright (C) 2006-2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "SkPoint.h"
18
19void SkIPoint::rotateCW(SkIPoint* dst) const {
20    SkASSERT(dst);
21
22    // use a tmp in case this == dst
23    int32_t tmp = fX;
24    dst->fX = -fY;
25    dst->fY = tmp;
26}
27
28void SkIPoint::rotateCCW(SkIPoint* dst) const {
29    SkASSERT(dst);
30
31    // use a tmp in case this == dst
32    int32_t tmp = fX;
33    dst->fX = fY;
34    dst->fY = -tmp;
35}
36
37///////////////////////////////////////////////////////////////////////////////
38
39void SkPoint::rotateCW(SkPoint* dst) const {
40    SkASSERT(dst);
41
42    // use a tmp in case this == dst
43    SkScalar tmp = fX;
44    dst->fX = -fY;
45    dst->fY = tmp;
46}
47
48void SkPoint::rotateCCW(SkPoint* dst) const {
49    SkASSERT(dst);
50
51    // use a tmp in case this == dst
52    SkScalar tmp = fX;
53    dst->fX = fY;
54    dst->fY = -tmp;
55}
56
57void SkPoint::scale(SkScalar scale, SkPoint* dst) const {
58    SkASSERT(dst);
59    dst->set(SkScalarMul(fX, scale), SkScalarMul(fY, scale));
60}
61
62#define kNearlyZero     (SK_Scalar1 / 8092)
63
64bool SkPoint::normalize() {
65    return this->setLength(fX, fY, SK_Scalar1);
66}
67
68bool SkPoint::setNormalize(SkScalar x, SkScalar y) {
69    return this->setLength(x, y, SK_Scalar1);
70}
71
72bool SkPoint::setLength(SkScalar length) {
73    return this->setLength(fX, fY, length);
74}
75
76#ifdef SK_SCALAR_IS_FLOAT
77
78SkScalar SkPoint::Length(SkScalar dx, SkScalar dy) {
79    return sk_float_sqrt(dx * dx + dy * dy);
80}
81
82bool SkPoint::setLength(float x, float y, float length) {
83    float mag = sk_float_sqrt(x * x + y * y);
84    if (mag > kNearlyZero) {
85        length /= mag;
86        fX = x * length;
87        fY = y * length;
88        return true;
89    }
90    return false;
91}
92
93#else
94
95#include "Sk64.h"
96
97SkScalar SkPoint::Length(SkScalar dx, SkScalar dy) {
98    Sk64    tmp1, tmp2;
99
100    tmp1.setMul(dx, dx);
101    tmp2.setMul(dy, dy);
102    tmp1.add(tmp2);
103
104    return tmp1.getSqrt();
105}
106
107#ifdef SK_DEBUGx
108static SkFixed fixlen(SkFixed x, SkFixed y) {
109    float fx = (float)x;
110    float fy = (float)y;
111
112    return (int)floorf(sqrtf(fx*fx + fy*fy) + 0.5f);
113}
114#endif
115
116static inline uint32_t squarefixed(unsigned x) {
117    x >>= 16;
118    return x*x;
119}
120
121#if 1   // Newton iter for setLength
122
123static inline unsigned invsqrt_iter(unsigned V, unsigned U) {
124    unsigned x = V * U >> 14;
125    x = x * U >> 14;
126    x = (3 << 14) - x;
127    x = (U >> 1) * x >> 14;
128    return x;
129}
130
131static const uint16_t gInvSqrt14GuessTable[] = {
132    0x4000, 0x3c57, 0x393e, 0x3695, 0x3441, 0x3235, 0x3061,
133    0x2ebd, 0x2d41, 0x2be7, 0x2aaa, 0x2987, 0x287a, 0x2780,
134    0x2698, 0x25be, 0x24f3, 0x2434, 0x2380, 0x22d6, 0x2235,
135    0x219d, 0x210c, 0x2083, 0x2000, 0x1f82, 0x1f0b, 0x1e99,
136    0x1e2b, 0x1dc2, 0x1d5d, 0x1cfc, 0x1c9f, 0x1c45, 0x1bee,
137    0x1b9b, 0x1b4a, 0x1afc, 0x1ab0, 0x1a67, 0x1a20, 0x19dc,
138    0x1999, 0x1959, 0x191a, 0x18dd, 0x18a2, 0x1868, 0x1830,
139    0x17fa, 0x17c4, 0x1791, 0x175e, 0x172d, 0x16fd, 0x16ce
140};
141
142#define BUILD_INVSQRT_TABLEx
143#ifdef BUILD_INVSQRT_TABLE
144static void build_invsqrt14_guess_table() {
145    for (int i = 8; i <= 63; i++) {
146        unsigned x = SkToU16((1 << 28) / SkSqrt32(i << 25));
147        printf("0x%x, ", x);
148    }
149    printf("\n");
150}
151#endif
152
153static unsigned fast_invsqrt(uint32_t x) {
154#ifdef BUILD_INVSQRT_TABLE
155    unsigned top2 = x >> 25;
156    SkASSERT(top2 >= 8 && top2 <= 63);
157
158    static bool gOnce;
159    if (!gOnce) {
160        build_invsqrt14_guess_table();
161        gOnce = true;
162    }
163#endif
164
165    unsigned V = x >> 14;   // make V .14
166
167    unsigned top = x >> 25;
168    SkASSERT(top >= 8 && top <= 63);
169    SkASSERT(top - 8 < SK_ARRAY_COUNT(gInvSqrt14GuessTable));
170    unsigned U = gInvSqrt14GuessTable[top - 8];
171
172    U = invsqrt_iter(V, U);
173    return invsqrt_iter(V, U);
174}
175
176/*  We "normalize" x,y to be .14 values (so we can square them and stay 32bits.
177    Then we Newton-iterate this in .14 space to compute the invser-sqrt, and
178    scale by it at the end. The .14 space means we can execute our iterations
179    and stay in 32bits as well, making the multiplies much cheaper than calling
180    SkFixedMul.
181*/
182bool SkPoint::setLength(SkFixed ox, SkFixed oy, SkFixed length) {
183    if (ox == 0) {
184        if (oy == 0) {
185            return false;
186        }
187        this->set(0, SkApplySign(length, SkExtractSign(oy)));
188        return true;
189    }
190    if (oy == 0) {
191        this->set(SkApplySign(length, SkExtractSign(ox)), 0);
192        return true;
193    }
194
195    unsigned x = SkAbs32(ox);
196    unsigned y = SkAbs32(oy);
197    int zeros = SkCLZ(x | y);
198
199    // make x,y 1.14 values so our fast sqr won't overflow
200    if (zeros > 17) {
201        x <<= zeros - 17;
202        y <<= zeros - 17;
203    } else {
204        x >>= 17 - zeros;
205        y >>= 17 - zeros;
206    }
207    SkASSERT((x | y) <= 0x7FFF);
208
209    unsigned invrt = fast_invsqrt(x*x + y*y);
210
211    x = x * invrt >> 12;
212    y = y * invrt >> 12;
213
214    if (length != SK_Fixed1) {
215        x = SkFixedMul(x, length);
216        y = SkFixedMul(y, length);
217    }
218    this->set(SkApplySign(x, SkExtractSign(ox)),
219              SkApplySign(y, SkExtractSign(oy)));
220    return true;
221}
222#else
223/*
224    Normalize x,y, and then scale them by length.
225
226    The obvious way to do this would be the following:
227        S64 tmp1, tmp2;
228        tmp1.setMul(x,x);
229        tmp2.setMul(y,y);
230        tmp1.add(tmp2);
231        len = tmp1.getSqrt();
232        x' = SkFixedDiv(x, len);
233        y' = SkFixedDiv(y, len);
234    This is fine, but slower than what we do below.
235
236    The present technique does not compute the starting length, but
237    rather fiddles with x,y iteratively, all the while checking its
238    magnitude^2 (avoiding a sqrt).
239
240    We normalize by first shifting x,y so that at least one of them
241    has bit 31 set (after taking the abs of them).
242    Then we loop, refining x,y by squaring them and comparing
243    against a very large 1.0 (1 << 28), and then adding or subtracting
244    a delta (which itself is reduced by half each time through the loop).
245    For speed we want the squaring to be with a simple integer mul. To keep
246    that from overflowing we shift our coordinates down until we are dealing
247    with at most 15 bits (2^15-1)^2 * 2 says withing 32 bits)
248    When our square is close to 1.0, we shift x,y down into fixed range.
249*/
250bool SkPoint::setLength(SkFixed ox, SkFixed oy, SkFixed length) {
251    if (ox == 0) {
252        if (oy == 0)
253            return false;
254        this->set(0, SkApplySign(length, SkExtractSign(oy)));
255        return true;
256    }
257    if (oy == 0) {
258        this->set(SkApplySign(length, SkExtractSign(ox)), 0);
259        return true;
260    }
261
262    SkFixed x = SkAbs32(ox);
263    SkFixed y = SkAbs32(oy);
264
265    // shift x,y so that the greater of them is 15bits (1.14 fixed point)
266    {
267        int shift = SkCLZ(x | y);
268        // make them .30
269        x <<= shift - 1;
270        y <<= shift - 1;
271    }
272
273    SkFixed dx = x;
274    SkFixed dy = y;
275
276    for (int i = 0; i < 17; i++) {
277        dx >>= 1;
278        dy >>= 1;
279
280        U32 len2 = squarefixed(x) + squarefixed(y);
281        if (len2 >> 28) {
282            x -= dx;
283            y -= dy;
284        } else {
285            x += dx;
286            y += dy;
287        }
288    }
289    x >>= 14;
290    y >>= 14;
291
292#ifdef SK_DEBUGx    // measure how far we are from unit-length
293    {
294        static int gMaxError;
295        static int gMaxDiff;
296
297        SkFixed len = fixlen(x, y);
298        int err = len - SK_Fixed1;
299        err = SkAbs32(err);
300
301        if (err > gMaxError) {
302            gMaxError = err;
303            SkDebugf("gMaxError %d\n", err);
304        }
305
306        float fx = SkAbs32(ox)/65536.0f;
307        float fy = SkAbs32(oy)/65536.0f;
308        float mag = sqrtf(fx*fx + fy*fy);
309        fx /= mag;
310        fy /= mag;
311        SkFixed xx = (int)floorf(fx * 65536 + 0.5f);
312        SkFixed yy = (int)floorf(fy * 65536 + 0.5f);
313        err = SkMax32(SkAbs32(xx-x), SkAbs32(yy-y));
314        if (err > gMaxDiff) {
315            gMaxDiff = err;
316            SkDebugf("gMaxDiff %d\n", err);
317        }
318    }
319#endif
320
321    x = SkApplySign(x, SkExtractSign(ox));
322    y = SkApplySign(y, SkExtractSign(oy));
323    if (length != SK_Fixed1) {
324        x = SkFixedMul(x, length);
325        y = SkFixedMul(y, length);
326    }
327
328    this->set(x, y);
329    return true;
330}
331#endif
332
333#endif
334
335