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