| OLD | NEW |
| (Empty) |
| 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 | |
| 19 void 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 | |
| 28 void 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 | |
| 39 void 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 | |
| 48 void 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 | |
| 57 void 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 | |
| 64 bool SkPoint::normalize() { | |
| 65 return this->setLength(fX, fY, SK_Scalar1); | |
| 66 } | |
| 67 | |
| 68 bool SkPoint::setNormalize(SkScalar x, SkScalar y) { | |
| 69 return this->setLength(x, y, SK_Scalar1); | |
| 70 } | |
| 71 | |
| 72 bool SkPoint::setLength(SkScalar length) { | |
| 73 return this->setLength(fX, fY, length); | |
| 74 } | |
| 75 | |
| 76 #ifdef SK_SCALAR_IS_FLOAT | |
| 77 | |
| 78 SkScalar SkPoint::Length(SkScalar dx, SkScalar dy) { | |
| 79 return sk_float_sqrt(dx * dx + dy * dy); | |
| 80 } | |
| 81 | |
| 82 bool 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 | |
| 97 SkScalar 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 | |
| 108 static 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 | |
| 116 static inline uint32_t squarefixed(unsigned x) { | |
| 117 x >>= 16; | |
| 118 return x*x; | |
| 119 } | |
| 120 | |
| 121 #if 1 // Newton iter for setLength | |
| 122 | |
| 123 static 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 | |
| 131 static 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 | |
| 144 static 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 | |
| 153 static 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 */ | |
| 182 bool 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 */ | |
| 250 bool 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 | |
| OLD | NEW |