1/*
2 * Copyright 2006 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkGeometry.h"
9#include "SkMatrix.h"
10#include "SkNx.h"
11#include "SkPoint3.h"
12#include "SkPointPriv.h"
13
14static SkVector to_vector(const Sk2s& x) {
15    SkVector vector;
16    x.store(&vector);
17    return vector;
18}
19
20////////////////////////////////////////////////////////////////////////
21
22static int is_not_monotonic(SkScalar a, SkScalar b, SkScalar c) {
23    SkScalar ab = a - b;
24    SkScalar bc = b - c;
25    if (ab < 0) {
26        bc = -bc;
27    }
28    return ab == 0 || bc < 0;
29}
30
31////////////////////////////////////////////////////////////////////////
32
33static bool is_unit_interval(SkScalar x) {
34    return x > 0 && x < SK_Scalar1;
35}
36
37static int valid_unit_divide(SkScalar numer, SkScalar denom, SkScalar* ratio) {
38    SkASSERT(ratio);
39
40    if (numer < 0) {
41        numer = -numer;
42        denom = -denom;
43    }
44
45    if (denom == 0 || numer == 0 || numer >= denom) {
46        return 0;
47    }
48
49    SkScalar r = numer / denom;
50    if (SkScalarIsNaN(r)) {
51        return 0;
52    }
53    SkASSERTF(r >= 0 && r < SK_Scalar1, "numer %f, denom %f, r %f", numer, denom, r);
54    if (r == 0) { // catch underflow if numer <<<< denom
55        return 0;
56    }
57    *ratio = r;
58    return 1;
59}
60
61/** From Numerical Recipes in C.
62
63    Q = -1/2 (B + sign(B) sqrt[B*B - 4*A*C])
64    x1 = Q / A
65    x2 = C / Q
66*/
67int SkFindUnitQuadRoots(SkScalar A, SkScalar B, SkScalar C, SkScalar roots[2]) {
68    SkASSERT(roots);
69
70    if (A == 0) {
71        return valid_unit_divide(-C, B, roots);
72    }
73
74    SkScalar* r = roots;
75
76    SkScalar R = B*B - 4*A*C;
77    if (R < 0 || !SkScalarIsFinite(R)) {  // complex roots
78        // if R is infinite, it's possible that it may still produce
79        // useful results if the operation was repeated in doubles
80        // the flipside is determining if the more precise answer
81        // isn't useful because surrounding machinery (e.g., subtracting
82        // the axis offset from C) already discards the extra precision
83        // more investigation and unit tests required...
84        return 0;
85    }
86    R = SkScalarSqrt(R);
87
88    SkScalar Q = (B < 0) ? -(B-R)/2 : -(B+R)/2;
89    r += valid_unit_divide(Q, A, r);
90    r += valid_unit_divide(C, Q, r);
91    if (r - roots == 2) {
92        if (roots[0] > roots[1])
93            SkTSwap<SkScalar>(roots[0], roots[1]);
94        else if (roots[0] == roots[1])  // nearly-equal?
95            r -= 1; // skip the double root
96    }
97    return (int)(r - roots);
98}
99
100///////////////////////////////////////////////////////////////////////////////
101///////////////////////////////////////////////////////////////////////////////
102
103void SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint* pt, SkVector* tangent) {
104    SkASSERT(src);
105    SkASSERT(t >= 0 && t <= SK_Scalar1);
106
107    if (pt) {
108        *pt = SkEvalQuadAt(src, t);
109    }
110    if (tangent) {
111        *tangent = SkEvalQuadTangentAt(src, t);
112    }
113}
114
115SkPoint SkEvalQuadAt(const SkPoint src[3], SkScalar t) {
116    return to_point(SkQuadCoeff(src).eval(t));
117}
118
119SkVector SkEvalQuadTangentAt(const SkPoint src[3], SkScalar t) {
120    // The derivative equation is 2(b - a +(a - 2b +c)t). This returns a
121    // zero tangent vector when t is 0 or 1, and the control point is equal
122    // to the end point. In this case, use the quad end points to compute the tangent.
123    if ((t == 0 && src[0] == src[1]) || (t == 1 && src[1] == src[2])) {
124        return src[2] - src[0];
125    }
126    SkASSERT(src);
127    SkASSERT(t >= 0 && t <= SK_Scalar1);
128
129    Sk2s P0 = from_point(src[0]);
130    Sk2s P1 = from_point(src[1]);
131    Sk2s P2 = from_point(src[2]);
132
133    Sk2s B = P1 - P0;
134    Sk2s A = P2 - P1 - B;
135    Sk2s T = A * Sk2s(t) + B;
136
137    return to_vector(T + T);
138}
139
140static inline Sk2s interp(const Sk2s& v0, const Sk2s& v1, const Sk2s& t) {
141    return v0 + (v1 - v0) * t;
142}
143
144void SkChopQuadAt(const SkPoint src[3], SkPoint dst[5], SkScalar t) {
145    SkASSERT(t > 0 && t < SK_Scalar1);
146
147    Sk2s p0 = from_point(src[0]);
148    Sk2s p1 = from_point(src[1]);
149    Sk2s p2 = from_point(src[2]);
150    Sk2s tt(t);
151
152    Sk2s p01 = interp(p0, p1, tt);
153    Sk2s p12 = interp(p1, p2, tt);
154
155    dst[0] = to_point(p0);
156    dst[1] = to_point(p01);
157    dst[2] = to_point(interp(p01, p12, tt));
158    dst[3] = to_point(p12);
159    dst[4] = to_point(p2);
160}
161
162void SkChopQuadAtHalf(const SkPoint src[3], SkPoint dst[5]) {
163    SkChopQuadAt(src, dst, 0.5f);
164}
165
166/** Quad'(t) = At + B, where
167    A = 2(a - 2b + c)
168    B = 2(b - a)
169    Solve for t, only if it fits between 0 < t < 1
170*/
171int SkFindQuadExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar tValue[1]) {
172    /*  At + B == 0
173        t = -B / A
174    */
175    return valid_unit_divide(a - b, a - b - b + c, tValue);
176}
177
178static inline void flatten_double_quad_extrema(SkScalar coords[14]) {
179    coords[2] = coords[6] = coords[4];
180}
181
182/*  Returns 0 for 1 quad, and 1 for two quads, either way the answer is
183 stored in dst[]. Guarantees that the 1/2 quads will be monotonic.
184 */
185int SkChopQuadAtYExtrema(const SkPoint src[3], SkPoint dst[5]) {
186    SkASSERT(src);
187    SkASSERT(dst);
188
189    SkScalar a = src[0].fY;
190    SkScalar b = src[1].fY;
191    SkScalar c = src[2].fY;
192
193    if (is_not_monotonic(a, b, c)) {
194        SkScalar    tValue;
195        if (valid_unit_divide(a - b, a - b - b + c, &tValue)) {
196            SkChopQuadAt(src, dst, tValue);
197            flatten_double_quad_extrema(&dst[0].fY);
198            return 1;
199        }
200        // if we get here, we need to force dst to be monotonic, even though
201        // we couldn't compute a unit_divide value (probably underflow).
202        b = SkScalarAbs(a - b) < SkScalarAbs(b - c) ? a : c;
203    }
204    dst[0].set(src[0].fX, a);
205    dst[1].set(src[1].fX, b);
206    dst[2].set(src[2].fX, c);
207    return 0;
208}
209
210/*  Returns 0 for 1 quad, and 1 for two quads, either way the answer is
211    stored in dst[]. Guarantees that the 1/2 quads will be monotonic.
212 */
213int SkChopQuadAtXExtrema(const SkPoint src[3], SkPoint dst[5]) {
214    SkASSERT(src);
215    SkASSERT(dst);
216
217    SkScalar a = src[0].fX;
218    SkScalar b = src[1].fX;
219    SkScalar c = src[2].fX;
220
221    if (is_not_monotonic(a, b, c)) {
222        SkScalar tValue;
223        if (valid_unit_divide(a - b, a - b - b + c, &tValue)) {
224            SkChopQuadAt(src, dst, tValue);
225            flatten_double_quad_extrema(&dst[0].fX);
226            return 1;
227        }
228        // if we get here, we need to force dst to be monotonic, even though
229        // we couldn't compute a unit_divide value (probably underflow).
230        b = SkScalarAbs(a - b) < SkScalarAbs(b - c) ? a : c;
231    }
232    dst[0].set(a, src[0].fY);
233    dst[1].set(b, src[1].fY);
234    dst[2].set(c, src[2].fY);
235    return 0;
236}
237
238//  F(t)    = a (1 - t) ^ 2 + 2 b t (1 - t) + c t ^ 2
239//  F'(t)   = 2 (b - a) + 2 (a - 2b + c) t
240//  F''(t)  = 2 (a - 2b + c)
241//
242//  A = 2 (b - a)
243//  B = 2 (a - 2b + c)
244//
245//  Maximum curvature for a quadratic means solving
246//  Fx' Fx'' + Fy' Fy'' = 0
247//
248//  t = - (Ax Bx + Ay By) / (Bx ^ 2 + By ^ 2)
249//
250SkScalar SkFindQuadMaxCurvature(const SkPoint src[3]) {
251    SkScalar    Ax = src[1].fX - src[0].fX;
252    SkScalar    Ay = src[1].fY - src[0].fY;
253    SkScalar    Bx = src[0].fX - src[1].fX - src[1].fX + src[2].fX;
254    SkScalar    By = src[0].fY - src[1].fY - src[1].fY + src[2].fY;
255    SkScalar    t = 0;  // 0 means don't chop
256
257    (void)valid_unit_divide(-(Ax * Bx + Ay * By), Bx * Bx + By * By, &t);
258    return t;
259}
260
261int SkChopQuadAtMaxCurvature(const SkPoint src[3], SkPoint dst[5]) {
262    SkScalar t = SkFindQuadMaxCurvature(src);
263    if (t == 0) {
264        memcpy(dst, src, 3 * sizeof(SkPoint));
265        return 1;
266    } else {
267        SkChopQuadAt(src, dst, t);
268        return 2;
269    }
270}
271
272void SkConvertQuadToCubic(const SkPoint src[3], SkPoint dst[4]) {
273    Sk2s scale(SkDoubleToScalar(2.0 / 3.0));
274    Sk2s s0 = from_point(src[0]);
275    Sk2s s1 = from_point(src[1]);
276    Sk2s s2 = from_point(src[2]);
277
278    dst[0] = src[0];
279    dst[1] = to_point(s0 + (s1 - s0) * scale);
280    dst[2] = to_point(s2 + (s1 - s2) * scale);
281    dst[3] = src[2];
282}
283
284//////////////////////////////////////////////////////////////////////////////
285///// CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS /////
286//////////////////////////////////////////////////////////////////////////////
287
288static SkVector eval_cubic_derivative(const SkPoint src[4], SkScalar t) {
289    SkQuadCoeff coeff;
290    Sk2s P0 = from_point(src[0]);
291    Sk2s P1 = from_point(src[1]);
292    Sk2s P2 = from_point(src[2]);
293    Sk2s P3 = from_point(src[3]);
294
295    coeff.fA = P3 + Sk2s(3) * (P1 - P2) - P0;
296    coeff.fB = times_2(P2 - times_2(P1) + P0);
297    coeff.fC = P1 - P0;
298    return to_vector(coeff.eval(t));
299}
300
301static SkVector eval_cubic_2ndDerivative(const SkPoint src[4], SkScalar t) {
302    Sk2s P0 = from_point(src[0]);
303    Sk2s P1 = from_point(src[1]);
304    Sk2s P2 = from_point(src[2]);
305    Sk2s P3 = from_point(src[3]);
306    Sk2s A = P3 + Sk2s(3) * (P1 - P2) - P0;
307    Sk2s B = P2 - times_2(P1) + P0;
308
309    return to_vector(A * Sk2s(t) + B);
310}
311
312void SkEvalCubicAt(const SkPoint src[4], SkScalar t, SkPoint* loc,
313                   SkVector* tangent, SkVector* curvature) {
314    SkASSERT(src);
315    SkASSERT(t >= 0 && t <= SK_Scalar1);
316
317    if (loc) {
318        *loc = to_point(SkCubicCoeff(src).eval(t));
319    }
320    if (tangent) {
321        // The derivative equation returns a zero tangent vector when t is 0 or 1, and the
322        // adjacent control point is equal to the end point. In this case, use the
323        // next control point or the end points to compute the tangent.
324        if ((t == 0 && src[0] == src[1]) || (t == 1 && src[2] == src[3])) {
325            if (t == 0) {
326                *tangent = src[2] - src[0];
327            } else {
328                *tangent = src[3] - src[1];
329            }
330            if (!tangent->fX && !tangent->fY) {
331                *tangent = src[3] - src[0];
332            }
333        } else {
334            *tangent = eval_cubic_derivative(src, t);
335        }
336    }
337    if (curvature) {
338        *curvature = eval_cubic_2ndDerivative(src, t);
339    }
340}
341
342/** Cubic'(t) = At^2 + Bt + C, where
343    A = 3(-a + 3(b - c) + d)
344    B = 6(a - 2b + c)
345    C = 3(b - a)
346    Solve for t, keeping only those that fit betwee 0 < t < 1
347*/
348int SkFindCubicExtrema(SkScalar a, SkScalar b, SkScalar c, SkScalar d,
349                       SkScalar tValues[2]) {
350    // we divide A,B,C by 3 to simplify
351    SkScalar A = d - a + 3*(b - c);
352    SkScalar B = 2*(a - b - b + c);
353    SkScalar C = b - a;
354
355    return SkFindUnitQuadRoots(A, B, C, tValues);
356}
357
358void SkChopCubicAt(const SkPoint src[4], SkPoint dst[7], SkScalar t) {
359    SkASSERT(t > 0 && t < SK_Scalar1);
360
361    Sk2s    p0 = from_point(src[0]);
362    Sk2s    p1 = from_point(src[1]);
363    Sk2s    p2 = from_point(src[2]);
364    Sk2s    p3 = from_point(src[3]);
365    Sk2s    tt(t);
366
367    Sk2s    ab = interp(p0, p1, tt);
368    Sk2s    bc = interp(p1, p2, tt);
369    Sk2s    cd = interp(p2, p3, tt);
370    Sk2s    abc = interp(ab, bc, tt);
371    Sk2s    bcd = interp(bc, cd, tt);
372    Sk2s    abcd = interp(abc, bcd, tt);
373
374    dst[0] = src[0];
375    dst[1] = to_point(ab);
376    dst[2] = to_point(abc);
377    dst[3] = to_point(abcd);
378    dst[4] = to_point(bcd);
379    dst[5] = to_point(cd);
380    dst[6] = src[3];
381}
382
383/*  http://code.google.com/p/skia/issues/detail?id=32
384
385    This test code would fail when we didn't check the return result of
386    valid_unit_divide in SkChopCubicAt(... tValues[], int roots). The reason is
387    that after the first chop, the parameters to valid_unit_divide are equal
388    (thanks to finite float precision and rounding in the subtracts). Thus
389    even though the 2nd tValue looks < 1.0, after we renormalize it, we end
390    up with 1.0, hence the need to check and just return the last cubic as
391    a degenerate clump of 4 points in the sampe place.
392
393    static void test_cubic() {
394        SkPoint src[4] = {
395            { 556.25000, 523.03003 },
396            { 556.23999, 522.96002 },
397            { 556.21997, 522.89001 },
398            { 556.21997, 522.82001 }
399        };
400        SkPoint dst[10];
401        SkScalar tval[] = { 0.33333334f, 0.99999994f };
402        SkChopCubicAt(src, dst, tval, 2);
403    }
404 */
405
406void SkChopCubicAt(const SkPoint src[4], SkPoint dst[],
407                   const SkScalar tValues[], int roots) {
408#ifdef SK_DEBUG
409    {
410        for (int i = 0; i < roots - 1; i++)
411        {
412            SkASSERT(is_unit_interval(tValues[i]));
413            SkASSERT(is_unit_interval(tValues[i+1]));
414            SkASSERT(tValues[i] < tValues[i+1]);
415        }
416    }
417#endif
418
419    if (dst) {
420        if (roots == 0) { // nothing to chop
421            memcpy(dst, src, 4*sizeof(SkPoint));
422        } else {
423            SkScalar    t = tValues[0];
424            SkPoint     tmp[4];
425
426            for (int i = 0; i < roots; i++) {
427                SkChopCubicAt(src, dst, t);
428                if (i == roots - 1) {
429                    break;
430                }
431
432                dst += 3;
433                // have src point to the remaining cubic (after the chop)
434                memcpy(tmp, dst, 4 * sizeof(SkPoint));
435                src = tmp;
436
437                // watch out in case the renormalized t isn't in range
438                if (!valid_unit_divide(tValues[i+1] - tValues[i],
439                                       SK_Scalar1 - tValues[i], &t)) {
440                    // if we can't, just create a degenerate cubic
441                    dst[4] = dst[5] = dst[6] = src[3];
442                    break;
443                }
444            }
445        }
446    }
447}
448
449void SkChopCubicAtHalf(const SkPoint src[4], SkPoint dst[7]) {
450    SkChopCubicAt(src, dst, 0.5f);
451}
452
453static void flatten_double_cubic_extrema(SkScalar coords[14]) {
454    coords[4] = coords[8] = coords[6];
455}
456
457/** Given 4 points on a cubic bezier, chop it into 1, 2, 3 beziers such that
458    the resulting beziers are monotonic in Y. This is called by the scan
459    converter.  Depending on what is returned, dst[] is treated as follows:
460    0   dst[0..3] is the original cubic
461    1   dst[0..3] and dst[3..6] are the two new cubics
462    2   dst[0..3], dst[3..6], dst[6..9] are the three new cubics
463    If dst == null, it is ignored and only the count is returned.
464*/
465int SkChopCubicAtYExtrema(const SkPoint src[4], SkPoint dst[10]) {
466    SkScalar    tValues[2];
467    int         roots = SkFindCubicExtrema(src[0].fY, src[1].fY, src[2].fY,
468                                           src[3].fY, tValues);
469
470    SkChopCubicAt(src, dst, tValues, roots);
471    if (dst && roots > 0) {
472        // we do some cleanup to ensure our Y extrema are flat
473        flatten_double_cubic_extrema(&dst[0].fY);
474        if (roots == 2) {
475            flatten_double_cubic_extrema(&dst[3].fY);
476        }
477    }
478    return roots;
479}
480
481int SkChopCubicAtXExtrema(const SkPoint src[4], SkPoint dst[10]) {
482    SkScalar    tValues[2];
483    int         roots = SkFindCubicExtrema(src[0].fX, src[1].fX, src[2].fX,
484                                           src[3].fX, tValues);
485
486    SkChopCubicAt(src, dst, tValues, roots);
487    if (dst && roots > 0) {
488        // we do some cleanup to ensure our Y extrema are flat
489        flatten_double_cubic_extrema(&dst[0].fX);
490        if (roots == 2) {
491            flatten_double_cubic_extrema(&dst[3].fX);
492        }
493    }
494    return roots;
495}
496
497/** http://www.faculty.idc.ac.il/arik/quality/appendixA.html
498
499    Inflection means that curvature is zero.
500    Curvature is [F' x F''] / [F'^3]
501    So we solve F'x X F''y - F'y X F''y == 0
502    After some canceling of the cubic term, we get
503    A = b - a
504    B = c - 2b + a
505    C = d - 3c + 3b - a
506    (BxCy - ByCx)t^2 + (AxCy - AyCx)t + AxBy - AyBx == 0
507*/
508int SkFindCubicInflections(const SkPoint src[4], SkScalar tValues[]) {
509    SkScalar    Ax = src[1].fX - src[0].fX;
510    SkScalar    Ay = src[1].fY - src[0].fY;
511    SkScalar    Bx = src[2].fX - 2 * src[1].fX + src[0].fX;
512    SkScalar    By = src[2].fY - 2 * src[1].fY + src[0].fY;
513    SkScalar    Cx = src[3].fX + 3 * (src[1].fX - src[2].fX) - src[0].fX;
514    SkScalar    Cy = src[3].fY + 3 * (src[1].fY - src[2].fY) - src[0].fY;
515
516    return SkFindUnitQuadRoots(Bx*Cy - By*Cx,
517                               Ax*Cy - Ay*Cx,
518                               Ax*By - Ay*Bx,
519                               tValues);
520}
521
522int SkChopCubicAtInflections(const SkPoint src[], SkPoint dst[10]) {
523    SkScalar    tValues[2];
524    int         count = SkFindCubicInflections(src, tValues);
525
526    if (dst) {
527        if (count == 0) {
528            memcpy(dst, src, 4 * sizeof(SkPoint));
529        } else {
530            SkChopCubicAt(src, dst, tValues, count);
531        }
532    }
533    return count + 1;
534}
535
536// Assumes the third component of points is 1.
537// Calcs p0 . (p1 x p2)
538static double calc_dot_cross_cubic(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2) {
539    const double xComp = (double) p0.fX * ((double) p1.fY - (double) p2.fY);
540    const double yComp = (double) p0.fY * ((double) p2.fX - (double) p1.fX);
541    const double wComp = (double) p1.fX * (double) p2.fY - (double) p1.fY * (double) p2.fX;
542    return (xComp + yComp + wComp);
543}
544
545// Calc coefficients of I(s,t) where roots of I are inflection points of curve
546// I(s,t) = t*(3*d0*s^2 - 3*d1*s*t + d2*t^2)
547// d0 = a1 - 2*a2+3*a3
548// d1 = -a2 + 3*a3
549// d2 = 3*a3
550// a1 = p0 . (p3 x p2)
551// a2 = p1 . (p0 x p3)
552// a3 = p2 . (p1 x p0)
553// Places the values of d1, d2, d3 in array d passed in
554static void calc_cubic_inflection_func(const SkPoint p[4], double d[4]) {
555    const double a1 = calc_dot_cross_cubic(p[0], p[3], p[2]);
556    const double a2 = calc_dot_cross_cubic(p[1], p[0], p[3]);
557    const double a3 = calc_dot_cross_cubic(p[2], p[1], p[0]);
558
559    d[3] = 3 * a3;
560    d[2] = d[3] - a2;
561    d[1] = d[2] - a2 + a1;
562    d[0] = 0;
563}
564
565static void normalize_t_s(double t[], double s[], int count) {
566    // Keep the exponents at or below zero to avoid overflow down the road.
567    for (int i = 0; i < count; ++i) {
568        SkASSERT(0 != s[i]); // classify_cubic should not call this method when s[i] is 0 or NaN.
569
570        uint64_t bitsT, bitsS;
571        memcpy(&bitsT, &t[i], sizeof(double));
572        memcpy(&bitsS, &s[i], sizeof(double));
573
574        uint64_t maxExponent = SkTMax(bitsT & 0x7ff0000000000000, bitsS & 0x7ff0000000000000);
575
576#ifdef SK_DEBUG
577        uint64_t maxExponentValue = maxExponent >> 52;
578        // Ensure max(absT,absS) is NOT in denormalized form. SkClassifyCubic is given fp32 points,
579        // and does not call this method when s==0, so this should never happen.
580        SkASSERT(0 != maxExponentValue);
581        // Ensure 1/max(absT,absS) will NOT be in denormalized form. SkClassifyCubic is given fp32
582        // points, so this should never happen.
583        SkASSERT(2046 != maxExponentValue);
584#endif
585
586        // Pick a normalizer that scales the larger exponent to 1 (aka 1023 in biased form), but
587        // does NOT change the mantissa (thus preserving accuracy).
588        double normalizer;
589        uint64_t normalizerExponent = (uint64_t(1023 * 2) << 52) - maxExponent;
590        memcpy(&normalizer, &normalizerExponent, sizeof(double));
591
592        t[i] *= normalizer;
593        s[i] *= normalizer;
594    }
595}
596
597static void sort_and_orient_t_s(double t[2], double s[2]) {
598    // This copysign/abs business orients the implicit function so positive values are always on the
599    // "left" side of the curve.
600    t[1] = -copysign(t[1], t[1] * s[1]);
601    s[1] = -fabs(s[1]);
602
603    // Ensure t[0]/s[0] <= t[1]/s[1] (s[1] is negative from above).
604    if (copysign(s[1], s[0]) * t[0] > -fabs(s[0]) * t[1]) {
605        std::swap(t[0], t[1]);
606        std::swap(s[0], s[1]);
607    }
608}
609
610// See "Resolution Independent Curve Rendering using Programmable Graphics Hardware"
611// https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
612// discr(I) = 3*d2^2 - 4*d1*d3
613// Classification:
614// d1 != 0, discr(I) > 0        Serpentine
615// d1 != 0, discr(I) < 0        Loop
616// d1 != 0, discr(I) = 0        Cusp (with inflection at infinity)
617// d1 = 0, d2 != 0              Cusp (with cusp at infinity)
618// d1 = d2 = 0, d3 != 0         Quadratic
619// d1 = d2 = d3 = 0             Line or Point
620static SkCubicType classify_cubic(const double d[4], double t[2], double s[2]) {
621    if (0 == d[1]) {
622        if (0 == d[2]) {
623            if (t && s) {
624                t[0] = t[1] = 1;
625                s[0] = s[1] = 0; // infinity
626            }
627            return 0 == d[3] ? SkCubicType::kLineOrPoint : SkCubicType::kQuadratic;
628        }
629        if (t && s) {
630            t[0] = d[3];
631            s[0] = 3 * d[2];
632            normalize_t_s(t, s, 1);
633            t[1] = 1;
634            s[1] = 0; // infinity
635        }
636        return SkCubicType::kCuspAtInfinity;
637    }
638
639    const double discr = 3 * d[2] * d[2] - 4 * d[1] * d[3];
640    if (discr > 0) {
641        if (t && s) {
642            const double q = 3 * d[2] + copysign(sqrt(3 * discr), d[2]);
643            t[0] = q;
644            s[0] = 6 * d[1];
645            t[1] = 2 * d[3];
646            s[1] = q;
647            normalize_t_s(t, s, 2);
648            sort_and_orient_t_s(t, s);
649        }
650        return SkCubicType::kSerpentine;
651    } else if (discr < 0) {
652        if (t && s) {
653            const double q = d[2] + copysign(sqrt(-discr), d[2]);
654            t[0] = q;
655            s[0] = 2 * d[1];
656            t[1] = 2 * (d[2] * d[2] - d[3] * d[1]);
657            s[1] = d[1] * q;
658            normalize_t_s(t, s, 2);
659            sort_and_orient_t_s(t, s);
660        }
661        return SkCubicType::kLoop;
662    } else {
663        if (t && s) {
664            t[0] = d[2];
665            s[0] = 2 * d[1];
666            normalize_t_s(t, s, 1);
667            t[1] = t[0];
668            s[1] = s[0];
669            sort_and_orient_t_s(t, s);
670        }
671        return SkCubicType::kLocalCusp;
672    }
673}
674
675SkCubicType SkClassifyCubic(const SkPoint src[4], double t[2], double s[2], double d[4]) {
676    double localD[4];
677    double* dd = d ? d : localD;
678    calc_cubic_inflection_func(src, dd);
679    return classify_cubic(dd, t, s);
680}
681
682template <typename T> void bubble_sort(T array[], int count) {
683    for (int i = count - 1; i > 0; --i)
684        for (int j = i; j > 0; --j)
685            if (array[j] < array[j-1])
686            {
687                T   tmp(array[j]);
688                array[j] = array[j-1];
689                array[j-1] = tmp;
690            }
691}
692
693/**
694 *  Given an array and count, remove all pair-wise duplicates from the array,
695 *  keeping the existing sorting, and return the new count
696 */
697static int collaps_duplicates(SkScalar array[], int count) {
698    for (int n = count; n > 1; --n) {
699        if (array[0] == array[1]) {
700            for (int i = 1; i < n; ++i) {
701                array[i - 1] = array[i];
702            }
703            count -= 1;
704        } else {
705            array += 1;
706        }
707    }
708    return count;
709}
710
711#ifdef SK_DEBUG
712
713#define TEST_COLLAPS_ENTRY(array)   array, SK_ARRAY_COUNT(array)
714
715static void test_collaps_duplicates() {
716    static bool gOnce;
717    if (gOnce) { return; }
718    gOnce = true;
719    const SkScalar src0[] = { 0 };
720    const SkScalar src1[] = { 0, 0 };
721    const SkScalar src2[] = { 0, 1 };
722    const SkScalar src3[] = { 0, 0, 0 };
723    const SkScalar src4[] = { 0, 0, 1 };
724    const SkScalar src5[] = { 0, 1, 1 };
725    const SkScalar src6[] = { 0, 1, 2 };
726    const struct {
727        const SkScalar* fData;
728        int fCount;
729        int fCollapsedCount;
730    } data[] = {
731        { TEST_COLLAPS_ENTRY(src0), 1 },
732        { TEST_COLLAPS_ENTRY(src1), 1 },
733        { TEST_COLLAPS_ENTRY(src2), 2 },
734        { TEST_COLLAPS_ENTRY(src3), 1 },
735        { TEST_COLLAPS_ENTRY(src4), 2 },
736        { TEST_COLLAPS_ENTRY(src5), 2 },
737        { TEST_COLLAPS_ENTRY(src6), 3 },
738    };
739    for (size_t i = 0; i < SK_ARRAY_COUNT(data); ++i) {
740        SkScalar dst[3];
741        memcpy(dst, data[i].fData, data[i].fCount * sizeof(dst[0]));
742        int count = collaps_duplicates(dst, data[i].fCount);
743        SkASSERT(data[i].fCollapsedCount == count);
744        for (int j = 1; j < count; ++j) {
745            SkASSERT(dst[j-1] < dst[j]);
746        }
747    }
748}
749#endif
750
751static SkScalar SkScalarCubeRoot(SkScalar x) {
752    return SkScalarPow(x, 0.3333333f);
753}
754
755/*  Solve coeff(t) == 0, returning the number of roots that
756    lie withing 0 < t < 1.
757    coeff[0]t^3 + coeff[1]t^2 + coeff[2]t + coeff[3]
758
759    Eliminates repeated roots (so that all tValues are distinct, and are always
760    in increasing order.
761*/
762static int solve_cubic_poly(const SkScalar coeff[4], SkScalar tValues[3]) {
763    if (SkScalarNearlyZero(coeff[0])) {  // we're just a quadratic
764        return SkFindUnitQuadRoots(coeff[1], coeff[2], coeff[3], tValues);
765    }
766
767    SkScalar a, b, c, Q, R;
768
769    {
770        SkASSERT(coeff[0] != 0);
771
772        SkScalar inva = SkScalarInvert(coeff[0]);
773        a = coeff[1] * inva;
774        b = coeff[2] * inva;
775        c = coeff[3] * inva;
776    }
777    Q = (a*a - b*3) / 9;
778    R = (2*a*a*a - 9*a*b + 27*c) / 54;
779
780    SkScalar Q3 = Q * Q * Q;
781    SkScalar R2MinusQ3 = R * R - Q3;
782    SkScalar adiv3 = a / 3;
783
784    SkScalar*   roots = tValues;
785    SkScalar    r;
786
787    if (R2MinusQ3 < 0) { // we have 3 real roots
788        // the divide/root can, due to finite precisions, be slightly outside of -1...1
789        SkScalar theta = SkScalarACos(SkScalarPin(R / SkScalarSqrt(Q3), -1, 1));
790        SkScalar neg2RootQ = -2 * SkScalarSqrt(Q);
791
792        r = neg2RootQ * SkScalarCos(theta/3) - adiv3;
793        if (is_unit_interval(r)) {
794            *roots++ = r;
795        }
796        r = neg2RootQ * SkScalarCos((theta + 2*SK_ScalarPI)/3) - adiv3;
797        if (is_unit_interval(r)) {
798            *roots++ = r;
799        }
800        r = neg2RootQ * SkScalarCos((theta - 2*SK_ScalarPI)/3) - adiv3;
801        if (is_unit_interval(r)) {
802            *roots++ = r;
803        }
804        SkDEBUGCODE(test_collaps_duplicates();)
805
806        // now sort the roots
807        int count = (int)(roots - tValues);
808        SkASSERT((unsigned)count <= 3);
809        bubble_sort(tValues, count);
810        count = collaps_duplicates(tValues, count);
811        roots = tValues + count;    // so we compute the proper count below
812    } else {              // we have 1 real root
813        SkScalar A = SkScalarAbs(R) + SkScalarSqrt(R2MinusQ3);
814        A = SkScalarCubeRoot(A);
815        if (R > 0) {
816            A = -A;
817        }
818        if (A != 0) {
819            A += Q / A;
820        }
821        r = A - adiv3;
822        if (is_unit_interval(r)) {
823            *roots++ = r;
824        }
825    }
826
827    return (int)(roots - tValues);
828}
829
830/*  Looking for F' dot F'' == 0
831
832    A = b - a
833    B = c - 2b + a
834    C = d - 3c + 3b - a
835
836    F' = 3Ct^2 + 6Bt + 3A
837    F'' = 6Ct + 6B
838
839    F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB
840*/
841static void formulate_F1DotF2(const SkScalar src[], SkScalar coeff[4]) {
842    SkScalar    a = src[2] - src[0];
843    SkScalar    b = src[4] - 2 * src[2] + src[0];
844    SkScalar    c = src[6] + 3 * (src[2] - src[4]) - src[0];
845
846    coeff[0] = c * c;
847    coeff[1] = 3 * b * c;
848    coeff[2] = 2 * b * b + c * a;
849    coeff[3] = a * b;
850}
851
852/*  Looking for F' dot F'' == 0
853
854    A = b - a
855    B = c - 2b + a
856    C = d - 3c + 3b - a
857
858    F' = 3Ct^2 + 6Bt + 3A
859    F'' = 6Ct + 6B
860
861    F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB
862*/
863int SkFindCubicMaxCurvature(const SkPoint src[4], SkScalar tValues[3]) {
864    SkScalar coeffX[4], coeffY[4];
865    int      i;
866
867    formulate_F1DotF2(&src[0].fX, coeffX);
868    formulate_F1DotF2(&src[0].fY, coeffY);
869
870    for (i = 0; i < 4; i++) {
871        coeffX[i] += coeffY[i];
872    }
873
874    SkScalar    t[3];
875    int         count = solve_cubic_poly(coeffX, t);
876    int         maxCount = 0;
877
878    // now remove extrema where the curvature is zero (mins)
879    // !!!! need a test for this !!!!
880    for (i = 0; i < count; i++) {
881        // if (not_min_curvature())
882        if (t[i] > 0 && t[i] < SK_Scalar1) {
883            tValues[maxCount++] = t[i];
884        }
885    }
886    return maxCount;
887}
888
889int SkChopCubicAtMaxCurvature(const SkPoint src[4], SkPoint dst[13],
890                              SkScalar tValues[3]) {
891    SkScalar    t_storage[3];
892
893    if (tValues == nullptr) {
894        tValues = t_storage;
895    }
896
897    int count = SkFindCubicMaxCurvature(src, tValues);
898
899    if (dst) {
900        if (count == 0) {
901            memcpy(dst, src, 4 * sizeof(SkPoint));
902        } else {
903            SkChopCubicAt(src, dst, tValues, count);
904        }
905    }
906    return count + 1;
907}
908
909#include "../pathops/SkPathOpsCubic.h"
910
911typedef int (SkDCubic::*InterceptProc)(double intercept, double roots[3]) const;
912
913static bool cubic_dchop_at_intercept(const SkPoint src[4], SkScalar intercept, SkPoint dst[7],
914                                     InterceptProc method) {
915    SkDCubic cubic;
916    double roots[3];
917    int count = (cubic.set(src).*method)(intercept, roots);
918    if (count > 0) {
919        SkDCubicPair pair = cubic.chopAt(roots[0]);
920        for (int i = 0; i < 7; ++i) {
921            dst[i] = pair.pts[i].asSkPoint();
922        }
923        return true;
924    }
925    return false;
926}
927
928bool SkChopMonoCubicAtY(SkPoint src[4], SkScalar y, SkPoint dst[7]) {
929    return cubic_dchop_at_intercept(src, y, dst, &SkDCubic::horizontalIntersect);
930}
931
932bool SkChopMonoCubicAtX(SkPoint src[4], SkScalar x, SkPoint dst[7]) {
933    return cubic_dchop_at_intercept(src, x, dst, &SkDCubic::verticalIntersect);
934}
935
936///////////////////////////////////////////////////////////////////////////////
937//
938// NURB representation for conics.  Helpful explanations at:
939//
940// http://citeseerx.ist.psu.edu/viewdoc/
941//   download?doi=10.1.1.44.5740&rep=rep1&type=ps
942// and
943// http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/NURBS/RB-conics.html
944//
945// F = (A (1 - t)^2 + C t^2 + 2 B (1 - t) t w)
946//     ------------------------------------------
947//         ((1 - t)^2 + t^2 + 2 (1 - t) t w)
948//
949//   = {t^2 (P0 + P2 - 2 P1 w), t (-2 P0 + 2 P1 w), P0}
950//     ------------------------------------------------
951//             {t^2 (2 - 2 w), t (-2 + 2 w), 1}
952//
953
954// F' = 2 (C t (1 + t (-1 + w)) - A (-1 + t) (t (-1 + w) - w) + B (1 - 2 t) w)
955//
956//  t^2 : (2 P0 - 2 P2 - 2 P0 w + 2 P2 w)
957//  t^1 : (-2 P0 + 2 P2 + 4 P0 w - 4 P1 w)
958//  t^0 : -2 P0 w + 2 P1 w
959//
960//  We disregard magnitude, so we can freely ignore the denominator of F', and
961//  divide the numerator by 2
962//
963//    coeff[0] for t^2
964//    coeff[1] for t^1
965//    coeff[2] for t^0
966//
967static void conic_deriv_coeff(const SkScalar src[],
968                              SkScalar w,
969                              SkScalar coeff[3]) {
970    const SkScalar P20 = src[4] - src[0];
971    const SkScalar P10 = src[2] - src[0];
972    const SkScalar wP10 = w * P10;
973    coeff[0] = w * P20 - P20;
974    coeff[1] = P20 - 2 * wP10;
975    coeff[2] = wP10;
976}
977
978static bool conic_find_extrema(const SkScalar src[], SkScalar w, SkScalar* t) {
979    SkScalar coeff[3];
980    conic_deriv_coeff(src, w, coeff);
981
982    SkScalar tValues[2];
983    int roots = SkFindUnitQuadRoots(coeff[0], coeff[1], coeff[2], tValues);
984    SkASSERT(0 == roots || 1 == roots);
985
986    if (1 == roots) {
987        *t = tValues[0];
988        return true;
989    }
990    return false;
991}
992
993// We only interpolate one dimension at a time (the first, at +0, +3, +6).
994static void p3d_interp(const SkScalar src[7], SkScalar dst[7], SkScalar t) {
995    SkScalar ab = SkScalarInterp(src[0], src[3], t);
996    SkScalar bc = SkScalarInterp(src[3], src[6], t);
997    dst[0] = ab;
998    dst[3] = SkScalarInterp(ab, bc, t);
999    dst[6] = bc;
1000}
1001
1002static void ratquad_mapTo3D(const SkPoint src[3], SkScalar w, SkPoint3 dst[3]) {
1003    dst[0].set(src[0].fX * 1, src[0].fY * 1, 1);
1004    dst[1].set(src[1].fX * w, src[1].fY * w, w);
1005    dst[2].set(src[2].fX * 1, src[2].fY * 1, 1);
1006}
1007
1008static SkPoint project_down(const SkPoint3& src) {
1009    return {src.fX / src.fZ, src.fY / src.fZ};
1010}
1011
1012// return false if infinity or NaN is generated; caller must check
1013bool SkConic::chopAt(SkScalar t, SkConic dst[2]) const {
1014    SkPoint3 tmp[3], tmp2[3];
1015
1016    ratquad_mapTo3D(fPts, fW, tmp);
1017
1018    p3d_interp(&tmp[0].fX, &tmp2[0].fX, t);
1019    p3d_interp(&tmp[0].fY, &tmp2[0].fY, t);
1020    p3d_interp(&tmp[0].fZ, &tmp2[0].fZ, t);
1021
1022    dst[0].fPts[0] = fPts[0];
1023    dst[0].fPts[1] = project_down(tmp2[0]);
1024    dst[0].fPts[2] = project_down(tmp2[1]); dst[1].fPts[0] = dst[0].fPts[2];
1025    dst[1].fPts[1] = project_down(tmp2[2]);
1026    dst[1].fPts[2] = fPts[2];
1027
1028    // to put in "standard form", where w0 and w2 are both 1, we compute the
1029    // new w1 as sqrt(w1*w1/w0*w2)
1030    // or
1031    // w1 /= sqrt(w0*w2)
1032    //
1033    // However, in our case, we know that for dst[0]:
1034    //     w0 == 1, and for dst[1], w2 == 1
1035    //
1036    SkScalar root = SkScalarSqrt(tmp2[1].fZ);
1037    dst[0].fW = tmp2[0].fZ / root;
1038    dst[1].fW = tmp2[2].fZ / root;
1039    SkASSERT(sizeof(dst[0]) == sizeof(SkScalar) * 7);
1040    SkASSERT(0 == offsetof(SkConic, fPts[0].fX));
1041    return SkScalarsAreFinite(&dst[0].fPts[0].fX, 7 * 2);
1042}
1043
1044void SkConic::chopAt(SkScalar t1, SkScalar t2, SkConic* dst) const {
1045    if (0 == t1 || 1 == t2) {
1046        if (0 == t1 && 1 == t2) {
1047            *dst = *this;
1048            return;
1049        } else {
1050            SkConic pair[2];
1051            if (this->chopAt(t1 ? t1 : t2, pair)) {
1052                *dst = pair[SkToBool(t1)];
1053                return;
1054            }
1055        }
1056    }
1057    SkConicCoeff coeff(*this);
1058    Sk2s tt1(t1);
1059    Sk2s aXY = coeff.fNumer.eval(tt1);
1060    Sk2s aZZ = coeff.fDenom.eval(tt1);
1061    Sk2s midTT((t1 + t2) / 2);
1062    Sk2s dXY = coeff.fNumer.eval(midTT);
1063    Sk2s dZZ = coeff.fDenom.eval(midTT);
1064    Sk2s tt2(t2);
1065    Sk2s cXY = coeff.fNumer.eval(tt2);
1066    Sk2s cZZ = coeff.fDenom.eval(tt2);
1067    Sk2s bXY = times_2(dXY) - (aXY + cXY) * Sk2s(0.5f);
1068    Sk2s bZZ = times_2(dZZ) - (aZZ + cZZ) * Sk2s(0.5f);
1069    dst->fPts[0] = to_point(aXY / aZZ);
1070    dst->fPts[1] = to_point(bXY / bZZ);
1071    dst->fPts[2] = to_point(cXY / cZZ);
1072    Sk2s ww = bZZ / (aZZ * cZZ).sqrt();
1073    dst->fW = ww[0];
1074}
1075
1076SkPoint SkConic::evalAt(SkScalar t) const {
1077    return to_point(SkConicCoeff(*this).eval(t));
1078}
1079
1080SkVector SkConic::evalTangentAt(SkScalar t) const {
1081    // The derivative equation returns a zero tangent vector when t is 0 or 1,
1082    // and the control point is equal to the end point.
1083    // In this case, use the conic endpoints to compute the tangent.
1084    if ((t == 0 && fPts[0] == fPts[1]) || (t == 1 && fPts[1] == fPts[2])) {
1085        return fPts[2] - fPts[0];
1086    }
1087    Sk2s p0 = from_point(fPts[0]);
1088    Sk2s p1 = from_point(fPts[1]);
1089    Sk2s p2 = from_point(fPts[2]);
1090    Sk2s ww(fW);
1091
1092    Sk2s p20 = p2 - p0;
1093    Sk2s p10 = p1 - p0;
1094
1095    Sk2s C = ww * p10;
1096    Sk2s A = ww * p20 - p20;
1097    Sk2s B = p20 - C - C;
1098
1099    return to_vector(SkQuadCoeff(A, B, C).eval(t));
1100}
1101
1102void SkConic::evalAt(SkScalar t, SkPoint* pt, SkVector* tangent) const {
1103    SkASSERT(t >= 0 && t <= SK_Scalar1);
1104
1105    if (pt) {
1106        *pt = this->evalAt(t);
1107    }
1108    if (tangent) {
1109        *tangent = this->evalTangentAt(t);
1110    }
1111}
1112
1113static SkScalar subdivide_w_value(SkScalar w) {
1114    return SkScalarSqrt(SK_ScalarHalf + w * SK_ScalarHalf);
1115}
1116
1117void SkConic::chop(SkConic * SK_RESTRICT dst) const {
1118    Sk2s scale = Sk2s(SkScalarInvert(SK_Scalar1 + fW));
1119    SkScalar newW = subdivide_w_value(fW);
1120
1121    Sk2s p0 = from_point(fPts[0]);
1122    Sk2s p1 = from_point(fPts[1]);
1123    Sk2s p2 = from_point(fPts[2]);
1124    Sk2s ww(fW);
1125
1126    Sk2s wp1 = ww * p1;
1127    Sk2s m = (p0 + times_2(wp1) + p2) * scale * Sk2s(0.5f);
1128
1129    dst[0].fPts[0] = fPts[0];
1130    dst[0].fPts[1] = to_point((p0 + wp1) * scale);
1131    dst[0].fPts[2] = dst[1].fPts[0] = to_point(m);
1132    dst[1].fPts[1] = to_point((wp1 + p2) * scale);
1133    dst[1].fPts[2] = fPts[2];
1134
1135    dst[0].fW = dst[1].fW = newW;
1136}
1137
1138/*
1139 *  "High order approximation of conic sections by quadratic splines"
1140 *      by Michael Floater, 1993
1141 */
1142#define AS_QUAD_ERROR_SETUP                                         \
1143    SkScalar a = fW - 1;                                            \
1144    SkScalar k = a / (4 * (2 + a));                                 \
1145    SkScalar x = k * (fPts[0].fX - 2 * fPts[1].fX + fPts[2].fX);    \
1146    SkScalar y = k * (fPts[0].fY - 2 * fPts[1].fY + fPts[2].fY);
1147
1148void SkConic::computeAsQuadError(SkVector* err) const {
1149    AS_QUAD_ERROR_SETUP
1150    err->set(x, y);
1151}
1152
1153bool SkConic::asQuadTol(SkScalar tol) const {
1154    AS_QUAD_ERROR_SETUP
1155    return (x * x + y * y) <= tol * tol;
1156}
1157
1158// Limit the number of suggested quads to approximate a conic
1159#define kMaxConicToQuadPOW2     5
1160
1161int SkConic::computeQuadPOW2(SkScalar tol) const {
1162    if (tol < 0 || !SkScalarIsFinite(tol) || !SkPointPriv::AreFinite(fPts, 3)) {
1163        return 0;
1164    }
1165
1166    AS_QUAD_ERROR_SETUP
1167
1168    SkScalar error = SkScalarSqrt(x * x + y * y);
1169    int pow2;
1170    for (pow2 = 0; pow2 < kMaxConicToQuadPOW2; ++pow2) {
1171        if (error <= tol) {
1172            break;
1173        }
1174        error *= 0.25f;
1175    }
1176    // float version -- using ceil gives the same results as the above.
1177    if (false) {
1178        SkScalar err = SkScalarSqrt(x * x + y * y);
1179        if (err <= tol) {
1180            return 0;
1181        }
1182        SkScalar tol2 = tol * tol;
1183        if (tol2 == 0) {
1184            return kMaxConicToQuadPOW2;
1185        }
1186        SkScalar fpow2 = SkScalarLog2((x * x + y * y) / tol2) * 0.25f;
1187        int altPow2 = SkScalarCeilToInt(fpow2);
1188        if (altPow2 != pow2) {
1189            SkDebugf("pow2 %d altPow2 %d fbits %g err %g tol %g\n", pow2, altPow2, fpow2, err, tol);
1190        }
1191        pow2 = altPow2;
1192    }
1193    return pow2;
1194}
1195
1196// This was originally developed and tested for pathops: see SkOpTypes.h
1197// returns true if (a <= b <= c) || (a >= b >= c)
1198static bool between(SkScalar a, SkScalar b, SkScalar c) {
1199    return (a - b) * (c - b) <= 0;
1200}
1201
1202static SkPoint* subdivide(const SkConic& src, SkPoint pts[], int level) {
1203    SkASSERT(level >= 0);
1204
1205    if (0 == level) {
1206        memcpy(pts, &src.fPts[1], 2 * sizeof(SkPoint));
1207        return pts + 2;
1208    } else {
1209        SkConic dst[2];
1210        src.chop(dst);
1211        const SkScalar startY = src.fPts[0].fY;
1212        const SkScalar endY = src.fPts[2].fY;
1213        if (between(startY, src.fPts[1].fY, endY)) {
1214            // If the input is monotonic and the output is not, the scan converter hangs.
1215            // Ensure that the chopped conics maintain their y-order.
1216            SkScalar midY = dst[0].fPts[2].fY;
1217            if (!between(startY, midY, endY)) {
1218                // If the computed midpoint is outside the ends, move it to the closer one.
1219                SkScalar closerY = SkTAbs(midY - startY) < SkTAbs(midY - endY) ? startY : endY;
1220                dst[0].fPts[2].fY = dst[1].fPts[0].fY = closerY;
1221            }
1222            if (!between(startY, dst[0].fPts[1].fY, dst[0].fPts[2].fY)) {
1223                // If the 1st control is not between the start and end, put it at the start.
1224                // This also reduces the quad to a line.
1225                dst[0].fPts[1].fY = startY;
1226            }
1227            if (!between(dst[1].fPts[0].fY, dst[1].fPts[1].fY, endY)) {
1228                // If the 2nd control is not between the start and end, put it at the end.
1229                // This also reduces the quad to a line.
1230                dst[1].fPts[1].fY = endY;
1231            }
1232            // Verify that all five points are in order.
1233            SkASSERT(between(startY, dst[0].fPts[1].fY, dst[0].fPts[2].fY));
1234            SkASSERT(between(dst[0].fPts[1].fY, dst[0].fPts[2].fY, dst[1].fPts[1].fY));
1235            SkASSERT(between(dst[0].fPts[2].fY, dst[1].fPts[1].fY, endY));
1236        }
1237        --level;
1238        pts = subdivide(dst[0], pts, level);
1239        return subdivide(dst[1], pts, level);
1240    }
1241}
1242
1243int SkConic::chopIntoQuadsPOW2(SkPoint pts[], int pow2) const {
1244    SkASSERT(pow2 >= 0);
1245    *pts = fPts[0];
1246    SkDEBUGCODE(SkPoint* endPts);
1247    if (pow2 == kMaxConicToQuadPOW2) {  // If an extreme weight generates many quads ...
1248        SkConic dst[2];
1249        this->chop(dst);
1250        // check to see if the first chop generates a pair of lines
1251        if (SkPointPriv::EqualsWithinTolerance(dst[0].fPts[1], dst[0].fPts[2]) &&
1252                SkPointPriv::EqualsWithinTolerance(dst[1].fPts[0], dst[1].fPts[1])) {
1253            pts[1] = pts[2] = pts[3] = dst[0].fPts[1];  // set ctrl == end to make lines
1254            pts[4] = dst[1].fPts[2];
1255            pow2 = 1;
1256            SkDEBUGCODE(endPts = &pts[5]);
1257            goto commonFinitePtCheck;
1258        }
1259    }
1260    SkDEBUGCODE(endPts = ) subdivide(*this, pts + 1, pow2);
1261commonFinitePtCheck:
1262    const int quadCount = 1 << pow2;
1263    const int ptCount = 2 * quadCount + 1;
1264    SkASSERT(endPts - pts == ptCount);
1265    if (!SkPointPriv::AreFinite(pts, ptCount)) {
1266        // if we generated a non-finite, pin ourselves to the middle of the hull,
1267        // as our first and last are already on the first/last pts of the hull.
1268        for (int i = 1; i < ptCount - 1; ++i) {
1269            pts[i] = fPts[1];
1270        }
1271    }
1272    return 1 << pow2;
1273}
1274
1275bool SkConic::findXExtrema(SkScalar* t) const {
1276    return conic_find_extrema(&fPts[0].fX, fW, t);
1277}
1278
1279bool SkConic::findYExtrema(SkScalar* t) const {
1280    return conic_find_extrema(&fPts[0].fY, fW, t);
1281}
1282
1283bool SkConic::chopAtXExtrema(SkConic dst[2]) const {
1284    SkScalar t;
1285    if (this->findXExtrema(&t)) {
1286        if (!this->chopAt(t, dst)) {
1287            // if chop can't return finite values, don't chop
1288            return false;
1289        }
1290        // now clean-up the middle, since we know t was meant to be at
1291        // an X-extrema
1292        SkScalar value = dst[0].fPts[2].fX;
1293        dst[0].fPts[1].fX = value;
1294        dst[1].fPts[0].fX = value;
1295        dst[1].fPts[1].fX = value;
1296        return true;
1297    }
1298    return false;
1299}
1300
1301bool SkConic::chopAtYExtrema(SkConic dst[2]) const {
1302    SkScalar t;
1303    if (this->findYExtrema(&t)) {
1304        if (!this->chopAt(t, dst)) {
1305            // if chop can't return finite values, don't chop
1306            return false;
1307        }
1308        // now clean-up the middle, since we know t was meant to be at
1309        // an Y-extrema
1310        SkScalar value = dst[0].fPts[2].fY;
1311        dst[0].fPts[1].fY = value;
1312        dst[1].fPts[0].fY = value;
1313        dst[1].fPts[1].fY = value;
1314        return true;
1315    }
1316    return false;
1317}
1318
1319void SkConic::computeTightBounds(SkRect* bounds) const {
1320    SkPoint pts[4];
1321    pts[0] = fPts[0];
1322    pts[1] = fPts[2];
1323    int count = 2;
1324
1325    SkScalar t;
1326    if (this->findXExtrema(&t)) {
1327        this->evalAt(t, &pts[count++]);
1328    }
1329    if (this->findYExtrema(&t)) {
1330        this->evalAt(t, &pts[count++]);
1331    }
1332    bounds->set(pts, count);
1333}
1334
1335void SkConic::computeFastBounds(SkRect* bounds) const {
1336    bounds->set(fPts, 3);
1337}
1338
1339#if 0  // unimplemented
1340bool SkConic::findMaxCurvature(SkScalar* t) const {
1341    // TODO: Implement me
1342    return false;
1343}
1344#endif
1345
1346SkScalar SkConic::TransformW(const SkPoint pts[], SkScalar w,
1347                             const SkMatrix& matrix) {
1348    if (!matrix.hasPerspective()) {
1349        return w;
1350    }
1351
1352    SkPoint3 src[3], dst[3];
1353
1354    ratquad_mapTo3D(pts, w, src);
1355
1356    matrix.mapHomogeneousPoints(dst, src, 3);
1357
1358    // w' = sqrt(w1*w1/w0*w2)
1359    SkScalar w0 = dst[0].fZ;
1360    SkScalar w1 = dst[1].fZ;
1361    SkScalar w2 = dst[2].fZ;
1362    w = SkScalarSqrt((w1 * w1) / (w0 * w2));
1363    return w;
1364}
1365
1366int SkConic::BuildUnitArc(const SkVector& uStart, const SkVector& uStop, SkRotationDirection dir,
1367                          const SkMatrix* userMatrix, SkConic dst[kMaxConicsForArc]) {
1368    // rotate by x,y so that uStart is (1.0)
1369    SkScalar x = SkPoint::DotProduct(uStart, uStop);
1370    SkScalar y = SkPoint::CrossProduct(uStart, uStop);
1371
1372    SkScalar absY = SkScalarAbs(y);
1373
1374    // check for (effectively) coincident vectors
1375    // this can happen if our angle is nearly 0 or nearly 180 (y == 0)
1376    // ... we use the dot-prod to distinguish between 0 and 180 (x > 0)
1377    if (absY <= SK_ScalarNearlyZero && x > 0 && ((y >= 0 && kCW_SkRotationDirection == dir) ||
1378                                                 (y <= 0 && kCCW_SkRotationDirection == dir))) {
1379        return 0;
1380    }
1381
1382    if (dir == kCCW_SkRotationDirection) {
1383        y = -y;
1384    }
1385
1386    // We decide to use 1-conic per quadrant of a circle. What quadrant does [xy] lie in?
1387    //      0 == [0  .. 90)
1388    //      1 == [90 ..180)
1389    //      2 == [180..270)
1390    //      3 == [270..360)
1391    //
1392    int quadrant = 0;
1393    if (0 == y) {
1394        quadrant = 2;        // 180
1395        SkASSERT(SkScalarAbs(x + SK_Scalar1) <= SK_ScalarNearlyZero);
1396    } else if (0 == x) {
1397        SkASSERT(absY - SK_Scalar1 <= SK_ScalarNearlyZero);
1398        quadrant = y > 0 ? 1 : 3; // 90 : 270
1399    } else {
1400        if (y < 0) {
1401            quadrant += 2;
1402        }
1403        if ((x < 0) != (y < 0)) {
1404            quadrant += 1;
1405        }
1406    }
1407
1408    const SkPoint quadrantPts[] = {
1409        { 1, 0 }, { 1, 1 }, { 0, 1 }, { -1, 1 }, { -1, 0 }, { -1, -1 }, { 0, -1 }, { 1, -1 }
1410    };
1411    const SkScalar quadrantWeight = SK_ScalarRoot2Over2;
1412
1413    int conicCount = quadrant;
1414    for (int i = 0; i < conicCount; ++i) {
1415        dst[i].set(&quadrantPts[i * 2], quadrantWeight);
1416    }
1417
1418    // Now compute any remaing (sub-90-degree) arc for the last conic
1419    const SkPoint finalP = { x, y };
1420    const SkPoint& lastQ = quadrantPts[quadrant * 2];  // will already be a unit-vector
1421    const SkScalar dot = SkVector::DotProduct(lastQ, finalP);
1422    SkASSERT(0 <= dot && dot <= SK_Scalar1 + SK_ScalarNearlyZero);
1423
1424    if (dot < 1) {
1425        SkVector offCurve = { lastQ.x() + x, lastQ.y() + y };
1426        // compute the bisector vector, and then rescale to be the off-curve point.
1427        // we compute its length from cos(theta/2) = length / 1, using half-angle identity we get
1428        // length = sqrt(2 / (1 + cos(theta)). We already have cos() when to computed the dot.
1429        // This is nice, since our computed weight is cos(theta/2) as well!
1430        //
1431        const SkScalar cosThetaOver2 = SkScalarSqrt((1 + dot) / 2);
1432        offCurve.setLength(SkScalarInvert(cosThetaOver2));
1433        if (!SkPointPriv::EqualsWithinTolerance(lastQ, offCurve)) {
1434            dst[conicCount].set(lastQ, offCurve, finalP, cosThetaOver2);
1435            conicCount += 1;
1436        }
1437    }
1438
1439    // now handle counter-clockwise and the initial unitStart rotation
1440    SkMatrix    matrix;
1441    matrix.setSinCos(uStart.fY, uStart.fX);
1442    if (dir == kCCW_SkRotationDirection) {
1443        matrix.preScale(SK_Scalar1, -SK_Scalar1);
1444    }
1445    if (userMatrix) {
1446        matrix.postConcat(*userMatrix);
1447    }
1448    for (int i = 0; i < conicCount; ++i) {
1449        matrix.mapPoints(dst[i].fPts, 3);
1450    }
1451    return conicCount;
1452}
1453