blob: 08731222743b66b6ab08a7caf39b9854ca732848 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
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 */
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00007
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkPathMeasure.h"
9#include "tests/Test.h"
reed@android.com5e5adfd2009-03-07 03:39:23 +000010
sugoi@google.com54f0d1b2013-02-27 19:17:41 +000011static void test_small_segment3() {
reed@google.comded44142012-04-27 20:22:07 +000012 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 };
rmistry@google.comd6176b02012-08-23 18:14:13 +000018
reed@google.comded44142012-04-27 20:22:07 +000019 path.moveTo(pts[0]);
bsalomon@google.com2fc23592012-10-03 19:10:31 +000020 for (size_t i = 1; i < SK_ARRAY_COUNT(pts); i += 3) {
reed@google.comded44142012-04-27 20:22:07 +000021 path.cubicTo(pts[i], pts[i + 1], pts[i + 2]);
22 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000023
reed@google.comded44142012-04-27 20:22:07 +000024 SkPathMeasure meas(path, false);
25 meas.getLength();
reed@google.comded44142012-04-27 20:22:07 +000026}
27
sugoi@google.com54f0d1b2013-02-27 19:17:41 +000028static void test_small_segment2() {
reed@google.comded44142012-04-27 20:22:07 +000029 SkPath path;
30 const SkPoint pts[] = {
31 { 0, 0 },
rmistry@google.comd6176b02012-08-23 18:14:13 +000032 { 100000000000.0f, 100000000000.0f }, { 0, 0 },
33 { 10, 10 }, { 0, 0 },
reed@google.comded44142012-04-27 20:22:07 +000034 };
rmistry@google.comd6176b02012-08-23 18:14:13 +000035
reed@google.comded44142012-04-27 20:22:07 +000036 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();
reed@google.comded44142012-04-27 20:22:07 +000042}
43
sugoi@google.com54f0d1b2013-02-27 19:17:41 +000044static void test_small_segment() {
reed@google.comfab1ddd2012-04-20 15:10:32 +000045 SkPath path;
46 const SkPoint pts[] = {
47 { 100000, 100000},
48 // big jump between these points, makes a big segment
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +000049 { 1.0005f, 0.9999f },
reed@google.comfab1ddd2012-04-20 15:10:32 +000050 // tiny (non-zero) jump between these points
robertphillips@google.comc6ce7502012-05-08 13:15:37 +000051 { SK_Scalar1, SK_Scalar1 },
reed@google.comfab1ddd2012-04-20 15:10:32 +000052 };
rmistry@google.comd6176b02012-08-23 18:14:13 +000053
reed@google.comfab1ddd2012-04-20 15:10:32 +000054 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;
reed@google.comded44142012-04-27 20:22:07 +000066
reed@google.comfab1ddd2012-04-20 15:10:32 +000067 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();
reed@google.comfab1ddd2012-04-20 15:10:32 +000074}
75
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +000076DEF_TEST(PathMeasure, reporter) {
reed@android.com5e5adfd2009-03-07 03:39:23 +000077 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
schenney@chromium.org510c6b12012-01-12 20:04:06 +0000101 // 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,
robertphillips@google.com6853e802012-04-16 15:50:18 +0000118 SkScalarNearlyEqual(position.fX,
119 -SK_ScalarHalf,
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000120 0.0001f));
schenney@chromium.org510c6b12012-01-12 20:04:06 +0000121 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,
robertphillips@google.com6853e802012-04-16 15:50:18 +0000143 SkScalarNearlyEqual(position.fX,
144 SK_ScalarHalf,
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000145 0.0001f));
schenney@chromium.org510c6b12012-01-12 20:04:06 +0000146 REPORTER_ASSERT(reporter, position.fY == 0);
147 REPORTER_ASSERT(reporter, tangent.fX == SK_Scalar1);
148 REPORTER_ASSERT(reporter, tangent.fY == 0);
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000149 REPORTER_ASSERT(reporter, meas.getPosTan(2.5f, &position, &tangent));
schenney@chromium.org510c6b12012-01-12 20:04:06 +0000150 REPORTER_ASSERT(reporter,
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000151 SkScalarNearlyEqual(position.fX, SK_Scalar1, 0.0001f));
schenney@chromium.org510c6b12012-01-12 20:04:06 +0000152 REPORTER_ASSERT(reporter,
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000153 SkScalarNearlyEqual(position.fY, 1.5f));
schenney@chromium.org510c6b12012-01-12 20:04:06 +0000154 REPORTER_ASSERT(reporter, tangent.fX == 0);
155 REPORTER_ASSERT(reporter, tangent.fY == SK_Scalar1);
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000156 REPORTER_ASSERT(reporter, meas.getPosTan(4.5f, &position, &tangent));
schenney@chromium.org510c6b12012-01-12 20:04:06 +0000157 REPORTER_ASSERT(reporter,
rmistry@google.comd6176b02012-08-23 18:14:13 +0000158 SkScalarNearlyEqual(position.fX,
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000159 2.5f,
160 0.0001f));
schenney@chromium.org510c6b12012-01-12 20:04:06 +0000161 REPORTER_ASSERT(reporter,
rmistry@google.comd6176b02012-08-23 18:14:13 +0000162 SkScalarNearlyEqual(position.fY,
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000163 2.0f,
164 0.0001f));
schenney@chromium.org510c6b12012-01-12 20:04:06 +0000165 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,
robertphillips@google.com6853e802012-04-16 15:50:18 +0000179 SkScalarNearlyEqual(position.fX,
180 SK_ScalarHalf,
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000181 0.0001f));
schenney@chromium.org510c6b12012-01-12 20:04:06 +0000182 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,
rmistry@google.comd6176b02012-08-23 18:14:13 +0000190 SkScalarNearlyEqual(position.fX,
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000191 1.5f,
192 0.0001f));
schenney@chromium.org510c6b12012-01-12 20:04:06 +0000193 REPORTER_ASSERT(reporter,
robertphillips@google.com6853e802012-04-16 15:50:18 +0000194 SkScalarNearlyEqual(position.fY,
commit-bot@chromium.org4b413c82013-11-25 19:44:07 +0000195 2.0f,
196 0.0001f));
schenney@chromium.org510c6b12012-01-12 20:04:06 +0000197 REPORTER_ASSERT(reporter, tangent.fX == -SK_Scalar1);
198 REPORTER_ASSERT(reporter, tangent.fY == 0);
reed@google.comfab1ddd2012-04-20 15:10:32 +0000199
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000200 test_small_segment();
201 test_small_segment2();
202 test_small_segment3();
reed@android.com5e5adfd2009-03-07 03:39:23 +0000203}
caryclarkb6474dd2016-01-19 08:07:49 -0800204
205DEF_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}
Mike Reed140ab362018-03-20 16:47:32 +0000220
221// Regression test for b/26425223
222DEF_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}
Mike Reedcc88f3a2019-02-06 11:40:27 -0500230
Mike Kleinc0bd9f92019-04-23 12:05:21 -0500231#include "include/core/SkContourMeasure.h"
Mike Reedcc88f3a2019-02-06 11:40:27 -0500232
233static 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
247static 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
Mike Reed62ad46b2019-02-13 11:48:35 -0500268static 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
Mike Reedcc88f3a2019-02-06 11:40:27 -0500282DEF_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);
Mike Reed62ad46b2019-02-13 11:48:35 -0500307 test_MLM_contours(reporter);
Mike Reedcc88f3a2019-02-06 11:40:27 -0500308}