OLD | NEW |
1 | 1 |
2 /* | 2 /* |
3 * Copyright 2008 The Android Open Source Project | 3 * Copyright 2008 The Android Open Source Project |
4 * | 4 * |
5 * Use of this source code is governed by a BSD-style license that can be | 5 * Use of this source code is governed by a BSD-style license that can be |
6 * found in the LICENSE file. | 6 * found in the LICENSE file. |
7 */ | 7 */ |
8 | 8 |
9 | 9 |
10 #include "SkPoint.h" | 10 #include "SkPoint.h" |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
80 } | 80 } |
81 | 81 |
82 bool SkPoint::setNormalize(SkScalar x, SkScalar y) { | 82 bool SkPoint::setNormalize(SkScalar x, SkScalar y) { |
83 return this->setLength(x, y, SK_Scalar1); | 83 return this->setLength(x, y, SK_Scalar1); |
84 } | 84 } |
85 | 85 |
86 bool SkPoint::setLength(SkScalar length) { | 86 bool SkPoint::setLength(SkScalar length) { |
87 return this->setLength(fX, fY, length); | 87 return this->setLength(fX, fY, length); |
88 } | 88 } |
89 | 89 |
90 #ifdef SK_SCALAR_IS_FLOAT | |
91 | |
92 // Returns the square of the Euclidian distance to (dx,dy). | 90 // Returns the square of the Euclidian distance to (dx,dy). |
93 static inline float getLengthSquared(float dx, float dy) { | 91 static inline float getLengthSquared(float dx, float dy) { |
94 return dx * dx + dy * dy; | 92 return dx * dx + dy * dy; |
95 } | 93 } |
96 | 94 |
97 // Calculates the square of the Euclidian distance to (dx,dy) and stores it in | 95 // Calculates the square of the Euclidian distance to (dx,dy) and stores it in |
98 // *lengthSquared. Returns true if the distance is judged to be "nearly zero". | 96 // *lengthSquared. Returns true if the distance is judged to be "nearly zero". |
99 // | 97 // |
100 // This logic is encapsulated in a helper method to make it explicit that we | 98 // This logic is encapsulated in a helper method to make it explicit that we |
101 // always perform this check in the same manner, to avoid inconsistencies | 99 // always perform this check in the same manner, to avoid inconsistencies |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
170 // divide by inf. and return (0,0) vector. | 168 // divide by inf. and return (0,0) vector. |
171 double xx = x; | 169 double xx = x; |
172 double yy = y; | 170 double yy = y; |
173 scale = (float)(length / sqrt(xx * xx + yy * yy)); | 171 scale = (float)(length / sqrt(xx * xx + yy * yy)); |
174 } | 172 } |
175 fX = x * scale; | 173 fX = x * scale; |
176 fY = y * scale; | 174 fY = y * scale; |
177 return true; | 175 return true; |
178 } | 176 } |
179 | 177 |
180 #else | 178 bool SkPoint::setLengthFast(float length) { |
181 | 179 return this->setLengthFast(fX, fY, length); |
182 #include "Sk64.h" | |
183 | |
184 // Returns the square of the Euclidian distance to (dx,dy) in *result. | |
185 static inline void getLengthSquared(SkScalar dx, SkScalar dy, Sk64 *result) { | |
186 Sk64 dySqr; | |
187 | |
188 result->setMul(dx, dx); | |
189 dySqr.setMul(dy, dy); | |
190 result->add(dySqr); | |
191 } | 180 } |
192 | 181 |
193 // Calculates the square of the Euclidian distance to (dx,dy) and stores it in | 182 bool SkPoint::setLengthFast(float x, float y, float length) { |
194 // *lengthSquared. Returns true if the distance is judged to be "nearly zero". | 183 float mag2; |
195 // | 184 if (isLengthNearlyZero(x, y, &mag2)) { |
196 // This logic is encapsulated in a helper method to make it explicit that we | 185 return false; |
197 // always perform this check in the same manner, to avoid inconsistencies | 186 } |
198 // (see http://code.google.com/p/skia/issues/detail?id=560 ). | |
199 static inline bool isLengthNearlyZero(SkScalar dx, SkScalar dy, | |
200 Sk64 *lengthSquared) { | |
201 Sk64 tolSqr; | |
202 getLengthSquared(dx, dy, lengthSquared); | |
203 | 187 |
204 // we want nearlyzero^2, but to compute it fast we want to just do a | 188 float scale; |
205 // 32bit multiply, so we require that it not exceed 31bits. That is true | 189 if (SkScalarIsFinite(mag2)) { |
206 // if nearlyzero is <= 0xB504, which should be trivial, since usually | 190 scale = length * sk_float_rsqrt(mag2); // <--- this is the difference |
207 // nearlyzero is a very small fixed-point value. | 191 } else { |
208 SkASSERT(SK_ScalarNearlyZero <= 0xB504); | 192 // our mag2 step overflowed to infinity, so use doubles instead. |
209 | 193 // much slower, but needed when x or y are very large, other wise we |
210 tolSqr.set(0, SK_ScalarNearlyZero * SK_ScalarNearlyZero); | 194 // divide by inf. and return (0,0) vector. |
211 return *lengthSquared <= tolSqr; | 195 double xx = x; |
| 196 double yy = y; |
| 197 scale = (float)(length / sqrt(xx * xx + yy * yy)); |
| 198 } |
| 199 fX = x * scale; |
| 200 fY = y * scale; |
| 201 return true; |
212 } | 202 } |
213 | 203 |
214 SkScalar SkPoint::Normalize(SkPoint* pt) { | |
215 Sk64 mag2; | |
216 if (!isLengthNearlyZero(pt->fX, pt->fY, &mag2)) { | |
217 SkScalar mag = mag2.getSqrt(); | |
218 SkScalar scale = SkScalarInvert(mag); | |
219 pt->fX = SkScalarMul(pt->fX, scale); | |
220 pt->fY = SkScalarMul(pt->fY, scale); | |
221 return mag; | |
222 } | |
223 return 0; | |
224 } | |
225 | |
226 bool SkPoint::CanNormalize(SkScalar dx, SkScalar dy) { | |
227 Sk64 mag2_unused; | |
228 return !isLengthNearlyZero(dx, dy, &mag2_unused); | |
229 } | |
230 | |
231 SkScalar SkPoint::Length(SkScalar dx, SkScalar dy) { | |
232 Sk64 tmp; | |
233 getLengthSquared(dx, dy, &tmp); | |
234 return tmp.getSqrt(); | |
235 } | |
236 | |
237 #ifdef SK_DEBUGx | |
238 static SkFixed fixlen(SkFixed x, SkFixed y) { | |
239 float fx = (float)x; | |
240 float fy = (float)y; | |
241 | |
242 return (int)floorf(sqrtf(fx*fx + fy*fy) + 0.5f); | |
243 } | |
244 #endif | |
245 | |
246 static inline uint32_t squarefixed(unsigned x) { | |
247 x >>= 16; | |
248 return x*x; | |
249 } | |
250 | |
251 #if 1 // Newton iter for setLength | |
252 | |
253 static inline unsigned invsqrt_iter(unsigned V, unsigned U) { | |
254 unsigned x = V * U >> 14; | |
255 x = x * U >> 14; | |
256 x = (3 << 14) - x; | |
257 x = (U >> 1) * x >> 14; | |
258 return x; | |
259 } | |
260 | |
261 static const uint16_t gInvSqrt14GuessTable[] = { | |
262 0x4000, 0x3c57, 0x393e, 0x3695, 0x3441, 0x3235, 0x3061, | |
263 0x2ebd, 0x2d41, 0x2be7, 0x2aaa, 0x2987, 0x287a, 0x2780, | |
264 0x2698, 0x25be, 0x24f3, 0x2434, 0x2380, 0x22d6, 0x2235, | |
265 0x219d, 0x210c, 0x2083, 0x2000, 0x1f82, 0x1f0b, 0x1e99, | |
266 0x1e2b, 0x1dc2, 0x1d5d, 0x1cfc, 0x1c9f, 0x1c45, 0x1bee, | |
267 0x1b9b, 0x1b4a, 0x1afc, 0x1ab0, 0x1a67, 0x1a20, 0x19dc, | |
268 0x1999, 0x1959, 0x191a, 0x18dd, 0x18a2, 0x1868, 0x1830, | |
269 0x17fa, 0x17c4, 0x1791, 0x175e, 0x172d, 0x16fd, 0x16ce | |
270 }; | |
271 | |
272 #define BUILD_INVSQRT_TABLEx | |
273 #ifdef BUILD_INVSQRT_TABLE | |
274 static void build_invsqrt14_guess_table() { | |
275 for (int i = 8; i <= 63; i++) { | |
276 unsigned x = SkToU16((1 << 28) / SkSqrt32(i << 25)); | |
277 printf("0x%x, ", x); | |
278 } | |
279 printf("\n"); | |
280 } | |
281 #endif | |
282 | |
283 static unsigned fast_invsqrt(uint32_t x) { | |
284 #ifdef BUILD_INVSQRT_TABLE | |
285 unsigned top2 = x >> 25; | |
286 SkASSERT(top2 >= 8 && top2 <= 63); | |
287 | |
288 static bool gOnce; | |
289 if (!gOnce) { | |
290 build_invsqrt14_guess_table(); | |
291 gOnce = true; | |
292 } | |
293 #endif | |
294 | |
295 unsigned V = x >> 14; // make V .14 | |
296 | |
297 unsigned top = x >> 25; | |
298 SkASSERT(top >= 8 && top <= 63); | |
299 SkASSERT(top - 8 < SK_ARRAY_COUNT(gInvSqrt14GuessTable)); | |
300 unsigned U = gInvSqrt14GuessTable[top - 8]; | |
301 | |
302 U = invsqrt_iter(V, U); | |
303 return invsqrt_iter(V, U); | |
304 } | |
305 | |
306 /* We "normalize" x,y to be .14 values (so we can square them and stay 32bits. | |
307 Then we Newton-iterate this in .14 space to compute the invser-sqrt, and | |
308 scale by it at the end. The .14 space means we can execute our iterations | |
309 and stay in 32bits as well, making the multiplies much cheaper than calling | |
310 SkFixedMul. | |
311 */ | |
312 bool SkPoint::setLength(SkFixed ox, SkFixed oy, SkFixed length) { | |
313 if (ox == 0) { | |
314 if (oy == 0) { | |
315 return false; | |
316 } | |
317 this->set(0, SkApplySign(length, SkExtractSign(oy))); | |
318 return true; | |
319 } | |
320 if (oy == 0) { | |
321 this->set(SkApplySign(length, SkExtractSign(ox)), 0); | |
322 return true; | |
323 } | |
324 | |
325 unsigned x = SkAbs32(ox); | |
326 unsigned y = SkAbs32(oy); | |
327 int zeros = SkCLZ(x | y); | |
328 | |
329 // make x,y 1.14 values so our fast sqr won't overflow | |
330 if (zeros > 17) { | |
331 x <<= zeros - 17; | |
332 y <<= zeros - 17; | |
333 } else { | |
334 x >>= 17 - zeros; | |
335 y >>= 17 - zeros; | |
336 } | |
337 SkASSERT((x | y) <= 0x7FFF); | |
338 | |
339 unsigned invrt = fast_invsqrt(x*x + y*y); | |
340 | |
341 x = x * invrt >> 12; | |
342 y = y * invrt >> 12; | |
343 | |
344 if (length != SK_Fixed1) { | |
345 x = SkFixedMul(x, length); | |
346 y = SkFixedMul(y, length); | |
347 } | |
348 this->set(SkApplySign(x, SkExtractSign(ox)), | |
349 SkApplySign(y, SkExtractSign(oy))); | |
350 return true; | |
351 } | |
352 #else | |
353 /* | |
354 Normalize x,y, and then scale them by length. | |
355 | |
356 The obvious way to do this would be the following: | |
357 S64 tmp1, tmp2; | |
358 tmp1.setMul(x,x); | |
359 tmp2.setMul(y,y); | |
360 tmp1.add(tmp2); | |
361 len = tmp1.getSqrt(); | |
362 x' = SkFixedDiv(x, len); | |
363 y' = SkFixedDiv(y, len); | |
364 This is fine, but slower than what we do below. | |
365 | |
366 The present technique does not compute the starting length, but | |
367 rather fiddles with x,y iteratively, all the while checking its | |
368 magnitude^2 (avoiding a sqrt). | |
369 | |
370 We normalize by first shifting x,y so that at least one of them | |
371 has bit 31 set (after taking the abs of them). | |
372 Then we loop, refining x,y by squaring them and comparing | |
373 against a very large 1.0 (1 << 28), and then adding or subtracting | |
374 a delta (which itself is reduced by half each time through the loop). | |
375 For speed we want the squaring to be with a simple integer mul. To keep | |
376 that from overflowing we shift our coordinates down until we are dealing | |
377 with at most 15 bits (2^15-1)^2 * 2 says withing 32 bits) | |
378 When our square is close to 1.0, we shift x,y down into fixed range. | |
379 */ | |
380 bool SkPoint::setLength(SkFixed ox, SkFixed oy, SkFixed length) { | |
381 if (ox == 0) { | |
382 if (oy == 0) | |
383 return false; | |
384 this->set(0, SkApplySign(length, SkExtractSign(oy))); | |
385 return true; | |
386 } | |
387 if (oy == 0) { | |
388 this->set(SkApplySign(length, SkExtractSign(ox)), 0); | |
389 return true; | |
390 } | |
391 | |
392 SkFixed x = SkAbs32(ox); | |
393 SkFixed y = SkAbs32(oy); | |
394 | |
395 // shift x,y so that the greater of them is 15bits (1.14 fixed point) | |
396 { | |
397 int shift = SkCLZ(x | y); | |
398 // make them .30 | |
399 x <<= shift - 1; | |
400 y <<= shift - 1; | |
401 } | |
402 | |
403 SkFixed dx = x; | |
404 SkFixed dy = y; | |
405 | |
406 for (int i = 0; i < 17; i++) { | |
407 dx >>= 1; | |
408 dy >>= 1; | |
409 | |
410 U32 len2 = squarefixed(x) + squarefixed(y); | |
411 if (len2 >> 28) { | |
412 x -= dx; | |
413 y -= dy; | |
414 } else { | |
415 x += dx; | |
416 y += dy; | |
417 } | |
418 } | |
419 x >>= 14; | |
420 y >>= 14; | |
421 | |
422 #ifdef SK_DEBUGx // measure how far we are from unit-length | |
423 { | |
424 static int gMaxError; | |
425 static int gMaxDiff; | |
426 | |
427 SkFixed len = fixlen(x, y); | |
428 int err = len - SK_Fixed1; | |
429 err = SkAbs32(err); | |
430 | |
431 if (err > gMaxError) { | |
432 gMaxError = err; | |
433 SkDebugf("gMaxError %d\n", err); | |
434 } | |
435 | |
436 float fx = SkAbs32(ox)/65536.0f; | |
437 float fy = SkAbs32(oy)/65536.0f; | |
438 float mag = sqrtf(fx*fx + fy*fy); | |
439 fx /= mag; | |
440 fy /= mag; | |
441 SkFixed xx = (int)floorf(fx * 65536 + 0.5f); | |
442 SkFixed yy = (int)floorf(fy * 65536 + 0.5f); | |
443 err = SkMax32(SkAbs32(xx-x), SkAbs32(yy-y)); | |
444 if (err > gMaxDiff) { | |
445 gMaxDiff = err; | |
446 SkDebugf("gMaxDiff %d\n", err); | |
447 } | |
448 } | |
449 #endif | |
450 | |
451 x = SkApplySign(x, SkExtractSign(ox)); | |
452 y = SkApplySign(y, SkExtractSign(oy)); | |
453 if (length != SK_Fixed1) { | |
454 x = SkFixedMul(x, length); | |
455 y = SkFixedMul(y, length); | |
456 } | |
457 | |
458 this->set(x, y); | |
459 return true; | |
460 } | |
461 #endif | |
462 | |
463 #endif | |
464 | 204 |
465 /////////////////////////////////////////////////////////////////////////////// | 205 /////////////////////////////////////////////////////////////////////////////// |
466 | 206 |
467 SkScalar SkPoint::distanceToLineBetweenSqd(const SkPoint& a, | 207 SkScalar SkPoint::distanceToLineBetweenSqd(const SkPoint& a, |
468 const SkPoint& b, | 208 const SkPoint& b, |
469 Side* side) const { | 209 Side* side) const { |
470 | 210 |
471 SkVector u = b - a; | 211 SkVector u = b - a; |
472 SkVector v = *this - a; | 212 SkVector v = *this - a; |
473 | 213 |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
508 | 248 |
509 if (uDotV <= 0) { | 249 if (uDotV <= 0) { |
510 return v.lengthSqd(); | 250 return v.lengthSqd(); |
511 } else if (uDotV > uLengthSqd) { | 251 } else if (uDotV > uLengthSqd) { |
512 return b.distanceToSqd(*this); | 252 return b.distanceToSqd(*this); |
513 } else { | 253 } else { |
514 SkScalar det = u.cross(v); | 254 SkScalar det = u.cross(v); |
515 return SkScalarMulDiv(det, det, uLengthSqd); | 255 return SkScalarMulDiv(det, det, uLengthSqd); |
516 } | 256 } |
517 } | 257 } |
OLD | NEW |