• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2011 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 
8 #include "GrPathUtils.h"
9 
10 #include "GrTypes.h"
11 #include "SkMathPriv.h"
12 
13 static const SkScalar gMinCurveTol = 0.0001f;
14 
scaleToleranceToSrc(SkScalar devTol,const SkMatrix & viewM,const SkRect & pathBounds)15 SkScalar GrPathUtils::scaleToleranceToSrc(SkScalar devTol,
16                                           const SkMatrix& viewM,
17                                           const SkRect& pathBounds) {
18     // In order to tesselate the path we get a bound on how much the matrix can
19     // scale when mapping to screen coordinates.
20     SkScalar stretch = viewM.getMaxScale();
21 
22     if (stretch < 0) {
23         // take worst case mapRadius amoung four corners.
24         // (less than perfect)
25         for (int i = 0; i < 4; ++i) {
26             SkMatrix mat;
27             mat.setTranslate((i % 2) ? pathBounds.fLeft : pathBounds.fRight,
28                              (i < 2) ? pathBounds.fTop : pathBounds.fBottom);
29             mat.postConcat(viewM);
30             stretch = SkMaxScalar(stretch, mat.mapRadius(SK_Scalar1));
31         }
32     }
33     SkScalar srcTol = devTol / stretch;
34     if (srcTol < gMinCurveTol) {
35         srcTol = gMinCurveTol;
36     }
37     return srcTol;
38 }
39 
quadraticPointCount(const SkPoint points[],SkScalar tol)40 uint32_t GrPathUtils::quadraticPointCount(const SkPoint points[], SkScalar tol) {
41     // You should have called scaleToleranceToSrc, which guarantees this
42     SkASSERT(tol >= gMinCurveTol);
43 
44     SkScalar d = points[1].distanceToLineSegmentBetween(points[0], points[2]);
45     if (!SkScalarIsFinite(d)) {
46         return kMaxPointsPerCurve;
47     } else if (d <= tol) {
48         return 1;
49     } else {
50         // Each time we subdivide, d should be cut in 4. So we need to
51         // subdivide x = log4(d/tol) times. x subdivisions creates 2^(x)
52         // points.
53         // 2^(log4(x)) = sqrt(x);
54         SkScalar divSqrt = SkScalarSqrt(d / tol);
55         if (((SkScalar)SK_MaxS32) <= divSqrt) {
56             return kMaxPointsPerCurve;
57         } else {
58             int temp = SkScalarCeilToInt(divSqrt);
59             int pow2 = GrNextPow2(temp);
60             // Because of NaNs & INFs we can wind up with a degenerate temp
61             // such that pow2 comes out negative. Also, our point generator
62             // will always output at least one pt.
63             if (pow2 < 1) {
64                 pow2 = 1;
65             }
66             return SkTMin(pow2, kMaxPointsPerCurve);
67         }
68     }
69 }
70 
generateQuadraticPoints(const SkPoint & p0,const SkPoint & p1,const SkPoint & p2,SkScalar tolSqd,SkPoint ** points,uint32_t pointsLeft)71 uint32_t GrPathUtils::generateQuadraticPoints(const SkPoint& p0,
72                                               const SkPoint& p1,
73                                               const SkPoint& p2,
74                                               SkScalar tolSqd,
75                                               SkPoint** points,
76                                               uint32_t pointsLeft) {
77     if (pointsLeft < 2 ||
78         (p1.distanceToLineSegmentBetweenSqd(p0, p2)) < tolSqd) {
79         (*points)[0] = p2;
80         *points += 1;
81         return 1;
82     }
83 
84     SkPoint q[] = {
85         { SkScalarAve(p0.fX, p1.fX), SkScalarAve(p0.fY, p1.fY) },
86         { SkScalarAve(p1.fX, p2.fX), SkScalarAve(p1.fY, p2.fY) },
87     };
88     SkPoint r = { SkScalarAve(q[0].fX, q[1].fX), SkScalarAve(q[0].fY, q[1].fY) };
89 
90     pointsLeft >>= 1;
91     uint32_t a = generateQuadraticPoints(p0, q[0], r, tolSqd, points, pointsLeft);
92     uint32_t b = generateQuadraticPoints(r, q[1], p2, tolSqd, points, pointsLeft);
93     return a + b;
94 }
95 
cubicPointCount(const SkPoint points[],SkScalar tol)96 uint32_t GrPathUtils::cubicPointCount(const SkPoint points[],
97                                            SkScalar tol) {
98     // You should have called scaleToleranceToSrc, which guarantees this
99     SkASSERT(tol >= gMinCurveTol);
100 
101     SkScalar d = SkTMax(
102         points[1].distanceToLineSegmentBetweenSqd(points[0], points[3]),
103         points[2].distanceToLineSegmentBetweenSqd(points[0], points[3]));
104     d = SkScalarSqrt(d);
105     if (!SkScalarIsFinite(d)) {
106         return kMaxPointsPerCurve;
107     } else if (d <= tol) {
108         return 1;
109     } else {
110         SkScalar divSqrt = SkScalarSqrt(d / tol);
111         if (((SkScalar)SK_MaxS32) <= divSqrt) {
112             return kMaxPointsPerCurve;
113         } else {
114             int temp = SkScalarCeilToInt(SkScalarSqrt(d / tol));
115             int pow2 = GrNextPow2(temp);
116             // Because of NaNs & INFs we can wind up with a degenerate temp
117             // such that pow2 comes out negative. Also, our point generator
118             // will always output at least one pt.
119             if (pow2 < 1) {
120                 pow2 = 1;
121             }
122             return SkTMin(pow2, kMaxPointsPerCurve);
123         }
124     }
125 }
126 
generateCubicPoints(const SkPoint & p0,const SkPoint & p1,const SkPoint & p2,const SkPoint & p3,SkScalar tolSqd,SkPoint ** points,uint32_t pointsLeft)127 uint32_t GrPathUtils::generateCubicPoints(const SkPoint& p0,
128                                           const SkPoint& p1,
129                                           const SkPoint& p2,
130                                           const SkPoint& p3,
131                                           SkScalar tolSqd,
132                                           SkPoint** points,
133                                           uint32_t pointsLeft) {
134     if (pointsLeft < 2 ||
135         (p1.distanceToLineSegmentBetweenSqd(p0, p3) < tolSqd &&
136          p2.distanceToLineSegmentBetweenSqd(p0, p3) < tolSqd)) {
137         (*points)[0] = p3;
138         *points += 1;
139         return 1;
140     }
141     SkPoint q[] = {
142         { SkScalarAve(p0.fX, p1.fX), SkScalarAve(p0.fY, p1.fY) },
143         { SkScalarAve(p1.fX, p2.fX), SkScalarAve(p1.fY, p2.fY) },
144         { SkScalarAve(p2.fX, p3.fX), SkScalarAve(p2.fY, p3.fY) }
145     };
146     SkPoint r[] = {
147         { SkScalarAve(q[0].fX, q[1].fX), SkScalarAve(q[0].fY, q[1].fY) },
148         { SkScalarAve(q[1].fX, q[2].fX), SkScalarAve(q[1].fY, q[2].fY) }
149     };
150     SkPoint s = { SkScalarAve(r[0].fX, r[1].fX), SkScalarAve(r[0].fY, r[1].fY) };
151     pointsLeft >>= 1;
152     uint32_t a = generateCubicPoints(p0, q[0], r[0], s, tolSqd, points, pointsLeft);
153     uint32_t b = generateCubicPoints(s, r[1], q[2], p3, tolSqd, points, pointsLeft);
154     return a + b;
155 }
156 
worstCasePointCount(const SkPath & path,int * subpaths,SkScalar tol)157 int GrPathUtils::worstCasePointCount(const SkPath& path, int* subpaths, SkScalar tol) {
158     // You should have called scaleToleranceToSrc, which guarantees this
159     SkASSERT(tol >= gMinCurveTol);
160 
161     int pointCount = 0;
162     *subpaths = 1;
163 
164     bool first = true;
165 
166     SkPath::Iter iter(path, false);
167     SkPath::Verb verb;
168 
169     SkPoint pts[4];
170     while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
171 
172         switch (verb) {
173             case SkPath::kLine_Verb:
174                 pointCount += 1;
175                 break;
176             case SkPath::kConic_Verb: {
177                 SkScalar weight = iter.conicWeight();
178                 SkAutoConicToQuads converter;
179                 const SkPoint* quadPts = converter.computeQuads(pts, weight, tol);
180                 for (int i = 0; i < converter.countQuads(); ++i) {
181                     pointCount += quadraticPointCount(quadPts + 2*i, tol);
182                 }
183             }
184             case SkPath::kQuad_Verb:
185                 pointCount += quadraticPointCount(pts, tol);
186                 break;
187             case SkPath::kCubic_Verb:
188                 pointCount += cubicPointCount(pts, tol);
189                 break;
190             case SkPath::kMove_Verb:
191                 pointCount += 1;
192                 if (!first) {
193                     ++(*subpaths);
194                 }
195                 break;
196             default:
197                 break;
198         }
199         first = false;
200     }
201     return pointCount;
202 }
203 
set(const SkPoint qPts[3])204 void GrPathUtils::QuadUVMatrix::set(const SkPoint qPts[3]) {
205     SkMatrix m;
206     // We want M such that M * xy_pt = uv_pt
207     // We know M * control_pts = [0  1/2 1]
208     //                           [0  0   1]
209     //                           [1  1   1]
210     // And control_pts = [x0 x1 x2]
211     //                   [y0 y1 y2]
212     //                   [1  1  1 ]
213     // We invert the control pt matrix and post concat to both sides to get M.
214     // Using the known form of the control point matrix and the result, we can
215     // optimize and improve precision.
216 
217     double x0 = qPts[0].fX;
218     double y0 = qPts[0].fY;
219     double x1 = qPts[1].fX;
220     double y1 = qPts[1].fY;
221     double x2 = qPts[2].fX;
222     double y2 = qPts[2].fY;
223     double det = x0*y1 - y0*x1 + x2*y0 - y2*x0 + x1*y2 - y1*x2;
224 
225     if (!sk_float_isfinite(det)
226         || SkScalarNearlyZero((float)det, SK_ScalarNearlyZero * SK_ScalarNearlyZero)) {
227         // The quad is degenerate. Hopefully this is rare. Find the pts that are
228         // farthest apart to compute a line (unless it is really a pt).
229         SkScalar maxD = qPts[0].distanceToSqd(qPts[1]);
230         int maxEdge = 0;
231         SkScalar d = qPts[1].distanceToSqd(qPts[2]);
232         if (d > maxD) {
233             maxD = d;
234             maxEdge = 1;
235         }
236         d = qPts[2].distanceToSqd(qPts[0]);
237         if (d > maxD) {
238             maxD = d;
239             maxEdge = 2;
240         }
241         // We could have a tolerance here, not sure if it would improve anything
242         if (maxD > 0) {
243             // Set the matrix to give (u = 0, v = distance_to_line)
244             SkVector lineVec = qPts[(maxEdge + 1)%3] - qPts[maxEdge];
245             // when looking from the point 0 down the line we want positive
246             // distances to be to the left. This matches the non-degenerate
247             // case.
248             lineVec.setOrthog(lineVec, SkPoint::kLeft_Side);
249             // first row
250             fM[0] = 0;
251             fM[1] = 0;
252             fM[2] = 0;
253             // second row
254             fM[3] = lineVec.fX;
255             fM[4] = lineVec.fY;
256             fM[5] = -lineVec.dot(qPts[maxEdge]);
257         } else {
258             // It's a point. It should cover zero area. Just set the matrix such
259             // that (u, v) will always be far away from the quad.
260             fM[0] = 0; fM[1] = 0; fM[2] = 100.f;
261             fM[3] = 0; fM[4] = 0; fM[5] = 100.f;
262         }
263     } else {
264         double scale = 1.0/det;
265 
266         // compute adjugate matrix
267         double a2, a3, a4, a5, a6, a7, a8;
268         a2 = x1*y2-x2*y1;
269 
270         a3 = y2-y0;
271         a4 = x0-x2;
272         a5 = x2*y0-x0*y2;
273 
274         a6 = y0-y1;
275         a7 = x1-x0;
276         a8 = x0*y1-x1*y0;
277 
278         // this performs the uv_pts*adjugate(control_pts) multiply,
279         // then does the scale by 1/det afterwards to improve precision
280         m[SkMatrix::kMScaleX] = (float)((0.5*a3 + a6)*scale);
281         m[SkMatrix::kMSkewX]  = (float)((0.5*a4 + a7)*scale);
282         m[SkMatrix::kMTransX] = (float)((0.5*a5 + a8)*scale);
283 
284         m[SkMatrix::kMSkewY]  = (float)(a6*scale);
285         m[SkMatrix::kMScaleY] = (float)(a7*scale);
286         m[SkMatrix::kMTransY] = (float)(a8*scale);
287 
288         // kMPersp0 & kMPersp1 should algebraically be zero
289         m[SkMatrix::kMPersp0] = 0.0f;
290         m[SkMatrix::kMPersp1] = 0.0f;
291         m[SkMatrix::kMPersp2] = (float)((a2 + a5 + a8)*scale);
292 
293         // It may not be normalized to have 1.0 in the bottom right
294         float m33 = m.get(SkMatrix::kMPersp2);
295         if (1.f != m33) {
296             m33 = 1.f / m33;
297             fM[0] = m33 * m.get(SkMatrix::kMScaleX);
298             fM[1] = m33 * m.get(SkMatrix::kMSkewX);
299             fM[2] = m33 * m.get(SkMatrix::kMTransX);
300             fM[3] = m33 * m.get(SkMatrix::kMSkewY);
301             fM[4] = m33 * m.get(SkMatrix::kMScaleY);
302             fM[5] = m33 * m.get(SkMatrix::kMTransY);
303         } else {
304             fM[0] = m.get(SkMatrix::kMScaleX);
305             fM[1] = m.get(SkMatrix::kMSkewX);
306             fM[2] = m.get(SkMatrix::kMTransX);
307             fM[3] = m.get(SkMatrix::kMSkewY);
308             fM[4] = m.get(SkMatrix::kMScaleY);
309             fM[5] = m.get(SkMatrix::kMTransY);
310         }
311     }
312 }
313 
314 ////////////////////////////////////////////////////////////////////////////////
315 
316 // k = (y2 - y0, x0 - x2, x2*y0 - x0*y2)
317 // l = (y1 - y0, x0 - x1, x1*y0 - x0*y1) * 2*w
318 // m = (y2 - y1, x1 - x2, x2*y1 - x1*y2) * 2*w
getConicKLM(const SkPoint p[3],const SkScalar weight,SkMatrix * out)319 void GrPathUtils::getConicKLM(const SkPoint p[3], const SkScalar weight, SkMatrix* out) {
320     SkMatrix& klm = *out;
321     const SkScalar w2 = 2.f * weight;
322     klm[0] = p[2].fY - p[0].fY;
323     klm[1] = p[0].fX - p[2].fX;
324     klm[2] = p[2].fX * p[0].fY - p[0].fX * p[2].fY;
325 
326     klm[3] = w2 * (p[1].fY - p[0].fY);
327     klm[4] = w2 * (p[0].fX - p[1].fX);
328     klm[5] = w2 * (p[1].fX * p[0].fY - p[0].fX * p[1].fY);
329 
330     klm[6] = w2 * (p[2].fY - p[1].fY);
331     klm[7] = w2 * (p[1].fX - p[2].fX);
332     klm[8] = w2 * (p[2].fX * p[1].fY - p[1].fX * p[2].fY);
333 
334     // scale the max absolute value of coeffs to 10
335     SkScalar scale = 0.f;
336     for (int i = 0; i < 9; ++i) {
337        scale = SkMaxScalar(scale, SkScalarAbs(klm[i]));
338     }
339     SkASSERT(scale > 0.f);
340     scale = 10.f / scale;
341     for (int i = 0; i < 9; ++i) {
342         klm[i] *= scale;
343     }
344 }
345 
346 ////////////////////////////////////////////////////////////////////////////////
347 
348 namespace {
349 
350 // a is the first control point of the cubic.
351 // ab is the vector from a to the second control point.
352 // dc is the vector from the fourth to the third control point.
353 // d is the fourth control point.
354 // p is the candidate quadratic control point.
355 // this assumes that the cubic doesn't inflect and is simple
is_point_within_cubic_tangents(const SkPoint & a,const SkVector & ab,const SkVector & dc,const SkPoint & d,SkPathPriv::FirstDirection dir,const SkPoint p)356 bool is_point_within_cubic_tangents(const SkPoint& a,
357                                     const SkVector& ab,
358                                     const SkVector& dc,
359                                     const SkPoint& d,
360                                     SkPathPriv::FirstDirection dir,
361                                     const SkPoint p) {
362     SkVector ap = p - a;
363     SkScalar apXab = ap.cross(ab);
364     if (SkPathPriv::kCW_FirstDirection == dir) {
365         if (apXab > 0) {
366             return false;
367         }
368     } else {
369         SkASSERT(SkPathPriv::kCCW_FirstDirection == dir);
370         if (apXab < 0) {
371             return false;
372         }
373     }
374 
375     SkVector dp = p - d;
376     SkScalar dpXdc = dp.cross(dc);
377     if (SkPathPriv::kCW_FirstDirection == dir) {
378         if (dpXdc < 0) {
379             return false;
380         }
381     } else {
382         SkASSERT(SkPathPriv::kCCW_FirstDirection == dir);
383         if (dpXdc > 0) {
384             return false;
385         }
386     }
387     return true;
388 }
389 
convert_noninflect_cubic_to_quads(const SkPoint p[4],SkScalar toleranceSqd,bool constrainWithinTangents,SkPathPriv::FirstDirection dir,SkTArray<SkPoint,true> * quads,int sublevel=0)390 void convert_noninflect_cubic_to_quads(const SkPoint p[4],
391                                        SkScalar toleranceSqd,
392                                        bool constrainWithinTangents,
393                                        SkPathPriv::FirstDirection dir,
394                                        SkTArray<SkPoint, true>* quads,
395                                        int sublevel = 0) {
396 
397     // Notation: Point a is always p[0]. Point b is p[1] unless p[1] == p[0], in which case it is
398     // p[2]. Point d is always p[3]. Point c is p[2] unless p[2] == p[3], in which case it is p[1].
399 
400     SkVector ab = p[1] - p[0];
401     SkVector dc = p[2] - p[3];
402 
403     if (ab.lengthSqd() < SK_ScalarNearlyZero) {
404         if (dc.lengthSqd() < SK_ScalarNearlyZero) {
405             SkPoint* degQuad = quads->push_back_n(3);
406             degQuad[0] = p[0];
407             degQuad[1] = p[0];
408             degQuad[2] = p[3];
409             return;
410         }
411         ab = p[2] - p[0];
412     }
413     if (dc.lengthSqd() < SK_ScalarNearlyZero) {
414         dc = p[1] - p[3];
415     }
416 
417     // When the ab and cd tangents are degenerate or nearly parallel with vector from d to a the
418     // constraint that the quad point falls between the tangents becomes hard to enforce and we are
419     // likely to hit the max subdivision count. However, in this case the cubic is approaching a
420     // line and the accuracy of the quad point isn't so important. We check if the two middle cubic
421     // control points are very close to the baseline vector. If so then we just pick quadratic
422     // points on the control polygon.
423 
424     if (constrainWithinTangents) {
425         SkVector da = p[0] - p[3];
426         bool doQuads = dc.lengthSqd() < SK_ScalarNearlyZero ||
427                        ab.lengthSqd() < SK_ScalarNearlyZero;
428         if (!doQuads) {
429             SkScalar invDALengthSqd = da.lengthSqd();
430             if (invDALengthSqd > SK_ScalarNearlyZero) {
431                 invDALengthSqd = SkScalarInvert(invDALengthSqd);
432                 // cross(ab, da)^2/length(da)^2 == sqd distance from b to line from d to a.
433                 // same goes for point c using vector cd.
434                 SkScalar detABSqd = ab.cross(da);
435                 detABSqd = SkScalarSquare(detABSqd);
436                 SkScalar detDCSqd = dc.cross(da);
437                 detDCSqd = SkScalarSquare(detDCSqd);
438                 if (detABSqd * invDALengthSqd < toleranceSqd &&
439                     detDCSqd * invDALengthSqd < toleranceSqd)
440                 {
441                     doQuads = true;
442                 }
443             }
444         }
445         if (doQuads) {
446             SkPoint b = p[0] + ab;
447             SkPoint c = p[3] + dc;
448             SkPoint mid = b + c;
449             mid.scale(SK_ScalarHalf);
450             // Insert two quadratics to cover the case when ab points away from d and/or dc
451             // points away from a.
452             if (SkVector::DotProduct(da, dc) < 0 || SkVector::DotProduct(ab,da) > 0) {
453                 SkPoint* qpts = quads->push_back_n(6);
454                 qpts[0] = p[0];
455                 qpts[1] = b;
456                 qpts[2] = mid;
457                 qpts[3] = mid;
458                 qpts[4] = c;
459                 qpts[5] = p[3];
460             } else {
461                 SkPoint* qpts = quads->push_back_n(3);
462                 qpts[0] = p[0];
463                 qpts[1] = mid;
464                 qpts[2] = p[3];
465             }
466             return;
467         }
468     }
469 
470     static const SkScalar kLengthScale = 3 * SK_Scalar1 / 2;
471     static const int kMaxSubdivs = 10;
472 
473     ab.scale(kLengthScale);
474     dc.scale(kLengthScale);
475 
476     // e0 and e1 are extrapolations along vectors ab and dc.
477     SkVector c0 = p[0];
478     c0 += ab;
479     SkVector c1 = p[3];
480     c1 += dc;
481 
482     SkScalar dSqd = sublevel > kMaxSubdivs ? 0 : c0.distanceToSqd(c1);
483     if (dSqd < toleranceSqd) {
484         SkPoint cAvg = c0;
485         cAvg += c1;
486         cAvg.scale(SK_ScalarHalf);
487 
488         bool subdivide = false;
489 
490         if (constrainWithinTangents &&
491             !is_point_within_cubic_tangents(p[0], ab, dc, p[3], dir, cAvg)) {
492             // choose a new cAvg that is the intersection of the two tangent lines.
493             ab.setOrthog(ab);
494             SkScalar z0 = -ab.dot(p[0]);
495             dc.setOrthog(dc);
496             SkScalar z1 = -dc.dot(p[3]);
497             cAvg.fX = ab.fY * z1 - z0 * dc.fY;
498             cAvg.fY = z0 * dc.fX - ab.fX * z1;
499             SkScalar z = ab.fX * dc.fY - ab.fY * dc.fX;
500             z = SkScalarInvert(z);
501             cAvg.fX *= z;
502             cAvg.fY *= z;
503             if (sublevel <= kMaxSubdivs) {
504                 SkScalar d0Sqd = c0.distanceToSqd(cAvg);
505                 SkScalar d1Sqd = c1.distanceToSqd(cAvg);
506                 // We need to subdivide if d0 + d1 > tolerance but we have the sqd values. We know
507                 // the distances and tolerance can't be negative.
508                 // (d0 + d1)^2 > toleranceSqd
509                 // d0Sqd + 2*d0*d1 + d1Sqd > toleranceSqd
510                 SkScalar d0d1 = SkScalarSqrt(d0Sqd * d1Sqd);
511                 subdivide = 2 * d0d1 + d0Sqd + d1Sqd > toleranceSqd;
512             }
513         }
514         if (!subdivide) {
515             SkPoint* pts = quads->push_back_n(3);
516             pts[0] = p[0];
517             pts[1] = cAvg;
518             pts[2] = p[3];
519             return;
520         }
521     }
522     SkPoint choppedPts[7];
523     SkChopCubicAtHalf(p, choppedPts);
524     convert_noninflect_cubic_to_quads(choppedPts + 0,
525                                       toleranceSqd,
526                                       constrainWithinTangents,
527                                       dir,
528                                       quads,
529                                       sublevel + 1);
530     convert_noninflect_cubic_to_quads(choppedPts + 3,
531                                       toleranceSqd,
532                                       constrainWithinTangents,
533                                       dir,
534                                       quads,
535                                       sublevel + 1);
536 }
537 }
538 
convertCubicToQuads(const SkPoint p[4],SkScalar tolScale,SkTArray<SkPoint,true> * quads)539 void GrPathUtils::convertCubicToQuads(const SkPoint p[4],
540                                       SkScalar tolScale,
541                                       SkTArray<SkPoint, true>* quads) {
542     SkPoint chopped[10];
543     int count = SkChopCubicAtInflections(p, chopped);
544 
545     const SkScalar tolSqd = SkScalarSquare(tolScale);
546 
547     for (int i = 0; i < count; ++i) {
548         SkPoint* cubic = chopped + 3*i;
549         // The direction param is ignored if the third param is false.
550         convert_noninflect_cubic_to_quads(cubic, tolSqd, false,
551                                           SkPathPriv::kCCW_FirstDirection, quads);
552     }
553 }
554 
convertCubicToQuadsConstrainToTangents(const SkPoint p[4],SkScalar tolScale,SkPathPriv::FirstDirection dir,SkTArray<SkPoint,true> * quads)555 void GrPathUtils::convertCubicToQuadsConstrainToTangents(const SkPoint p[4],
556                                                          SkScalar tolScale,
557                                                          SkPathPriv::FirstDirection dir,
558                                                          SkTArray<SkPoint, true>* quads) {
559     SkPoint chopped[10];
560     int count = SkChopCubicAtInflections(p, chopped);
561 
562     const SkScalar tolSqd = SkScalarSquare(tolScale);
563 
564     for (int i = 0; i < count; ++i) {
565         SkPoint* cubic = chopped + 3*i;
566         convert_noninflect_cubic_to_quads(cubic, tolSqd, true, dir, quads);
567     }
568 }
569 
570 ////////////////////////////////////////////////////////////////////////////////
571 
572 /**
573  * Computes an SkMatrix that can find the cubic KLM functionals as follows:
574  *
575  *     | ..K.. |   | ..kcoeffs.. |
576  *     | ..L.. | = | ..lcoeffs.. | * inverse_transpose_power_basis_matrix
577  *     | ..M.. |   | ..mcoeffs.. |
578  *
579  * 'kcoeffs' are the power basis coefficients to a scalar valued cubic function that returns the
580  * signed distance to line K from a given point on the curve:
581  *
582  *     k(t,s) = C(t,s) * K   [C(t,s) is defined in the following comment]
583  *
584  * The same applies for lcoeffs and mcoeffs. These are found separately, depending on the type of
585  * curve. There are 4 coefficients but 3 rows in the matrix, so in order to do this calculation the
586  * caller must first remove a specific column of coefficients.
587  *
588  * @return which column of klm coefficients to exclude from the calculation.
589  */
calc_inverse_transpose_power_basis_matrix(const SkPoint pts[4],SkMatrix * out)590 static int calc_inverse_transpose_power_basis_matrix(const SkPoint pts[4], SkMatrix* out) {
591     using SkScalar4 = SkNx<4, SkScalar>;
592 
593     // First we convert the bezier coordinates 'pts' to power basis coefficients X,Y,W=[0 0 0 1].
594     // M3 is the matrix that does this conversion. The homogeneous equation for the cubic becomes:
595     //
596     //                                     | X   Y   0 |
597     // C(t,s) = [t^3  t^2*s  t*s^2  s^3] * | .   .   0 |
598     //                                     | .   .   0 |
599     //                                     | .   .   1 |
600     //
601     const SkScalar4 M3[3] = {SkScalar4(-1, 3, -3, 1),
602                              SkScalar4(3, -6, 3, 0),
603                              SkScalar4(-3, 3, 0, 0)};
604     // 4th column of M3   =  SkScalar4(1, 0, 0, 0)};
605     SkScalar4 X(pts[3].x(), 0, 0, 0);
606     SkScalar4 Y(pts[3].y(), 0, 0, 0);
607     for (int i = 2; i >= 0; --i) {
608         X += M3[i] * pts[i].x();
609         Y += M3[i] * pts[i].y();
610     }
611 
612     // The matrix is 3x4. In order to invert it, we first need to make it square by throwing out one
613     // of the top three rows. We toss the row that leaves us with the largest absolute determinant.
614     // Since the right column will be [0 0 1], the determinant reduces to x0*y1 - y0*x1.
615     SkScalar absDet[4];
616     const SkScalar4 DETX1 = SkNx_shuffle<1,0,0,3>(X), DETY1 = SkNx_shuffle<1,0,0,3>(Y);
617     const SkScalar4 DETX2 = SkNx_shuffle<2,2,1,3>(X), DETY2 = SkNx_shuffle<2,2,1,3>(Y);
618     const SkScalar4 DET = DETX1 * DETY2 - DETY1 * DETX2;
619     DET.abs().store(absDet);
620     const int skipRow = absDet[0] > absDet[2] ? (absDet[0] > absDet[1] ? 0 : 1)
621                                               : (absDet[1] > absDet[2] ? 1 : 2);
622     const SkScalar rdet = 1 / DET[skipRow];
623     const int row0 = (0 != skipRow) ? 0 : 1;
624     const int row1 = (2 == skipRow) ? 1 : 2;
625 
626     // Compute the inverse-transpose of the power basis matrix with the 'skipRow'th row removed.
627     // Since W=[0 0 0 1], it follows that our corresponding solution will be equal to:
628     //
629     //             |  y1  -x1   x1*y2 - y1*x2 |
630     //     1/det * | -y0   x0  -x0*y2 + y0*x2 |
631     //             |   0    0             det |
632     //
633     const SkScalar4 R(rdet, rdet, rdet, 1);
634     X *= R;
635     Y *= R;
636 
637     SkScalar x[4], y[4], z[4];
638     X.store(x);
639     Y.store(y);
640     (X * SkNx_shuffle<3,3,3,3>(Y) - Y * SkNx_shuffle<3,3,3,3>(X)).store(z);
641 
642     out->setAll( y[row1], -x[row1],  z[row1],
643                 -y[row0],  x[row0], -z[row0],
644                        0,        0,        1);
645 
646     return skipRow;
647 }
648 
calc_serp_klm(const SkPoint pts[4],SkScalar tl,SkScalar sl,SkScalar tm,SkScalar sm,SkMatrix * klm)649 static void calc_serp_klm(const SkPoint pts[4], SkScalar tl, SkScalar sl, SkScalar tm, SkScalar sm,
650                           SkMatrix* klm) {
651     SkMatrix CIT;
652     int skipCol = calc_inverse_transpose_power_basis_matrix(pts, &CIT);
653 
654     SkMatrix klmCoeffs;
655     int col = 0;
656     if (0 != skipCol) {
657         klmCoeffs[0] = 0;
658         klmCoeffs[3] = -sl * sl * sl;
659         klmCoeffs[6] = -sm * sm * sm;
660         ++col;
661     }
662     if (1 != skipCol) {
663         klmCoeffs[col + 0] = sl * sm;
664         klmCoeffs[col + 3] = 3 * sl * sl * tl;
665         klmCoeffs[col + 6] = 3 * sm * sm * tm;
666         ++col;
667     }
668     if (2 != skipCol) {
669         klmCoeffs[col + 0] = -tl * sm - tm * sl;
670         klmCoeffs[col + 3] = -3 * sl * tl * tl;
671         klmCoeffs[col + 6] = -3 * sm * tm * tm;
672         ++col;
673     }
674 
675     SkASSERT(2 == col);
676     klmCoeffs[2] = tl * tm;
677     klmCoeffs[5] = tl * tl * tl;
678     klmCoeffs[8] = tm * tm * tm;
679 
680     klm->setConcat(klmCoeffs, CIT);
681 }
682 
calc_loop_klm(const SkPoint pts[4],SkScalar td,SkScalar sd,SkScalar te,SkScalar se,SkMatrix * klm)683 static void calc_loop_klm(const SkPoint pts[4], SkScalar td, SkScalar sd, SkScalar te, SkScalar se,
684                           SkMatrix* klm) {
685     SkMatrix CIT;
686     int skipCol = calc_inverse_transpose_power_basis_matrix(pts, &CIT);
687 
688     const SkScalar tdse = td * se;
689     const SkScalar tesd = te * sd;
690 
691     SkMatrix klmCoeffs;
692     int col = 0;
693     if (0 != skipCol) {
694         klmCoeffs[0] = 0;
695         klmCoeffs[3] = -sd * sd * se;
696         klmCoeffs[6] = -se * se * sd;
697         ++col;
698     }
699     if (1 != skipCol) {
700         klmCoeffs[col + 0] = sd * se;
701         klmCoeffs[col + 3] = sd * (2 * tdse + tesd);
702         klmCoeffs[col + 6] = se * (2 * tesd + tdse);
703         ++col;
704     }
705     if (2 != skipCol) {
706         klmCoeffs[col + 0] = -tdse - tesd;
707         klmCoeffs[col + 3] = -td * (tdse + 2 * tesd);
708         klmCoeffs[col + 6] = -te * (tesd + 2 * tdse);
709         ++col;
710     }
711 
712     SkASSERT(2 == col);
713     klmCoeffs[2] = td * te;
714     klmCoeffs[5] = td * td * te;
715     klmCoeffs[8] = te * te * td;
716 
717     klm->setConcat(klmCoeffs, CIT);
718 }
719 
720 // For the case when we have a cusp at a parameter value of infinity (discr == 0, d1 == 0).
calc_inf_cusp_klm(const SkPoint pts[4],SkScalar tn,SkScalar sn,SkMatrix * klm)721 static void calc_inf_cusp_klm(const SkPoint pts[4], SkScalar tn, SkScalar sn, SkMatrix* klm) {
722     SkMatrix CIT;
723     int skipCol = calc_inverse_transpose_power_basis_matrix(pts, &CIT);
724 
725     SkMatrix klmCoeffs;
726     int col = 0;
727     if (0 != skipCol) {
728         klmCoeffs[0] = 0;
729         klmCoeffs[3] = -sn * sn * sn;
730         ++col;
731     }
732     if (1 != skipCol) {
733         klmCoeffs[col + 0] = 0;
734         klmCoeffs[col + 3] = 3 * sn * sn * tn;
735         ++col;
736     }
737     if (2 != skipCol) {
738         klmCoeffs[col + 0] = -sn;
739         klmCoeffs[col + 3] = -3 * sn * tn * tn;
740         ++col;
741     }
742 
743     SkASSERT(2 == col);
744     klmCoeffs[2] = tn;
745     klmCoeffs[5] = tn * tn * tn;
746 
747     klmCoeffs[6] = 0;
748     klmCoeffs[7] = 0;
749     klmCoeffs[8] = 1;
750 
751     klm->setConcat(klmCoeffs, CIT);
752 }
753 
754 // For the case when a cubic bezier is actually a quadratic. We duplicate k in l so that the
755 // implicit becomes:
756 //
757 //     k^3 - l*m == k^3 - l*k == k * (k^2 - l)
758 //
759 // In the quadratic case we can simply assign fixed values at each control point:
760 //
761 //     | ..K.. |     | pts[0]  pts[1]  pts[2]  pts[3] |      | 0   1/3  2/3  1 |
762 //     | ..L.. |  *  |   .       .       .       .    |  ==  | 0     0  1/3  1 |
763 //     | ..K.. |     |   1       1       1       1    |      | 0   1/3  2/3  1 |
764 //
calc_quadratic_klm(const SkPoint pts[4],double d3,SkMatrix * klm)765 static void calc_quadratic_klm(const SkPoint pts[4], double d3, SkMatrix* klm) {
766     SkMatrix klmAtPts;
767     klmAtPts.setAll(0,  1.f/3,  1,
768                     0,      0,  1,
769                     0,  1.f/3,  1);
770 
771     SkMatrix inversePts;
772     inversePts.setAll(pts[0].x(),  pts[1].x(),  pts[3].x(),
773                       pts[0].y(),  pts[1].y(),  pts[3].y(),
774                                1,           1,           1);
775     SkAssertResult(inversePts.invert(&inversePts));
776 
777     klm->setConcat(klmAtPts, inversePts);
778 
779     // If d3 > 0 we need to flip the orientation of our curve
780     // This is done by negating the k and l values
781     if (d3 > 0) {
782         klm->postScale(-1, -1);
783     }
784 }
785 
786 // For the case when a cubic bezier is actually a line. We set K=0, L=1, M=-line, which results in
787 // the following implicit:
788 //
789 //     k^3 - l*m == 0^3 - 1*(-line) == -(-line) == line
790 //
calc_line_klm(const SkPoint pts[4],SkMatrix * klm)791 static void calc_line_klm(const SkPoint pts[4], SkMatrix* klm) {
792     SkScalar ny = pts[0].x() - pts[3].x();
793     SkScalar nx = pts[3].y() - pts[0].y();
794     SkScalar k = nx * pts[0].x() + ny * pts[0].y();
795     klm->setAll(  0,   0, 0,
796                   0,   0, 1,
797                 -nx, -ny, k);
798 }
799 
getCubicKLM(const SkPoint src[4],SkMatrix * klm,double t[2],double s[2])800 SkCubicType GrPathUtils::getCubicKLM(const SkPoint src[4], SkMatrix* klm, double t[2],
801                                      double s[2]) {
802     double d[4];
803     SkCubicType type = SkClassifyCubic(src, t, s, d);
804 
805     const SkScalar tt[2] = {static_cast<SkScalar>(t[0]), static_cast<SkScalar>(t[1])};
806     const SkScalar ss[2] = {static_cast<SkScalar>(s[0]), static_cast<SkScalar>(s[1])};
807 
808     switch (type) {
809         case SkCubicType::kSerpentine:
810             calc_serp_klm(src, tt[0], ss[0], tt[1], ss[1], klm);
811             break;
812         case SkCubicType::kLoop:
813             calc_loop_klm(src, tt[0], ss[0], tt[1], ss[1], klm);
814             break;
815         case SkCubicType::kLocalCusp:
816             calc_serp_klm(src, tt[0], ss[0], tt[1], ss[1], klm);
817             break;
818         case SkCubicType::kCuspAtInfinity:
819             calc_inf_cusp_klm(src, tt[0], ss[0], klm);
820             break;
821         case SkCubicType::kQuadratic:
822             calc_quadratic_klm(src, d[3], klm);
823             break;
824         case SkCubicType::kLineOrPoint:
825             calc_line_klm(src, klm);
826             break;
827     }
828 
829     return type;
830 }
831 
chopCubicAtLoopIntersection(const SkPoint src[4],SkPoint dst[10],SkMatrix * klm,int * loopIndex)832 int GrPathUtils::chopCubicAtLoopIntersection(const SkPoint src[4], SkPoint dst[10], SkMatrix* klm,
833                                              int* loopIndex) {
834     SkSTArray<2, SkScalar> chops;
835     *loopIndex = -1;
836 
837     double t[2], s[2];
838     if (SkCubicType::kLoop == GrPathUtils::getCubicKLM(src, klm, t, s)) {
839         SkScalar t0 = static_cast<SkScalar>(t[0] / s[0]);
840         SkScalar t1 = static_cast<SkScalar>(t[1] / s[1]);
841         SkASSERT(t0 <= t1); // Technically t0 != t1 in a loop, but there may be FP error.
842 
843         if (t0 < 1 && t1 > 0) {
844             *loopIndex = 0;
845             if (t0 > 0) {
846                 chops.push_back(t0);
847                 *loopIndex = 1;
848             }
849             if (t1 < 1) {
850                 chops.push_back(t1);
851                 *loopIndex = chops.count() - 1;
852             }
853         }
854     }
855 
856     SkChopCubicAt(src, dst, chops.begin(), chops.count());
857     return chops.count() + 1;
858 }
859