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 SkScalar SkPoint::Normalize(SkPoint* pt) { 83 float mag = SkPoint::Length(pt->fX, pt->fY); 84 if (mag > kNearlyZero) { 85 float scale = 1 / mag; 86 pt->fX *= scale; 87 pt->fY *= scale; 88 return mag; 89 } 90 return 0; 91 } 92 93 bool SkPoint::setLength(float x, float y, float length) { 94 float mag = sk_float_sqrt(x * x + y * y); 95 if (mag > kNearlyZero) { 96 length /= mag; 97 fX = x * length; 98 fY = y * length; 99 return true; 100 } 101 return false; 102 } 103 104 #else 105 106 #include "Sk64.h" 107 108 SkScalar SkPoint::Length(SkScalar dx, SkScalar dy) { 109 Sk64 tmp1, tmp2; 110 111 tmp1.setMul(dx, dx); 112 tmp2.setMul(dy, dy); 113 tmp1.add(tmp2); 114 115 return tmp1.getSqrt(); 116 } 117 118 #ifdef SK_DEBUGx 119 static SkFixed fixlen(SkFixed x, SkFixed y) { 120 float fx = (float)x; 121 float fy = (float)y; 122 123 return (int)floorf(sqrtf(fx*fx + fy*fy) + 0.5f); 124 } 125 #endif 126 127 static inline uint32_t squarefixed(unsigned x) { 128 x >>= 16; 129 return x*x; 130 } 131 132 #if 1 // Newton iter for setLength 133 134 static inline unsigned invsqrt_iter(unsigned V, unsigned U) { 135 unsigned x = V * U >> 14; 136 x = x * U >> 14; 137 x = (3 << 14) - x; 138 x = (U >> 1) * x >> 14; 139 return x; 140 } 141 142 static const uint16_t gInvSqrt14GuessTable[] = { 143 0x4000, 0x3c57, 0x393e, 0x3695, 0x3441, 0x3235, 0x3061, 144 0x2ebd, 0x2d41, 0x2be7, 0x2aaa, 0x2987, 0x287a, 0x2780, 145 0x2698, 0x25be, 0x24f3, 0x2434, 0x2380, 0x22d6, 0x2235, 146 0x219d, 0x210c, 0x2083, 0x2000, 0x1f82, 0x1f0b, 0x1e99, 147 0x1e2b, 0x1dc2, 0x1d5d, 0x1cfc, 0x1c9f, 0x1c45, 0x1bee, 148 0x1b9b, 0x1b4a, 0x1afc, 0x1ab0, 0x1a67, 0x1a20, 0x19dc, 149 0x1999, 0x1959, 0x191a, 0x18dd, 0x18a2, 0x1868, 0x1830, 150 0x17fa, 0x17c4, 0x1791, 0x175e, 0x172d, 0x16fd, 0x16ce 151 }; 152 153 #define BUILD_INVSQRT_TABLEx 154 #ifdef BUILD_INVSQRT_TABLE 155 static void build_invsqrt14_guess_table() { 156 for (int i = 8; i <= 63; i++) { 157 unsigned x = SkToU16((1 << 28) / SkSqrt32(i << 25)); 158 printf("0x%x, ", x); 159 } 160 printf("\n"); 161 } 162 #endif 163 164 static unsigned fast_invsqrt(uint32_t x) { 165 #ifdef BUILD_INVSQRT_TABLE 166 unsigned top2 = x >> 25; 167 SkASSERT(top2 >= 8 && top2 <= 63); 168 169 static bool gOnce; 170 if (!gOnce) { 171 build_invsqrt14_guess_table(); 172 gOnce = true; 173 } 174 #endif 175 176 unsigned V = x >> 14; // make V .14 177 178 unsigned top = x >> 25; 179 SkASSERT(top >= 8 && top <= 63); 180 SkASSERT(top - 8 < SK_ARRAY_COUNT(gInvSqrt14GuessTable)); 181 unsigned U = gInvSqrt14GuessTable[top - 8]; 182 183 U = invsqrt_iter(V, U); 184 return invsqrt_iter(V, U); 185 } 186 187 /* We "normalize" x,y to be .14 values (so we can square them and stay 32bits. 188 Then we Newton-iterate this in .14 space to compute the invser-sqrt, and 189 scale by it at the end. The .14 space means we can execute our iterations 190 and stay in 32bits as well, making the multiplies much cheaper than calling 191 SkFixedMul. 192 */ 193 bool SkPoint::setLength(SkFixed ox, SkFixed oy, SkFixed length) { 194 if (ox == 0) { 195 if (oy == 0) { 196 return false; 197 } 198 this->set(0, SkApplySign(length, SkExtractSign(oy))); 199 return true; 200 } 201 if (oy == 0) { 202 this->set(SkApplySign(length, SkExtractSign(ox)), 0); 203 return true; 204 } 205 206 unsigned x = SkAbs32(ox); 207 unsigned y = SkAbs32(oy); 208 int zeros = SkCLZ(x | y); 209 210 // make x,y 1.14 values so our fast sqr won't overflow 211 if (zeros > 17) { 212 x <<= zeros - 17; 213 y <<= zeros - 17; 214 } else { 215 x >>= 17 - zeros; 216 y >>= 17 - zeros; 217 } 218 SkASSERT((x | y) <= 0x7FFF); 219 220 unsigned invrt = fast_invsqrt(x*x + y*y); 221 222 x = x * invrt >> 12; 223 y = y * invrt >> 12; 224 225 if (length != SK_Fixed1) { 226 x = SkFixedMul(x, length); 227 y = SkFixedMul(y, length); 228 } 229 this->set(SkApplySign(x, SkExtractSign(ox)), 230 SkApplySign(y, SkExtractSign(oy))); 231 return true; 232 } 233 #else 234 /* 235 Normalize x,y, and then scale them by length. 236 237 The obvious way to do this would be the following: 238 S64 tmp1, tmp2; 239 tmp1.setMul(x,x); 240 tmp2.setMul(y,y); 241 tmp1.add(tmp2); 242 len = tmp1.getSqrt(); 243 x' = SkFixedDiv(x, len); 244 y' = SkFixedDiv(y, len); 245 This is fine, but slower than what we do below. 246 247 The present technique does not compute the starting length, but 248 rather fiddles with x,y iteratively, all the while checking its 249 magnitude^2 (avoiding a sqrt). 250 251 We normalize by first shifting x,y so that at least one of them 252 has bit 31 set (after taking the abs of them). 253 Then we loop, refining x,y by squaring them and comparing 254 against a very large 1.0 (1 << 28), and then adding or subtracting 255 a delta (which itself is reduced by half each time through the loop). 256 For speed we want the squaring to be with a simple integer mul. To keep 257 that from overflowing we shift our coordinates down until we are dealing 258 with at most 15 bits (2^15-1)^2 * 2 says withing 32 bits) 259 When our square is close to 1.0, we shift x,y down into fixed range. 260 */ 261 bool SkPoint::setLength(SkFixed ox, SkFixed oy, SkFixed length) { 262 if (ox == 0) { 263 if (oy == 0) 264 return false; 265 this->set(0, SkApplySign(length, SkExtractSign(oy))); 266 return true; 267 } 268 if (oy == 0) { 269 this->set(SkApplySign(length, SkExtractSign(ox)), 0); 270 return true; 271 } 272 273 SkFixed x = SkAbs32(ox); 274 SkFixed y = SkAbs32(oy); 275 276 // shift x,y so that the greater of them is 15bits (1.14 fixed point) 277 { 278 int shift = SkCLZ(x | y); 279 // make them .30 280 x <<= shift - 1; 281 y <<= shift - 1; 282 } 283 284 SkFixed dx = x; 285 SkFixed dy = y; 286 287 for (int i = 0; i < 17; i++) { 288 dx >>= 1; 289 dy >>= 1; 290 291 U32 len2 = squarefixed(x) + squarefixed(y); 292 if (len2 >> 28) { 293 x -= dx; 294 y -= dy; 295 } else { 296 x += dx; 297 y += dy; 298 } 299 } 300 x >>= 14; 301 y >>= 14; 302 303 #ifdef SK_DEBUGx // measure how far we are from unit-length 304 { 305 static int gMaxError; 306 static int gMaxDiff; 307 308 SkFixed len = fixlen(x, y); 309 int err = len - SK_Fixed1; 310 err = SkAbs32(err); 311 312 if (err > gMaxError) { 313 gMaxError = err; 314 SkDebugf("gMaxError %d\n", err); 315 } 316 317 float fx = SkAbs32(ox)/65536.0f; 318 float fy = SkAbs32(oy)/65536.0f; 319 float mag = sqrtf(fx*fx + fy*fy); 320 fx /= mag; 321 fy /= mag; 322 SkFixed xx = (int)floorf(fx * 65536 + 0.5f); 323 SkFixed yy = (int)floorf(fy * 65536 + 0.5f); 324 err = SkMax32(SkAbs32(xx-x), SkAbs32(yy-y)); 325 if (err > gMaxDiff) { 326 gMaxDiff = err; 327 SkDebugf("gMaxDiff %d\n", err); 328 } 329 } 330 #endif 331 332 x = SkApplySign(x, SkExtractSign(ox)); 333 y = SkApplySign(y, SkExtractSign(oy)); 334 if (length != SK_Fixed1) { 335 x = SkFixedMul(x, length); 336 y = SkFixedMul(y, length); 337 } 338 339 this->set(x, y); 340 return true; 341 } 342 #endif 343 344 #endif 345 346