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