Flutter Engine
The Flutter Engine
PathMeasureTest.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
16#include "src/core/SkPathPriv.h"
17#include "tests/Test.h"
18
19#include <array>
20#include <cstddef>
21#include <initializer_list>
22#include <utility>
23
26 const SkPoint pts[] = {
27 { 0, 0 },
28 { 100000000000.0f, 100000000000.0f }, { 0, 0 }, { 10, 10 },
29 { 10, 10 }, { 0, 0 }, { 10, 10 }
30 };
31
32 path.moveTo(pts[0]);
33 for (size_t i = 1; i < std::size(pts); i += 3) {
34 path.cubicTo(pts[i], pts[i + 1], pts[i + 2]);
35 }
36
37 SkPathMeasure meas(path, false);
38 meas.getLength();
39
40 // Now check that we cap the segment size even with very large resolution scales.
41 // Earlier versions allowed the pathmeasure to recurse without limit in the face
42 // of a very large scale.
43 //
44 // Before this limit, the above meas had 15K segments, and when built with
45 // a resScale of 100, it had 184K segments -- for 1 cubic!
46 {
48 REPORTER_ASSERT(reporter, n < 300);
49
50 constexpr float resScale = 1000;
52 REPORTER_ASSERT(reporter, n < 300);
53 }
54}
55
56static void test_small_segment2() {
58 const SkPoint pts[] = {
59 { 0, 0 },
60 { 100000000000.0f, 100000000000.0f }, { 0, 0 },
61 { 10, 10 }, { 0, 0 },
62 };
63
64 path.moveTo(pts[0]);
65 for (size_t i = 1; i < std::size(pts); i += 2) {
66 path.quadTo(pts[i], pts[i + 1]);
67 }
68 SkPathMeasure meas(path, false);
69 meas.getLength();
70}
71
72static void test_small_segment() {
74 const SkPoint pts[] = {
75 { 100000, 100000},
76 // big jump between these points, makes a big segment
77 { 1.0005f, 0.9999f },
78 // tiny (non-zero) jump between these points
80 };
81
82 path.moveTo(pts[0]);
83 for (size_t i = 1; i < std::size(pts); ++i) {
84 path.lineTo(pts[i]);
85 }
86 SkPathMeasure meas(path, false);
87
88 /* this would assert (before a fix) because we added a segment with
89 the same length as the prev segment, due to the follow (bad) pattern
90
91 d = distance(pts[0], pts[1]);
92 distance += d;
93 seg->fDistance = distance;
94
95 SkASSERT(d > 0); // TRUE
96 SkASSERT(seg->fDistance > prevSeg->fDistance); // FALSE
97
98 This 2nd assert failes because (distance += d) didn't affect distance
99 because distance >>> d.
100 */
101 meas.getLength();
102}
103
105 SkPath path;
106
107 path.moveTo(0, 0);
108 path.lineTo(SK_Scalar1, 0);
109 path.lineTo(SK_Scalar1, SK_Scalar1);
110 path.lineTo(0, SK_Scalar1);
111
112 SkPathMeasure meas(path, true);
113 SkScalar length = meas.getLength();
115
116 path.reset();
117 path.moveTo(0, 0);
118 path.lineTo(SK_Scalar1*3, SK_Scalar1*4);
119 meas.setPath(&path, false);
120 length = meas.getLength();
122
123 path.reset();
124 path.addCircle(0, 0, SK_Scalar1);
125 meas.setPath(&path, true);
126 length = meas.getLength();
127// SkDebugf("circle arc-length = %g\n", length);
128
129 // Test the behavior following a close not followed by a move.
130 path.reset();
131 path.lineTo(SK_Scalar1, 0);
132 path.lineTo(SK_Scalar1, SK_Scalar1);
133 path.lineTo(0, SK_Scalar1);
134 path.close();
135 path.lineTo(-SK_Scalar1, 0);
136 meas.setPath(&path, false);
137 length = meas.getLength();
139 meas.nextContour();
140 length = meas.getLength();
142 SkPoint position;
143 SkVector tangent;
144 REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent));
146 SkScalarNearlyEqual(position.fX,
148 0.0001f));
149 REPORTER_ASSERT(reporter, position.fY == 0);
151 REPORTER_ASSERT(reporter, tangent.fY == 0);
152
153 // Test degenerate paths
154 path.reset();
155 path.moveTo(0, 0);
156 path.lineTo(0, 0);
157 path.lineTo(SK_Scalar1, 0);
158 path.quadTo(SK_Scalar1, 0, SK_Scalar1, 0);
160 path.cubicTo(SK_Scalar1, SK_Scalar1 * 2,
163 path.cubicTo(SK_Scalar1*2, SK_Scalar1 * 2,
164 SK_Scalar1*3, SK_Scalar1 * 2,
165 SK_Scalar1*4, SK_Scalar1 * 2);
166 meas.setPath(&path, false);
167 length = meas.getLength();
169 REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent));
171 SkScalarNearlyEqual(position.fX,
173 0.0001f));
174 REPORTER_ASSERT(reporter, position.fY == 0);
176 REPORTER_ASSERT(reporter, tangent.fY == 0);
177 REPORTER_ASSERT(reporter, meas.getPosTan(2.5f, &position, &tangent));
179 SkScalarNearlyEqual(position.fX, SK_Scalar1, 0.0001f));
181 SkScalarNearlyEqual(position.fY, 1.5f));
182 REPORTER_ASSERT(reporter, tangent.fX == 0);
184 REPORTER_ASSERT(reporter, meas.getPosTan(4.5f, &position, &tangent));
186 SkScalarNearlyEqual(position.fX,
187 2.5f,
188 0.0001f));
190 SkScalarNearlyEqual(position.fY,
191 2.0f,
192 0.0001f));
194 REPORTER_ASSERT(reporter, tangent.fY == 0);
195
196 path.reset();
197 path.moveTo(0, 0);
198 path.lineTo(SK_Scalar1, 0);
199 path.moveTo(SK_Scalar1, SK_Scalar1);
200 path.moveTo(SK_Scalar1 * 2, SK_Scalar1 * 2);
201 path.lineTo(SK_Scalar1, SK_Scalar1 * 2);
202 meas.setPath(&path, false);
203 length = meas.getLength();
205 REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent));
207 SkScalarNearlyEqual(position.fX,
209 0.0001f));
210 REPORTER_ASSERT(reporter, position.fY == 0);
212 REPORTER_ASSERT(reporter, tangent.fY == 0);
213 meas.nextContour();
214 length = meas.getLength();
216 REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent));
218 SkScalarNearlyEqual(position.fX,
219 1.5f,
220 0.0001f));
222 SkScalarNearlyEqual(position.fY,
223 2.0f,
224 0.0001f));
226 REPORTER_ASSERT(reporter, tangent.fY == 0);
227
231
232 // SkPathMeasure isn't copyable, but it should be move-able
233 SkPathMeasure meas2(std::move(meas));
234 meas = std::move(meas2);
235}
236
237DEF_TEST(PathMeasureConic, reporter) {
238 SkPoint stdP, hiP, pts[] = {{0,0}, {100,0}, {100,0}};
239 SkPath p;
240 p.moveTo(0, 0);
241 p.conicTo(pts[1], pts[2], 1);
242 SkPathMeasure stdm(p, false);
243 REPORTER_ASSERT(reporter, stdm.getPosTan(20, &stdP, nullptr));
244 p.reset();
245 p.moveTo(0, 0);
246 p.conicTo(pts[1], pts[2], 10);
247 stdm.setPath(&p, false);
248 REPORTER_ASSERT(reporter, stdm.getPosTan(20, &hiP, nullptr));
249 REPORTER_ASSERT(reporter, 19.5f < stdP.fX && stdP.fX < 20.5f);
250 REPORTER_ASSERT(reporter, 19.5f < hiP.fX && hiP.fX < 20.5f);
251}
252
253// Regression test for b/26425223
254DEF_TEST(PathMeasure_nextctr, reporter) {
255 SkPath path;
256 path.moveTo(0, 0); path.lineTo(100, 0);
257
258 SkPathMeasure meas(path, false);
259 // only expect 1 contour, even if we didn't explicitly call getLength() ourselves
261}
262
265 SkPoint pos;
266 SkVector tan;
267 SkScalar distance = cm->length() / 4;
268 bool success = cm->getPosTan(distance, &pos, &tan);
269
270 REPORTER_ASSERT(reporter, success);
275}
276
278 SkPath path;
279
280 path.moveTo(0, 0).lineTo(100, 100).lineTo(200, 100);
281 path.moveTo(2, 2).moveTo(3, 3); // zero-length(s)
282 path.moveTo(4, 4).close().close().close(); // zero-length
283 path.moveTo(5, 5).lineTo(5, 5); // zero-length
284 path.moveTo(5, 5).lineTo(5, 5).close(); // zero-length
285 path.moveTo(5, 5).lineTo(5, 5).close().close(); // zero-length
286 path.moveTo(6, 6).lineTo(7, 7);
287 path.moveTo(10, 10); // zero-length
288
289 SkContourMeasureIter fact(path, false);
290
291 // given the above construction, we expect only 2 contours (the rest are "empty")
292
295 REPORTER_ASSERT(reporter, !fact.next());
296}
297
299 SkPath path;
300
301 // This odd sequence (with a trailing moveTo) used to return a 2nd contour, which is
302 // wrong, since the contract for a measure is to only return non-zero length contours.
303 path.moveTo(10, 10).lineTo(20, 20).moveTo(30, 30);
304
305 for (bool forceClosed : {false, true}) {
306 SkContourMeasureIter fact(path, forceClosed);
308 REPORTER_ASSERT(reporter, !fact.next());
309 }
310}
311
313 SkPath path;
314 path.addRect({1, 2, 3, 4});
315 path.incReserve(100); // give shrinkToFit() something to do
316
317 SkContourMeasureIter iter(path, false);
318
319 // shrinks the allocation, possibly relocating the underlying arrays.
320 // The contouremasureiter needs to have safely copied path, to be unaffected by this
321 // change to "path".
323
324 // Note, this failed (before the fix) on an ASAN build, which notices that we were
325 // using an internal iterator of the passed-in path, not our copy.
326 while (iter.next())
327 ;
328}
329
330DEF_TEST(contour_measure, reporter) {
331 SkPath path;
332 path.addCircle(0, 0, 100);
333 path.addCircle(0, 0, 10);
334
335 SkContourMeasureIter fact(path, false);
336 path.reset(); // we should not need the path avert we created the factory
337
338 auto cm0 = fact.next();
339 auto cm1 = fact.next();
340
341 REPORTER_ASSERT(reporter, cm0->isClosed());
342 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(cm0->length(), 200 * SK_ScalarPI, 1.5f));
343
344 test_90_degrees(cm0, 100, reporter);
345
346 REPORTER_ASSERT(reporter, cm1->isClosed());
347 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(cm1->length(), 20 * SK_ScalarPI, 0.5f));
348
349 test_90_degrees(cm1, 10, reporter);
350
351 auto cm2 = fact.next();
353
356
358}
reporter
Definition: FontMgrTest.cpp:39
SkPoint pos
static void test_empty_contours(skiatest::Reporter *reporter)
static void test_small_segment3(skiatest::Reporter *reporter)
static void test_small_segment2()
static void test_shrink(skiatest::Reporter *reporter)
DEF_TEST(PathMeasure, reporter)
static void test_90_degrees(const sk_sp< SkContourMeasure > &cm, SkScalar radius, skiatest::Reporter *reporter)
static void test_small_segment()
static void test_MLM_contours(skiatest::Reporter *reporter)
#define SkASSERT(cond)
Definition: SkAssert.h:116
static bool SkScalarNearlyEqual(SkScalar x, SkScalar y, SkScalar tolerance=SK_ScalarNearlyZero)
Definition: SkScalar.h:107
#define SK_Scalar1
Definition: SkScalar.h:18
#define SK_ScalarHalf
Definition: SkScalar.h:19
#define SK_ScalarPI
Definition: SkScalar.h:21
#define REPORTER_ASSERT(r, cond,...)
Definition: Test.h:286
sk_sp< SkContourMeasure > next()
bool getPosTan(SkScalar distance, SkPoint *position, SkVector *tangent) const
SkScalar length() const
static size_t CountSegments(const SkPathMeasure &)
SkScalar getLength()
void setPath(const SkPath *, bool forceClosed)
bool getPosTan(SkScalar distance, SkPoint *position, SkVector *tangent)
static void ShrinkToFit(SkPath *path)
Definition: SkPathPriv.h:130
Definition: SkPath.h:59
float SkScalar
Definition: extension.cpp:12
size_t length
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
Definition: switches.h:57
CanvasPathMeasure PathMeasure
Definition: dart_ui.cc:56
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
float fX
x-axis value
Definition: SkPoint_impl.h:164
float fY
y-axis value
Definition: SkPoint_impl.h:165