• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2012 Google Inc.
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 #include "SkGeometry.h"
8 #include "SkLineParameters.h"
9 #include "SkPathOpsConic.h"
10 #include "SkPathOpsCubic.h"
11 #include "SkPathOpsCurve.h"
12 #include "SkPathOpsLine.h"
13 #include "SkPathOpsQuad.h"
14 #include "SkPathOpsRect.h"
15 #include "SkTSort.h"
16 
17 const int SkDCubic::gPrecisionUnit = 256;  // FIXME: test different values in test framework
18 
align(int endIndex,int ctrlIndex,SkDPoint * dstPt) const19 void SkDCubic::align(int endIndex, int ctrlIndex, SkDPoint* dstPt) const {
20     if (fPts[endIndex].fX == fPts[ctrlIndex].fX) {
21         dstPt->fX = fPts[endIndex].fX;
22     }
23     if (fPts[endIndex].fY == fPts[ctrlIndex].fY) {
24         dstPt->fY = fPts[endIndex].fY;
25     }
26 }
27 
28 // give up when changing t no longer moves point
29 // also, copy point rather than recompute it when it does change
binarySearch(double min,double max,double axisIntercept,SearchAxis xAxis) const30 double SkDCubic::binarySearch(double min, double max, double axisIntercept,
31         SearchAxis xAxis) const {
32     double t = (min + max) / 2;
33     double step = (t - min) / 2;
34     SkDPoint cubicAtT = ptAtT(t);
35     double calcPos = (&cubicAtT.fX)[xAxis];
36     double calcDist = calcPos - axisIntercept;
37     do {
38         double priorT = t - step;
39         SkOPASSERT(priorT >= min);
40         SkDPoint lessPt = ptAtT(priorT);
41         if (approximately_equal_half(lessPt.fX, cubicAtT.fX)
42                 && approximately_equal_half(lessPt.fY, cubicAtT.fY)) {
43             return -1;  // binary search found no point at this axis intercept
44         }
45         double lessDist = (&lessPt.fX)[xAxis] - axisIntercept;
46 #if DEBUG_CUBIC_BINARY_SEARCH
47         SkDebugf("t=%1.9g calc=%1.9g dist=%1.9g step=%1.9g less=%1.9g\n", t, calcPos, calcDist,
48                 step, lessDist);
49 #endif
50         double lastStep = step;
51         step /= 2;
52         if (calcDist > 0 ? calcDist > lessDist : calcDist < lessDist) {
53             t = priorT;
54         } else {
55             double nextT = t + lastStep;
56             if (nextT > max) {
57                 return -1;
58             }
59             SkDPoint morePt = ptAtT(nextT);
60             if (approximately_equal_half(morePt.fX, cubicAtT.fX)
61                     && approximately_equal_half(morePt.fY, cubicAtT.fY)) {
62                 return -1;  // binary search found no point at this axis intercept
63             }
64             double moreDist = (&morePt.fX)[xAxis] - axisIntercept;
65             if (calcDist > 0 ? calcDist <= moreDist : calcDist >= moreDist) {
66                 continue;
67             }
68             t = nextT;
69         }
70         SkDPoint testAtT = ptAtT(t);
71         cubicAtT = testAtT;
72         calcPos = (&cubicAtT.fX)[xAxis];
73         calcDist = calcPos - axisIntercept;
74     } while (!approximately_equal(calcPos, axisIntercept));
75     return t;
76 }
77 
78 // get the rough scale of the cubic; used to determine if curvature is extreme
calcPrecision() const79 double SkDCubic::calcPrecision() const {
80     return ((fPts[1] - fPts[0]).length()
81             + (fPts[2] - fPts[1]).length()
82             + (fPts[3] - fPts[2]).length()) / gPrecisionUnit;
83 }
84 
85 /* classic one t subdivision */
interp_cubic_coords(const double * src,double * dst,double t)86 static void interp_cubic_coords(const double* src, double* dst, double t) {
87     double ab = SkDInterp(src[0], src[2], t);
88     double bc = SkDInterp(src[2], src[4], t);
89     double cd = SkDInterp(src[4], src[6], t);
90     double abc = SkDInterp(ab, bc, t);
91     double bcd = SkDInterp(bc, cd, t);
92     double abcd = SkDInterp(abc, bcd, t);
93 
94     dst[0] = src[0];
95     dst[2] = ab;
96     dst[4] = abc;
97     dst[6] = abcd;
98     dst[8] = bcd;
99     dst[10] = cd;
100     dst[12] = src[6];
101 }
102 
chopAt(double t) const103 SkDCubicPair SkDCubic::chopAt(double t) const {
104     SkDCubicPair dst;
105     if (t == 0.5) {
106         dst.pts[0] = fPts[0];
107         dst.pts[1].fX = (fPts[0].fX + fPts[1].fX) / 2;
108         dst.pts[1].fY = (fPts[0].fY + fPts[1].fY) / 2;
109         dst.pts[2].fX = (fPts[0].fX + 2 * fPts[1].fX + fPts[2].fX) / 4;
110         dst.pts[2].fY = (fPts[0].fY + 2 * fPts[1].fY + fPts[2].fY) / 4;
111         dst.pts[3].fX = (fPts[0].fX + 3 * (fPts[1].fX + fPts[2].fX) + fPts[3].fX) / 8;
112         dst.pts[3].fY = (fPts[0].fY + 3 * (fPts[1].fY + fPts[2].fY) + fPts[3].fY) / 8;
113         dst.pts[4].fX = (fPts[1].fX + 2 * fPts[2].fX + fPts[3].fX) / 4;
114         dst.pts[4].fY = (fPts[1].fY + 2 * fPts[2].fY + fPts[3].fY) / 4;
115         dst.pts[5].fX = (fPts[2].fX + fPts[3].fX) / 2;
116         dst.pts[5].fY = (fPts[2].fY + fPts[3].fY) / 2;
117         dst.pts[6] = fPts[3];
118         return dst;
119     }
120     interp_cubic_coords(&fPts[0].fX, &dst.pts[0].fX, t);
121     interp_cubic_coords(&fPts[0].fY, &dst.pts[0].fY, t);
122     return dst;
123 }
124 
Coefficients(const double * src,double * A,double * B,double * C,double * D)125 void SkDCubic::Coefficients(const double* src, double* A, double* B, double* C, double* D) {
126     *A = src[6];  // d
127     *B = src[4] * 3;  // 3*c
128     *C = src[2] * 3;  // 3*b
129     *D = src[0];  // a
130     *A -= *D - *C + *B;     // A =   -a + 3*b - 3*c + d
131     *B += 3 * *D - 2 * *C;  // B =  3*a - 6*b + 3*c
132     *C -= 3 * *D;           // C = -3*a + 3*b
133 }
134 
endsAreExtremaInXOrY() const135 bool SkDCubic::endsAreExtremaInXOrY() const {
136     return (between(fPts[0].fX, fPts[1].fX, fPts[3].fX)
137             && between(fPts[0].fX, fPts[2].fX, fPts[3].fX))
138             || (between(fPts[0].fY, fPts[1].fY, fPts[3].fY)
139             && between(fPts[0].fY, fPts[2].fY, fPts[3].fY));
140 }
141 
142 // Do a quick reject by rotating all points relative to a line formed by
143 // a pair of one cubic's points. If the 2nd cubic's points
144 // are on the line or on the opposite side from the 1st cubic's 'odd man', the
145 // curves at most intersect at the endpoints.
146 /* if returning true, check contains true if cubic's hull collapsed, making the cubic linear
147    if returning false, check contains true if the the cubic pair have only the end point in common
148 */
hullIntersects(const SkDPoint * pts,int ptCount,bool * isLinear) const149 bool SkDCubic::hullIntersects(const SkDPoint* pts, int ptCount, bool* isLinear) const {
150     bool linear = true;
151     char hullOrder[4];
152     int hullCount = convexHull(hullOrder);
153     int end1 = hullOrder[0];
154     int hullIndex = 0;
155     const SkDPoint* endPt[2];
156     endPt[0] = &fPts[end1];
157     do {
158         hullIndex = (hullIndex + 1) % hullCount;
159         int end2 = hullOrder[hullIndex];
160         endPt[1] = &fPts[end2];
161         double origX = endPt[0]->fX;
162         double origY = endPt[0]->fY;
163         double adj = endPt[1]->fX - origX;
164         double opp = endPt[1]->fY - origY;
165         int oddManMask = other_two(end1, end2);
166         int oddMan = end1 ^ oddManMask;
167         double sign = (fPts[oddMan].fY - origY) * adj - (fPts[oddMan].fX - origX) * opp;
168         int oddMan2 = end2 ^ oddManMask;
169         double sign2 = (fPts[oddMan2].fY - origY) * adj - (fPts[oddMan2].fX - origX) * opp;
170         if (sign * sign2 < 0) {
171             continue;
172         }
173         if (approximately_zero(sign)) {
174             sign = sign2;
175             if (approximately_zero(sign)) {
176                 continue;
177             }
178         }
179         linear = false;
180         bool foundOutlier = false;
181         for (int n = 0; n < ptCount; ++n) {
182             double test = (pts[n].fY - origY) * adj - (pts[n].fX - origX) * opp;
183             if (test * sign > 0 && !precisely_zero(test)) {
184                 foundOutlier = true;
185                 break;
186             }
187         }
188         if (!foundOutlier) {
189             return false;
190         }
191         endPt[0] = endPt[1];
192         end1 = end2;
193     } while (hullIndex);
194     *isLinear = linear;
195     return true;
196 }
197 
hullIntersects(const SkDCubic & c2,bool * isLinear) const198 bool SkDCubic::hullIntersects(const SkDCubic& c2, bool* isLinear) const {
199     return hullIntersects(c2.fPts, c2.kPointCount, isLinear);
200 }
201 
hullIntersects(const SkDQuad & quad,bool * isLinear) const202 bool SkDCubic::hullIntersects(const SkDQuad& quad, bool* isLinear) const {
203     return hullIntersects(quad.fPts, quad.kPointCount, isLinear);
204 }
205 
hullIntersects(const SkDConic & conic,bool * isLinear) const206 bool SkDCubic::hullIntersects(const SkDConic& conic, bool* isLinear) const {
207 
208     return hullIntersects(conic.fPts, isLinear);
209 }
210 
isLinear(int startIndex,int endIndex) const211 bool SkDCubic::isLinear(int startIndex, int endIndex) const {
212     if (fPts[0].approximatelyDEqual(fPts[3]))  {
213         return ((const SkDQuad *) this)->isLinear(0, 2);
214     }
215     SkLineParameters lineParameters;
216     lineParameters.cubicEndPoints(*this, startIndex, endIndex);
217     // FIXME: maybe it's possible to avoid this and compare non-normalized
218     lineParameters.normalize();
219     double tiniest = SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(SkTMin(fPts[0].fX, fPts[0].fY),
220             fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY), fPts[3].fX), fPts[3].fY);
221     double largest = SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(SkTMax(fPts[0].fX, fPts[0].fY),
222             fPts[1].fX), fPts[1].fY), fPts[2].fX), fPts[2].fY), fPts[3].fX), fPts[3].fY);
223     largest = SkTMax(largest, -tiniest);
224     double distance = lineParameters.controlPtDistance(*this, 1);
225     if (!approximately_zero_when_compared_to(distance, largest)) {
226         return false;
227     }
228     distance = lineParameters.controlPtDistance(*this, 2);
229     return approximately_zero_when_compared_to(distance, largest);
230 }
231 
232 // from http://www.cs.sunysb.edu/~qin/courses/geometry/4.pdf
233 // c(t)  = a(1-t)^3 + 3bt(1-t)^2 + 3c(1-t)t^2 + dt^3
234 // c'(t) = -3a(1-t)^2 + 3b((1-t)^2 - 2t(1-t)) + 3c(2t(1-t) - t^2) + 3dt^2
235 //       = 3(b-a)(1-t)^2 + 6(c-b)t(1-t) + 3(d-c)t^2
derivative_at_t(const double * src,double t)236 static double derivative_at_t(const double* src, double t) {
237     double one_t = 1 - t;
238     double a = src[0];
239     double b = src[2];
240     double c = src[4];
241     double d = src[6];
242     return 3 * ((b - a) * one_t * one_t + 2 * (c - b) * t * one_t + (d - c) * t * t);
243 }
244 
ComplexBreak(const SkPoint pointsPtr[4],SkScalar * t)245 int SkDCubic::ComplexBreak(const SkPoint pointsPtr[4], SkScalar* t) {
246     SkDCubic cubic;
247     cubic.set(pointsPtr);
248     if (cubic.monotonicInX() && cubic.monotonicInY()) {
249         return 0;
250     }
251     double tt[2], ss[2];
252     SkCubicType cubicType = SkClassifyCubic(pointsPtr, tt, ss);
253     switch (cubicType) {
254         case SkCubicType::kLoop: {
255             const double &td = tt[0], &te = tt[1], &sd = ss[0], &se = ss[1];
256             if (roughly_between(0, td, sd) && roughly_between(0, te, se)) {
257                 SkASSERT(roughly_between(0, td/sd, 1) && roughly_between(0, te/se, 1));
258                 t[0] = static_cast<SkScalar>((td * se + te * sd) / (2 * sd * se));
259                 SkASSERT(roughly_between(0, *t, 1));
260                 return (int) (t[0] > 0 && t[0] < 1);
261             }
262         }
263         // fall through if no t value found
264         case SkCubicType::kSerpentine:
265         case SkCubicType::kLocalCusp:
266         case SkCubicType::kCuspAtInfinity: {
267             double inflectionTs[2];
268             int infTCount = cubic.findInflections(inflectionTs);
269             double maxCurvature[3];
270             int roots = cubic.findMaxCurvature(maxCurvature);
271     #if DEBUG_CUBIC_SPLIT
272             SkDebugf("%s\n", __FUNCTION__);
273             cubic.dump();
274             for (int index = 0; index < infTCount; ++index) {
275                 SkDebugf("inflectionsTs[%d]=%1.9g ", index, inflectionTs[index]);
276                 SkDPoint pt = cubic.ptAtT(inflectionTs[index]);
277                 SkDVector dPt = cubic.dxdyAtT(inflectionTs[index]);
278                 SkDLine perp = {{pt - dPt, pt + dPt}};
279                 perp.dump();
280             }
281             for (int index = 0; index < roots; ++index) {
282                 SkDebugf("maxCurvature[%d]=%1.9g ", index, maxCurvature[index]);
283                 SkDPoint pt = cubic.ptAtT(maxCurvature[index]);
284                 SkDVector dPt = cubic.dxdyAtT(maxCurvature[index]);
285                 SkDLine perp = {{pt - dPt, pt + dPt}};
286                 perp.dump();
287             }
288     #endif
289             if (infTCount == 2) {
290                 for (int index = 0; index < roots; ++index) {
291                     if (between(inflectionTs[0], maxCurvature[index], inflectionTs[1])) {
292                         t[0] = maxCurvature[index];
293                         return (int) (t[0] > 0 && t[0] < 1);
294                     }
295                 }
296             } else {
297                 int resultCount = 0;
298                 // FIXME: constant found through experimentation -- maybe there's a better way....
299                 double precision = cubic.calcPrecision() * 2;
300                 for (int index = 0; index < roots; ++index) {
301                     double testT = maxCurvature[index];
302                     if (0 >= testT || testT >= 1) {
303                         continue;
304                     }
305                     // don't call dxdyAtT since we want (0,0) results
306                     SkDVector dPt = { derivative_at_t(&cubic.fPts[0].fX, testT),
307                             derivative_at_t(&cubic.fPts[0].fY, testT) };
308                     double dPtLen = dPt.length();
309                     if (dPtLen < precision) {
310                         t[resultCount++] = testT;
311                     }
312                 }
313                 if (!resultCount && infTCount == 1) {
314                     t[0] = inflectionTs[0];
315                     resultCount = (int) (t[0] > 0 && t[0] < 1);
316                 }
317                 return resultCount;
318             }
319         }
320         default:
321             ;
322     }
323     return 0;
324 }
325 
monotonicInX() const326 bool SkDCubic::monotonicInX() const {
327     return precisely_between(fPts[0].fX, fPts[1].fX, fPts[3].fX)
328             && precisely_between(fPts[0].fX, fPts[2].fX, fPts[3].fX);
329 }
330 
monotonicInY() const331 bool SkDCubic::monotonicInY() const {
332     return precisely_between(fPts[0].fY, fPts[1].fY, fPts[3].fY)
333             && precisely_between(fPts[0].fY, fPts[2].fY, fPts[3].fY);
334 }
335 
otherPts(int index,const SkDPoint * o1Pts[kPointCount-1]) const336 void SkDCubic::otherPts(int index, const SkDPoint* o1Pts[kPointCount - 1]) const {
337     int offset = (int) !SkToBool(index);
338     o1Pts[0] = &fPts[offset];
339     o1Pts[1] = &fPts[++offset];
340     o1Pts[2] = &fPts[++offset];
341 }
342 
searchRoots(double extremeTs[6],int extrema,double axisIntercept,SearchAxis xAxis,double * validRoots) const343 int SkDCubic::searchRoots(double extremeTs[6], int extrema, double axisIntercept,
344         SearchAxis xAxis, double* validRoots) const {
345     extrema += findInflections(&extremeTs[extrema]);
346     extremeTs[extrema++] = 0;
347     extremeTs[extrema] = 1;
348     SkASSERT(extrema < 6);
349     SkTQSort(extremeTs, extremeTs + extrema);
350     int validCount = 0;
351     for (int index = 0; index < extrema; ) {
352         double min = extremeTs[index];
353         double max = extremeTs[++index];
354         if (min == max) {
355             continue;
356         }
357         double newT = binarySearch(min, max, axisIntercept, xAxis);
358         if (newT >= 0) {
359             if (validCount >= 3) {
360                 return 0;
361             }
362             validRoots[validCount++] = newT;
363         }
364     }
365     return validCount;
366 }
367 
368 // cubic roots
369 
370 static const double PI = 3.141592653589793;
371 
372 // from SkGeometry.cpp (and Numeric Solutions, 5.6)
RootsValidT(double A,double B,double C,double D,double t[3])373 int SkDCubic::RootsValidT(double A, double B, double C, double D, double t[3]) {
374     double s[3];
375     int realRoots = RootsReal(A, B, C, D, s);
376     int foundRoots = SkDQuad::AddValidTs(s, realRoots, t);
377     for (int index = 0; index < realRoots; ++index) {
378         double tValue = s[index];
379         if (!approximately_one_or_less(tValue) && between(1, tValue, 1.00005)) {
380             for (int idx2 = 0; idx2 < foundRoots; ++idx2) {
381                 if (approximately_equal(t[idx2], 1)) {
382                     goto nextRoot;
383                 }
384             }
385             SkASSERT(foundRoots < 3);
386             t[foundRoots++] = 1;
387         } else if (!approximately_zero_or_more(tValue) && between(-0.00005, tValue, 0)) {
388             for (int idx2 = 0; idx2 < foundRoots; ++idx2) {
389                 if (approximately_equal(t[idx2], 0)) {
390                     goto nextRoot;
391                 }
392             }
393             SkASSERT(foundRoots < 3);
394             t[foundRoots++] = 0;
395         }
396 nextRoot:
397         ;
398     }
399     return foundRoots;
400 }
401 
RootsReal(double A,double B,double C,double D,double s[3])402 int SkDCubic::RootsReal(double A, double B, double C, double D, double s[3]) {
403 #ifdef SK_DEBUG
404     // create a string mathematica understands
405     // GDB set print repe 15 # if repeated digits is a bother
406     //     set print elements 400 # if line doesn't fit
407     char str[1024];
408     sk_bzero(str, sizeof(str));
409     SK_SNPRINTF(str, sizeof(str), "Solve[%1.19g x^3 + %1.19g x^2 + %1.19g x + %1.19g == 0, x]",
410             A, B, C, D);
411     SkPathOpsDebug::MathematicaIze(str, sizeof(str));
412 #if ONE_OFF_DEBUG && ONE_OFF_DEBUG_MATHEMATICA
413     SkDebugf("%s\n", str);
414 #endif
415 #endif
416     if (approximately_zero(A)
417             && approximately_zero_when_compared_to(A, B)
418             && approximately_zero_when_compared_to(A, C)
419             && approximately_zero_when_compared_to(A, D)) {  // we're just a quadratic
420         return SkDQuad::RootsReal(B, C, D, s);
421     }
422     if (approximately_zero_when_compared_to(D, A)
423             && approximately_zero_when_compared_to(D, B)
424             && approximately_zero_when_compared_to(D, C)) {  // 0 is one root
425         int num = SkDQuad::RootsReal(A, B, C, s);
426         for (int i = 0; i < num; ++i) {
427             if (approximately_zero(s[i])) {
428                 return num;
429             }
430         }
431         s[num++] = 0;
432         return num;
433     }
434     if (approximately_zero(A + B + C + D)) {  // 1 is one root
435         int num = SkDQuad::RootsReal(A, A + B, -D, s);
436         for (int i = 0; i < num; ++i) {
437             if (AlmostDequalUlps(s[i], 1)) {
438                 return num;
439             }
440         }
441         s[num++] = 1;
442         return num;
443     }
444     double a, b, c;
445     {
446         double invA = 1 / A;
447         a = B * invA;
448         b = C * invA;
449         c = D * invA;
450     }
451     double a2 = a * a;
452     double Q = (a2 - b * 3) / 9;
453     double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54;
454     double R2 = R * R;
455     double Q3 = Q * Q * Q;
456     double R2MinusQ3 = R2 - Q3;
457     double adiv3 = a / 3;
458     double r;
459     double* roots = s;
460     if (R2MinusQ3 < 0) {   // we have 3 real roots
461         // the divide/root can, due to finite precisions, be slightly outside of -1...1
462         double theta = acos(SkTPin(R / sqrt(Q3), -1., 1.));
463         double neg2RootQ = -2 * sqrt(Q);
464 
465         r = neg2RootQ * cos(theta / 3) - adiv3;
466         *roots++ = r;
467 
468         r = neg2RootQ * cos((theta + 2 * PI) / 3) - adiv3;
469         if (!AlmostDequalUlps(s[0], r)) {
470             *roots++ = r;
471         }
472         r = neg2RootQ * cos((theta - 2 * PI) / 3) - adiv3;
473         if (!AlmostDequalUlps(s[0], r) && (roots - s == 1 || !AlmostDequalUlps(s[1], r))) {
474             *roots++ = r;
475         }
476     } else {  // we have 1 real root
477         double sqrtR2MinusQ3 = sqrt(R2MinusQ3);
478         double A = fabs(R) + sqrtR2MinusQ3;
479         A = SkDCubeRoot(A);
480         if (R > 0) {
481             A = -A;
482         }
483         if (A != 0) {
484             A += Q / A;
485         }
486         r = A - adiv3;
487         *roots++ = r;
488         if (AlmostDequalUlps((double) R2, (double) Q3)) {
489             r = -A / 2 - adiv3;
490             if (!AlmostDequalUlps(s[0], r)) {
491                 *roots++ = r;
492             }
493         }
494     }
495     return static_cast<int>(roots - s);
496 }
497 
498 // OPTIMIZE? compute t^2, t(1-t), and (1-t)^2 and pass them to another version of derivative at t?
dxdyAtT(double t) const499 SkDVector SkDCubic::dxdyAtT(double t) const {
500     SkDVector result = { derivative_at_t(&fPts[0].fX, t), derivative_at_t(&fPts[0].fY, t) };
501     if (result.fX == 0 && result.fY == 0) {
502         if (t == 0) {
503             result = fPts[2] - fPts[0];
504         } else if (t == 1) {
505             result = fPts[3] - fPts[1];
506         } else {
507             // incomplete
508             SkDebugf("!c");
509         }
510         if (result.fX == 0 && result.fY == 0 && zero_or_one(t)) {
511             result = fPts[3] - fPts[0];
512         }
513     }
514     return result;
515 }
516 
517 // OPTIMIZE? share code with formulate_F1DotF2
findInflections(double tValues[]) const518 int SkDCubic::findInflections(double tValues[]) const {
519     double Ax = fPts[1].fX - fPts[0].fX;
520     double Ay = fPts[1].fY - fPts[0].fY;
521     double Bx = fPts[2].fX - 2 * fPts[1].fX + fPts[0].fX;
522     double By = fPts[2].fY - 2 * fPts[1].fY + fPts[0].fY;
523     double Cx = fPts[3].fX + 3 * (fPts[1].fX - fPts[2].fX) - fPts[0].fX;
524     double Cy = fPts[3].fY + 3 * (fPts[1].fY - fPts[2].fY) - fPts[0].fY;
525     return SkDQuad::RootsValidT(Bx * Cy - By * Cx, Ax * Cy - Ay * Cx, Ax * By - Ay * Bx, tValues);
526 }
527 
formulate_F1DotF2(const double src[],double coeff[4])528 static void formulate_F1DotF2(const double src[], double coeff[4]) {
529     double a = src[2] - src[0];
530     double b = src[4] - 2 * src[2] + src[0];
531     double c = src[6] + 3 * (src[2] - src[4]) - src[0];
532     coeff[0] = c * c;
533     coeff[1] = 3 * b * c;
534     coeff[2] = 2 * b * b + c * a;
535     coeff[3] = a * b;
536 }
537 
538 /** SkDCubic'(t) = At^2 + Bt + C, where
539     A = 3(-a + 3(b - c) + d)
540     B = 6(a - 2b + c)
541     C = 3(b - a)
542     Solve for t, keeping only those that fit between 0 < t < 1
543 */
FindExtrema(const double src[],double tValues[2])544 int SkDCubic::FindExtrema(const double src[], double tValues[2]) {
545     // we divide A,B,C by 3 to simplify
546     double a = src[0];
547     double b = src[2];
548     double c = src[4];
549     double d = src[6];
550     double A = d - a + 3 * (b - c);
551     double B = 2 * (a - b - b + c);
552     double C = b - a;
553 
554     return SkDQuad::RootsValidT(A, B, C, tValues);
555 }
556 
557 /*  from SkGeometry.cpp
558     Looking for F' dot F'' == 0
559 
560     A = b - a
561     B = c - 2b + a
562     C = d - 3c + 3b - a
563 
564     F' = 3Ct^2 + 6Bt + 3A
565     F'' = 6Ct + 6B
566 
567     F' dot F'' -> CCt^3 + 3BCt^2 + (2BB + CA)t + AB
568 */
findMaxCurvature(double tValues[]) const569 int SkDCubic::findMaxCurvature(double tValues[]) const {
570     double coeffX[4], coeffY[4];
571     int i;
572     formulate_F1DotF2(&fPts[0].fX, coeffX);
573     formulate_F1DotF2(&fPts[0].fY, coeffY);
574     for (i = 0; i < 4; i++) {
575         coeffX[i] = coeffX[i] + coeffY[i];
576     }
577     return RootsValidT(coeffX[0], coeffX[1], coeffX[2], coeffX[3], tValues);
578 }
579 
ptAtT(double t) const580 SkDPoint SkDCubic::ptAtT(double t) const {
581     if (0 == t) {
582         return fPts[0];
583     }
584     if (1 == t) {
585         return fPts[3];
586     }
587     double one_t = 1 - t;
588     double one_t2 = one_t * one_t;
589     double a = one_t2 * one_t;
590     double b = 3 * one_t2 * t;
591     double t2 = t * t;
592     double c = 3 * one_t * t2;
593     double d = t2 * t;
594     SkDPoint result = {a * fPts[0].fX + b * fPts[1].fX + c * fPts[2].fX + d * fPts[3].fX,
595             a * fPts[0].fY + b * fPts[1].fY + c * fPts[2].fY + d * fPts[3].fY};
596     return result;
597 }
598 
599 /*
600  Given a cubic c, t1, and t2, find a small cubic segment.
601 
602  The new cubic is defined as points A, B, C, and D, where
603  s1 = 1 - t1
604  s2 = 1 - t2
605  A = c[0]*s1*s1*s1 + 3*c[1]*s1*s1*t1 + 3*c[2]*s1*t1*t1 + c[3]*t1*t1*t1
606  D = c[0]*s2*s2*s2 + 3*c[1]*s2*s2*t2 + 3*c[2]*s2*t2*t2 + c[3]*t2*t2*t2
607 
608  We don't have B or C. So We define two equations to isolate them.
609  First, compute two reference T values 1/3 and 2/3 from t1 to t2:
610 
611  c(at (2*t1 + t2)/3) == E
612  c(at (t1 + 2*t2)/3) == F
613 
614  Next, compute where those values must be if we know the values of B and C:
615 
616  _12   =  A*2/3 + B*1/3
617  12_   =  A*1/3 + B*2/3
618  _23   =  B*2/3 + C*1/3
619  23_   =  B*1/3 + C*2/3
620  _34   =  C*2/3 + D*1/3
621  34_   =  C*1/3 + D*2/3
622  _123  = (A*2/3 + B*1/3)*2/3 + (B*2/3 + C*1/3)*1/3 = A*4/9 + B*4/9 + C*1/9
623  123_  = (A*1/3 + B*2/3)*1/3 + (B*1/3 + C*2/3)*2/3 = A*1/9 + B*4/9 + C*4/9
624  _234  = (B*2/3 + C*1/3)*2/3 + (C*2/3 + D*1/3)*1/3 = B*4/9 + C*4/9 + D*1/9
625  234_  = (B*1/3 + C*2/3)*1/3 + (C*1/3 + D*2/3)*2/3 = B*1/9 + C*4/9 + D*4/9
626  _1234 = (A*4/9 + B*4/9 + C*1/9)*2/3 + (B*4/9 + C*4/9 + D*1/9)*1/3
627        =  A*8/27 + B*12/27 + C*6/27 + D*1/27
628        =  E
629  1234_ = (A*1/9 + B*4/9 + C*4/9)*1/3 + (B*1/9 + C*4/9 + D*4/9)*2/3
630        =  A*1/27 + B*6/27 + C*12/27 + D*8/27
631        =  F
632  E*27  =  A*8    + B*12   + C*6     + D
633  F*27  =  A      + B*6    + C*12    + D*8
634 
635 Group the known values on one side:
636 
637  M       = E*27 - A*8 - D     = B*12 + C* 6
638  N       = F*27 - A   - D*8   = B* 6 + C*12
639  M*2 - N = B*18
640  N*2 - M = C*18
641  B       = (M*2 - N)/18
642  C       = (N*2 - M)/18
643  */
644 
interp_cubic_coords(const double * src,double t)645 static double interp_cubic_coords(const double* src, double t) {
646     double ab = SkDInterp(src[0], src[2], t);
647     double bc = SkDInterp(src[2], src[4], t);
648     double cd = SkDInterp(src[4], src[6], t);
649     double abc = SkDInterp(ab, bc, t);
650     double bcd = SkDInterp(bc, cd, t);
651     double abcd = SkDInterp(abc, bcd, t);
652     return abcd;
653 }
654 
subDivide(double t1,double t2) const655 SkDCubic SkDCubic::subDivide(double t1, double t2) const {
656     if (t1 == 0 || t2 == 1) {
657         if (t1 == 0 && t2 == 1) {
658             return *this;
659         }
660         SkDCubicPair pair = chopAt(t1 == 0 ? t2 : t1);
661         SkDCubic dst = t1 == 0 ? pair.first() : pair.second();
662         return dst;
663     }
664     SkDCubic dst;
665     double ax = dst[0].fX = interp_cubic_coords(&fPts[0].fX, t1);
666     double ay = dst[0].fY = interp_cubic_coords(&fPts[0].fY, t1);
667     double ex = interp_cubic_coords(&fPts[0].fX, (t1*2+t2)/3);
668     double ey = interp_cubic_coords(&fPts[0].fY, (t1*2+t2)/3);
669     double fx = interp_cubic_coords(&fPts[0].fX, (t1+t2*2)/3);
670     double fy = interp_cubic_coords(&fPts[0].fY, (t1+t2*2)/3);
671     double dx = dst[3].fX = interp_cubic_coords(&fPts[0].fX, t2);
672     double dy = dst[3].fY = interp_cubic_coords(&fPts[0].fY, t2);
673     double mx = ex * 27 - ax * 8 - dx;
674     double my = ey * 27 - ay * 8 - dy;
675     double nx = fx * 27 - ax - dx * 8;
676     double ny = fy * 27 - ay - dy * 8;
677     /* bx = */ dst[1].fX = (mx * 2 - nx) / 18;
678     /* by = */ dst[1].fY = (my * 2 - ny) / 18;
679     /* cx = */ dst[2].fX = (nx * 2 - mx) / 18;
680     /* cy = */ dst[2].fY = (ny * 2 - my) / 18;
681     // FIXME: call align() ?
682     return dst;
683 }
684 
subDivide(const SkDPoint & a,const SkDPoint & d,double t1,double t2,SkDPoint dst[2]) const685 void SkDCubic::subDivide(const SkDPoint& a, const SkDPoint& d,
686                          double t1, double t2, SkDPoint dst[2]) const {
687     SkASSERT(t1 != t2);
688     // this approach assumes that the control points computed directly are accurate enough
689     SkDCubic sub = subDivide(t1, t2);
690     dst[0] = sub[1] + (a - sub[0]);
691     dst[1] = sub[2] + (d - sub[3]);
692     if (t1 == 0 || t2 == 0) {
693         align(0, 1, t1 == 0 ? &dst[0] : &dst[1]);
694     }
695     if (t1 == 1 || t2 == 1) {
696         align(3, 2, t1 == 1 ? &dst[0] : &dst[1]);
697     }
698     if (AlmostBequalUlps(dst[0].fX, a.fX)) {
699         dst[0].fX = a.fX;
700     }
701     if (AlmostBequalUlps(dst[0].fY, a.fY)) {
702         dst[0].fY = a.fY;
703     }
704     if (AlmostBequalUlps(dst[1].fX, d.fX)) {
705         dst[1].fX = d.fX;
706     }
707     if (AlmostBequalUlps(dst[1].fY, d.fY)) {
708         dst[1].fY = d.fY;
709     }
710 }
711 
toFloatPoints(SkPoint * pts) const712 bool SkDCubic::toFloatPoints(SkPoint* pts) const {
713     const double* dCubic = &fPts[0].fX;
714     SkScalar* cubic = &pts[0].fX;
715     for (int index = 0; index < kPointCount * 2; ++index) {
716         *cubic++ = SkDoubleToScalar(*dCubic++);
717     }
718     return SkScalarsAreFinite(&pts->fX, kPointCount * 2);
719 }
720 
top(const SkDCubic & dCurve,double startT,double endT,SkDPoint * topPt) const721 double SkDCubic::top(const SkDCubic& dCurve, double startT, double endT, SkDPoint*topPt) const {
722     double extremeTs[2];
723     double topT = -1;
724     int roots = SkDCubic::FindExtrema(&fPts[0].fY, extremeTs);
725     for (int index = 0; index < roots; ++index) {
726         double t = startT + (endT - startT) * extremeTs[index];
727         SkDPoint mid = dCurve.ptAtT(t);
728         if (topPt->fY > mid.fY || (topPt->fY == mid.fY && topPt->fX > mid.fX)) {
729             topT = t;
730             *topPt = mid;
731         }
732     }
733     return topT;
734 }
735