LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/core - SkGeometry.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 151 688 21.9 %
Date: 2017-07-14 16:53:18 Functions: 19 72 26.4 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13