• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2011 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkPathMeasure.h"
9 #include "src/core/SkPathPriv.h"
10 #include "tests/Test.h"
11 
test_small_segment3()12 static void test_small_segment3() {
13     SkPath path;
14     const SkPoint pts[] = {
15         { 0, 0 },
16         { 100000000000.0f, 100000000000.0f }, { 0, 0 }, { 10, 10 },
17         { 10, 10 }, { 0, 0 }, { 10, 10 }
18     };
19 
20     path.moveTo(pts[0]);
21     for (size_t i = 1; i < SK_ARRAY_COUNT(pts); i += 3) {
22         path.cubicTo(pts[i], pts[i + 1], pts[i + 2]);
23     }
24 
25     SkPathMeasure meas(path, false);
26     meas.getLength();
27 }
28 
test_small_segment2()29 static void test_small_segment2() {
30     SkPath path;
31     const SkPoint pts[] = {
32         { 0, 0 },
33         { 100000000000.0f, 100000000000.0f }, { 0, 0 },
34         { 10, 10 }, { 0, 0 },
35     };
36 
37     path.moveTo(pts[0]);
38     for (size_t i = 1; i < SK_ARRAY_COUNT(pts); i += 2) {
39         path.quadTo(pts[i], pts[i + 1]);
40     }
41     SkPathMeasure meas(path, false);
42     meas.getLength();
43 }
44 
test_small_segment()45 static void test_small_segment() {
46     SkPath path;
47     const SkPoint pts[] = {
48         { 100000, 100000},
49         // big jump between these points, makes a big segment
50         { 1.0005f, 0.9999f },
51         // tiny (non-zero) jump between these points
52         { SK_Scalar1, SK_Scalar1 },
53     };
54 
55     path.moveTo(pts[0]);
56     for (size_t i = 1; i < SK_ARRAY_COUNT(pts); ++i) {
57         path.lineTo(pts[i]);
58     }
59     SkPathMeasure meas(path, false);
60 
61     /*  this would assert (before a fix) because we added a segment with
62         the same length as the prev segment, due to the follow (bad) pattern
63 
64         d = distance(pts[0], pts[1]);
65         distance += d;
66         seg->fDistance = distance;
67 
68         SkASSERT(d > 0);    // TRUE
69         SkASSERT(seg->fDistance > prevSeg->fDistance);  // FALSE
70 
71         This 2nd assert failes because (distance += d) didn't affect distance
72         because distance >>> d.
73      */
74     meas.getLength();
75 }
76 
DEF_TEST(PathMeasure,reporter)77 DEF_TEST(PathMeasure, reporter) {
78     SkPath  path;
79 
80     path.moveTo(0, 0);
81     path.lineTo(SK_Scalar1, 0);
82     path.lineTo(SK_Scalar1, SK_Scalar1);
83     path.lineTo(0, SK_Scalar1);
84 
85     SkPathMeasure   meas(path, true);
86     SkScalar        length = meas.getLength();
87     SkASSERT(length == SK_Scalar1*4);
88 
89     path.reset();
90     path.moveTo(0, 0);
91     path.lineTo(SK_Scalar1*3, SK_Scalar1*4);
92     meas.setPath(&path, false);
93     length = meas.getLength();
94     REPORTER_ASSERT(reporter, length == SK_Scalar1*5);
95 
96     path.reset();
97     path.addCircle(0, 0, SK_Scalar1);
98     meas.setPath(&path, true);
99     length = meas.getLength();
100 //    SkDebugf("circle arc-length = %g\n", length);
101 
102     // Test the behavior following a close not followed by a move.
103     path.reset();
104     path.lineTo(SK_Scalar1, 0);
105     path.lineTo(SK_Scalar1, SK_Scalar1);
106     path.lineTo(0, SK_Scalar1);
107     path.close();
108     path.lineTo(-SK_Scalar1, 0);
109     meas.setPath(&path, false);
110     length = meas.getLength();
111     REPORTER_ASSERT(reporter, length == SK_Scalar1 * 4);
112     meas.nextContour();
113     length = meas.getLength();
114     REPORTER_ASSERT(reporter, length == SK_Scalar1);
115     SkPoint position;
116     SkVector tangent;
117     REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent));
118     REPORTER_ASSERT(reporter,
119         SkScalarNearlyEqual(position.fX,
120                             -SK_ScalarHalf,
121                             0.0001f));
122     REPORTER_ASSERT(reporter, position.fY == 0);
123     REPORTER_ASSERT(reporter, tangent.fX == -SK_Scalar1);
124     REPORTER_ASSERT(reporter, tangent.fY == 0);
125 
126     // Test degenerate paths
127     path.reset();
128     path.moveTo(0, 0);
129     path.lineTo(0, 0);
130     path.lineTo(SK_Scalar1, 0);
131     path.quadTo(SK_Scalar1, 0, SK_Scalar1, 0);
132     path.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1 * 2);
133     path.cubicTo(SK_Scalar1, SK_Scalar1 * 2,
134                  SK_Scalar1, SK_Scalar1 * 2,
135                  SK_Scalar1, SK_Scalar1 * 2);
136     path.cubicTo(SK_Scalar1*2, SK_Scalar1 * 2,
137                  SK_Scalar1*3, SK_Scalar1 * 2,
138                  SK_Scalar1*4, SK_Scalar1 * 2);
139     meas.setPath(&path, false);
140     length = meas.getLength();
141     REPORTER_ASSERT(reporter, length == SK_Scalar1 * 6);
142     REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent));
143     REPORTER_ASSERT(reporter,
144         SkScalarNearlyEqual(position.fX,
145                             SK_ScalarHalf,
146                             0.0001f));
147     REPORTER_ASSERT(reporter, position.fY == 0);
148     REPORTER_ASSERT(reporter, tangent.fX == SK_Scalar1);
149     REPORTER_ASSERT(reporter, tangent.fY == 0);
150     REPORTER_ASSERT(reporter, meas.getPosTan(2.5f, &position, &tangent));
151     REPORTER_ASSERT(reporter,
152         SkScalarNearlyEqual(position.fX, SK_Scalar1, 0.0001f));
153     REPORTER_ASSERT(reporter,
154         SkScalarNearlyEqual(position.fY, 1.5f));
155     REPORTER_ASSERT(reporter, tangent.fX == 0);
156     REPORTER_ASSERT(reporter, tangent.fY == SK_Scalar1);
157     REPORTER_ASSERT(reporter, meas.getPosTan(4.5f, &position, &tangent));
158     REPORTER_ASSERT(reporter,
159         SkScalarNearlyEqual(position.fX,
160                             2.5f,
161                             0.0001f));
162     REPORTER_ASSERT(reporter,
163         SkScalarNearlyEqual(position.fY,
164                             2.0f,
165                             0.0001f));
166     REPORTER_ASSERT(reporter, tangent.fX == SK_Scalar1);
167     REPORTER_ASSERT(reporter, tangent.fY == 0);
168 
169     path.reset();
170     path.moveTo(0, 0);
171     path.lineTo(SK_Scalar1, 0);
172     path.moveTo(SK_Scalar1, SK_Scalar1);
173     path.moveTo(SK_Scalar1 * 2, SK_Scalar1 * 2);
174     path.lineTo(SK_Scalar1, SK_Scalar1 * 2);
175     meas.setPath(&path, false);
176     length = meas.getLength();
177     REPORTER_ASSERT(reporter, length == SK_Scalar1);
178     REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent));
179     REPORTER_ASSERT(reporter,
180         SkScalarNearlyEqual(position.fX,
181                             SK_ScalarHalf,
182                             0.0001f));
183     REPORTER_ASSERT(reporter, position.fY == 0);
184     REPORTER_ASSERT(reporter, tangent.fX == SK_Scalar1);
185     REPORTER_ASSERT(reporter, tangent.fY == 0);
186     meas.nextContour();
187     length = meas.getLength();
188     REPORTER_ASSERT(reporter, length == SK_Scalar1);
189     REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent));
190     REPORTER_ASSERT(reporter,
191         SkScalarNearlyEqual(position.fX,
192                             1.5f,
193                             0.0001f));
194     REPORTER_ASSERT(reporter,
195         SkScalarNearlyEqual(position.fY,
196                             2.0f,
197                             0.0001f));
198     REPORTER_ASSERT(reporter, tangent.fX == -SK_Scalar1);
199     REPORTER_ASSERT(reporter, tangent.fY == 0);
200 
201     test_small_segment();
202     test_small_segment2();
203     test_small_segment3();
204 }
205 
DEF_TEST(PathMeasureConic,reporter)206 DEF_TEST(PathMeasureConic, reporter) {
207     SkPoint stdP, hiP, pts[] = {{0,0}, {100,0}, {100,0}};
208     SkPath p;
209     p.moveTo(0, 0);
210     p.conicTo(pts[1], pts[2], 1);
211     SkPathMeasure stdm(p, false);
212     REPORTER_ASSERT(reporter, stdm.getPosTan(20, &stdP, nullptr));
213     p.reset();
214     p.moveTo(0, 0);
215     p.conicTo(pts[1], pts[2], 10);
216     stdm.setPath(&p, false);
217     REPORTER_ASSERT(reporter, stdm.getPosTan(20, &hiP, nullptr));
218     REPORTER_ASSERT(reporter, 19.5f < stdP.fX && stdP.fX < 20.5f);
219     REPORTER_ASSERT(reporter, 19.5f < hiP.fX && hiP.fX < 20.5f);
220 }
221 
222 // Regression test for b/26425223
DEF_TEST(PathMeasure_nextctr,reporter)223 DEF_TEST(PathMeasure_nextctr, reporter) {
224     SkPath path;
225     path.moveTo(0, 0); path.lineTo(100, 0);
226 
227     SkPathMeasure meas(path, false);
228     // only expect 1 contour, even if we didn't explicitly call getLength() ourselves
229     REPORTER_ASSERT(reporter, !meas.nextContour());
230 }
231 
232 #include "include/core/SkContourMeasure.h"
233 
test_90_degrees(sk_sp<SkContourMeasure> cm,SkScalar radius,skiatest::Reporter * reporter)234 static void test_90_degrees(sk_sp<SkContourMeasure> cm, SkScalar radius,
235                             skiatest::Reporter* reporter) {
236     SkPoint pos;
237     SkVector tan;
238     SkScalar distance = cm->length() / 4;
239     bool success = cm->getPosTan(distance, &pos, &tan);
240 
241     REPORTER_ASSERT(reporter, success);
242     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(pos.fX, 0));
243     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(pos.fY, radius));
244     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(tan.fX, -1));
245     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(tan.fY, 0));
246 }
247 
test_empty_contours(skiatest::Reporter * reporter)248 static void test_empty_contours(skiatest::Reporter* reporter) {
249     SkPath path;
250 
251     path.moveTo(0, 0).lineTo(100, 100).lineTo(200, 100);
252     path.moveTo(2, 2).moveTo(3, 3);                 // zero-length(s)
253     path.moveTo(4, 4).close().close().close();      // zero-length
254     path.moveTo(5, 5).lineTo(5, 5);                 // zero-length
255     path.moveTo(5, 5).lineTo(5, 5).close();         // zero-length
256     path.moveTo(5, 5).lineTo(5, 5).close().close(); // zero-length
257     path.moveTo(6, 6).lineTo(7, 7);
258     path.moveTo(10, 10);                            // zero-length
259 
260     SkContourMeasureIter fact(path, false);
261 
262     // given the above construction, we expect only 2 contours (the rest are "empty")
263 
264     REPORTER_ASSERT(reporter, fact.next());
265     REPORTER_ASSERT(reporter, fact.next());
266     REPORTER_ASSERT(reporter, !fact.next());
267 }
268 
test_MLM_contours(skiatest::Reporter * reporter)269 static void test_MLM_contours(skiatest::Reporter* reporter) {
270     SkPath path;
271 
272     // This odd sequence (with a trailing moveTo) used to return a 2nd contour, which is
273     // wrong, since the contract for a measure is to only return non-zero length contours.
274     path.moveTo(10, 10).lineTo(20, 20).moveTo(30, 30);
275 
276     for (bool forceClosed : {false, true}) {
277         SkContourMeasureIter fact(path, forceClosed);
278         REPORTER_ASSERT(reporter, fact.next());
279         REPORTER_ASSERT(reporter, !fact.next());
280     }
281 }
282 
test_shrink(skiatest::Reporter * reporter)283 static void test_shrink(skiatest::Reporter* reporter) {
284     SkPath path;
285     path.addRect({1, 2, 3, 4});
286     path.incReserve(100);   // give shrinkToFit() something to do
287 
288     SkContourMeasureIter iter(path, false);
289 
290     // shrinks the allocation, possibly relocating the underlying arrays.
291     // The contouremasureiter needs to have safely copied path, to be unaffected by this
292     // change to "path".
293     SkPathPriv::ShrinkToFit(&path);
294 
295     // Note, this failed (before the fix) on an ASAN build, which notices that we were
296     // using an internal iterator of the passed-in path, not our copy.
297     while (iter.next())
298         ;
299 }
300 
DEF_TEST(contour_measure,reporter)301 DEF_TEST(contour_measure, reporter) {
302     SkPath path;
303     path.addCircle(0, 0, 100);
304     path.addCircle(0, 0, 10);
305 
306     SkContourMeasureIter fact(path, false);
307     path.reset();   // we should not need the path avert we created the factory
308 
309     auto cm0 = fact.next();
310     auto cm1 = fact.next();
311 
312     REPORTER_ASSERT(reporter, cm0->isClosed());
313     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(cm0->length(), 200 * SK_ScalarPI, 1.5f));
314 
315     test_90_degrees(cm0, 100, reporter);
316 
317     REPORTER_ASSERT(reporter, cm1->isClosed());
318     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(cm1->length(), 20 * SK_ScalarPI, 0.5f));
319 
320     test_90_degrees(cm1, 10, reporter);
321 
322     auto cm2 = fact.next();
323     REPORTER_ASSERT(reporter, !cm2);
324 
325     test_empty_contours(reporter);
326     test_MLM_contours(reporter);
327 
328     test_shrink(reporter);
329 }
330