Flutter Engine
The Flutter Engine
GeometryTest.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
11#include "include/core/SkSpan.h"
14#include "src/base/SkRandom.h"
15#include "src/core/SkGeometry.h"
17#include "tests/Test.h"
18
19#include <array>
20#include <cmath>
21#include <cstdlib>
22#include <limits>
23#include <string>
24
25static bool nearly_equal(const SkPoint& a, const SkPoint& b) {
26 return SkScalarNearlyEqual(a.fX, b.fX) && SkScalarNearlyEqual(a.fY, b.fY);
27}
28
30 /*
31 Inspired by this test, which used to assert that the tValues had dups
32
33 <path stroke="#202020" d="M0,0 C0,0 1,1 2190,5130 C2190,5070 2220,5010 2205,4980" />
34 */
35 const SkPoint src[] = {
36 { SkIntToScalar(2190), SkIntToScalar(5130) },
37 { SkIntToScalar(2190), SkIntToScalar(5070) },
38 { SkIntToScalar(2220), SkIntToScalar(5010) },
39 { SkIntToScalar(2205), SkIntToScalar(4980) },
40 };
41 SkPoint dst[13];
42 SkScalar tValues[3];
43 // make sure we don't assert internally
44 int count = SkChopCubicAtMaxCurvature(src, dst, tValues);
45 if ((false)) { // avoid bit rot, suppress warning
47 }
48 // Make sure src and dst can be the same pointer.
49 {
50 SkPoint pts[7];
51 for (int i = 0; i < 7; ++i) {
52 pts[i].set(i, i);
53 }
54 SkChopCubicAt(pts, pts, .5f);
55 for (int i = 0; i < 7; ++i) {
56 REPORTER_ASSERT(reporter, pts[i].fX == pts[i].fY);
57 REPORTER_ASSERT(reporter, pts[i].fX == i * .5f);
58 }
59 }
60
61 static const float chopTs[] = {
62 0, 3/83.f, 3/79.f, 3/73.f, 3/71.f, 3/67.f, 3/61.f, 3/59.f, 3/53.f, 3/47.f, 3/43.f, 3/41.f,
63 3/37.f, 3/31.f, 3/29.f, 3/23.f, 3/19.f, 3/17.f, 3/13.f, 3/11.f, 3/7.f, 3/5.f, 1,
64 };
65 float ones[] = {1,1,1,1,1};
66
67 // Ensure an odd number of T values so we exercise the single chop code at the end of
68 // SkChopCubicAt form multiple T.
69 static_assert(std::size(chopTs) % 2 == 1);
70 static_assert(std::size(ones) % 2 == 1);
71
72 SkRandom rand;
73 for (int iterIdx = 0; iterIdx < 5; ++iterIdx) {
74 SkPoint pts[4] = {{rand.nextF(), rand.nextF()}, {rand.nextF(), rand.nextF()},
75 {rand.nextF(), rand.nextF()}, {rand.nextF(), rand.nextF()}};
76
77 SkPoint allChops[4 + std::size(chopTs)*3];
78 SkChopCubicAt(pts, allChops, chopTs, std::size(chopTs));
79 int i = 3;
80 for (float chopT : chopTs) {
81 // Ensure we chop at approximately the correct points when we chop an entire list.
82 SkPoint expectedPt;
83 SkEvalCubicAt(pts, chopT, &expectedPt, nullptr, nullptr);
84 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(allChops[i].x(), expectedPt.x()));
85 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(allChops[i].y(), expectedPt.y()));
86 if (chopT == 0) {
87 REPORTER_ASSERT(reporter, allChops[i] == pts[0]);
88 }
89 if (chopT == 1) {
90 REPORTER_ASSERT(reporter, allChops[i] == pts[3]);
91 }
92 i += 3;
93
94 // Ensure the middle is exactly degenerate when we chop at two equal points.
95 SkPoint localChops[10];
96 SkChopCubicAt(pts, localChops, chopT, chopT);
97 REPORTER_ASSERT(reporter, localChops[3] == localChops[4]);
98 REPORTER_ASSERT(reporter, localChops[3] == localChops[5]);
99 REPORTER_ASSERT(reporter, localChops[3] == localChops[6]);
100 if (chopT == 0) {
101 // Also ensure the first curve is exactly p0 when we chop at T=0.
102 REPORTER_ASSERT(reporter, localChops[0] == pts[0]);
103 REPORTER_ASSERT(reporter, localChops[1] == pts[0]);
104 REPORTER_ASSERT(reporter, localChops[2] == pts[0]);
105 REPORTER_ASSERT(reporter, localChops[3] == pts[0]);
106 }
107 if (chopT == 1) {
108 // Also ensure the last curve is exactly p3 when we chop at T=1.
109 REPORTER_ASSERT(reporter, localChops[6] == pts[3]);
110 REPORTER_ASSERT(reporter, localChops[7] == pts[3]);
111 REPORTER_ASSERT(reporter, localChops[8] == pts[3]);
112 REPORTER_ASSERT(reporter, localChops[9] == pts[3]);
113 }
114 }
115
116 // Now test what happens when SkChopCubicAt does 0/0 and gets NaN values.
117 SkPoint oneChops[4 + std::size(ones)*3];
118 SkChopCubicAt(pts, oneChops, ones, std::size(ones));
119 REPORTER_ASSERT(reporter, oneChops[0] == pts[0]);
120 REPORTER_ASSERT(reporter, oneChops[1] == pts[1]);
121 REPORTER_ASSERT(reporter, oneChops[2] == pts[2]);
122 for (size_t index = 3; index < std::size(oneChops); ++index) {
123 REPORTER_ASSERT(reporter, oneChops[index] == pts[3]);
124 }
125 }
126}
127
128static void check_pairs(skiatest::Reporter* reporter, int index, SkScalar t, const char name[],
129 SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1) {
130 bool eq = SkScalarNearlyEqual(x0, x1) && SkScalarNearlyEqual(y0, y1);
131 if (!eq) {
132 SkDebugf("%s [%d %g] p0 [%10.8f %10.8f] p1 [%10.8f %10.8f]\n",
133 name, index, t, x0, y0, x1, y1);
135 }
136}
137
139 SkRandom rand;
140 for (int i = 0; i < 1000; ++i) {
141 SkPoint pts[3];
142 for (int j = 0; j < 3; ++j) {
143 pts[j].set(rand.nextSScalar1() * 100, rand.nextSScalar1() * 100);
144 }
145 const SkScalar dt = SK_Scalar1 / 128;
146 SkScalar t = dt;
147 for (int j = 1; j < 128; ++j) {
148 SkPoint r0;
149 SkEvalQuadAt(pts, t, &r0);
150 SkPoint r1 = SkEvalQuadAt(pts, t);
151 check_pairs(reporter, i, t, "quad-pos", r0.fX, r0.fY, r1.fX, r1.fY);
152
153 SkVector v0;
154 SkEvalQuadAt(pts, t, nullptr, &v0);
155 SkVector v1 = SkEvalQuadTangentAt(pts, t);
156 check_pairs(reporter, i, t, "quad-tan", v0.fX, v0.fY, v1.fX, v1.fY);
157
158 t += dt;
159 }
160 }
161}
162
164 SkPoint p0, p1;
165 conic.evalAt(t, &p0, nullptr);
166 p1 = conic.evalAt(t);
167 check_pairs(reporter, 0, t, "conic-pos", p0.fX, p0.fY, p1.fX, p1.fY);
168}
169
171 SkVector v0, v1;
172 conic.evalAt(t, nullptr, &v0);
173 v1 = conic.evalTangentAt(t);
174 check_pairs(reporter, 0, t, "conic-tan", v0.fX, v0.fY, v1.fX, v1.fY);
175}
176
178 SkRandom rand;
179 for (int i = 0; i < 1000; ++i) {
180 SkPoint pts[3];
181 for (int j = 0; j < 3; ++j) {
182 pts[j].set(rand.nextSScalar1() * 100, rand.nextSScalar1() * 100);
183 }
184 for (int k = 0; k < 10; ++k) {
185 SkScalar w = rand.nextUScalar1() * 2;
186 SkConic conic(pts, w);
187
188 const SkScalar dt = SK_Scalar1 / 128;
189 SkScalar t = dt;
190 for (int j = 1; j < 128; ++j) {
193 t += dt;
194 }
195 }
196 }
197}
198
200 SkPoint pts[] = {
201 {10, 20}, {10, 20}, {20, 30},
202 {10, 20}, {15, 25}, {20, 30},
203 {10, 20}, {20, 30}, {20, 30},
204 };
205 int count = (int) std::size(pts) / 3;
206 for (int index = 0; index < count; ++index) {
207 SkConic conic(&pts[index * 3], 0.707f);
208 SkVector start = SkEvalQuadTangentAt(&pts[index * 3], 0);
209 SkVector mid = SkEvalQuadTangentAt(&pts[index * 3], .5f);
210 SkVector end = SkEvalQuadTangentAt(&pts[index * 3], 1);
212 REPORTER_ASSERT(reporter, mid.fX && mid.fY);
213 REPORTER_ASSERT(reporter, end.fX && end.fY);
216 }
217}
218
220 SkPoint pts[] = {
221 { 10, 20}, {10, 20}, {20, 30},
222 { 10, 20}, {15, 25}, {20, 30},
223 { 10, 20}, {20, 30}, {20, 30}
224 };
225 int count = (int) std::size(pts) / 3;
226 for (int index = 0; index < count; ++index) {
227 SkConic conic(&pts[index * 3], 0.707f);
228 SkVector start = conic.evalTangentAt(0);
229 SkVector mid = conic.evalTangentAt(.5f);
230 SkVector end = conic.evalTangentAt(1);
232 REPORTER_ASSERT(reporter, mid.fX && mid.fY);
233 REPORTER_ASSERT(reporter, end.fX && end.fY);
236 }
237}
238
240 SkAutoConicToQuads quadder;
241 const SkPoint* qpts = quadder.computeQuads(pts, w, 0.25);
242 const int qcount = quadder.countQuads();
243 const int pcount = qcount * 2 + 1;
244
245 REPORTER_ASSERT(r, SkPointPriv::AreFinite(qpts, pcount));
246}
247
248/**
249 * We need to ensure that when a conic is approximated by quads, that we always return finite
250 * values in the quads.
251 *
252 * Inspired by crbug_627414
253 */
255 const SkPoint triples[] = {
256 { 0, 0 }, { 1, 0 }, { 1, 1 },
257 { 0, 0 }, { 3.58732e-43f, 2.72084f }, { 3.00392f, 3.00392f },
258 { 0, 0 }, { 100000, 0 }, { 100000, 100000 },
259 { 0, 0 }, { 1e30f, 0 }, { 1e30f, 1e30f },
260 };
261 const int N = sizeof(triples) / sizeof(SkPoint);
262
263 for (int i = 0; i < N; i += 3) {
264 const SkPoint* pts = &triples[i];
265
266 SkScalar w = 1e30f;
267 do {
268 w *= 2;
270 } while (SkIsFinite(w));
272 }
273}
274
276 SkPoint pts[] = {
277 { 10, 20}, {10, 20}, {20, 30}, {30, 40},
278 { 10, 20}, {15, 25}, {20, 30}, {30, 40},
279 { 10, 20}, {20, 30}, {30, 40}, {30, 40},
280 };
281 int count = (int) std::size(pts) / 4;
282 for (int index = 0; index < count; ++index) {
283 SkConic conic(&pts[index * 3], 0.707f);
284 SkVector start, mid, end;
285 SkEvalCubicAt(&pts[index * 4], 0, nullptr, &start, nullptr);
286 SkEvalCubicAt(&pts[index * 4], .5f, nullptr, &mid, nullptr);
287 SkEvalCubicAt(&pts[index * 4], 1, nullptr, &end, nullptr);
289 REPORTER_ASSERT(reporter, mid.fX && mid.fY);
290 REPORTER_ASSERT(reporter, end.fX && end.fY);
293 }
294}
295
297 const std::array<SkPoint, 4>& bezierPoints, SkCubicType expectedType,
298 bool undefined = false) {
299 // Classify the cubic even if the results will be undefined: check for crashes and asserts.
300 SkCubicType actualType = SkClassifyCubic(bezierPoints.data());
301 if (!undefined) {
302 REPORTER_ASSERT(reporter, actualType == expectedType,
303 "%d != %d", (int)actualType, (int)expectedType);
304 }
305}
306
308 float x1, float y1, float x2, float y2,
309 bool undefined = false) {
311 static constexpr SkCubicType expectations[24] = {
336 };
337 SkPoint points[] = {{x1, y1}, {x2, y1}, {x2, y2}, {x1, y2}};
338 std::array<SkPoint, 4> bezier;
339 for (int i=0; i < 4; ++i) {
340 bezier[0] = points[i];
341 for (int j=0; j < 3; ++j) {
342 int jidx = (j < i) ? j : j+1;
343 bezier[1] = points[jidx];
344 for (int k=0, kidx=0; k < 2; ++k, ++kidx) {
345 for (int n = 0; n < 2; ++n) {
346 kidx = (kidx == i || kidx == jidx) ? kidx+1 : kidx;
347 }
348 bezier[2] = points[kidx];
349 for (int l = 0; l < 4; ++l) {
350 if (l != i && l != jidx && l != kidx) {
351 bezier[3] = points[l];
352 break;
353 }
354 }
355 check_cubic_type(reporter, bezier, expectations[i*6 + j*2 + k], undefined);
356 }
357 }
358 }
359 for (int i=0; i < 4; ++i) {
360 bezier[0] = points[i];
361 for (int j=0; j < 3; ++j) {
362 int jidx = (j < i) ? j : j+1;
363 bezier[1] = points[jidx];
364 bezier[2] = points[jidx];
365 for (int k=0, kidx=0; k < 2; ++k, ++kidx) {
366 for (int n = 0; n < 2; ++n) {
367 kidx = (kidx == i || kidx == jidx) ? kidx+1 : kidx;
368 }
369 bezier[3] = points[kidx];
371 }
372 }
373 }
374}
375
376static std::array<SkPoint, 4> kSerpentines[] = {
377 {{{149.325f, 107.705f}, {149.325f, 103.783f}, {151.638f, 100.127f}, {156.263f, 96.736f}}},
378 {{{225.694f, 223.15f}, {209.831f, 224.837f}, {195.994f, 230.237f}, {184.181f, 239.35f}}},
379 {{{4.873f, 5.581f}, {5.083f, 5.2783f}, {5.182f, 4.8593f}, {5.177f, 4.3242f}}},
380 {{{285.625f, 499.687f}, {411.625f, 808.188f}, {1064.62f, 135.688f}, {1042.63f, 585.187f}}}
381};
382
383static std::array<SkPoint, 4> kLoops[] = {
384 {{{635.625f, 614.687f}, {171.625f, 236.188f}, {1064.62f, 135.688f}, {516.625f, 570.187f}}},
385 {{{653.050f, 725.049f}, {663.000f, 176.000f}, {1189.000f, 508.000f}, {288.050f, 564.950f}}},
386 {{{631.050f, 478.049f}, {730.000f, 302.000f}, {870.000f, 350.000f}, {905.050f, 528.950f}}},
387 {{{631.050f, 478.0499f}, {221.000f, 230.000f}, {1265.000f, 451.000f}, {905.050f, 528.950f}}}
388};
389
390static std::array<SkPoint, 4> kLinearCubics[] = {
391 {{{0, 0}, {0, 1}, {0, 2}, {0, 3}}}, // 0-degree flat line.
392 {{{0, 0}, {1, 0}, {1, 0}, {0, 0}}}, // 180-degree flat line
393 {{{0, 1}, {0, 0}, {0, 2}, {0, 3}}}, // 180-degree flat line
394 {{{0, 1}, {0, 0}, {0, 3}, {0, 2}}}, // 360-degree flat line
395 {{{0, 0}, {2, 0}, {1, 0}, {64, 0}}}, // 360-degree flat line
396 {{{1, 0}, {0, 0}, {3, 0}, {-64, 0}}} // 360-degree flat line
397};
398
400 for (const auto& serp : kSerpentines) {
402 }
403 for (const auto& loop : kLoops) {
405 }
406 for (const auto& loop : kLinearCubics) {
408 }
409 check_cubic_around_rect("small box", reporter, 0, 0, 1, 1);
410 check_cubic_around_rect("biggest box", reporter,
415 check_cubic_around_rect("large quadrant", reporter, 1, 1,
418 check_cubic_around_rect("smallest box", reporter,
423 check_cubic_around_rect("slightly negative box",reporter,
424 +1, -std::numeric_limits<float>::min(), -1, -1);
425 check_cubic_around_rect("infinite box", reporter,
426 -std::numeric_limits<float>::infinity(),
427 -std::numeric_limits<float>::infinity(),
428 +std::numeric_limits<float>::infinity(),
429 +std::numeric_limits<float>::infinity(),
430 true);
431 check_cubic_around_rect("one sided infinite box", reporter,
432 0, 0, 1, +std::numeric_limits<float>::infinity(), true);
434 -std::numeric_limits<float>::quiet_NaN(),
435 -std::numeric_limits<float>::quiet_NaN(),
436 +std::numeric_limits<float>::quiet_NaN(),
437 +std::numeric_limits<float>::quiet_NaN(),
438 true);
439 check_cubic_around_rect("partial nan box", reporter,
440 0, 0, 1, +std::numeric_limits<float>::quiet_NaN(), true);
441}
442
443static std::array<SkPoint, 4> kCusps[] = {
444 {{{0, 0}, {1, 1}, {1, 0}, {0, 1}}},
445 {{{0, 0}, {1, 1}, {0, 1}, {1, 0}}},
446 {{{0, 1}, {1, 0}, {0, 0}, {1, 1}}},
447 {{{0, 1}, {1, 0}, {1, 1}, {0, 0}}},
448};
449
451 std::array<SkPoint, 4> noCusps[] = {
452 {{{0, 0}, {1, 1}, {2, 2}, {3, 3}}},
453 {{{0, 0}, {1, 0}, {1, 1}, {0, 1}}},
454 {{{0, 0}, {1, 0}, {2, 1}, {2, 2}}},
455 {{{0, 0}, {1, 0}, {1, 1}, {2, 1}}},
456 };
457 for (auto noCusp : noCusps) {
458 REPORTER_ASSERT(reporter, SkFindCubicCusp(noCusp.data()) < 0);
459 }
460 for (auto cusp : kCusps) {
461 REPORTER_ASSERT(reporter, SkFindCubicCusp(cusp.data()) > 0);
462 }
463}
464
466 SkMatrix::MakeAll(1,0,0, 0,1,0, 0,0,1),
467 SkMatrix::MakeAll(1,-1,0, 1,1,0, 0,0,1),
468 SkMatrix::MakeAll(.889f,.553f,0, -.443f,.123f,0, 0,0,1),
469};
470
472 constexpr float kTolerance = 1e-3f;
473 for (const SkMatrix& m : kSkewMatrices) {
474 SkPoint mapped[3];
475 m.mapPoints(mapped, pts, 3);
476 float fullRotation = SkMeasureQuadRotation(pts);
477 SkPoint chopped[5];
478 SkChopQuadAtMidTangent(pts, chopped);
479 float leftRotation = SkMeasureQuadRotation(chopped);
480 float rightRotation = SkMeasureQuadRotation(chopped+2);
481 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(leftRotation, fullRotation/2, kTolerance));
482 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rightRotation, fullRotation/2, kTolerance));
483 }
484}
485
487 SkCubicType cubicType) {
488 constexpr float kTolerance = 1e-3f;
489 int n = std::size(kSkewMatrices);
490 if (cubicType == SkCubicType::kLocalCusp || cubicType == SkCubicType::kLineOrPoint) {
491 // FP precision isn't always enough to get the exact correct T value of the mid-tangent on
492 // cusps and lines. Only test the identity matrix and the matrix with all 1's.
493 n = 2;
494 }
495 for (int i = 0; i < n; ++i) {
496 SkPoint mapped[4];
497 kSkewMatrices[i].mapPoints(mapped, pts, 4);
498 float fullRotation = SkMeasureNonInflectCubicRotation(mapped);
499 SkPoint chopped[7];
500 SkChopCubicAtMidTangent(mapped, chopped);
501 float leftRotation = SkMeasureNonInflectCubicRotation(chopped);
502 float rightRotation = SkMeasureNonInflectCubicRotation(chopped+3);
503 if (cubicType == SkCubicType::kLineOrPoint &&
504 (SkScalarNearlyEqual(fullRotation, 2*SK_ScalarPI, kTolerance) ||
505 SkScalarNearlyEqual(fullRotation, 0, kTolerance))) {
506 // 0- and 360-degree flat lines don't have single points of midtangent.
507 // (tangent == midtangent at every point on these curves except the cusp points.)
508 // Instead verify the promise from SkChopCubicAtMidTangent that neither side will rotate
509 // more than 180 degrees.
512 continue;
513 }
514 float expectedChoppedRotation = fullRotation/2;
515 if (cubicType == SkCubicType::kLocalCusp ||
516 (cubicType == SkCubicType::kLineOrPoint &&
517 SkScalarNearlyEqual(fullRotation, SK_ScalarPI, kTolerance))) {
518 // If we chop a cubic at a cusp, we lose 180 degrees of rotation.
519 expectedChoppedRotation = (fullRotation - SK_ScalarPI)/2;
520 }
521 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(leftRotation, expectedChoppedRotation,
522 kTolerance));
523 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(rightRotation, expectedChoppedRotation,
524 kTolerance));
525 }
526}
527
528static std::array<SkPoint, 3> kQuads[] = {
529 {{{10, 20}, {15, 35}, {30, 40}}},
530 {{{176.324f, 392.705f}, {719.325f, 205.782f}, {297.263f, 347.735f}}},
531 {{{652.050f, 602.049f}, {481.000f, 533.000f}, {288.050f, 564.950f}}},
532 {{{460.625f, 557.187f}, {707.121f, 209.688f}, {779.628f, 577.687f}}},
533 {{{359.050f, 578.049f}, {759.000f, 274.000f}, {288.050f, 564.950f}}}
534};
535
536SkPoint lerp(const SkPoint& a, const SkPoint& b, float t) {
537 return a * (1 - t) + b * t;
538}
539
541 static SkPoint kFlatCubic[4] = {{0, 0}, {0, 1}, {0, 2}, {0, 3}};
543
544 static SkPoint kFlatCubic180_1[4] = {{0, 0}, {1, 0}, {3, 0}, {2, 0}};
546 SK_ScalarPI));
547
548 static SkPoint kFlatCubic180_2[4] = {{0, 1}, {0, 0}, {0, 2}, {0, 3}};
550 SK_ScalarPI));
551
552 static SkPoint kFlatCubic360[4] = {{0, 1}, {0, 0}, {0, 3}, {0, 2}};
554 2*SK_ScalarPI));
555
556 static SkPoint kSquare180[4] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
558 SK_ScalarPI));
559
560 auto checkQuadRotation = [=](const SkPoint pts[3], float expectedRotation) {
561 float r = SkMeasureQuadRotation(pts);
562 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(r, expectedRotation));
563
564 SkPoint cubic1[4] = {pts[0], pts[0], pts[1], pts[2]};
566 expectedRotation));
567
568 SkPoint cubic2[4] = {pts[0], pts[1], pts[1], pts[2]};
570 expectedRotation));
571
572 SkPoint cubic3[4] = {pts[0], pts[1], pts[2], pts[2]};
574 expectedRotation));
575 };
576
577 static SkPoint kFlatQuad[4] = {{0, 0}, {0, 1}, {0, 2}};
578 checkQuadRotation(kFlatQuad, 0);
579
580 static SkPoint kFlatQuad180_1[4] = {{1, 0}, {0, 0}, {2, 0}};
581 checkQuadRotation(kFlatQuad180_1, SK_ScalarPI);
582
583 static SkPoint kFlatQuad180_2[4] = {{0, 0}, {0, 2}, {0, 1}};
584 checkQuadRotation(kFlatQuad180_2, SK_ScalarPI);
585
586 static SkPoint kTri120[3] = {{0, 0}, {.5f, std::sqrt(3.f)/2}, {1, 0}};
587 checkQuadRotation(kTri120, 2*SK_ScalarPI/3);
588}
589
591 SkPoint chops[10];
592 for (const auto& serp : kSerpentines) {
594 int n = SkChopCubicAtInflections(serp.data(), chops);
595 for (int i = 0; i < n; ++i) {
597 }
598 }
599 for (const auto& loop : kLoops) {
602 }
603 for (const auto& line : kLinearCubics) {
606 }
607 for (const auto& cusp : kCusps) {
610 }
611 for (const auto& quad : kQuads) {
613 SkPoint asCubic[4] = {
614 quad[0], lerp(quad[0], quad[1], 2/3.f), lerp(quad[1], quad[2], 1/3.f), quad[2]};
616 }
617
618 static const SkPoint kExactQuad[4] = {{0,0}, {6,2}, {10,2}, {12,0}};
621
622 static const SkPoint kExactCuspAtInf[4] = {{0,0}, {1,0}, {0,1}, {1,1}};
624 int n = SkChopCubicAtInflections(kExactCuspAtInf, chops);
625 for (int i = 0; i < n; ++i) {
627 }
628}
629
630DEF_TEST(Geometry, reporter) {
631 SkPoint pts[5];
632
633 pts[0].set(0, 0);
634 pts[1].set(100, 50);
635 pts[2].set(0, 100);
636
637 int count = SkChopQuadAtMaxCurvature(pts, pts); // Ensure src and dst can be the same pointer.
638 REPORTER_ASSERT(reporter, count == 1 || count == 2);
639
640 // This previously crashed because the computed t of max curvature is NaN and SkChopQuadAt
641 // asserts that the passed t is in 0..1. Passes by not asserting.
642 pts[0].set(15.1213f, 7.77647f);
643 pts[1].set(6.2168e+19f, 1.51338e+20f);
644 pts[2].set(1.4579e+19f, 1.55558e+21f);
646
647 pts[0].set(0, 0);
648 pts[1].set(3, 0);
649 pts[2].set(3, 3);
650 SkConvertQuadToCubic(pts, pts);
651 const SkPoint cubic[] = {
652 { 0, 0, }, { 2, 0, }, { 3, 1, }, { 3, 3 },
653 };
654 for (int i = 0; i < 4; ++i) {
656 }
657
669}
670
672 SkSpan<const SkPoint> curveInputs, SkScalar yToChopAt,
673 SkSpan<const SkPoint> expectedOutputs) {
675 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(expectedOutputs[3].y(), yToChopAt),
676 "Invalid test case. 4th point's Y should be %f", yToChopAt);
677
678 SkPoint outputs[7];
679 // Make sure it actually chopped
680 REPORTER_ASSERT(reporter, SkChopMonoCubicAtY(curveInputs.begin(), yToChopAt, outputs));
681
682 for (int i = 0; i < 7; ++i) {
683 REPORTER_ASSERT(reporter, nearly_equal(expectedOutputs[i], outputs[i]),
684 "(%f, %f) != (%f, %f) at index %d",
685 expectedOutputs[i].x(), expectedOutputs[i].y(),
686 outputs[i].x(), outputs[i].y(), i);
687 }
688}
689
690DEF_TEST(GeometryChopMonoCubicAtY_Successful, reporter) {
691 // These cubics are all arbitrary, picked using Desmos for something that looked "nice".
692
693 testChopMonoCubicAtY(reporter, "straight, positive slope @ 2.5",
694 {{ 0, 0 }, { 0, 0 }, { 10, 10 }, { 10, 10 }},
695 2.5f,
696 {{ 0.000000f, 0.000000f }, { 0.000000f, 0.000000f }, { 1.065055f, 1.065055f },
697 { 2.500000f, 2.500000f },
698 { 5.461981f, 5.461981f }, { 10.000000f, 10.000000f }, { 10.000000f, 10.000000f }}
699 );
700 testChopMonoCubicAtY(reporter, "straight, positive slope @ 5.0",
701 {{ 0, 0 }, { 0, 0 }, { 10, 10 }, { 10, 10 }},
702 5.0f,
703 {{ 0.000000f, 0.000000f }, { 0.000000f, 0.000000f }, { 2.500000f, 2.500000f },
704 { 5.000000f, 5.000000f },
705 { 7.500000f, 7.500000f }, { 10.000000f, 10.000000f }, { 10.000000f, 10.000000f }}
706 );
707 testChopMonoCubicAtY(reporter, "straight, positive slope @ 9.0",
708 {{ 0, 0 }, { 0, 0 }, { 10, 10 }, { 10, 10 }},
709 9.0f,
710 {{ 0.000000f, 0.000000f }, { 0.000000f, 0.000000f }, { 6.467375f, 6.467375f },
711 { 9.000000f, 9.000000f },
712 { 9.616623f, 9.616623f }, { 10.000000f, 10.000000f }, { 10.000000f, 10.000000f }}
713 );
714 testChopMonoCubicAtY(reporter, "straight, positive slope @ 10.0",
715 {{ 0, 0 }, { 0, 0 }, { 10, 10 }, { 10, 10 }},
716 10.0f,
717 {{ 0.000000f, 0.000000f }, { 0.000000f, 0.000000f }, { 10.000000f, 10.000000f },
718 { 10.000000f, 10.000000f },
719 { 10.000000f, 10.000000f }, { 10.000000f, 10.000000f }, { 10.000000f, 10.000000f }}
720 );
721
722 testChopMonoCubicAtY(reporter, "curve, positive slope @ 2.0",
723 {{ 1, 1 }, { 5, 2 }, { 7, 4 }, { 8, 7 }},
724 2.0f,
725 {{ 1.000000f, 1.000000f }, { 2.055050f, 1.263763f }, { 2.970959f, 1.597096f },
726 { 3.766077f, 2.000000f },
727 { 5.985480f, 3.124621f }, { 7.263762f, 4.791288f }, { 8.000000f, 7.000000f }}
728 );
729 testChopMonoCubicAtY(reporter, "curve, positive slope @ 5.0",
730 {{ 1, 1 }, { 5, 2 }, { 7, 4 }, { 8, 7 }},
731 5.0f,
732 {{ 1.000000f, 1.000000f }, { 4.033223f, 1.758306f }, { 5.916391f, 3.091639f },
733 { 7.085550f, 5.000000f },
734 { 7.458195f, 5.608251f }, { 7.758306f, 6.274917f }, { 8.000000f, 7.000000f }}
735 );
736
737 testChopMonoCubicAtY(reporter, "curve, negative slope @ 5.0",
738 {{ 2, 7 }, { 3, 2 }, { 6, 3 }, { 11, 2 }},
739 5.0f,
740 {{ 2.000000f, 7.000000f }, { 2.162856f, 6.185719f }, { 2.378757f, 5.530570f },
741 { 2.647702f, 5.000000f },
742 { 4.030182f, 2.272668f }, { 6.814281f, 2.837144f }, { 11.000000f, 2.000000f }}
743 );
744 testChopMonoCubicAtY(reporter, "curve, negative slope @ 3.0",
745 {{ 2, 7 }, { 3, 2 }, { 6, 3 }, { 11, 2 }},
746 3.0f,
747 {{ 2.000000f, 7.000000f }, { 2.500000f, 4.500000f }, { 3.500000f, 3.500000f },
748 { 5.000000f, 3.000000f },
749 { 6.500000f, 2.500000f }, { 8.500000f, 2.500000f }, { 11.000000f, 2.000000f }}
750 );
751 testChopMonoCubicAtY(reporter, "curve, negative slope @ 2.5",
752 {{ 2, 7 }, { 3, 2 }, { 6, 3 }, { 11, 2 }},
753 2.5f,
754 {{ 2.000000f, 7.000000f }, { 2.750000f, 3.250000f }, { 4.625000f, 2.875000f },
755 { 7.625000f, 2.500000f },
756 { 8.625000f, 2.375000f }, { 9.750000f, 2.250000f }, { 11.000000f, 2.000000f }}
757 );
758
759 // This is the same curve as above, just the 4 points given in the opposite order.
760 // We would expect the math to result in the same chop points, with the outputs
761 // in the opposite order too.
762 testChopMonoCubicAtY(reporter, "inverted curve, negative slope @ 5.0",
763 {{ 11, 2 }, { 6, 3 }, { 3, 2 }, { 2, 7 }},
764 5.0f,
765 {{ 11.000000f, 2.000000f }, { 6.814281f, 2.837144f }, { 4.030182f, 2.272668f },
766 { 2.647702f, 5.000000f },
767 { 2.378757f, 5.530570f }, { 2.162856f, 6.185719f }, { 2.000000f, 7.000000f }}
768 );
769 testChopMonoCubicAtY(reporter, "inverted curve, negative slope @ 3.0",
770 {{ 11, 2 }, { 6, 3 }, { 3, 2 }, { 2, 7 }},
771 3.0f,
772 {{ 11.000000f, 2.000000f }, { 8.500000f, 2.500000f }, { 6.500000f, 2.500000f },
773 { 5.000000f, 3.000000f },
774 { 3.500000f, 3.500000f }, { 2.500000f, 4.500000f }, { 2.000000f, 7.000000f }}
775 );
776 testChopMonoCubicAtY(reporter, "inverted curve, negative slope @ 2.5",
777 {{ 11, 2 }, { 6, 3 }, { 3, 2 }, { 2, 7 }},
778 2.5f,
779 {{ 11.000000f, 2.000000f }, { 9.750000f, 2.250000f }, { 8.625000f, 2.375000f },
780 { 7.625000f, 2.500000f },
781 { 4.625000f, 2.875000f }, { 2.750000f, 3.250000f }, { 2.000000f, 7.000000f }}
782 );
783
784 testChopMonoCubicAtY(reporter, "big curve, negative slope @ 90",
785 {{ -2, 100 }, { 0, 0 }, { 0, 0 }, { 100, -2 }},
786 90.f,
787 {{ -2.000000f,100.000000f }, { -1.930979f, 96.548965f }, { -1.864341f, 93.217033f },
788 { -1.795892f, 90.000000f },
789 { 0.119096f, -0.002382f }, { 3.451032f, -0.069021f }, {100.000000f, -2.000000f }}
790 );
791 testChopMonoCubicAtY(reporter, "big curve, negative slope @ 10",
792 {{ -2, 100 }, { 0, 0 }, { 0, 0 }, { 100, -2 }},
793 10.f,
794 {{ -2.000000f,100.000000f }, { -0.937505f, 46.875271f }, { -0.439458f, 21.972910f },
795 { 14.787060f, 10.000000f },
796 { 28.222368f, -0.564447f }, { 53.124729f, -1.062495f }, {100.000000f, -2.000000f }}
797 );
798 testChopMonoCubicAtY(reporter, "big curve, negative slope @ 0",
799 {{ -2, 100 }, { 0, 0 }, { 0, 0 }, { 100, -2 }},
800 0.f,
801 {{ -2.000000f,100.000000f }, { -0.426983f, 21.349131f }, { -0.091157f, 4.557854f },
802 { 48.633648f, 0.000000f },
803 { 61.859592f, -1.237192f }, { 78.650871f, -1.573017f }, {100.000000f, -2.000000f }}
804 );
805
806 testChopMonoCubicAtY(reporter, "ossfuzz:55680 curve barely crosses Y axis",
807 {{-250.121582f, -1180.09509f}, {10.007843f, -1180.09509f},
808 {20.015685f, -786.041259f}, {40.0313721f, 2.0664072f}},
809 0.f,
810 {{-250.121582f, -1180.095093f}, {9.780392f, -1180.095093f}, {19.997992f, -786.730042f},
811 {39.978889f, 0.000000f},
812 {39.996376f, 0.688501f}, {40.013870f, 1.377304f}, {40.031372f, 2.066407f}}
813 );
814}
815
816DEF_TEST(GeometryChopMonoCubicAtY_OutOfRangeReturnFalse, reporter) {
817 SkPoint inputs[] = {{ 0, 0 }, { 0, 0 }, { 10, 10 }, { 10, 10 }};
818 SkPoint outputs[7];
819
820 // Too low
822 // Too high
824}
825
827 SkSpan<const SkPoint> curveInputs, SkScalar xToChopAt,
828 SkSpan<const SkPoint> expectedOutputs) {
830 REPORTER_ASSERT(reporter, curveInputs.size() == 4,
831 "Invalid test case. Input curve should have 4 points");
832 REPORTER_ASSERT(reporter, expectedOutputs.size() == 7,
833 "Invalid test case. Outputs should have 7 points");
834 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(expectedOutputs[3].x(), xToChopAt),
835 "Invalid test case. 4th point's X should be %f", xToChopAt);
836
837 SkPoint outputs[7];
838 // Make sure it actually chopped
839 REPORTER_ASSERT(reporter, SkChopMonoCubicAtX(curveInputs.begin(), xToChopAt, outputs));
840
841 for (int i = 0; i < 7; ++i) {
842 REPORTER_ASSERT(reporter, nearly_equal(expectedOutputs[i], outputs[i]),
843 "(%f, %f) != (%f, %f) at index %d",
844 expectedOutputs[i].x(), expectedOutputs[i].y(),
845 outputs[i].x(), outputs[i].y(), i);
846 }
847}
848
849DEF_TEST(GeometryChopMonoCubicAtX_Successful, reporter) {
850 // These cubics are all arbitrary, picked using Desmos for something that looked "nice".
851
852 testChopMonoCubicAtX(reporter, "straight, positive slope @ 2.5",
853 {{ 0, 0 }, { 0, 0 }, { 10, 10 }, { 10, 10 }},
854 2.5f,
855 {{ 0.000000f, 0.000000f }, { 0.000000f, 0.000000f }, { 1.065055f, 1.065055f },
856 { 2.500000f, 2.500000f },
857 { 5.461981f, 5.461981f }, { 10.000000f, 10.000000f }, { 10.000000f, 10.000000f }}
858 );
859 testChopMonoCubicAtX(reporter, "straight, positive slope @ 5.0",
860 {{ 0, 0 }, { 0, 0 }, { 10, 10 }, { 10, 10 }},
861 5.0f,
862 {{ 0.000000f, 0.000000f }, { 0.000000f, 0.000000f }, { 2.500000f, 2.500000f },
863 { 5.000000f, 5.000000f },
864 { 7.500000f, 7.500000f }, { 10.000000f, 10.000000f }, { 10.000000f, 10.000000f }}
865 );
866 testChopMonoCubicAtX(reporter, "straight, positive slope @ 9.0",
867 {{ 0, 0 }, { 0, 0 }, { 10, 10 }, { 10, 10 }},
868 9.0f,
869 {{ 0.000000f, 0.000000f }, { 0.000000f, 0.000000f }, { 6.467375f, 6.467375f },
870 { 9.000000f, 9.000000f },
871 { 9.616623f, 9.616623f }, { 10.000000f, 10.000000f }, { 10.000000f, 10.000000f }}
872 );
873 testChopMonoCubicAtX(reporter, "straight, positive slope @ 10.0",
874 {{ 0, 0 }, { 0, 0 }, { 10, 10 }, { 10, 10 }},
875 10.0f,
876 {{ 0.000000f, 0.000000f }, { 0.000000f, 0.000000f }, { 10.000000f, 10.000000f },
877 { 10.000000f, 10.000000f },
878 { 10.000000f, 10.000000f }, { 10.000000f, 10.000000f }, { 10.000000f, 10.000000f }}
879 );
880
881 testChopMonoCubicAtX(reporter, "curve, positive slope @ 2.0",
882 {{ 1, 1 }, { 5, 2 }, { 7, 4 }, { 8, 7 }},
883 2.0f,
884 {{ 1.000000f, 1.000000f }, { 1.348275f, 1.087069f }, { 1.681389f, 1.181719f },
885 { 2.000000f, 1.283949f },
886 { 5.340694f, 2.355856f }, { 7.087069f, 4.261207f }, { 8.000000f, 7.000000f }}
887 );
888 testChopMonoCubicAtX(reporter, "curve, positive slope @ 5.0",
889 {{ 1, 1 }, { 5, 2 }, { 7, 4 }, { 8, 7 }},
890 5.0f,
891 {{ 1.000000f, 1.000000f }, { 2.650396f, 1.412599f }, { 3.960316f, 1.995436f },
892 { 5.000000f, 2.748511f },
893 { 6.480158f, 3.820634f }, { 7.412599f, 5.237797f }, { 8.000000f, 7.000000f }}
894 );
895
896 testChopMonoCubicAtX(reporter, "curve, negative slope @ 5.0",
897 {{ 2, 7 }, { 3, 2 }, { 6, 3 }, { 11, 2 }},
898 5.0f,
899 {{ 2.000000f, 7.000000f }, { 2.500000f, 4.500000f }, { 3.500000f, 3.500000f },
900 { 5.000000f, 3.000000f },
901 { 6.500000f, 2.500000f }, { 8.500000f, 2.500000f }, { 11.000000f, 2.000000f }}
902 );
903 testChopMonoCubicAtX(reporter, "curve, negative slope @ 3.0",
904 {{ 2, 7 }, { 3, 2 }, { 6, 3 }, { 11, 2 }},
905 3.0f,
906 {{ 2.000000f, 7.000000f }, { 2.228714f, 5.856432f }, { 2.562047f, 5.026724f },
907 { 3.000000f, 4.415163f },
908 { 4.476901f, 2.352807f }, { 7.143568f, 2.771286f }, { 11.000000f, 2.000000f }}
909 );
910 testChopMonoCubicAtX(reporter, "curve, negative slope @ 2.5",
911 {{ 2, 7 }, { 3, 2 }, { 6, 3 }, { 11, 2 }},
912 2.5f,
913 {{ 2.000000f, 7.000000f }, { 2.131881f, 6.340593f }, { 2.298548f, 5.785543f },
914 { 2.500000f, 5.316498f },
915 { 3.826073f, 2.228977f }, { 6.659407f, 2.868119f }, { 11.000000f, 2.000000f }}
916 );
917
918 // This is the same curve as above, just the 4 points given in the opposite order.
919 // We would expect the math to result in the same chop points, with the outputs
920 // in the opposite order too.
921 testChopMonoCubicAtX(reporter, "inverted curve, negative slope @ 5.0",
922 {{ 11, 2 }, { 6, 3 }, { 3, 2 }, { 2, 7 }},
923 5.0f,
924 {{ 11.000000f, 2.000000f }, { 8.500000f, 2.500000f }, { 6.500000f, 2.500000f },
925 { 5.000000f, 3.000000f },
926 { 3.500000f, 3.500000f }, { 2.500000f, 4.500000f }, { 2.000000f, 7.000000f }}
927 );
928 testChopMonoCubicAtX(reporter, "inverted curve, negative slope @ 3.0",
929 {{ 11, 2 }, { 6, 3 }, { 3, 2 }, { 2, 7 }},
930 3.0f,
931 {{ 11.000000f, 2.000000f }, { 7.143568f, 2.771286f }, { 4.476901f, 2.352807f },
932 { 3.000000f, 4.415163f },
933 { 2.562047f, 5.026724f }, { 2.228714f, 5.856432f }, { 2.000000f, 7.000000f }}
934 );
935 testChopMonoCubicAtX(reporter, "inverted curve, negative slope @ 2.5",
936 {{ 11, 2 }, { 6, 3 }, { 3, 2 }, { 2, 7 }},
937 2.5f,
938 {{ 11.000000f, 2.000000f }, { 6.659407f, 2.868119f }, { 3.826073f, 2.228977f },
939 { 2.500000f, 5.316498f },
940 { 2.298548f, 5.785543f }, { 2.131881f, 6.340593f }, { 2.000000f, 7.000000f }}
941 );
942
943 testChopMonoCubicAtX(reporter, "big curve, negative slope @ 90",
944 {{ -2, 100 }, { 0, 0 }, { 0, 0 }, { 100, -2 }},
945 90.f,
946 {{ -2.000000f,100.000000f }, { -0.069021f, 3.451032f }, { -0.002382f, 0.119096f },
947 { 90.000000f, -1.795892f },
948 { 93.217033f, -1.864341f }, { 96.548965f, -1.930979f }, {100.000000f, -2.000000f }}
949 );
950 testChopMonoCubicAtX(reporter, "big curve, negative slope @ 10",
951 {{ -2, 100 }, { 0, 0 }, { 0, 0 }, { 100, -2 }},
952 10.f,
953 {{ -2.000000f,100.000000f }, { -1.062495f, 53.124729f }, { -0.564447f, 28.222368f },
954 { 10.000000f, 14.787060f },
955 { 21.972910f, -0.439458f }, { 46.875271f, -0.937505f }, {100.000000f, -2.000000f }}
956 );
957 testChopMonoCubicAtX(reporter, "big curve, negative slope @ 0",
958 {{ -2, 100 }, { 0, 0 }, { 0, 0 }, { 100, -2 }},
959 0.f,
960 {{ -2.000000f,100.000000f }, { -1.573017f, 78.650871f }, { -1.237192f, 61.859592f },
961 { 0.000000f, 48.633648f },
962 { 4.557854f, -0.091157f }, { 21.349131f, -0.426983f }, {100.000000f, -2.000000f }}
963 );
964}
965
966DEF_TEST(GeometryChopMonoCubicAtX_OutOfRangeReturnFalse, reporter) {
967 SkPoint inputs[] = {{ 0, 0 }, { 0, 0 }, { 10, 10 }, { 10, 10 }};
968 SkPoint outputs[7];
969
970 // Too low
972 // Too high
974}
reporter
Definition: FontMgrTest.cpp:39
int count
Definition: FontMgrTest.cpp:50
static std::array< SkPoint, 4 > kCusps[]
static void test_conic_to_quads(skiatest::Reporter *reporter)
static bool nearly_equal(const SkPoint &a, const SkPoint &b)
static void testChopCubic(skiatest::Reporter *reporter)
static std::array< SkPoint, 4 > kLoops[]
static void test_measure_rotation(skiatest::Reporter *reporter)
static void test_chop_cubic_at_midtangent(skiatest::Reporter *reporter, const SkPoint pts[4], SkCubicType cubicType)
static SkMatrix kSkewMatrices[]
static void test_conic_eval_pos(skiatest::Reporter *reporter, const SkConic &conic, SkScalar t)
static void check_cubic_around_rect(std::string name, skiatest::Reporter *reporter, float x1, float y1, float x2, float y2, bool undefined=false)
DEF_TEST(Geometry, reporter)
static void test_chop_at_midtangent(skiatest::Reporter *reporter)
static std::array< SkPoint, 4 > kSerpentines[]
static void test_classify_cubic(skiatest::Reporter *reporter)
static void testChopMonoCubicAtX(skiatest::Reporter *reporter, std::string name, SkSpan< const SkPoint > curveInputs, SkScalar xToChopAt, SkSpan< const SkPoint > expectedOutputs)
static void test_conic_eval_tan(skiatest::Reporter *reporter, const SkConic &conic, SkScalar t)
static void check_cubic_type(skiatest::Reporter *reporter, const std::array< SkPoint, 4 > &bezierPoints, SkCubicType expectedType, bool undefined=false)
static std::array< SkPoint, 3 > kQuads[]
static void test_conic_tangents(skiatest::Reporter *reporter)
static void test_conic(skiatest::Reporter *reporter)
static void test_cubic_cusps(skiatest::Reporter *reporter)
static void check_pairs(skiatest::Reporter *reporter, int index, SkScalar t, const char name[], SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1)
static std::array< SkPoint, 4 > kLinearCubics[]
static void test_this_conic_to_quad(skiatest::Reporter *r, const SkPoint pts[3], SkScalar w)
static void test_cubic_tangents(skiatest::Reporter *reporter)
static void testChopMonoCubicAtY(skiatest::Reporter *reporter, std::string name, SkSpan< const SkPoint > curveInputs, SkScalar yToChopAt, SkSpan< const SkPoint > expectedOutputs)
SkPoint lerp(const SkPoint &a, const SkPoint &b, float t)
static void test_evalquadat(skiatest::Reporter *reporter)
static void test_quad_tangents(skiatest::Reporter *reporter)
static void test_chop_quad_at_midtangent(skiatest::Reporter *reporter, const SkPoint pts[3])
static constexpr float kTolerance
Definition: GrQuadUtils.cpp:29
static const int points[]
static bool eq(const SkM44 &a, const SkM44 &b, float tol)
Definition: M44Test.cpp:18
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
static bool SkIsFinite(T x, Pack... values)
int SkChopCubicAtInflections(const SkPoint src[4], SkPoint dst[10])
Definition: SkGeometry.cpp:755
float SkMeasureNonInflectCubicRotation(const SkPoint pts[4])
Definition: SkGeometry.cpp:579
void SkChopCubicAt(const SkPoint src[4], SkPoint dst[7], SkScalar t)
Definition: SkGeometry.cpp:473
void SkEvalCubicAt(const SkPoint src[4], SkScalar t, SkPoint *loc, SkVector *tangent, SkVector *curvature)
Definition: SkGeometry.cpp:418
SkScalar SkFindCubicCusp(const SkPoint src[4])
void SkConvertQuadToCubic(const SkPoint src[3], SkPoint dst[4])
Definition: SkGeometry.cpp:378
SkVector SkEvalQuadTangentAt(const SkPoint src[3], SkScalar t)
Definition: SkGeometry.cpp:148
SkCubicType SkClassifyCubic(const SkPoint P[4], double t[2], double s[2], double d[4])
Definition: SkGeometry.cpp:809
void SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint *pt, SkVector *tangent)
Definition: SkGeometry.cpp:132
bool SkChopMonoCubicAtY(const SkPoint src[4], SkScalar y, SkPoint dst[7])
int SkChopCubicAtMaxCurvature(const SkPoint src[4], SkPoint dst[13], SkScalar tValues[3])
bool SkChopMonoCubicAtX(const SkPoint src[4], SkScalar x, SkPoint dst[7])
int SkChopQuadAtMaxCurvature(const SkPoint src[3], SkPoint dst[5])
Definition: SkGeometry.cpp:367
void SkChopCubicAtMidTangent(const SkPoint src[4], SkPoint dst[7])
Definition: SkGeometry.h:195
void SkChopQuadAtMidTangent(const SkPoint src[3], SkPoint dst[5])
Definition: SkGeometry.h:91
SkCubicType
Definition: SkGeometry.h:264
float SkMeasureQuadRotation(const SkPoint pts[3])
Definition: SkGeometry.h:79
static bool SkScalarNearlyZero(SkScalar x, SkScalar tolerance=SK_ScalarNearlyZero)
Definition: SkScalar.h:101
static bool SkScalarNearlyEqual(SkScalar x, SkScalar y, SkScalar tolerance=SK_ScalarNearlyZero)
Definition: SkScalar.h:107
#define SK_Scalar1
Definition: SkScalar.h:18
#define SK_ScalarNaN
Definition: SkScalar.h:28
#define SkIntToScalar(x)
Definition: SkScalar.h:57
#define SK_ScalarPI
Definition: SkScalar.h:21
#define REPORTER_ASSERT(r, cond,...)
Definition: Test.h:286
#define N
Definition: beziers.cpp:19
const SkPoint * computeQuads(const SkConic &conic, SkScalar tol)
Definition: SkGeometry.h:524
int countQuads() const
Definition: SkGeometry.h:539
void mapPoints(SkPoint dst[], const SkPoint src[], int count) const
Definition: SkMatrix.cpp:770
static SkMatrix MakeAll(SkScalar scaleX, SkScalar skewX, SkScalar transX, SkScalar skewY, SkScalar scaleY, SkScalar transY, SkScalar pers0, SkScalar pers1, SkScalar pers2)
Definition: SkMatrix.h:179
static bool AreFinite(const SkPoint array[], int count)
Definition: SkPointPriv.h:22
float nextF()
Definition: SkRandom.h:55
SkScalar nextUScalar1()
Definition: SkRandom.h:101
SkScalar nextSScalar1()
Definition: SkRandom.h:113
constexpr T * begin() const
Definition: SkSpan_impl.h:90
constexpr size_t size() const
Definition: SkSpan_impl.h:95
float SkScalar
Definition: extension.cpp:12
static bool b
struct MyStruct a[10]
glong glong end
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
double y
double x
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
dictionary outputs
Definition: bazel_build.py:29
dst
Definition: cp.py:12
AI float conic(float tolerance, const SkPoint pts[], float w, const VectorXform &vectorXform=VectorXform())
Definition: WangsFormula.h:287
AI float cubic(float precision, const SkPoint pts[], const VectorXform &vectorXform=VectorXform())
Definition: WangsFormula.h:195
SIN Vec< N, float > abs(const Vec< N, float > &x)
Definition: SkVx.h:707
SIN Vec< N, float > sqrt(const Vec< N, float > &x)
Definition: SkVx.h:706
SkScalar w
float fX
x-axis value
Definition: SkPoint_impl.h:164
void set(float x, float y)
Definition: SkPoint_impl.h:200
float cross(const SkVector &vec) const
Definition: SkPoint_impl.h:545
float fY
y-axis value
Definition: SkPoint_impl.h:165
constexpr float y() const
Definition: SkPoint_impl.h:187
constexpr float x() const
Definition: SkPoint_impl.h:181