blob: 34a0fe0647aff914479cfc4a8432ef69c79be533 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/* libs/graphics/sgl/SkGeometry.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9** http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#include "SkGeometry.h"
19#include "Sk64.h"
20#include "SkMatrix.h"
21
reed@android.com945a1392010-02-05 20:41:02 +000022bool SkXRayCrossesLine(const SkXRay& pt, const SkPoint pts[2]) {
23 // Determine quick discards.
24 // Consider query line going exactly through point 0 to not
25 // intersect, for symmetry with SkXRayCrossesMonotonicCubic.
26 if (pt.fY == pts[0].fY)
27 return false;
28 if (pt.fY < pts[0].fY && pt.fY < pts[1].fY)
29 return false;
30 if (pt.fY > pts[0].fY && pt.fY > pts[1].fY)
31 return false;
32 if (pt.fX > pts[0].fX && pt.fX > pts[1].fX)
33 return false;
34 // Determine degenerate cases
35 if (SkScalarNearlyZero(pts[0].fY - pts[1].fY))
36 return false;
37 if (SkScalarNearlyZero(pts[0].fX - pts[1].fX))
38 // We've already determined the query point lies within the
39 // vertical range of the line segment.
40 return pt.fX <= pts[0].fX;
41 // Full line segment evaluation
42 SkScalar delta_y = pts[1].fY - pts[0].fY;
43 SkScalar delta_x = pts[1].fX - pts[0].fX;
44 SkScalar slope = SkScalarDiv(delta_y, delta_x);
45 SkScalar b = pts[0].fY - SkScalarMul(slope, pts[0].fX);
46 // Solve for x coordinate at y = pt.fY
47 SkScalar x = SkScalarDiv(pt.fY - b, slope);
48 return pt.fX <= x;
49}
50
reed@android.com8a1c16f2008-12-17 15:59:43 +000051/** If defined, this makes eval_quad and eval_cubic do more setup (sometimes
52 involving integer multiplies by 2 or 3, but fewer calls to SkScalarMul.
53 May also introduce overflow of fixed when we compute our setup.
54*/
55#ifdef SK_SCALAR_IS_FIXED
56 #define DIRECT_EVAL_OF_POLYNOMIALS
57#endif
58
59////////////////////////////////////////////////////////////////////////
60
61#ifdef SK_SCALAR_IS_FIXED
62 static int is_not_monotonic(int a, int b, int c, int d)
63 {
64 return (((a - b) | (b - c) | (c - d)) & ((b - a) | (c - b) | (d - c))) >> 31;
65 }
66
67 static int is_not_monotonic(int a, int b, int c)
68 {
69 return (((a - b) | (b - c)) & ((b - a) | (c - b))) >> 31;
70 }
71#else
72 static int is_not_monotonic(float a, float b, float c)
73 {
74 float ab = a - b;
75 float bc = b - c;
76 if (ab < 0)
77 bc = -bc;
78 return ab == 0 || bc < 0;
79 }
80#endif
81
82////////////////////////////////////////////////////////////////////////
83
84static bool is_unit_interval(SkScalar x)
85{
86 return x > 0 && x < SK_Scalar1;
87}
88
89static int valid_unit_divide(SkScalar numer, SkScalar denom, SkScalar* ratio)
90{
91 SkASSERT(ratio);
92
93 if (numer < 0)
94 {
95 numer = -numer;
96 denom = -denom;
97 }
98
99 if (denom == 0 || numer == 0 || numer >= denom)
100 return 0;
101
102 SkScalar r = SkScalarDiv(numer, denom);
reed@android.com15161622010-03-08 17:44:42 +0000103 if (SkScalarIsNaN(r)) {
104 return 0;
105 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106 SkASSERT(r >= 0 && r < SK_Scalar1);
107 if (r == 0) // catch underflow if numer <<<< denom
108 return 0;
109 *ratio = r;
110 return 1;
111}
112
113/** From Numerical Recipes in C.
114
115 Q = -1/2 (B + sign(B) sqrt[B*B - 4*A*C])
116 x1 = Q / A
117 x2 = C / Q
118*/
119int SkFindUnitQuadRoots(SkScalar A, SkScalar B, SkScalar C, SkScalar roots[2])
120{
121 SkASSERT(roots);
122
123 if (A == 0)
124 return valid_unit_divide(-C, B, roots);
125
126 SkScalar* r = roots;
127
128#ifdef SK_SCALAR_IS_FLOAT
129 float R = B*B - 4*A*C;
reed@android.com15161622010-03-08 17:44:42 +0000130 if (R < 0 || SkScalarIsNaN(R)) { // complex roots
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131 return 0;
reed@android.com15161622010-03-08 17:44:42 +0000132 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133 R = sk_float_sqrt(R);
134#else
135 Sk64 RR, tmp;
136
137 RR.setMul(B,B);
138 tmp.setMul(A,C);
139 tmp.shiftLeft(2);
140 RR.sub(tmp);
141 if (RR.isNeg())
142 return 0;
143 SkFixed R = RR.getSqrt();
144#endif
145
146 SkScalar Q = (B < 0) ? -(B-R)/2 : -(B+R)/2;
147 r += valid_unit_divide(Q, A, r);
148 r += valid_unit_divide(C, Q, r);
149 if (r - roots == 2)
150 {
151 if (roots[0] > roots[1])
152 SkTSwap<SkScalar>(roots[0], roots[1]);
153 else if (roots[0] == roots[1]) // nearly-equal?
154 r -= 1; // skip the double root
155 }
156 return (int)(r - roots);
157}
158
159#ifdef SK_SCALAR_IS_FIXED
160/** Trim A/B/C down so that they are all <= 32bits
161 and then call SkFindUnitQuadRoots()
162*/
163static int Sk64FindFixedQuadRoots(const Sk64& A, const Sk64& B, const Sk64& C, SkFixed roots[2])
164{
165 int na = A.shiftToMake32();
166 int nb = B.shiftToMake32();
167 int nc = C.shiftToMake32();
168
169 int shift = SkMax32(na, SkMax32(nb, nc));
170 SkASSERT(shift >= 0);
171
172 return SkFindUnitQuadRoots(A.getShiftRight(shift), B.getShiftRight(shift), C.getShiftRight(shift), roots);
173}
174#endif
175
176/////////////////////////////////////////////////////////////////////////////////////
177/////////////////////////////////////////////////////////////////////////////////////
178
179static SkScalar eval_quad(const SkScalar src[], SkScalar t)
180{
181 SkASSERT(src);
182 SkASSERT(t >= 0 && t <= SK_Scalar1);
183
184#ifdef DIRECT_EVAL_OF_POLYNOMIALS
185 SkScalar C = src[0];
186 SkScalar A = src[4] - 2 * src[2] + C;
187 SkScalar B = 2 * (src[2] - C);
188 return SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C);
189#else
190 SkScalar ab = SkScalarInterp(src[0], src[2], t);
191 SkScalar bc = SkScalarInterp(src[2], src[4], t);
192 return SkScalarInterp(ab, bc, t);
193#endif
194}
195
196static SkScalar eval_quad_derivative(const SkScalar src[], SkScalar t)
197{
198 SkScalar A = src[4] - 2 * src[2] + src[0];
199 SkScalar B = src[2] - src[0];
200
201 return 2 * SkScalarMulAdd(A, t, B);
202}
203
204static SkScalar eval_quad_derivative_at_half(const SkScalar src[])
205{
206 SkScalar A = src[4] - 2 * src[2] + src[0];
207 SkScalar B = src[2] - src[0];
208 return A + 2 * B;
209}
210
211void SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint* pt, SkVector* tangent)
212{
213 SkASSERT(src);
214 SkASSERT(t >= 0 && t <= SK_Scalar1);
215
216 if (pt)
217 pt->set(eval_quad(&src[0].fX, t), eval_quad(&src[0].fY, t));
218 if (tangent)
219 tangent->set(eval_quad_derivative(&src[0].fX, t),
220 eval_quad_derivative(&src[0].fY, t));
221}
222
223void SkEvalQuadAtHalf(const SkPoint src[3], SkPoint* pt, SkVector* tangent)
224{
225 SkASSERT(src);
226
227 if (pt)
228 {
229 SkScalar x01 = SkScalarAve(src[0].fX, src[1].fX);
230 SkScalar y01 = SkScalarAve(src[0].fY, src[1].fY);
231 SkScalar x12 = SkScalarAve(src[1].fX, src[2].fX);
232 SkScalar y12 = SkScalarAve(src[1].fY, src[2].fY);
233 pt->set(SkScalarAve(x01, x12), SkScalarAve(y01, y12));
234 }
235 if (tangent)
236 tangent->set(eval_quad_derivative_at_half(&src[0].fX),
237 eval_quad_derivative_at_half(&src[0].fY));
238}
239
240static void interp_quad_coords(const SkScalar* src, SkScalar* dst, SkScalar t)
241{
242 SkScalar ab = SkScalarInterp(src[0], src[2], t);
243 SkScalar bc = SkScalarInterp(src[2], src[4], t);
244
245 dst[0] = src[0];
246 dst[2] = ab;
247 dst[4] = SkScalarInterp(ab, bc, t);
248 dst[6] = bc;
249 dst[8] = src[4];
250}
251
252void SkChopQuadAt(const SkPoint src[3], SkPoint dst[5], SkScalar t)
253{
254 SkASSERT(t > 0 && t < SK_Scalar1);
255
256 interp_quad_coords(&src[0].fX, &dst[0].fX, t);
257 interp_quad_coords(&src[0].fY, &dst[0].fY, t);
258}
259
260void SkChopQuadAtHalf(const SkPoint src[3], SkPoint dst[5])
261{
262 SkScalar x01 = SkScalarAve(src[0].fX, src[1].fX);
263 SkScalar y01 = SkScalarAve(src[0].fY, src[1].fY);
264 SkScalar x12 = SkScalarAve(src[1].fX, src[2].fX);
265 SkScalar y12 = SkScalarAve(src[1].fY, src[2].fY);
266
267 dst[0] = src[0];
268 dst[1].set(x01, y01);
269 dst[2].set(SkScalarAve(x01, x12), SkScalarAve(y01, y12));
270 dst[3].set(x12, y12);
271 dst[4] = src[2];
272}
273
274/** Quad'(t) = At + B, where
275 A = 2(a - 2b + c)
276 B = 2(b - a)
277 Solve for t, only if it fits between 0 < t < 1
278*/
279int SkFindQuadExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar tValue[1])
280{
281 /* At + B == 0
282 t = -B / A
283 */
284#ifdef SK_SCALAR_IS_FIXED
285 return is_not_monotonic(a, b, c) && valid_unit_divide(a - b, a - b - b + c, tValue);
286#else
287 return valid_unit_divide(a - b, a - b - b + c, tValue);
288#endif
289}
290
reed@android.comfc25abd2009-01-15 14:38:33 +0000291static inline void flatten_double_quad_extrema(SkScalar coords[14])
reed@android.com8a1c16f2008-12-17 15:59:43 +0000292{
293 coords[2] = coords[6] = coords[4];
294}
295
reed@android.com8a1c16f2008-12-17 15:59:43 +0000296/* Returns 0 for 1 quad, and 1 for two quads, either way the answer is
reed@android.com77f0ef72009-11-17 18:47:52 +0000297 stored in dst[]. Guarantees that the 1/2 quads will be monotonic.
298 */
reed@android.com8a1c16f2008-12-17 15:59:43 +0000299int SkChopQuadAtYExtrema(const SkPoint src[3], SkPoint dst[5])
300{
301 SkASSERT(src);
302 SkASSERT(dst);
reed@android.com77f0ef72009-11-17 18:47:52 +0000303
reed@android.com8a1c16f2008-12-17 15:59:43 +0000304#if 0
305 static bool once = true;
306 if (once)
307 {
308 once = false;
309 SkPoint s[3] = { 0, 26398, 0, 26331, 0, 20621428 };
310 SkPoint d[6];
311
312 int n = SkChopQuadAtYExtrema(s, d);
313 SkDebugf("chop=%d, Y=[%x %x %x %x %x %x]\n", n, d[0].fY, d[1].fY, d[2].fY, d[3].fY, d[4].fY, d[5].fY);
314 }
315#endif
reed@android.com77f0ef72009-11-17 18:47:52 +0000316
reed@android.com8a1c16f2008-12-17 15:59:43 +0000317 SkScalar a = src[0].fY;
318 SkScalar b = src[1].fY;
319 SkScalar c = src[2].fY;
reed@android.com77f0ef72009-11-17 18:47:52 +0000320
reed@android.com8a1c16f2008-12-17 15:59:43 +0000321 if (is_not_monotonic(a, b, c))
322 {
323 SkScalar tValue;
324 if (valid_unit_divide(a - b, a - b - b + c, &tValue))
325 {
326 SkChopQuadAt(src, dst, tValue);
327 flatten_double_quad_extrema(&dst[0].fY);
328 return 1;
329 }
330 // if we get here, we need to force dst to be monotonic, even though
331 // we couldn't compute a unit_divide value (probably underflow).
332 b = SkScalarAbs(a - b) < SkScalarAbs(b - c) ? a : c;
333 }
334 dst[0].set(src[0].fX, a);
335 dst[1].set(src[1].fX, b);
336 dst[2].set(src[2].fX, c);
337 return 0;
338}
339
reed@android.com77f0ef72009-11-17 18:47:52 +0000340/* Returns 0 for 1 quad, and 1 for two quads, either way the answer is
341 stored in dst[]. Guarantees that the 1/2 quads will be monotonic.
342 */
343int SkChopQuadAtXExtrema(const SkPoint src[3], SkPoint dst[5])
344{
345 SkASSERT(src);
346 SkASSERT(dst);
347
348 SkScalar a = src[0].fX;
349 SkScalar b = src[1].fX;
350 SkScalar c = src[2].fX;
351
352 if (is_not_monotonic(a, b, c)) {
353 SkScalar tValue;
354 if (valid_unit_divide(a - b, a - b - b + c, &tValue)) {
355 SkChopQuadAt(src, dst, tValue);
356 flatten_double_quad_extrema(&dst[0].fX);
357 return 1;
358 }
359 // if we get here, we need to force dst to be monotonic, even though
360 // we couldn't compute a unit_divide value (probably underflow).
361 b = SkScalarAbs(a - b) < SkScalarAbs(b - c) ? a : c;
362 }
363 dst[0].set(a, src[0].fY);
364 dst[1].set(b, src[1].fY);
365 dst[2].set(c, src[2].fY);
366 return 0;
367}
368
reed@android.com8a1c16f2008-12-17 15:59:43 +0000369// F(t) = a (1 - t) ^ 2 + 2 b t (1 - t) + c t ^ 2
370// F'(t) = 2 (b - a) + 2 (a - 2b + c) t
371// F''(t) = 2 (a - 2b + c)
372//
373// A = 2 (b - a)
374// B = 2 (a - 2b + c)
375//
376// Maximum curvature for a quadratic means solving
377// Fx' Fx'' + Fy' Fy'' = 0
378//
379// t = - (Ax Bx + Ay By) / (Bx ^ 2 + By ^ 2)
380//
381int SkChopQuadAtMaxCurvature(const SkPoint src[3], SkPoint dst[5])
382{
383 SkScalar Ax = src[1].fX - src[0].fX;
384 SkScalar Ay = src[1].fY - src[0].fY;
385 SkScalar Bx = src[0].fX - src[1].fX - src[1].fX + src[2].fX;
386 SkScalar By = src[0].fY - src[1].fY - src[1].fY + src[2].fY;
387 SkScalar t = 0; // 0 means don't chop
388
389#ifdef SK_SCALAR_IS_FLOAT
390 (void)valid_unit_divide(-(Ax * Bx + Ay * By), Bx * Bx + By * By, &t);
391#else
392 // !!! should I use SkFloat here? seems like it
393 Sk64 numer, denom, tmp;
394
395 numer.setMul(Ax, -Bx);
396 tmp.setMul(Ay, -By);
397 numer.add(tmp);
398
399 if (numer.isPos()) // do nothing if numer <= 0
400 {
401 denom.setMul(Bx, Bx);
402 tmp.setMul(By, By);
403 denom.add(tmp);
404 SkASSERT(!denom.isNeg());
405 if (numer < denom)
406 {
407 t = numer.getFixedDiv(denom);
408 SkASSERT(t >= 0 && t <= SK_Fixed1); // assert that we're numerically stable (ha!)
409 if ((unsigned)t >= SK_Fixed1) // runtime check for numerical stability
410 t = 0; // ignore the chop
411 }
412 }
413#endif
414
415 if (t == 0)
416 {
417 memcpy(dst, src, 3 * sizeof(SkPoint));
418 return 1;
419 }
420 else
421 {
422 SkChopQuadAt(src, dst, t);
423 return 2;
424 }
425}
426
reed@android.com945a1392010-02-05 20:41:02 +0000427void SkConvertQuadToCubic(const SkPoint src[3], SkPoint dst[4])
428{
429 SkScalar two = SkIntToScalar(2);
430 SkScalar one_third = SkScalarDiv(SK_Scalar1, SkIntToScalar(3));
431 dst[0].set(src[0].fX, src[0].fY);
432 dst[1].set(
433 SkScalarMul(SkScalarMulAdd(src[1].fX, two, src[0].fX), one_third),
434 SkScalarMul(SkScalarMulAdd(src[1].fY, two, src[0].fY), one_third));
435 dst[2].set(
436 SkScalarMul(SkScalarMulAdd(src[1].fX, two, src[2].fX), one_third),
437 SkScalarMul(SkScalarMulAdd(src[1].fY, two, src[2].fY), one_third));
438 dst[3].set(src[2].fX, src[2].fY);
439}
440
reed@android.com8a1c16f2008-12-17 15:59:43 +0000441////////////////////////////////////////////////////////////////////////////////////////
442///// CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS /////
443////////////////////////////////////////////////////////////////////////////////////////
444
445static void get_cubic_coeff(const SkScalar pt[], SkScalar coeff[4])
446{
447 coeff[0] = pt[6] + 3*(pt[2] - pt[4]) - pt[0];
448 coeff[1] = 3*(pt[4] - pt[2] - pt[2] + pt[0]);
449 coeff[2] = 3*(pt[2] - pt[0]);
450 coeff[3] = pt[0];
451}
452
453void SkGetCubicCoeff(const SkPoint pts[4], SkScalar cx[4], SkScalar cy[4])
454{
455 SkASSERT(pts);
456
457 if (cx)
458 get_cubic_coeff(&pts[0].fX, cx);
459 if (cy)
460 get_cubic_coeff(&pts[0].fY, cy);
461}
462
463static SkScalar eval_cubic(const SkScalar src[], SkScalar t)
464{
465 SkASSERT(src);
466 SkASSERT(t >= 0 && t <= SK_Scalar1);
467
468 if (t == 0)
469 return src[0];
470
471#ifdef DIRECT_EVAL_OF_POLYNOMIALS
472 SkScalar D = src[0];
473 SkScalar A = src[6] + 3*(src[2] - src[4]) - D;
474 SkScalar B = 3*(src[4] - src[2] - src[2] + D);
475 SkScalar C = 3*(src[2] - D);
476
477 return SkScalarMulAdd(SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C), t, D);
478#else
479 SkScalar ab = SkScalarInterp(src[0], src[2], t);
480 SkScalar bc = SkScalarInterp(src[2], src[4], t);
481 SkScalar cd = SkScalarInterp(src[4], src[6], t);
482 SkScalar abc = SkScalarInterp(ab, bc, t);
483 SkScalar bcd = SkScalarInterp(bc, cd, t);
484 return SkScalarInterp(abc, bcd, t);
485#endif
486}
487
488/** return At^2 + Bt + C
489*/
490static SkScalar eval_quadratic(SkScalar A, SkScalar B, SkScalar C, SkScalar t)
491{
492 SkASSERT(t >= 0 && t <= SK_Scalar1);
493
494 return SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C);
495}
496
497static SkScalar eval_cubic_derivative(const SkScalar src[], SkScalar t)
498{
499 SkScalar A = src[6] + 3*(src[2] - src[4]) - src[0];
500 SkScalar B = 2*(src[4] - 2 * src[2] + src[0]);
501 SkScalar C = src[2] - src[0];
502
503 return eval_quadratic(A, B, C, t);
504}
505
506static SkScalar eval_cubic_2ndDerivative(const SkScalar src[], SkScalar t)
507{
508 SkScalar A = src[6] + 3*(src[2] - src[4]) - src[0];
509 SkScalar B = src[4] - 2 * src[2] + src[0];
510
511 return SkScalarMulAdd(A, t, B);
512}
513
514void SkEvalCubicAt(const SkPoint src[4], SkScalar t, SkPoint* loc, SkVector* tangent, SkVector* curvature)
515{
516 SkASSERT(src);
517 SkASSERT(t >= 0 && t <= SK_Scalar1);
518
519 if (loc)
520 loc->set(eval_cubic(&src[0].fX, t), eval_cubic(&src[0].fY, t));
521 if (tangent)
522 tangent->set(eval_cubic_derivative(&src[0].fX, t),
523 eval_cubic_derivative(&src[0].fY, t));
524 if (curvature)
525 curvature->set(eval_cubic_2ndDerivative(&src[0].fX, t),
526 eval_cubic_2ndDerivative(&src[0].fY, t));
527}
528
529/** Cubic'(t) = At^2 + Bt + C, where
530 A = 3(-a + 3(b - c) + d)
531 B = 6(a - 2b + c)
532 C = 3(b - a)
533 Solve for t, keeping only those that fit betwee 0 < t < 1
534*/
535int SkFindCubicExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar d, SkScalar tValues[2])
536{
537#ifdef SK_SCALAR_IS_FIXED
538 if (!is_not_monotonic(a, b, c, d))
539 return 0;
540#endif
541
542 // we divide A,B,C by 3 to simplify
543 SkScalar A = d - a + 3*(b - c);
544 SkScalar B = 2*(a - b - b + c);
545 SkScalar C = b - a;
546
547 return SkFindUnitQuadRoots(A, B, C, tValues);
548}
549
550static void interp_cubic_coords(const SkScalar* src, SkScalar* dst, SkScalar t)
551{
552 SkScalar ab = SkScalarInterp(src[0], src[2], t);
553 SkScalar bc = SkScalarInterp(src[2], src[4], t);
554 SkScalar cd = SkScalarInterp(src[4], src[6], t);
555 SkScalar abc = SkScalarInterp(ab, bc, t);
556 SkScalar bcd = SkScalarInterp(bc, cd, t);
557 SkScalar abcd = SkScalarInterp(abc, bcd, t);
558
559 dst[0] = src[0];
560 dst[2] = ab;
561 dst[4] = abc;
562 dst[6] = abcd;
563 dst[8] = bcd;
564 dst[10] = cd;
565 dst[12] = src[6];
566}
567
568void SkChopCubicAt(const SkPoint src[4], SkPoint dst[7], SkScalar t)
569{
570 SkASSERT(t > 0 && t < SK_Scalar1);
571
572 interp_cubic_coords(&src[0].fX, &dst[0].fX, t);
573 interp_cubic_coords(&src[0].fY, &dst[0].fY, t);
574}
575
reed@android.coma9640282009-08-28 20:06:54 +0000576/* http://code.google.com/p/skia/issues/detail?id=32
577
578 This test code would fail when we didn't check the return result of
579 valid_unit_divide in SkChopCubicAt(... tValues[], int roots). The reason is
580 that after the first chop, the parameters to valid_unit_divide are equal
581 (thanks to finite float precision and rounding in the subtracts). Thus
582 even though the 2nd tValue looks < 1.0, after we renormalize it, we end
583 up with 1.0, hence the need to check and just return the last cubic as
584 a degenerate clump of 4 points in the sampe place.
585
586 static void test_cubic() {
587 SkPoint src[4] = {
588 { 556.25000, 523.03003 },
589 { 556.23999, 522.96002 },
590 { 556.21997, 522.89001 },
591 { 556.21997, 522.82001 }
592 };
593 SkPoint dst[10];
594 SkScalar tval[] = { 0.33333334f, 0.99999994f };
595 SkChopCubicAt(src, dst, tval, 2);
596 }
597 */
598
reed@android.com8a1c16f2008-12-17 15:59:43 +0000599void SkChopCubicAt(const SkPoint src[4], SkPoint dst[], const SkScalar tValues[], int roots)
600{
601#ifdef SK_DEBUG
602 {
603 for (int i = 0; i < roots - 1; i++)
604 {
605 SkASSERT(is_unit_interval(tValues[i]));
606 SkASSERT(is_unit_interval(tValues[i+1]));
607 SkASSERT(tValues[i] < tValues[i+1]);
608 }
609 }
610#endif
611
612 if (dst)
613 {
614 if (roots == 0) // nothing to chop
615 memcpy(dst, src, 4*sizeof(SkPoint));
616 else
617 {
618 SkScalar t = tValues[0];
619 SkPoint tmp[4];
620
621 for (int i = 0; i < roots; i++)
622 {
623 SkChopCubicAt(src, dst, t);
624 if (i == roots - 1)
625 break;
626
reed@android.com8a1c16f2008-12-17 15:59:43 +0000627 dst += 3;
reed@android.coma9640282009-08-28 20:06:54 +0000628 // have src point to the remaining cubic (after the chop)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000629 memcpy(tmp, dst, 4 * sizeof(SkPoint));
630 src = tmp;
reed@android.coma9640282009-08-28 20:06:54 +0000631
632 // watch out in case the renormalized t isn't in range
633 if (!valid_unit_divide(tValues[i+1] - tValues[i],
634 SK_Scalar1 - tValues[i], &t)) {
635 // if we can't, just create a degenerate cubic
636 dst[4] = dst[5] = dst[6] = src[3];
637 break;
638 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000639 }
640 }
641 }
642}
643
644void SkChopCubicAtHalf(const SkPoint src[4], SkPoint dst[7])
645{
646 SkScalar x01 = SkScalarAve(src[0].fX, src[1].fX);
647 SkScalar y01 = SkScalarAve(src[0].fY, src[1].fY);
648 SkScalar x12 = SkScalarAve(src[1].fX, src[2].fX);
649 SkScalar y12 = SkScalarAve(src[1].fY, src[2].fY);
650 SkScalar x23 = SkScalarAve(src[2].fX, src[3].fX);
651 SkScalar y23 = SkScalarAve(src[2].fY, src[3].fY);
652
653 SkScalar x012 = SkScalarAve(x01, x12);
654 SkScalar y012 = SkScalarAve(y01, y12);
655 SkScalar x123 = SkScalarAve(x12, x23);
656 SkScalar y123 = SkScalarAve(y12, y23);
657
658 dst[0] = src[0];
659 dst[1].set(x01, y01);
660 dst[2].set(x012, y012);
661 dst[3].set(SkScalarAve(x012, x123), SkScalarAve(y012, y123));
662 dst[4].set(x123, y123);
663 dst[5].set(x23, y23);
664 dst[6] = src[3];
665}
666
667static void flatten_double_cubic_extrema(SkScalar coords[14])
668{
669 coords[4] = coords[8] = coords[6];
670}
671
672/** Given 4 points on a cubic bezier, chop it into 1, 2, 3 beziers such that
673 the resulting beziers are monotonic in Y. This is called by the scan converter.
674 Depending on what is returned, dst[] is treated as follows
675 0 dst[0..3] is the original cubic
676 1 dst[0..3] and dst[3..6] are the two new cubics
677 2 dst[0..3], dst[3..6], dst[6..9] are the three new cubics
678 If dst == null, it is ignored and only the count is returned.
679*/
reed@android.combb135862009-11-18 13:47:40 +0000680int SkChopCubicAtYExtrema(const SkPoint src[4], SkPoint dst[10]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000681 SkScalar tValues[2];
reed@android.combb135862009-11-18 13:47:40 +0000682 int roots = SkFindCubicExtrema(src[0].fY, src[1].fY, src[2].fY,
683 src[3].fY, tValues);
684
reed@android.com8a1c16f2008-12-17 15:59:43 +0000685 SkChopCubicAt(src, dst, tValues, roots);
reed@android.combb135862009-11-18 13:47:40 +0000686 if (dst && roots > 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000687 // we do some cleanup to ensure our Y extrema are flat
688 flatten_double_cubic_extrema(&dst[0].fY);
reed@android.combb135862009-11-18 13:47:40 +0000689 if (roots == 2) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000690 flatten_double_cubic_extrema(&dst[3].fY);
reed@android.combb135862009-11-18 13:47:40 +0000691 }
692 }
693 return roots;
694}
695
696int SkChopCubicAtXExtrema(const SkPoint src[4], SkPoint dst[10]) {
697 SkScalar tValues[2];
698 int roots = SkFindCubicExtrema(src[0].fX, src[1].fX, src[2].fX,
699 src[3].fX, tValues);
700
701 SkChopCubicAt(src, dst, tValues, roots);
702 if (dst && roots > 0) {
703 // we do some cleanup to ensure our Y extrema are flat
704 flatten_double_cubic_extrema(&dst[0].fX);
705 if (roots == 2) {
706 flatten_double_cubic_extrema(&dst[3].fX);
707 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000708 }
709 return roots;
710}
711
712/** http://www.faculty.idc.ac.il/arik/quality/appendixA.html
713
714 Inflection means that curvature is zero.
715 Curvature is [F' x F''] / [F'^3]
716 So we solve F'x X F''y - F'y X F''y == 0
717 After some canceling of the cubic term, we get
718 A = b - a
719 B = c - 2b + a
720 C = d - 3c + 3b - a
721 (BxCy - ByCx)t^2 + (AxCy - AyCx)t + AxBy - AyBx == 0
722*/
723int SkFindCubicInflections(const SkPoint src[4], SkScalar tValues[])
724{
725 SkScalar Ax = src[1].fX - src[0].fX;
726 SkScalar Ay = src[1].fY - src[0].fY;
727 SkScalar Bx = src[2].fX - 2 * src[1].fX + src[0].fX;
728 SkScalar By = src[2].fY - 2 * src[1].fY + src[0].fY;
729 SkScalar Cx = src[3].fX + 3 * (src[1].fX - src[2].fX) - src[0].fX;
730 SkScalar Cy = src[3].fY + 3 * (src[1].fY - src[2].fY) - src[0].fY;
731 int count;
732
733#ifdef SK_SCALAR_IS_FLOAT
734 count = SkFindUnitQuadRoots(Bx*Cy - By*Cx, Ax*Cy - Ay*Cx, Ax*By - Ay*Bx, tValues);
735#else
736 Sk64 A, B, C, tmp;
737
738 A.setMul(Bx, Cy);
739 tmp.setMul(By, Cx);
740 A.sub(tmp);
741
742 B.setMul(Ax, Cy);
743 tmp.setMul(Ay, Cx);
744 B.sub(tmp);
745
746 C.setMul(Ax, By);
747 tmp.setMul(Ay, Bx);
748 C.sub(tmp);
749
750 count = Sk64FindFixedQuadRoots(A, B, C, tValues);
751#endif
752
753 return count;
754}
755
756int SkChopCubicAtInflections(const SkPoint src[], SkPoint dst[10])
757{
758 SkScalar tValues[2];
759 int count = SkFindCubicInflections(src, tValues);
760
761 if (dst)
762 {
763 if (count == 0)
764 memcpy(dst, src, 4 * sizeof(SkPoint));
765 else
766 SkChopCubicAt(src, dst, tValues, count);
767 }
768 return count + 1;
769}
770
771template <typename T> void bubble_sort(T array[], int count)
772{
773 for (int i = count - 1; i > 0; --i)
774 for (int j = i; j > 0; --j)
775 if (array[j] < array[j-1])
776 {
777 T tmp(array[j]);
778 array[j] = array[j-1];
779 array[j-1] = tmp;
780 }
781}
782
783#include "SkFP.h"
784
785// newton refinement
786#if 0
787static SkScalar refine_cubic_root(const SkFP coeff[4], SkScalar root)
788{
789 // x1 = x0 - f(t) / f'(t)
790
791 SkFP T = SkScalarToFloat(root);
792 SkFP N, D;
793
794 // f' = 3*coeff[0]*T^2 + 2*coeff[1]*T + coeff[2]
795 D = SkFPMul(SkFPMul(coeff[0], SkFPMul(T,T)), 3);
796 D = SkFPAdd(D, SkFPMulInt(SkFPMul(coeff[1], T), 2));
797 D = SkFPAdd(D, coeff[2]);
798
799 if (D == 0)
800 return root;
801
802 // f = coeff[0]*T^3 + coeff[1]*T^2 + coeff[2]*T + coeff[3]
803 N = SkFPMul(SkFPMul(SkFPMul(T, T), T), coeff[0]);
804 N = SkFPAdd(N, SkFPMul(SkFPMul(T, T), coeff[1]));
805 N = SkFPAdd(N, SkFPMul(T, coeff[2]));
806 N = SkFPAdd(N, coeff[3]);
807
808 if (N)
809 {
810 SkScalar delta = SkFPToScalar(SkFPDiv(N, D));
811
812 if (delta)
813 root -= delta;
814 }
815 return root;
816}
817#endif
818
819#if defined _WIN32 && _MSC_VER >= 1300 && defined SK_SCALAR_IS_FIXED // disable warning : unreachable code if building fixed point for windows desktop
820#pragma warning ( disable : 4702 )
821#endif
822
823/* Solve coeff(t) == 0, returning the number of roots that
824 lie withing 0 < t < 1.
825 coeff[0]t^3 + coeff[1]t^2 + coeff[2]t + coeff[3]
826*/
827static int solve_cubic_polynomial(const SkFP coeff[4], SkScalar tValues[3])
828{
829#ifndef SK_SCALAR_IS_FLOAT
830 return 0; // this is not yet implemented for software float
831#endif
832
833 if (SkScalarNearlyZero(coeff[0])) // we're just a quadratic
834 {
835 return SkFindUnitQuadRoots(coeff[1], coeff[2], coeff[3], tValues);
836 }
837
838 SkFP a, b, c, Q, R;
839
840 {
841 SkASSERT(coeff[0] != 0);
842
843 SkFP inva = SkFPInvert(coeff[0]);
844 a = SkFPMul(coeff[1], inva);
845 b = SkFPMul(coeff[2], inva);
846 c = SkFPMul(coeff[3], inva);
847 }
848 Q = SkFPDivInt(SkFPSub(SkFPMul(a,a), SkFPMulInt(b, 3)), 9);
849// R = (2*a*a*a - 9*a*b + 27*c) / 54;
850 R = SkFPMulInt(SkFPMul(SkFPMul(a, a), a), 2);
851 R = SkFPSub(R, SkFPMulInt(SkFPMul(a, b), 9));
852 R = SkFPAdd(R, SkFPMulInt(c, 27));
853 R = SkFPDivInt(R, 54);
854
855 SkFP Q3 = SkFPMul(SkFPMul(Q, Q), Q);
856 SkFP R2MinusQ3 = SkFPSub(SkFPMul(R,R), Q3);
857 SkFP adiv3 = SkFPDivInt(a, 3);
858
859 SkScalar* roots = tValues;
860 SkScalar r;
861
862 if (SkFPLT(R2MinusQ3, 0)) // we have 3 real roots
863 {
864#ifdef SK_SCALAR_IS_FLOAT
865 float theta = sk_float_acos(R / sk_float_sqrt(Q3));
866 float neg2RootQ = -2 * sk_float_sqrt(Q);
867
868 r = neg2RootQ * sk_float_cos(theta/3) - adiv3;
869 if (is_unit_interval(r))
870 *roots++ = r;
871
872 r = neg2RootQ * sk_float_cos((theta + 2*SK_ScalarPI)/3) - adiv3;
873 if (is_unit_interval(r))
874 *roots++ = r;
875
876 r = neg2RootQ * sk_float_cos((theta - 2*SK_ScalarPI)/3) - adiv3;
877 if (is_unit_interval(r))
878 *roots++ = r;
879
880 // now sort the roots
881 bubble_sort(tValues, (int)(roots - tValues));
882#endif
883 }
884 else // we have 1 real root
885 {
886 SkFP A = SkFPAdd(SkFPAbs(R), SkFPSqrt(R2MinusQ3));
887 A = SkFPCubeRoot(A);
888 if (SkFPGT(R, 0))
889 A = SkFPNeg(A);
890
891 if (A != 0)
892 A = SkFPAdd(A, SkFPDiv(Q, A));
893 r = SkFPToScalar(SkFPSub(A, adiv3));
894 if (is_unit_interval(r))
895 *roots++ = r;
896 }
897
898 return (int)(roots - tValues);
899}
900
901/* Looking for F' dot F'' == 0
902
903 A = b - a
904 B = c - 2b + a
905 C = d - 3c + 3b - a
906
907 F' = 3Ct^2 + 6Bt + 3A
908 F'' = 6Ct + 6B
909
910 F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB
911*/
912static void formulate_F1DotF2(const SkScalar src[], SkFP coeff[4])
913{
914 SkScalar a = src[2] - src[0];
915 SkScalar b = src[4] - 2 * src[2] + src[0];
916 SkScalar c = src[6] + 3 * (src[2] - src[4]) - src[0];
917
918 SkFP A = SkScalarToFP(a);
919 SkFP B = SkScalarToFP(b);
920 SkFP C = SkScalarToFP(c);
921
922 coeff[0] = SkFPMul(C, C);
923 coeff[1] = SkFPMulInt(SkFPMul(B, C), 3);
924 coeff[2] = SkFPMulInt(SkFPMul(B, B), 2);
925 coeff[2] = SkFPAdd(coeff[2], SkFPMul(C, A));
926 coeff[3] = SkFPMul(A, B);
927}
928
929// EXPERIMENTAL: can set this to zero to accept all t-values 0 < t < 1
930//#define kMinTValueForChopping (SK_Scalar1 / 256)
931#define kMinTValueForChopping 0
932
933/* Looking for F' dot F'' == 0
934
935 A = b - a
936 B = c - 2b + a
937 C = d - 3c + 3b - a
938
939 F' = 3Ct^2 + 6Bt + 3A
940 F'' = 6Ct + 6B
941
942 F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB
943*/
944int SkFindCubicMaxCurvature(const SkPoint src[4], SkScalar tValues[3])
945{
946 SkFP coeffX[4], coeffY[4];
947 int i;
948
949 formulate_F1DotF2(&src[0].fX, coeffX);
950 formulate_F1DotF2(&src[0].fY, coeffY);
951
952 for (i = 0; i < 4; i++)
953 coeffX[i] = SkFPAdd(coeffX[i],coeffY[i]);
954
955 SkScalar t[3];
956 int count = solve_cubic_polynomial(coeffX, t);
957 int maxCount = 0;
958
959 // now remove extrema where the curvature is zero (mins)
960 // !!!! need a test for this !!!!
961 for (i = 0; i < count; i++)
962 {
963 // if (not_min_curvature())
964 if (t[i] > kMinTValueForChopping && t[i] < SK_Scalar1 - kMinTValueForChopping)
965 tValues[maxCount++] = t[i];
966 }
967 return maxCount;
968}
969
970int SkChopCubicAtMaxCurvature(const SkPoint src[4], SkPoint dst[13], SkScalar tValues[3])
971{
972 SkScalar t_storage[3];
973
974 if (tValues == NULL)
975 tValues = t_storage;
976
977 int count = SkFindCubicMaxCurvature(src, tValues);
978
979 if (dst)
980 {
981 if (count == 0)
982 memcpy(dst, src, 4 * sizeof(SkPoint));
983 else
984 SkChopCubicAt(src, dst, tValues, count);
985 }
986 return count + 1;
987}
988
reed@android.com945a1392010-02-05 20:41:02 +0000989bool SkXRayCrossesMonotonicCubic(const SkXRay& pt, const SkPoint cubic[4]) {
990 // Find the minimum and maximum y of the extrema, which are the
991 // first and last points since this cubic is monotonic
992 SkScalar min_y = SkMinScalar(cubic[0].fY, cubic[3].fY);
993 SkScalar max_y = SkMaxScalar(cubic[0].fY, cubic[3].fY);
994
995 if (pt.fY == cubic[0].fY
996 || pt.fY < min_y
997 || pt.fY > max_y) {
998 // The query line definitely does not cross the curve
999 return false;
1000 }
1001
1002 SkScalar min_x =
1003 SkMinScalar(
1004 SkMinScalar(
1005 SkMinScalar(cubic[0].fX, cubic[1].fX),
1006 cubic[2].fX),
1007 cubic[3].fX);
1008 if (pt.fX < min_x) {
1009 // The query line definitely crosses the curve
1010 return true;
1011 }
1012
1013 SkScalar max_x =
1014 SkMaxScalar(
1015 SkMaxScalar(
1016 SkMaxScalar(cubic[0].fX, cubic[1].fX),
1017 cubic[2].fX),
1018 cubic[3].fX);
1019 if (pt.fX > max_x) {
1020 // The query line definitely does not cross the curve
1021 return false;
1022 }
1023
1024 // Do a binary search to find the parameter value which makes y as
1025 // close as possible to the query point. See whether the query
1026 // line's origin is to the left of the associated x coordinate.
1027
1028 // kMaxIter is chosen as the number of mantissa bits for a float,
1029 // since there's no way we are going to get more precision by
1030 // iterating more times than that.
1031 const int kMaxIter = 23;
1032 SkPoint eval;
1033 int iter = 0;
1034 SkScalar upper_t;
1035 SkScalar lower_t;
1036 // Need to invert direction of t parameter if cubic goes up
1037 // instead of down
1038 if (cubic[3].fY > cubic[0].fY) {
1039 upper_t = SK_Scalar1;
1040 lower_t = SkFloatToScalar(0);
1041 } else {
1042 upper_t = SkFloatToScalar(0);
1043 lower_t = SK_Scalar1;
1044 }
1045 do {
1046 SkScalar t = SkScalarAve(upper_t, lower_t);
1047 SkEvalCubicAt(cubic, t, &eval, NULL, NULL);
1048 if (pt.fY > eval.fY) {
1049 lower_t = t;
1050 } else {
1051 upper_t = t;
1052 }
1053 } while (++iter < kMaxIter
1054 && !SkScalarNearlyZero(eval.fY - pt.fY));
1055 if (pt.fX <= eval.fX) {
1056 return true;
1057 }
1058 return false;
1059}
1060
1061int SkNumXRayCrossingsForCubic(const SkXRay& pt, const SkPoint cubic[4]) {
1062 int num_crossings = 0;
1063 SkPoint monotonic_cubics[10];
1064 int num_monotonic_cubics = SkChopCubicAtYExtrema(cubic, monotonic_cubics);
1065 if (SkXRayCrossesMonotonicCubic(pt, &monotonic_cubics[0]))
1066 ++num_crossings;
1067 if (num_monotonic_cubics > 0)
1068 if (SkXRayCrossesMonotonicCubic(pt, &monotonic_cubics[3]))
1069 ++num_crossings;
1070 if (num_monotonic_cubics > 1)
1071 if (SkXRayCrossesMonotonicCubic(pt, &monotonic_cubics[6]))
1072 ++num_crossings;
1073 return num_crossings;
1074}
1075
reed@android.com8a1c16f2008-12-17 15:59:43 +00001076////////////////////////////////////////////////////////////////////////////////
1077
1078/* Find t value for quadratic [a, b, c] = d.
1079 Return 0 if there is no solution within [0, 1)
1080*/
1081static SkScalar quad_solve(SkScalar a, SkScalar b, SkScalar c, SkScalar d)
1082{
1083 // At^2 + Bt + C = d
1084 SkScalar A = a - 2 * b + c;
1085 SkScalar B = 2 * (b - a);
1086 SkScalar C = a - d;
1087
1088 SkScalar roots[2];
1089 int count = SkFindUnitQuadRoots(A, B, C, roots);
1090
1091 SkASSERT(count <= 1);
1092 return count == 1 ? roots[0] : 0;
1093}
1094
1095/* given a quad-curve and a point (x,y), chop the quad at that point and return
1096 the new quad's offCurve point. Should only return false if the computed pos
1097 is the start of the curve (i.e. root == 0)
1098*/
1099static bool quad_pt2OffCurve(const SkPoint quad[3], SkScalar x, SkScalar y, SkPoint* offCurve)
1100{
1101 const SkScalar* base;
1102 SkScalar value;
1103
1104 if (SkScalarAbs(x) < SkScalarAbs(y)) {
1105 base = &quad[0].fX;
1106 value = x;
1107 } else {
1108 base = &quad[0].fY;
1109 value = y;
1110 }
1111
1112 // note: this returns 0 if it thinks value is out of range, meaning the
1113 // root might return something outside of [0, 1)
1114 SkScalar t = quad_solve(base[0], base[2], base[4], value);
1115
1116 if (t > 0)
1117 {
1118 SkPoint tmp[5];
1119 SkChopQuadAt(quad, tmp, t);
1120 *offCurve = tmp[1];
1121 return true;
1122 } else {
1123 /* t == 0 means either the value triggered a root outside of [0, 1)
1124 For our purposes, we can ignore the <= 0 roots, but we want to
1125 catch the >= 1 roots (which given our caller, will basically mean
1126 a root of 1, give-or-take numerical instability). If we are in the
1127 >= 1 case, return the existing offCurve point.
1128
1129 The test below checks to see if we are close to the "end" of the
1130 curve (near base[4]). Rather than specifying a tolerance, I just
1131 check to see if value is on to the right/left of the middle point
1132 (depending on the direction/sign of the end points).
1133 */
1134 if ((base[0] < base[4] && value > base[2]) ||
1135 (base[0] > base[4] && value < base[2])) // should root have been 1
1136 {
1137 *offCurve = quad[1];
1138 return true;
1139 }
1140 }
1141 return false;
1142}
1143
1144static const SkPoint gQuadCirclePts[kSkBuildQuadArcStorage] = {
1145 { SK_Scalar1, 0 },
1146 { SK_Scalar1, SK_ScalarTanPIOver8 },
1147 { SK_ScalarRoot2Over2, SK_ScalarRoot2Over2 },
1148 { SK_ScalarTanPIOver8, SK_Scalar1 },
1149
1150 { 0, SK_Scalar1 },
1151 { -SK_ScalarTanPIOver8, SK_Scalar1 },
1152 { -SK_ScalarRoot2Over2, SK_ScalarRoot2Over2 },
1153 { -SK_Scalar1, SK_ScalarTanPIOver8 },
1154
1155 { -SK_Scalar1, 0 },
1156 { -SK_Scalar1, -SK_ScalarTanPIOver8 },
1157 { -SK_ScalarRoot2Over2, -SK_ScalarRoot2Over2 },
1158 { -SK_ScalarTanPIOver8, -SK_Scalar1 },
1159
1160 { 0, -SK_Scalar1 },
1161 { SK_ScalarTanPIOver8, -SK_Scalar1 },
1162 { SK_ScalarRoot2Over2, -SK_ScalarRoot2Over2 },
1163 { SK_Scalar1, -SK_ScalarTanPIOver8 },
1164
1165 { SK_Scalar1, 0 }
1166};
1167
1168int SkBuildQuadArc(const SkVector& uStart, const SkVector& uStop,
1169 SkRotationDirection dir, const SkMatrix* userMatrix,
1170 SkPoint quadPoints[])
1171{
1172 // rotate by x,y so that uStart is (1.0)
1173 SkScalar x = SkPoint::DotProduct(uStart, uStop);
1174 SkScalar y = SkPoint::CrossProduct(uStart, uStop);
1175
1176 SkScalar absX = SkScalarAbs(x);
1177 SkScalar absY = SkScalarAbs(y);
1178
1179 int pointCount;
1180
1181 // check for (effectively) coincident vectors
1182 // this can happen if our angle is nearly 0 or nearly 180 (y == 0)
1183 // ... we use the dot-prod to distinguish between 0 and 180 (x > 0)
1184 if (absY <= SK_ScalarNearlyZero && x > 0 &&
1185 ((y >= 0 && kCW_SkRotationDirection == dir) ||
1186 (y <= 0 && kCCW_SkRotationDirection == dir))) {
1187
1188 // just return the start-point
1189 quadPoints[0].set(SK_Scalar1, 0);
1190 pointCount = 1;
1191 } else {
1192 if (dir == kCCW_SkRotationDirection)
1193 y = -y;
1194
1195 // what octant (quadratic curve) is [xy] in?
1196 int oct = 0;
1197 bool sameSign = true;
1198
1199 if (0 == y)
1200 {
1201 oct = 4; // 180
1202 SkASSERT(SkScalarAbs(x + SK_Scalar1) <= SK_ScalarNearlyZero);
1203 }
1204 else if (0 == x)
1205 {
1206 SkASSERT(absY - SK_Scalar1 <= SK_ScalarNearlyZero);
1207 if (y > 0)
1208 oct = 2; // 90
1209 else
1210 oct = 6; // 270
1211 }
1212 else
1213 {
1214 if (y < 0)
1215 oct += 4;
1216 if ((x < 0) != (y < 0))
1217 {
1218 oct += 2;
1219 sameSign = false;
1220 }
1221 if ((absX < absY) == sameSign)
1222 oct += 1;
1223 }
1224
1225 int wholeCount = oct << 1;
1226 memcpy(quadPoints, gQuadCirclePts, (wholeCount + 1) * sizeof(SkPoint));
1227
1228 const SkPoint* arc = &gQuadCirclePts[wholeCount];
1229 if (quad_pt2OffCurve(arc, x, y, &quadPoints[wholeCount + 1]))
1230 {
1231 quadPoints[wholeCount + 2].set(x, y);
1232 wholeCount += 2;
1233 }
1234 pointCount = wholeCount + 1;
1235 }
1236
1237 // now handle counter-clockwise and the initial unitStart rotation
1238 SkMatrix matrix;
1239 matrix.setSinCos(uStart.fY, uStart.fX);
1240 if (dir == kCCW_SkRotationDirection) {
1241 matrix.preScale(SK_Scalar1, -SK_Scalar1);
1242 }
1243 if (userMatrix) {
1244 matrix.postConcat(*userMatrix);
1245 }
1246 matrix.mapPoints(quadPoints, pointCount);
1247 return pointCount;
1248}
1249