Flutter Engine
The Flutter Engine
GrPathUtils.cpp
Go to the documentation of this file.
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
9
11#include "include/core/SkRect.h"
14#include "src/core/SkGeometry.h"
18
19#include <algorithm>
20
21using namespace skia_private;
22
23static const SkScalar kMinCurveTol = 0.0001f;
24
25static float tolerance_to_wangs_precision(float srcTol) {
26 // You should have called scaleToleranceToSrc, which guarantees this
27 SkASSERT(srcTol >= kMinCurveTol);
28
29 // The GrPathUtil API defines tolerance as the max distance the linear segment can be from
30 // the real curve. Wang's formula guarantees the linear segments will be within 1/precision
31 // of the true curve, so precision = 1/srcTol
32 return 1.f / srcTol;
33}
34
35uint32_t max_bezier_vertices(uint32_t chopCount) {
36 static constexpr uint32_t kMaxChopsPerCurve = 10;
37 static_assert((1 << kMaxChopsPerCurve) == GrPathUtils::kMaxPointsPerCurve);
38 return 1 << std::min(chopCount, kMaxChopsPerCurve);
39}
40
42 const SkMatrix& viewM,
43 const SkRect& pathBounds) {
44 // In order to tesselate the path we get a bound on how much the matrix can
45 // scale when mapping to screen coordinates.
46 SkScalar stretch = viewM.getMaxScale();
47
48 if (stretch < 0) {
49 // take worst case mapRadius amoung four corners.
50 // (less than perfect)
51 for (int i = 0; i < 4; ++i) {
52 SkMatrix mat;
53 mat.setTranslate((i % 2) ? pathBounds.fLeft : pathBounds.fRight,
54 (i < 2) ? pathBounds.fTop : pathBounds.fBottom);
55 mat.postConcat(viewM);
56 stretch = std::max(stretch, mat.mapRadius(SK_Scalar1));
57 }
58 }
59 SkScalar srcTol = 0;
60 if (stretch <= 0) {
61 // We have degenerate bounds or some degenerate matrix. Thus we set the tolerance to be the
62 // max of the path pathBounds width and height.
63 srcTol = std::max(pathBounds.width(), pathBounds.height());
64 } else {
65 srcTol = devTol / stretch;
66 }
67 if (srcTol < kMinCurveTol) {
68 srcTol = kMinCurveTol;
69 }
70 return srcTol;
71}
72
76}
77
79 const SkPoint& p1,
80 const SkPoint& p2,
81 SkScalar tolSqd,
83 uint32_t pointsLeft) {
84 if (pointsLeft < 2 ||
85 (SkPointPriv::DistanceToLineSegmentBetweenSqd(p1, p0, p2)) < tolSqd) {
86 (*points)[0] = p2;
87 *points += 1;
88 return 1;
89 }
90
91 SkPoint q[] = {
92 { SkScalarAve(p0.fX, p1.fX), SkScalarAve(p0.fY, p1.fY) },
93 { SkScalarAve(p1.fX, p2.fX), SkScalarAve(p1.fY, p2.fY) },
94 };
95 SkPoint r = { SkScalarAve(q[0].fX, q[1].fX), SkScalarAve(q[0].fY, q[1].fY) };
96
97 pointsLeft >>= 1;
98 uint32_t a = generateQuadraticPoints(p0, q[0], r, tolSqd, points, pointsLeft);
99 uint32_t b = generateQuadraticPoints(r, q[1], p2, tolSqd, points, pointsLeft);
100 return a + b;
101}
102
106}
107
109 const SkPoint& p1,
110 const SkPoint& p2,
111 const SkPoint& p3,
112 SkScalar tolSqd,
113 SkPoint** points,
114 uint32_t pointsLeft) {
115 if (pointsLeft < 2 ||
116 (SkPointPriv::DistanceToLineSegmentBetweenSqd(p1, p0, p3) < tolSqd &&
117 SkPointPriv::DistanceToLineSegmentBetweenSqd(p2, p0, p3) < tolSqd)) {
118 (*points)[0] = p3;
119 *points += 1;
120 return 1;
121 }
122 SkPoint q[] = {
123 { SkScalarAve(p0.fX, p1.fX), SkScalarAve(p0.fY, p1.fY) },
124 { SkScalarAve(p1.fX, p2.fX), SkScalarAve(p1.fY, p2.fY) },
125 { SkScalarAve(p2.fX, p3.fX), SkScalarAve(p2.fY, p3.fY) }
126 };
127 SkPoint r[] = {
128 { SkScalarAve(q[0].fX, q[1].fX), SkScalarAve(q[0].fY, q[1].fY) },
129 { SkScalarAve(q[1].fX, q[2].fX), SkScalarAve(q[1].fY, q[2].fY) }
130 };
131 SkPoint s = { SkScalarAve(r[0].fX, r[1].fX), SkScalarAve(r[0].fY, r[1].fY) };
132 pointsLeft >>= 1;
133 uint32_t a = generateCubicPoints(p0, q[0], r[0], s, tolSqd, points, pointsLeft);
134 uint32_t b = generateCubicPoints(s, r[1], q[2], p3, tolSqd, points, pointsLeft);
135 return a + b;
136}
137
139 // We want M such that M * xy_pt = uv_pt
140 // We know M * control_pts = [0 1/2 1]
141 // [0 0 1]
142 // [1 1 1]
143 // And control_pts = [x0 x1 x2]
144 // [y0 y1 y2]
145 // [1 1 1 ]
146 // We invert the control pt matrix and post concat to both sides to get M.
147 // Using the known form of the control point matrix and the result, we can
148 // optimize and improve precision.
149
150 double x0 = qPts[0].fX;
151 double y0 = qPts[0].fY;
152 double x1 = qPts[1].fX;
153 double y1 = qPts[1].fY;
154 double x2 = qPts[2].fX;
155 double y2 = qPts[2].fY;
156
157 // pre-calculate some adjugate matrix factors for determinant
158 double a2 = x1*y2-x2*y1;
159 double a5 = x2*y0-x0*y2;
160 double a8 = x0*y1-x1*y0;
161 double det = a2 + a5 + a8;
162
163 if (!SkIsFinite(det)
165 // The quad is degenerate. Hopefully this is rare. Find the pts that are
166 // farthest apart to compute a line (unless it is really a pt).
167 SkScalar maxD = SkPointPriv::DistanceToSqd(qPts[0], qPts[1]);
168 int maxEdge = 0;
169 SkScalar d = SkPointPriv::DistanceToSqd(qPts[1], qPts[2]);
170 if (d > maxD) {
171 maxD = d;
172 maxEdge = 1;
173 }
174 d = SkPointPriv::DistanceToSqd(qPts[2], qPts[0]);
175 if (d > maxD) {
176 maxD = d;
177 maxEdge = 2;
178 }
179 // We could have a tolerance here, not sure if it would improve anything
180 if (maxD > 0) {
181 // Set the matrix to give (u = 0, v = distance_to_line)
182 SkVector lineVec = qPts[(maxEdge + 1)%3] - qPts[maxEdge];
183 // when looking from the point 0 down the line we want positive
184 // distances to be to the left. This matches the non-degenerate
185 // case.
187 // first row
188 fM[0] = 0;
189 fM[1] = 0;
190 fM[2] = 0;
191 // second row
192 fM[3] = lineVec.fX;
193 fM[4] = lineVec.fY;
194 fM[5] = -lineVec.dot(qPts[maxEdge]);
195 } else {
196 // It's a point. It should cover zero area. Just set the matrix such
197 // that (u, v) will always be far away from the quad.
198 fM[0] = 0; fM[1] = 0; fM[2] = 100.f;
199 fM[3] = 0; fM[4] = 0; fM[5] = 100.f;
200 }
201 } else {
202 double scale = 1.0/det;
203
204 // compute adjugate matrix
205 double a3, a4, a6, a7;
206 a3 = y2-y0;
207 a4 = x0-x2;
208
209 a6 = y0-y1;
210 a7 = x1-x0;
211
212 // this performs the uv_pts*adjugate(control_pts) multiply,
213 // then does the scale by 1/det afterwards to improve precision
214 fM[0] = (float)((0.5*a3 + a6)*scale);
215 fM[1] = (float)((0.5*a4 + a7)*scale);
216 fM[2] = (float)((0.5*a5 + a8)*scale);
217 fM[3] = (float)(a6*scale);
218 fM[4] = (float)(a7*scale);
219 fM[5] = (float)(a8*scale);
220 }
221}
222
223////////////////////////////////////////////////////////////////////////////////
224
225// k = (y2 - y0, x0 - x2, x2*y0 - x0*y2)
226// l = (y1 - y0, x0 - x1, x1*y0 - x0*y1) * 2*w
227// m = (y2 - y1, x1 - x2, x2*y1 - x1*y2) * 2*w
228void GrPathUtils::getConicKLM(const SkPoint p[3], const SkScalar weight, SkMatrix* out) {
229 SkMatrix& klm = *out;
230 const SkScalar w2 = 2.f * weight;
231 klm[0] = p[2].fY - p[0].fY;
232 klm[1] = p[0].fX - p[2].fX;
233 klm[2] = p[2].fX * p[0].fY - p[0].fX * p[2].fY;
234
235 klm[3] = w2 * (p[1].fY - p[0].fY);
236 klm[4] = w2 * (p[0].fX - p[1].fX);
237 klm[5] = w2 * (p[1].fX * p[0].fY - p[0].fX * p[1].fY);
238
239 klm[6] = w2 * (p[2].fY - p[1].fY);
240 klm[7] = w2 * (p[1].fX - p[2].fX);
241 klm[8] = w2 * (p[2].fX * p[1].fY - p[1].fX * p[2].fY);
242
243 // scale the max absolute value of coeffs to 10
244 SkScalar scale = 0.f;
245 for (int i = 0; i < 9; ++i) {
246 scale = std::max(scale, SkScalarAbs(klm[i]));
247 }
248 SkASSERT(scale > 0.f);
249 scale = 10.f / scale;
250 for (int i = 0; i < 9; ++i) {
251 klm[i] *= scale;
252 }
253}
254
255////////////////////////////////////////////////////////////////////////////////
256
257namespace {
258
259// a is the first control point of the cubic.
260// ab is the vector from a to the second control point.
261// dc is the vector from the fourth to the third control point.
262// d is the fourth control point.
263// p is the candidate quadratic control point.
264// this assumes that the cubic doesn't inflect and is simple
265bool is_point_within_cubic_tangents(const SkPoint& a,
266 const SkVector& ab,
267 const SkVector& dc,
268 const SkPoint& d,
270 const SkPoint p) {
271 SkVector ap = p - a;
272 SkScalar apXab = ap.cross(ab);
274 if (apXab > 0) {
275 return false;
276 }
277 } else {
279 if (apXab < 0) {
280 return false;
281 }
282 }
283
284 SkVector dp = p - d;
285 SkScalar dpXdc = dp.cross(dc);
287 if (dpXdc < 0) {
288 return false;
289 }
290 } else {
292 if (dpXdc > 0) {
293 return false;
294 }
295 }
296 return true;
297}
298
299void convert_noninflect_cubic_to_quads(const SkPoint p[4],
300 SkScalar toleranceSqd,
302 int sublevel = 0,
303 bool preserveFirstTangent = true,
304 bool preserveLastTangent = true) {
305 // Notation: Point a is always p[0]. Point b is p[1] unless p[1] == p[0], in which case it is
306 // 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].
307 SkVector ab = p[1] - p[0];
308 SkVector dc = p[2] - p[3];
309
312 SkPoint* degQuad = quads->push_back_n(3);
313 degQuad[0] = p[0];
314 degQuad[1] = p[0];
315 degQuad[2] = p[3];
316 return;
317 }
318 ab = p[2] - p[0];
319 }
321 dc = p[1] - p[3];
322 }
323
324 static const SkScalar kLengthScale = 3 * SK_Scalar1 / 2;
325 static const int kMaxSubdivs = 10;
326
327 ab.scale(kLengthScale);
328 dc.scale(kLengthScale);
329
330 // c0 and c1 are extrapolations along vectors ab and dc.
331 SkPoint c0 = p[0] + ab;
332 SkPoint c1 = p[3] + dc;
333
334 SkScalar dSqd = sublevel > kMaxSubdivs ? 0 : SkPointPriv::DistanceToSqd(c0, c1);
335 if (dSqd < toleranceSqd) {
336 SkPoint newC;
337 if (preserveFirstTangent == preserveLastTangent) {
338 // We used to force a split when both tangents need to be preserved and c0 != c1.
339 // This introduced a large performance regression for tiny paths for no noticeable
340 // quality improvement. However, we aren't quite fulfilling our contract of guaranteeing
341 // the two tangent vectors and this could introduce a missed pixel in
342 // AAHairlinePathRenderer.
343 newC = (c0 + c1) * 0.5f;
344 } else if (preserveFirstTangent) {
345 newC = c0;
346 } else {
347 newC = c1;
348 }
349
350 SkPoint* pts = quads->push_back_n(3);
351 pts[0] = p[0];
352 pts[1] = newC;
353 pts[2] = p[3];
354 return;
355 }
356 SkPoint choppedPts[7];
357 SkChopCubicAtHalf(p, choppedPts);
358 convert_noninflect_cubic_to_quads(
359 choppedPts + 0, toleranceSqd, quads, sublevel + 1, preserveFirstTangent, false);
360 convert_noninflect_cubic_to_quads(
361 choppedPts + 3, toleranceSqd, quads, sublevel + 1, false, preserveLastTangent);
362}
363
364void convert_noninflect_cubic_to_quads_with_constraint(const SkPoint p[4],
365 SkScalar toleranceSqd,
368 int sublevel = 0) {
369 // Notation: Point a is always p[0]. Point b is p[1] unless p[1] == p[0], in which case it is
370 // 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].
371
372 SkVector ab = p[1] - p[0];
373 SkVector dc = p[2] - p[3];
374
377 SkPoint* degQuad = quads->push_back_n(3);
378 degQuad[0] = p[0];
379 degQuad[1] = p[0];
380 degQuad[2] = p[3];
381 return;
382 }
383 ab = p[2] - p[0];
384 }
386 dc = p[1] - p[3];
387 }
388
389 // When the ab and cd tangents are degenerate or nearly parallel with vector from d to a the
390 // constraint that the quad point falls between the tangents becomes hard to enforce and we are
391 // likely to hit the max subdivision count. However, in this case the cubic is approaching a
392 // line and the accuracy of the quad point isn't so important. We check if the two middle cubic
393 // control points are very close to the baseline vector. If so then we just pick quadratic
394 // points on the control polygon.
395
396 SkVector da = p[0] - p[3];
397 bool doQuads = SkPointPriv::LengthSqd(dc) < SK_ScalarNearlyZero ||
399 if (!doQuads) {
400 SkScalar invDALengthSqd = SkPointPriv::LengthSqd(da);
401 if (invDALengthSqd > SK_ScalarNearlyZero) {
402 invDALengthSqd = SkScalarInvert(invDALengthSqd);
403 // cross(ab, da)^2/length(da)^2 == sqd distance from b to line from d to a.
404 // same goes for point c using vector cd.
405 SkScalar detABSqd = ab.cross(da);
406 detABSqd = SkScalarSquare(detABSqd);
407 SkScalar detDCSqd = dc.cross(da);
408 detDCSqd = SkScalarSquare(detDCSqd);
409 if (detABSqd * invDALengthSqd < toleranceSqd &&
410 detDCSqd * invDALengthSqd < toleranceSqd) {
411 doQuads = true;
412 }
413 }
414 }
415 if (doQuads) {
416 SkPoint b = p[0] + ab;
417 SkPoint c = p[3] + dc;
418 SkPoint mid = b + c;
419 mid.scale(SK_ScalarHalf);
420 // Insert two quadratics to cover the case when ab points away from d and/or dc
421 // points away from a.
422 if (SkVector::DotProduct(da, dc) < 0 || SkVector::DotProduct(ab, da) > 0) {
423 SkPoint* qpts = quads->push_back_n(6);
424 qpts[0] = p[0];
425 qpts[1] = b;
426 qpts[2] = mid;
427 qpts[3] = mid;
428 qpts[4] = c;
429 qpts[5] = p[3];
430 } else {
431 SkPoint* qpts = quads->push_back_n(3);
432 qpts[0] = p[0];
433 qpts[1] = mid;
434 qpts[2] = p[3];
435 }
436 return;
437 }
438
439 static const SkScalar kLengthScale = 3 * SK_Scalar1 / 2;
440 static const int kMaxSubdivs = 10;
441
442 ab.scale(kLengthScale);
443 dc.scale(kLengthScale);
444
445 // c0 and c1 are extrapolations along vectors ab and dc.
446 SkVector c0 = p[0] + ab;
447 SkVector c1 = p[3] + dc;
448
449 SkScalar dSqd = sublevel > kMaxSubdivs ? 0 : SkPointPriv::DistanceToSqd(c0, c1);
450 if (dSqd < toleranceSqd) {
451 SkPoint cAvg = (c0 + c1) * 0.5f;
452 bool subdivide = false;
453
454 if (!is_point_within_cubic_tangents(p[0], ab, dc, p[3], dir, cAvg)) {
455 // choose a new cAvg that is the intersection of the two tangent lines.
457 SkScalar z0 = -ab.dot(p[0]);
459 SkScalar z1 = -dc.dot(p[3]);
460 cAvg.fX = ab.fY * z1 - z0 * dc.fY;
461 cAvg.fY = z0 * dc.fX - ab.fX * z1;
462 SkScalar z = ab.fX * dc.fY - ab.fY * dc.fX;
463 z = sk_ieee_float_divide(1.0f, z);
464 cAvg.fX *= z;
465 cAvg.fY *= z;
466 if (sublevel <= kMaxSubdivs) {
467 SkScalar d0Sqd = SkPointPriv::DistanceToSqd(c0, cAvg);
468 SkScalar d1Sqd = SkPointPriv::DistanceToSqd(c1, cAvg);
469 // We need to subdivide if d0 + d1 > tolerance but we have the sqd values. We know
470 // the distances and tolerance can't be negative.
471 // (d0 + d1)^2 > toleranceSqd
472 // d0Sqd + 2*d0*d1 + d1Sqd > toleranceSqd
473 SkScalar d0d1 = SkScalarSqrt(d0Sqd * d1Sqd);
474 subdivide = 2 * d0d1 + d0Sqd + d1Sqd > toleranceSqd;
475 }
476 }
477 if (!subdivide) {
478 SkPoint* pts = quads->push_back_n(3);
479 pts[0] = p[0];
480 pts[1] = cAvg;
481 pts[2] = p[3];
482 return;
483 }
484 }
485 SkPoint choppedPts[7];
486 SkChopCubicAtHalf(p, choppedPts);
487 convert_noninflect_cubic_to_quads_with_constraint(
488 choppedPts + 0, toleranceSqd, dir, quads, sublevel + 1);
489 convert_noninflect_cubic_to_quads_with_constraint(
490 choppedPts + 3, toleranceSqd, dir, quads, sublevel + 1);
491}
492} // namespace
493
495 SkScalar tolScale,
496 TArray<SkPoint, true>* quads) {
497 if (!p[0].isFinite() || !p[1].isFinite() || !p[2].isFinite() || !p[3].isFinite()) {
498 return;
499 }
500 if (!SkIsFinite(tolScale)) {
501 return;
502 }
503 SkPoint chopped[10];
504 int count = SkChopCubicAtInflections(p, chopped);
505
506 const SkScalar tolSqd = SkScalarSquare(tolScale);
507
508 for (int i = 0; i < count; ++i) {
509 SkPoint* cubic = chopped + 3*i;
510 convert_noninflect_cubic_to_quads(cubic, tolSqd, quads);
511 }
512}
513
515 SkScalar tolScale,
517 TArray<SkPoint, true>* quads) {
518 if (!p[0].isFinite() || !p[1].isFinite() || !p[2].isFinite() || !p[3].isFinite()) {
519 return;
520 }
521 if (!SkIsFinite(tolScale)) {
522 return;
523 }
524 SkPoint chopped[10];
525 int count = SkChopCubicAtInflections(p, chopped);
526
527 const SkScalar tolSqd = SkScalarSquare(tolScale);
528
529 for (int i = 0; i < count; ++i) {
530 SkPoint* cubic = chopped + 3*i;
531 convert_noninflect_cubic_to_quads_with_constraint(cubic, tolSqd, dir, quads);
532 }
533}
int count
Definition: FontMgrTest.cpp:50
static float tolerance_to_wangs_precision(float srcTol)
Definition: GrPathUtils.cpp:25
uint32_t max_bezier_vertices(uint32_t chopCount)
Definition: GrPathUtils.cpp:35
static const SkScalar kMinCurveTol
Definition: GrPathUtils.cpp:23
static const int points[]
static bool isFinite(const SkRect &r)
Definition: MathBench.cpp:230
#define SkASSERT(cond)
Definition: SkAssert.h:116
static bool SkIsFinite(T x, Pack... values)
static constexpr float sk_ieee_float_divide(float numer, float denom)
void SkChopCubicAtHalf(const SkPoint src[4], SkPoint dst[7])
Definition: SkGeometry.cpp:575
int SkChopCubicAtInflections(const SkPoint src[4], SkPoint dst[10])
Definition: SkGeometry.cpp:755
static SkPoint * subdivide(const SkConic &src, SkPoint pts[], int level)
SkPathFirstDirection
Definition: SkPathEnums.h:19
#define SkScalarInvert(x)
Definition: SkScalar.h:73
static bool SkScalarNearlyZero(SkScalar x, SkScalar tolerance=SK_ScalarNearlyZero)
Definition: SkScalar.h:101
#define SK_Scalar1
Definition: SkScalar.h:18
#define SkScalarAve(a, b)
Definition: SkScalar.h:74
#define SK_ScalarHalf
Definition: SkScalar.h:19
#define SK_ScalarNearlyZero
Definition: SkScalar.h:99
#define SkScalarSqrt(x)
Definition: SkScalar.h:42
static SkScalar SkScalarSquare(SkScalar x)
Definition: SkScalar.h:71
#define SkScalarAbs(x)
Definition: SkScalar.h:39
void set(const SkPoint controlPts[3])
SkScalar mapRadius(SkScalar radius) const
Definition: SkMatrix.cpp:1170
SkMatrix & postConcat(const SkMatrix &other)
Definition: SkMatrix.cpp:683
SkMatrix & setTranslate(SkScalar dx, SkScalar dy)
Definition: SkMatrix.cpp:254
SkScalar getMaxScale() const
Definition: SkMatrix.cpp:1531
static SkPoint MakeOrthog(const SkPoint &vec, Side side=kLeft_Side)
Definition: SkPointPriv.h:96
static SkScalar LengthSqd(const SkPoint &pt)
Definition: SkPointPriv.h:63
static SkScalar DistanceToLineSegmentBetweenSqd(const SkPoint &pt, const SkPoint &a, const SkPoint &b)
Definition: SkPoint.cpp:126
static SkScalar DistanceToSqd(const SkPoint &pt, const SkPoint &a)
Definition: SkPointPriv.h:48
T * push_back_n(int n)
Definition: SkTArray.h:267
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition: main.cc:19
float SkScalar
Definition: extension.cpp:12
static bool b
struct MyStruct s
struct MyStruct a[10]
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
void convertCubicToQuads(const SkPoint p[4], SkScalar tolScale, skia_private::TArray< SkPoint, true > *quads)
uint32_t generateCubicPoints(const SkPoint &p0, const SkPoint &p1, const SkPoint &p2, const SkPoint &p3, SkScalar tolSqd, SkPoint **points, uint32_t pointsLeft)
uint32_t quadraticPointCount(const SkPoint points[], SkScalar tol)
Definition: GrPathUtils.cpp:73
uint32_t cubicPointCount(const SkPoint points[], SkScalar tol)
SkScalar scaleToleranceToSrc(SkScalar devTol, const SkMatrix &viewM, const SkRect &pathBounds)
Definition: GrPathUtils.cpp:41
void convertCubicToQuadsConstrainToTangents(const SkPoint p[4], SkScalar tolScale, SkPathFirstDirection dir, skia_private::TArray< SkPoint, true > *quads)
void getConicKLM(const SkPoint p[3], const SkScalar weight, SkMatrix *klm)
static const int kMaxPointsPerCurve
Definition: GrPathUtils.h:35
uint32_t generateQuadraticPoints(const SkPoint &p0, const SkPoint &p1, const SkPoint &p2, SkScalar tolSqd, SkPoint **points, uint32_t pointsLeft)
Definition: GrPathUtils.cpp:78
Definition: ab.py:1
const CatchEntryMove ab[]
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets dir
Definition: switches.h:145
AI int cubic_log2(float precision, const SkPoint pts[], const VectorXform &vectorXform=VectorXform())
Definition: WangsFormula.h:203
AI int quadratic_log2(float precision, const SkPoint pts[], const VectorXform &vectorXform=VectorXform())
Definition: WangsFormula.h:164
AI float cubic(float precision, const SkPoint pts[], const VectorXform &vectorXform=VectorXform())
Definition: WangsFormula.h:195
const Scalar scale
float fX
x-axis value
Definition: SkPoint_impl.h:164
static float DotProduct(const SkVector &a, const SkVector &b)
Definition: SkPoint_impl.h:518
float dot(const SkVector &vec) const
Definition: SkPoint_impl.h:554
float cross(const SkVector &vec) const
Definition: SkPoint_impl.h:545
void scale(float scale, SkPoint *dst) const
Definition: SkPoint.cpp:17
float fY
y-axis value
Definition: SkPoint_impl.h:165
SkScalar fBottom
larger y-axis bounds
Definition: extension.cpp:17
SkScalar fLeft
smaller x-axis bounds
Definition: extension.cpp:14
SkScalar fRight
larger x-axis bounds
Definition: extension.cpp:16
constexpr float height() const
Definition: SkRect.h:769
constexpr float width() const
Definition: SkRect.h:762
SkScalar fTop
smaller y-axis bounds
Definition: extension.cpp:15