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