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 "src/gpu/geometry/GrPathUtils.h"
9
10 #include "include/gpu/GrTypes.h"
11 #include "src/core/SkMathPriv.h"
12 #include "src/core/SkPointPriv.h"
13 #include "src/core/SkUtils.h"
14 #include "src/gpu/geometry/GrWangsFormula.h"
15
16 static const SkScalar kMinCurveTol = 0.0001f;
17
tolerance_to_wangs_precision(float srcTol)18 static float tolerance_to_wangs_precision(float srcTol) {
19 // You should have called scaleToleranceToSrc, which guarantees this
20 SkASSERT(srcTol >= kMinCurveTol);
21
22 // The GrPathUtil API defines tolerance as the max distance the linear segment can be from
23 // the real curve. Wang's formula guarantees the linear segments will be within 1/precision
24 // of the true curve, so precision = 1/srcTol
25 return 1.f / srcTol;
26 }
27
max_bezier_vertices(uint32_t chopCount)28 uint32_t max_bezier_vertices(uint32_t chopCount) {
29 static constexpr uint32_t kMaxChopsPerCurve = 10;
30 static_assert((1 << kMaxChopsPerCurve) == GrPathUtils::kMaxPointsPerCurve);
31 return 1 << std::min(chopCount, kMaxChopsPerCurve);
32 }
33
scaleToleranceToSrc(SkScalar devTol,const SkMatrix & viewM,const SkRect & pathBounds)34 SkScalar GrPathUtils::scaleToleranceToSrc(SkScalar devTol,
35 const SkMatrix& viewM,
36 const SkRect& pathBounds) {
37 // In order to tesselate the path we get a bound on how much the matrix can
38 // scale when mapping to screen coordinates.
39 SkScalar stretch = viewM.getMaxScale();
40
41 if (stretch < 0) {
42 // take worst case mapRadius amoung four corners.
43 // (less than perfect)
44 for (int i = 0; i < 4; ++i) {
45 SkMatrix mat;
46 mat.setTranslate((i % 2) ? pathBounds.fLeft : pathBounds.fRight,
47 (i < 2) ? pathBounds.fTop : pathBounds.fBottom);
48 mat.postConcat(viewM);
49 stretch = std::max(stretch, mat.mapRadius(SK_Scalar1));
50 }
51 }
52 SkScalar srcTol = 0;
53 if (stretch <= 0) {
54 // We have degenerate bounds or some degenerate matrix. Thus we set the tolerance to be the
55 // max of the path pathBounds width and height.
56 srcTol = std::max(pathBounds.width(), pathBounds.height());
57 } else {
58 srcTol = devTol / stretch;
59 }
60 if (srcTol < kMinCurveTol) {
61 srcTol = kMinCurveTol;
62 }
63 return srcTol;
64 }
65
quadraticPointCount(const SkPoint points[],SkScalar tol)66 uint32_t GrPathUtils::quadraticPointCount(const SkPoint points[], SkScalar tol) {
67 return max_bezier_vertices(GrWangsFormula::quadratic_log2(
68 tolerance_to_wangs_precision(tol), points));
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 (SkPointPriv::DistanceToLineSegmentBetweenSqd(p1, 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[], SkScalar tol) {
97 return max_bezier_vertices(GrWangsFormula::cubic_log2(
98 tolerance_to_wangs_precision(tol), points));
99 }
100
generateCubicPoints(const SkPoint & p0,const SkPoint & p1,const SkPoint & p2,const SkPoint & p3,SkScalar tolSqd,SkPoint ** points,uint32_t pointsLeft)101 uint32_t GrPathUtils::generateCubicPoints(const SkPoint& p0,
102 const SkPoint& p1,
103 const SkPoint& p2,
104 const SkPoint& p3,
105 SkScalar tolSqd,
106 SkPoint** points,
107 uint32_t pointsLeft) {
108 if (pointsLeft < 2 ||
109 (SkPointPriv::DistanceToLineSegmentBetweenSqd(p1, p0, p3) < tolSqd &&
110 SkPointPriv::DistanceToLineSegmentBetweenSqd(p2, p0, p3) < tolSqd)) {
111 (*points)[0] = p3;
112 *points += 1;
113 return 1;
114 }
115 SkPoint q[] = {
116 { SkScalarAve(p0.fX, p1.fX), SkScalarAve(p0.fY, p1.fY) },
117 { SkScalarAve(p1.fX, p2.fX), SkScalarAve(p1.fY, p2.fY) },
118 { SkScalarAve(p2.fX, p3.fX), SkScalarAve(p2.fY, p3.fY) }
119 };
120 SkPoint r[] = {
121 { SkScalarAve(q[0].fX, q[1].fX), SkScalarAve(q[0].fY, q[1].fY) },
122 { SkScalarAve(q[1].fX, q[2].fX), SkScalarAve(q[1].fY, q[2].fY) }
123 };
124 SkPoint s = { SkScalarAve(r[0].fX, r[1].fX), SkScalarAve(r[0].fY, r[1].fY) };
125 pointsLeft >>= 1;
126 uint32_t a = generateCubicPoints(p0, q[0], r[0], s, tolSqd, points, pointsLeft);
127 uint32_t b = generateCubicPoints(s, r[1], q[2], p3, tolSqd, points, pointsLeft);
128 return a + b;
129 }
130
set(const SkPoint qPts[3])131 void GrPathUtils::QuadUVMatrix::set(const SkPoint qPts[3]) {
132 SkMatrix m;
133 // We want M such that M * xy_pt = uv_pt
134 // We know M * control_pts = [0 1/2 1]
135 // [0 0 1]
136 // [1 1 1]
137 // And control_pts = [x0 x1 x2]
138 // [y0 y1 y2]
139 // [1 1 1 ]
140 // We invert the control pt matrix and post concat to both sides to get M.
141 // Using the known form of the control point matrix and the result, we can
142 // optimize and improve precision.
143
144 double x0 = qPts[0].fX;
145 double y0 = qPts[0].fY;
146 double x1 = qPts[1].fX;
147 double y1 = qPts[1].fY;
148 double x2 = qPts[2].fX;
149 double y2 = qPts[2].fY;
150 double det = x0*y1 - y0*x1 + x2*y0 - y2*x0 + x1*y2 - y1*x2;
151
152 if (!sk_float_isfinite(det)
153 || SkScalarNearlyZero((float)det, SK_ScalarNearlyZero * SK_ScalarNearlyZero)) {
154 // The quad is degenerate. Hopefully this is rare. Find the pts that are
155 // farthest apart to compute a line (unless it is really a pt).
156 SkScalar maxD = SkPointPriv::DistanceToSqd(qPts[0], qPts[1]);
157 int maxEdge = 0;
158 SkScalar d = SkPointPriv::DistanceToSqd(qPts[1], qPts[2]);
159 if (d > maxD) {
160 maxD = d;
161 maxEdge = 1;
162 }
163 d = SkPointPriv::DistanceToSqd(qPts[2], qPts[0]);
164 if (d > maxD) {
165 maxD = d;
166 maxEdge = 2;
167 }
168 // We could have a tolerance here, not sure if it would improve anything
169 if (maxD > 0) {
170 // Set the matrix to give (u = 0, v = distance_to_line)
171 SkVector lineVec = qPts[(maxEdge + 1)%3] - qPts[maxEdge];
172 // when looking from the point 0 down the line we want positive
173 // distances to be to the left. This matches the non-degenerate
174 // case.
175 lineVec = SkPointPriv::MakeOrthog(lineVec, SkPointPriv::kLeft_Side);
176 // first row
177 fM[0] = 0;
178 fM[1] = 0;
179 fM[2] = 0;
180 // second row
181 fM[3] = lineVec.fX;
182 fM[4] = lineVec.fY;
183 fM[5] = -lineVec.dot(qPts[maxEdge]);
184 } else {
185 // It's a point. It should cover zero area. Just set the matrix such
186 // that (u, v) will always be far away from the quad.
187 fM[0] = 0; fM[1] = 0; fM[2] = 100.f;
188 fM[3] = 0; fM[4] = 0; fM[5] = 100.f;
189 }
190 } else {
191 double scale = 1.0/det;
192
193 // compute adjugate matrix
194 double a2, a3, a4, a5, a6, a7, a8;
195 a2 = x1*y2-x2*y1;
196
197 a3 = y2-y0;
198 a4 = x0-x2;
199 a5 = x2*y0-x0*y2;
200
201 a6 = y0-y1;
202 a7 = x1-x0;
203 a8 = x0*y1-x1*y0;
204
205 // this performs the uv_pts*adjugate(control_pts) multiply,
206 // then does the scale by 1/det afterwards to improve precision
207 m[SkMatrix::kMScaleX] = (float)((0.5*a3 + a6)*scale);
208 m[SkMatrix::kMSkewX] = (float)((0.5*a4 + a7)*scale);
209 m[SkMatrix::kMTransX] = (float)((0.5*a5 + a8)*scale);
210
211 m[SkMatrix::kMSkewY] = (float)(a6*scale);
212 m[SkMatrix::kMScaleY] = (float)(a7*scale);
213 m[SkMatrix::kMTransY] = (float)(a8*scale);
214
215 // kMPersp0 & kMPersp1 should algebraically be zero
216 m[SkMatrix::kMPersp0] = 0.0f;
217 m[SkMatrix::kMPersp1] = 0.0f;
218 m[SkMatrix::kMPersp2] = (float)((a2 + a5 + a8)*scale);
219
220 // It may not be normalized to have 1.0 in the bottom right
221 float m33 = m.get(SkMatrix::kMPersp2);
222 if (1.f != m33) {
223 m33 = 1.f / m33;
224 fM[0] = m33 * m.get(SkMatrix::kMScaleX);
225 fM[1] = m33 * m.get(SkMatrix::kMSkewX);
226 fM[2] = m33 * m.get(SkMatrix::kMTransX);
227 fM[3] = m33 * m.get(SkMatrix::kMSkewY);
228 fM[4] = m33 * m.get(SkMatrix::kMScaleY);
229 fM[5] = m33 * m.get(SkMatrix::kMTransY);
230 } else {
231 fM[0] = m.get(SkMatrix::kMScaleX);
232 fM[1] = m.get(SkMatrix::kMSkewX);
233 fM[2] = m.get(SkMatrix::kMTransX);
234 fM[3] = m.get(SkMatrix::kMSkewY);
235 fM[4] = m.get(SkMatrix::kMScaleY);
236 fM[5] = m.get(SkMatrix::kMTransY);
237 }
238 }
239 }
240
241 ////////////////////////////////////////////////////////////////////////////////
242
243 // k = (y2 - y0, x0 - x2, x2*y0 - x0*y2)
244 // l = (y1 - y0, x0 - x1, x1*y0 - x0*y1) * 2*w
245 // m = (y2 - y1, x1 - x2, x2*y1 - x1*y2) * 2*w
getConicKLM(const SkPoint p[3],const SkScalar weight,SkMatrix * out)246 void GrPathUtils::getConicKLM(const SkPoint p[3], const SkScalar weight, SkMatrix* out) {
247 SkMatrix& klm = *out;
248 const SkScalar w2 = 2.f * weight;
249 klm[0] = p[2].fY - p[0].fY;
250 klm[1] = p[0].fX - p[2].fX;
251 klm[2] = p[2].fX * p[0].fY - p[0].fX * p[2].fY;
252
253 klm[3] = w2 * (p[1].fY - p[0].fY);
254 klm[4] = w2 * (p[0].fX - p[1].fX);
255 klm[5] = w2 * (p[1].fX * p[0].fY - p[0].fX * p[1].fY);
256
257 klm[6] = w2 * (p[2].fY - p[1].fY);
258 klm[7] = w2 * (p[1].fX - p[2].fX);
259 klm[8] = w2 * (p[2].fX * p[1].fY - p[1].fX * p[2].fY);
260
261 // scale the max absolute value of coeffs to 10
262 SkScalar scale = 0.f;
263 for (int i = 0; i < 9; ++i) {
264 scale = std::max(scale, SkScalarAbs(klm[i]));
265 }
266 SkASSERT(scale > 0.f);
267 scale = 10.f / scale;
268 for (int i = 0; i < 9; ++i) {
269 klm[i] *= scale;
270 }
271 }
272
273 ////////////////////////////////////////////////////////////////////////////////
274
275 namespace {
276
277 // a is the first control point of the cubic.
278 // ab is the vector from a to the second control point.
279 // dc is the vector from the fourth to the third control point.
280 // d is the fourth control point.
281 // p is the candidate quadratic control point.
282 // 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,SkPathFirstDirection dir,const SkPoint p)283 bool is_point_within_cubic_tangents(const SkPoint& a,
284 const SkVector& ab,
285 const SkVector& dc,
286 const SkPoint& d,
287 SkPathFirstDirection dir,
288 const SkPoint p) {
289 SkVector ap = p - a;
290 SkScalar apXab = ap.cross(ab);
291 if (SkPathFirstDirection::kCW == dir) {
292 if (apXab > 0) {
293 return false;
294 }
295 } else {
296 SkASSERT(SkPathFirstDirection::kCCW == dir);
297 if (apXab < 0) {
298 return false;
299 }
300 }
301
302 SkVector dp = p - d;
303 SkScalar dpXdc = dp.cross(dc);
304 if (SkPathFirstDirection::kCW == dir) {
305 if (dpXdc < 0) {
306 return false;
307 }
308 } else {
309 SkASSERT(SkPathFirstDirection::kCCW == dir);
310 if (dpXdc > 0) {
311 return false;
312 }
313 }
314 return true;
315 }
316
convert_noninflect_cubic_to_quads(const SkPoint p[4],SkScalar toleranceSqd,SkTArray<SkPoint,true> * quads,int sublevel=0,bool preserveFirstTangent=true,bool preserveLastTangent=true)317 void convert_noninflect_cubic_to_quads(const SkPoint p[4],
318 SkScalar toleranceSqd,
319 SkTArray<SkPoint, true>* quads,
320 int sublevel = 0,
321 bool preserveFirstTangent = true,
322 bool preserveLastTangent = true) {
323 // Notation: Point a is always p[0]. Point b is p[1] unless p[1] == p[0], in which case it is
324 // 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].
325 SkVector ab = p[1] - p[0];
326 SkVector dc = p[2] - p[3];
327
328 if (SkPointPriv::LengthSqd(ab) < SK_ScalarNearlyZero) {
329 if (SkPointPriv::LengthSqd(dc) < SK_ScalarNearlyZero) {
330 SkPoint* degQuad = quads->push_back_n(3);
331 degQuad[0] = p[0];
332 degQuad[1] = p[0];
333 degQuad[2] = p[3];
334 return;
335 }
336 ab = p[2] - p[0];
337 }
338 if (SkPointPriv::LengthSqd(dc) < SK_ScalarNearlyZero) {
339 dc = p[1] - p[3];
340 }
341
342 static const SkScalar kLengthScale = 3 * SK_Scalar1 / 2;
343 static const int kMaxSubdivs = 10;
344
345 ab.scale(kLengthScale);
346 dc.scale(kLengthScale);
347
348 // c0 and c1 are extrapolations along vectors ab and dc.
349 SkPoint c0 = p[0] + ab;
350 SkPoint c1 = p[3] + dc;
351
352 SkScalar dSqd = sublevel > kMaxSubdivs ? 0 : SkPointPriv::DistanceToSqd(c0, c1);
353 if (dSqd < toleranceSqd) {
354 SkPoint newC;
355 if (preserveFirstTangent == preserveLastTangent) {
356 // We used to force a split when both tangents need to be preserved and c0 != c1.
357 // This introduced a large performance regression for tiny paths for no noticeable
358 // quality improvement. However, we aren't quite fulfilling our contract of guaranteeing
359 // the two tangent vectors and this could introduce a missed pixel in
360 // GrAAHairlinePathRenderer.
361 newC = (c0 + c1) * 0.5f;
362 } else if (preserveFirstTangent) {
363 newC = c0;
364 } else {
365 newC = c1;
366 }
367
368 SkPoint* pts = quads->push_back_n(3);
369 pts[0] = p[0];
370 pts[1] = newC;
371 pts[2] = p[3];
372 return;
373 }
374 SkPoint choppedPts[7];
375 SkChopCubicAtHalf(p, choppedPts);
376 convert_noninflect_cubic_to_quads(
377 choppedPts + 0, toleranceSqd, quads, sublevel + 1, preserveFirstTangent, false);
378 convert_noninflect_cubic_to_quads(
379 choppedPts + 3, toleranceSqd, quads, sublevel + 1, false, preserveLastTangent);
380 }
381
convert_noninflect_cubic_to_quads_with_constraint(const SkPoint p[4],SkScalar toleranceSqd,SkPathFirstDirection dir,SkTArray<SkPoint,true> * quads,int sublevel=0)382 void convert_noninflect_cubic_to_quads_with_constraint(const SkPoint p[4],
383 SkScalar toleranceSqd,
384 SkPathFirstDirection dir,
385 SkTArray<SkPoint, true>* quads,
386 int sublevel = 0) {
387 // Notation: Point a is always p[0]. Point b is p[1] unless p[1] == p[0], in which case it is
388 // 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].
389
390 SkVector ab = p[1] - p[0];
391 SkVector dc = p[2] - p[3];
392
393 if (SkPointPriv::LengthSqd(ab) < SK_ScalarNearlyZero) {
394 if (SkPointPriv::LengthSqd(dc) < SK_ScalarNearlyZero) {
395 SkPoint* degQuad = quads->push_back_n(3);
396 degQuad[0] = p[0];
397 degQuad[1] = p[0];
398 degQuad[2] = p[3];
399 return;
400 }
401 ab = p[2] - p[0];
402 }
403 if (SkPointPriv::LengthSqd(dc) < SK_ScalarNearlyZero) {
404 dc = p[1] - p[3];
405 }
406
407 // When the ab and cd tangents are degenerate or nearly parallel with vector from d to a the
408 // constraint that the quad point falls between the tangents becomes hard to enforce and we are
409 // likely to hit the max subdivision count. However, in this case the cubic is approaching a
410 // line and the accuracy of the quad point isn't so important. We check if the two middle cubic
411 // control points are very close to the baseline vector. If so then we just pick quadratic
412 // points on the control polygon.
413
414 SkVector da = p[0] - p[3];
415 bool doQuads = SkPointPriv::LengthSqd(dc) < SK_ScalarNearlyZero ||
416 SkPointPriv::LengthSqd(ab) < SK_ScalarNearlyZero;
417 if (!doQuads) {
418 SkScalar invDALengthSqd = SkPointPriv::LengthSqd(da);
419 if (invDALengthSqd > SK_ScalarNearlyZero) {
420 invDALengthSqd = SkScalarInvert(invDALengthSqd);
421 // cross(ab, da)^2/length(da)^2 == sqd distance from b to line from d to a.
422 // same goes for point c using vector cd.
423 SkScalar detABSqd = ab.cross(da);
424 detABSqd = SkScalarSquare(detABSqd);
425 SkScalar detDCSqd = dc.cross(da);
426 detDCSqd = SkScalarSquare(detDCSqd);
427 if (detABSqd * invDALengthSqd < toleranceSqd &&
428 detDCSqd * invDALengthSqd < toleranceSqd) {
429 doQuads = true;
430 }
431 }
432 }
433 if (doQuads) {
434 SkPoint b = p[0] + ab;
435 SkPoint c = p[3] + dc;
436 SkPoint mid = b + c;
437 mid.scale(SK_ScalarHalf);
438 // Insert two quadratics to cover the case when ab points away from d and/or dc
439 // points away from a.
440 if (SkVector::DotProduct(da, dc) < 0 || SkVector::DotProduct(ab, da) > 0) {
441 SkPoint* qpts = quads->push_back_n(6);
442 qpts[0] = p[0];
443 qpts[1] = b;
444 qpts[2] = mid;
445 qpts[3] = mid;
446 qpts[4] = c;
447 qpts[5] = p[3];
448 } else {
449 SkPoint* qpts = quads->push_back_n(3);
450 qpts[0] = p[0];
451 qpts[1] = mid;
452 qpts[2] = p[3];
453 }
454 return;
455 }
456
457 static const SkScalar kLengthScale = 3 * SK_Scalar1 / 2;
458 static const int kMaxSubdivs = 10;
459
460 ab.scale(kLengthScale);
461 dc.scale(kLengthScale);
462
463 // c0 and c1 are extrapolations along vectors ab and dc.
464 SkVector c0 = p[0] + ab;
465 SkVector c1 = p[3] + dc;
466
467 SkScalar dSqd = sublevel > kMaxSubdivs ? 0 : SkPointPriv::DistanceToSqd(c0, c1);
468 if (dSqd < toleranceSqd) {
469 SkPoint cAvg = (c0 + c1) * 0.5f;
470 bool subdivide = false;
471
472 if (!is_point_within_cubic_tangents(p[0], ab, dc, p[3], dir, cAvg)) {
473 // choose a new cAvg that is the intersection of the two tangent lines.
474 ab = SkPointPriv::MakeOrthog(ab);
475 SkScalar z0 = -ab.dot(p[0]);
476 dc = SkPointPriv::MakeOrthog(dc);
477 SkScalar z1 = -dc.dot(p[3]);
478 cAvg.fX = ab.fY * z1 - z0 * dc.fY;
479 cAvg.fY = z0 * dc.fX - ab.fX * z1;
480 SkScalar z = ab.fX * dc.fY - ab.fY * dc.fX;
481 z = SkScalarInvert(z);
482 cAvg.fX *= z;
483 cAvg.fY *= z;
484 if (sublevel <= kMaxSubdivs) {
485 SkScalar d0Sqd = SkPointPriv::DistanceToSqd(c0, cAvg);
486 SkScalar d1Sqd = SkPointPriv::DistanceToSqd(c1, cAvg);
487 // We need to subdivide if d0 + d1 > tolerance but we have the sqd values. We know
488 // the distances and tolerance can't be negative.
489 // (d0 + d1)^2 > toleranceSqd
490 // d0Sqd + 2*d0*d1 + d1Sqd > toleranceSqd
491 SkScalar d0d1 = SkScalarSqrt(d0Sqd * d1Sqd);
492 subdivide = 2 * d0d1 + d0Sqd + d1Sqd > toleranceSqd;
493 }
494 }
495 if (!subdivide) {
496 SkPoint* pts = quads->push_back_n(3);
497 pts[0] = p[0];
498 pts[1] = cAvg;
499 pts[2] = p[3];
500 return;
501 }
502 }
503 SkPoint choppedPts[7];
504 SkChopCubicAtHalf(p, choppedPts);
505 convert_noninflect_cubic_to_quads_with_constraint(
506 choppedPts + 0, toleranceSqd, dir, quads, sublevel + 1);
507 convert_noninflect_cubic_to_quads_with_constraint(
508 choppedPts + 3, toleranceSqd, dir, quads, sublevel + 1);
509 }
510 } // namespace
511
convertCubicToQuads(const SkPoint p[4],SkScalar tolScale,SkTArray<SkPoint,true> * quads)512 void GrPathUtils::convertCubicToQuads(const SkPoint p[4],
513 SkScalar tolScale,
514 SkTArray<SkPoint, true>* quads) {
515 if (!p[0].isFinite() || !p[1].isFinite() || !p[2].isFinite() || !p[3].isFinite()) {
516 return;
517 }
518 if (!SkScalarIsFinite(tolScale)) {
519 return;
520 }
521 SkPoint chopped[10];
522 int count = SkChopCubicAtInflections(p, chopped);
523
524 const SkScalar tolSqd = SkScalarSquare(tolScale);
525
526 for (int i = 0; i < count; ++i) {
527 SkPoint* cubic = chopped + 3*i;
528 convert_noninflect_cubic_to_quads(cubic, tolSqd, quads);
529 }
530 }
531
convertCubicToQuadsConstrainToTangents(const SkPoint p[4],SkScalar tolScale,SkPathFirstDirection dir,SkTArray<SkPoint,true> * quads)532 void GrPathUtils::convertCubicToQuadsConstrainToTangents(const SkPoint p[4],
533 SkScalar tolScale,
534 SkPathFirstDirection dir,
535 SkTArray<SkPoint, true>* quads) {
536 if (!p[0].isFinite() || !p[1].isFinite() || !p[2].isFinite() || !p[3].isFinite()) {
537 return;
538 }
539 if (!SkScalarIsFinite(tolScale)) {
540 return;
541 }
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 convert_noninflect_cubic_to_quads_with_constraint(cubic, tolSqd, dir, quads);
550 }
551 }
552
findCubicConvex180Chops(const SkPoint pts[],float T[2],bool * areCusps)553 int GrPathUtils::findCubicConvex180Chops(const SkPoint pts[], float T[2], bool* areCusps) {
554 using grvx::float2;
555 SkASSERT(pts);
556 SkASSERT(T);
557 SkASSERT(areCusps);
558
559 // If a chop falls within a distance of "kEpsilon" from 0 or 1, throw it out. Tangents become
560 // unstable when we chop too close to the boundary. This works out because the tessellation
561 // shaders don't allow more than 2^10 parametric segments, and they snap the beginning and
562 // ending edges at 0 and 1. So if we overstep an inflection or point of 180-degree rotation by a
563 // fraction of a tessellation segment, it just gets snapped.
564 constexpr static float kEpsilon = 1.f / (1 << 11);
565 // Floating-point representation of "1 - 2*kEpsilon".
566 constexpr static uint32_t kIEEE_one_minus_2_epsilon = (127 << 23) - 2 * (1 << (24 - 11));
567 // Unfortunately we don't have a way to static_assert this, but we can runtime assert that the
568 // kIEEE_one_minus_2_epsilon bits are correct.
569 SkASSERT(sk_bit_cast<float>(kIEEE_one_minus_2_epsilon) == 1 - 2*kEpsilon);
570
571 float2 p0 = skvx::bit_pun<float2>(pts[0]);
572 float2 p1 = skvx::bit_pun<float2>(pts[1]);
573 float2 p2 = skvx::bit_pun<float2>(pts[2]);
574 float2 p3 = skvx::bit_pun<float2>(pts[3]);
575
576 // Find the cubic's power basis coefficients. These define the bezier curve as:
577 //
578 // |T^3|
579 // Cubic(T) = x,y = |A 3B 3C| * |T^2| + P0
580 // |. . .| |T |
581 //
582 // And the tangent direction (scaled by a uniform 1/3) will be:
583 //
584 // |T^2|
585 // Tangent_Direction(T) = dx,dy = |A 2B C| * |T |
586 // |. . .| |1 |
587 //
588 float2 C = p1 - p0;
589 float2 D = p2 - p1;
590 float2 E = p3 - p0;
591 float2 B = D - C;
592 float2 A = grvx::fast_madd<2>(-3,D,E);
593
594 // Now find the cubic's inflection function. There are inflections where F' x F'' == 0.
595 // We formulate this as a quadratic equation: F' x F'' == aT^2 + bT + c == 0.
596 // See: https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf
597 // NOTE: We only need the roots, so a uniform scale factor does not affect the solution.
598 float a = grvx::cross(A,B);
599 float b = grvx::cross(A,C);
600 float c = grvx::cross(B,C);
601 float b_over_minus_2 = -.5f * b;
602 float discr_over_4 = b_over_minus_2*b_over_minus_2 - a*c;
603
604 // If -cuspThreshold <= discr_over_4 <= cuspThreshold, it means the two roots are within
605 // kEpsilon of one another (in parametric space). This is close enough for our purposes to
606 // consider them a single cusp.
607 float cuspThreshold = a * (kEpsilon/2);
608 cuspThreshold *= cuspThreshold;
609
610 if (discr_over_4 < -cuspThreshold) {
611 // The curve does not inflect or cusp. This means it might rotate more than 180 degrees
612 // instead. Chop were rotation == 180 deg. (This is the 2nd root where the tangent is
613 // parallel to tan0.)
614 //
615 // Tangent_Direction(T) x tan0 == 0
616 // (AT^2 x tan0) + (2BT x tan0) + (C x tan0) == 0
617 // (A x C)T^2 + (2B x C)T + (C x C) == 0 [[because tan0 == P1 - P0 == C]]
618 // bT^2 + 2cT + 0 == 0 [[because A x C == b, B x C == c]]
619 // T = [0, -2c/b]
620 //
621 // NOTE: if C == 0, then C != tan0. But this is fine because the curve is definitely
622 // convex-180 if any points are colocated, and T[0] will equal NaN which returns 0 chops.
623 *areCusps = false;
624 float root = sk_ieee_float_divide(c, b_over_minus_2);
625 // Is "root" inside the range [kEpsilon, 1 - kEpsilon)?
626 if (sk_bit_cast<uint32_t>(root - kEpsilon) < kIEEE_one_minus_2_epsilon) {
627 T[0] = root;
628 return 1;
629 }
630 return 0;
631 }
632
633 *areCusps = (discr_over_4 <= cuspThreshold);
634 if (*areCusps) {
635 // The two roots are close enough that we can consider them a single cusp.
636 if (a != 0 || b_over_minus_2 != 0 || c != 0) {
637 // Pick the average of both roots.
638 float root = sk_ieee_float_divide(b_over_minus_2, a);
639 // Is "root" inside the range [kEpsilon, 1 - kEpsilon)?
640 if (sk_bit_cast<uint32_t>(root - kEpsilon) < kIEEE_one_minus_2_epsilon) {
641 T[0] = root;
642 return 1;
643 }
644 return 0;
645 }
646
647 // The curve is a flat line. The standard inflection function doesn't detect cusps from flat
648 // lines. Find cusps by searching instead for points where the tangent is perpendicular to
649 // tan0. This will find any cusp point.
650 //
651 // dot(tan0, Tangent_Direction(T)) == 0
652 //
653 // |T^2|
654 // tan0 * |A 2B C| * |T | == 0
655 // |. . .| |1 |
656 //
657 float2 tan0 = skvx::if_then_else(C != 0, C, p2 - p0);
658 a = grvx::dot(tan0, A);
659 b_over_minus_2 = -grvx::dot(tan0, B);
660 c = grvx::dot(tan0, C);
661 discr_over_4 = std::max(b_over_minus_2*b_over_minus_2 - a*c, 0.f);
662 }
663
664 // Solve our quadratic equation to find where to chop. See the quadratic formula from
665 // Numerical Recipes in C.
666 float q = sqrtf(discr_over_4);
667 q = copysignf(q, b_over_minus_2);
668 q = q + b_over_minus_2;
669 float2 roots = float2{q,c} / float2{a,q};
670
671 auto inside = (roots > kEpsilon) & (roots < (1 - kEpsilon));
672 if (inside[0]) {
673 if (inside[1] && roots[0] != roots[1]) {
674 if (roots[0] > roots[1]) {
675 roots = skvx::shuffle<1,0>(roots); // Sort.
676 }
677 roots.store(T);
678 return 2;
679 }
680 T[0] = roots[0];
681 return 1;
682 }
683 if (inside[1]) {
684 T[0] = roots[1];
685 return 1;
686 }
687 return 0;
688 }
689