• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* 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 
SkXRayCrossesLine(const SkXRay & pt,const SkPoint pts[2])22 bool 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 
51 /** 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
is_not_monotonic(int a,int b,int c,int d)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 
is_not_monotonic(int a,int b,int c)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
is_not_monotonic(float a,float b,float c)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 
is_unit_interval(SkScalar x)84 static bool is_unit_interval(SkScalar x)
85 {
86     return x > 0 && x < SK_Scalar1;
87 }
88 
valid_unit_divide(SkScalar numer,SkScalar denom,SkScalar * ratio)89 static 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     if (SkScalarIsNaN(r)) {
104         return 0;
105     }
106     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 */
SkFindUnitQuadRoots(SkScalar A,SkScalar B,SkScalar C,SkScalar roots[2])119 int 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;
130     if (R < 0 || SkScalarIsNaN(R)) {  // complex roots
131         return 0;
132     }
133     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 */
Sk64FindFixedQuadRoots(const Sk64 & A,const Sk64 & B,const Sk64 & C,SkFixed roots[2])163 static 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 
eval_quad(const SkScalar src[],SkScalar t)179 static 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 
eval_quad_derivative(const SkScalar src[],SkScalar t)196 static 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 
eval_quad_derivative_at_half(const SkScalar src[])204 static 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 
SkEvalQuadAt(const SkPoint src[3],SkScalar t,SkPoint * pt,SkVector * tangent)211 void 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 
SkEvalQuadAtHalf(const SkPoint src[3],SkPoint * pt,SkVector * tangent)223 void 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 
interp_quad_coords(const SkScalar * src,SkScalar * dst,SkScalar t)240 static 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 
SkChopQuadAt(const SkPoint src[3],SkPoint dst[5],SkScalar t)252 void 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 
SkChopQuadAtHalf(const SkPoint src[3],SkPoint dst[5])260 void 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 */
SkFindQuadExtrema(SkScalar a,SkScalar b,SkScalar c,SkScalar tValue[1])279 int 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 
flatten_double_quad_extrema(SkScalar coords[14])291 static inline void flatten_double_quad_extrema(SkScalar coords[14])
292 {
293     coords[2] = coords[6] = coords[4];
294 }
295 
296 /*  Returns 0 for 1 quad, and 1 for two quads, either way the answer is
297  stored in dst[]. Guarantees that the 1/2 quads will be monotonic.
298  */
SkChopQuadAtYExtrema(const SkPoint src[3],SkPoint dst[5])299 int SkChopQuadAtYExtrema(const SkPoint src[3], SkPoint dst[5])
300 {
301     SkASSERT(src);
302     SkASSERT(dst);
303 
304 #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
316 
317     SkScalar a = src[0].fY;
318     SkScalar b = src[1].fY;
319     SkScalar c = src[2].fY;
320 
321     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 
340 /*  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  */
SkChopQuadAtXExtrema(const SkPoint src[3],SkPoint dst[5])343 int 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 
369 //  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 //
SkChopQuadAtMaxCurvature(const SkPoint src[3],SkPoint dst[5])381 int 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 
SkConvertQuadToCubic(const SkPoint src[3],SkPoint dst[4])427 void 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 
441 ////////////////////////////////////////////////////////////////////////////////////////
442 ///// CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS /////
443 ////////////////////////////////////////////////////////////////////////////////////////
444 
get_cubic_coeff(const SkScalar pt[],SkScalar coeff[4])445 static 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 
SkGetCubicCoeff(const SkPoint pts[4],SkScalar cx[4],SkScalar cy[4])453 void 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 
eval_cubic(const SkScalar src[],SkScalar t)463 static 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 */
eval_quadratic(SkScalar A,SkScalar B,SkScalar C,SkScalar t)490 static 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 
eval_cubic_derivative(const SkScalar src[],SkScalar t)497 static 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 
eval_cubic_2ndDerivative(const SkScalar src[],SkScalar t)506 static 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 
SkEvalCubicAt(const SkPoint src[4],SkScalar t,SkPoint * loc,SkVector * tangent,SkVector * curvature)514 void 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 */
SkFindCubicExtrema(SkScalar a,SkScalar b,SkScalar c,SkScalar d,SkScalar tValues[2])535 int 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 
interp_cubic_coords(const SkScalar * src,SkScalar * dst,SkScalar t)550 static 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 
SkChopCubicAt(const SkPoint src[4],SkPoint dst[7],SkScalar t)568 void 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 
576 /*  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 
SkChopCubicAt(const SkPoint src[4],SkPoint dst[],const SkScalar tValues[],int roots)599 void 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 
627                 dst += 3;
628                 // have src point to the remaining cubic (after the chop)
629                 memcpy(tmp, dst, 4 * sizeof(SkPoint));
630                 src = tmp;
631 
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                 }
639             }
640         }
641     }
642 }
643 
SkChopCubicAtHalf(const SkPoint src[4],SkPoint dst[7])644 void 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 
flatten_double_cubic_extrema(SkScalar coords[14])667 static 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 */
SkChopCubicAtYExtrema(const SkPoint src[4],SkPoint dst[10])680 int SkChopCubicAtYExtrema(const SkPoint src[4], SkPoint dst[10]) {
681     SkScalar    tValues[2];
682     int         roots = SkFindCubicExtrema(src[0].fY, src[1].fY, src[2].fY,
683                                            src[3].fY, tValues);
684 
685     SkChopCubicAt(src, dst, tValues, roots);
686     if (dst && roots > 0) {
687         // we do some cleanup to ensure our Y extrema are flat
688         flatten_double_cubic_extrema(&dst[0].fY);
689         if (roots == 2) {
690             flatten_double_cubic_extrema(&dst[3].fY);
691         }
692     }
693     return roots;
694 }
695 
SkChopCubicAtXExtrema(const SkPoint src[4],SkPoint dst[10])696 int 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         }
708     }
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 */
SkFindCubicInflections(const SkPoint src[4],SkScalar tValues[])723 int 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 
SkChopCubicAtInflections(const SkPoint src[],SkPoint dst[10])756 int 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 
bubble_sort(T array[],int count)771 template <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
787 static 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 */
solve_cubic_polynomial(const SkFP coeff[4],SkScalar tValues[3])827 static 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 */
formulate_F1DotF2(const SkScalar src[],SkFP coeff[4])912 static 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 */
SkFindCubicMaxCurvature(const SkPoint src[4],SkScalar tValues[3])944 int 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 
SkChopCubicAtMaxCurvature(const SkPoint src[4],SkPoint dst[13],SkScalar tValues[3])970 int 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 
SkXRayCrossesMonotonicCubic(const SkXRay & pt,const SkPoint cubic[4])989 bool 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 
SkNumXRayCrossingsForCubic(const SkXRay & pt,const SkPoint cubic[4])1061 int 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 
1076 ////////////////////////////////////////////////////////////////////////////////
1077 
1078 /*  Find t value for quadratic [a, b, c] = d.
1079     Return 0 if there is no solution within [0, 1)
1080 */
quad_solve(SkScalar a,SkScalar b,SkScalar c,SkScalar d)1081 static 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 */
quad_pt2OffCurve(const SkPoint quad[3],SkScalar x,SkScalar y,SkPoint * offCurve)1099 static 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 
1144 static 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 
SkBuildQuadArc(const SkVector & uStart,const SkVector & uStop,SkRotationDirection dir,const SkMatrix * userMatrix,SkPoint quadPoints[])1168 int 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 
1250