xref: /aosp_15_r20/external/skia/tests/PathMeasureTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2011 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkContourMeasure.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPath.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPathMeasure.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPathTypes.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkScalar.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkPathMeasurePriv.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkPathPriv.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "tests/Test.h"
19*c8dee2aaSAndroid Build Coastguard Worker 
20*c8dee2aaSAndroid Build Coastguard Worker #include <array>
21*c8dee2aaSAndroid Build Coastguard Worker #include <cstddef>
22*c8dee2aaSAndroid Build Coastguard Worker #include <initializer_list>
23*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
24*c8dee2aaSAndroid Build Coastguard Worker 
test_small_segment3(skiatest::Reporter * reporter)25*c8dee2aaSAndroid Build Coastguard Worker static void test_small_segment3(skiatest::Reporter* reporter) {
26*c8dee2aaSAndroid Build Coastguard Worker     SkPath path;
27*c8dee2aaSAndroid Build Coastguard Worker     const SkPoint pts[] = {
28*c8dee2aaSAndroid Build Coastguard Worker         { 0, 0 },
29*c8dee2aaSAndroid Build Coastguard Worker         { 100000000000.0f, 100000000000.0f }, { 0, 0 }, { 10, 10 },
30*c8dee2aaSAndroid Build Coastguard Worker         { 10, 10 }, { 0, 0 }, { 10, 10 }
31*c8dee2aaSAndroid Build Coastguard Worker     };
32*c8dee2aaSAndroid Build Coastguard Worker 
33*c8dee2aaSAndroid Build Coastguard Worker     path.moveTo(pts[0]);
34*c8dee2aaSAndroid Build Coastguard Worker     for (size_t i = 1; i < std::size(pts); i += 3) {
35*c8dee2aaSAndroid Build Coastguard Worker         path.cubicTo(pts[i], pts[i + 1], pts[i + 2]);
36*c8dee2aaSAndroid Build Coastguard Worker     }
37*c8dee2aaSAndroid Build Coastguard Worker 
38*c8dee2aaSAndroid Build Coastguard Worker     SkPathMeasure meas(path, false);
39*c8dee2aaSAndroid Build Coastguard Worker     meas.getLength();
40*c8dee2aaSAndroid Build Coastguard Worker 
41*c8dee2aaSAndroid Build Coastguard Worker     // Now check that we cap the segment size even with very large resolution scales.
42*c8dee2aaSAndroid Build Coastguard Worker     // Earlier versions allowed the pathmeasure to recurse without limit in the face
43*c8dee2aaSAndroid Build Coastguard Worker     // of a very large scale.
44*c8dee2aaSAndroid Build Coastguard Worker     //
45*c8dee2aaSAndroid Build Coastguard Worker     //  Before this limit, the above meas had 15K segments, and when built with
46*c8dee2aaSAndroid Build Coastguard Worker     // a resScale of 100, it had 184K segments -- for 1 cubic!
47*c8dee2aaSAndroid Build Coastguard Worker     {
48*c8dee2aaSAndroid Build Coastguard Worker         auto n = SkPathMeasurePriv::CountSegments(meas);
49*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, n < 300);
50*c8dee2aaSAndroid Build Coastguard Worker 
51*c8dee2aaSAndroid Build Coastguard Worker         constexpr float resScale = 1000;
52*c8dee2aaSAndroid Build Coastguard Worker         n = SkPathMeasurePriv::CountSegments(SkPathMeasure(path, false, resScale));
53*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, n < 300);
54*c8dee2aaSAndroid Build Coastguard Worker     }
55*c8dee2aaSAndroid Build Coastguard Worker }
56*c8dee2aaSAndroid Build Coastguard Worker 
test_small_segment2()57*c8dee2aaSAndroid Build Coastguard Worker static void test_small_segment2() {
58*c8dee2aaSAndroid Build Coastguard Worker     SkPath path;
59*c8dee2aaSAndroid Build Coastguard Worker     const SkPoint pts[] = {
60*c8dee2aaSAndroid Build Coastguard Worker         { 0, 0 },
61*c8dee2aaSAndroid Build Coastguard Worker         { 100000000000.0f, 100000000000.0f }, { 0, 0 },
62*c8dee2aaSAndroid Build Coastguard Worker         { 10, 10 }, { 0, 0 },
63*c8dee2aaSAndroid Build Coastguard Worker     };
64*c8dee2aaSAndroid Build Coastguard Worker 
65*c8dee2aaSAndroid Build Coastguard Worker     path.moveTo(pts[0]);
66*c8dee2aaSAndroid Build Coastguard Worker     for (size_t i = 1; i < std::size(pts); i += 2) {
67*c8dee2aaSAndroid Build Coastguard Worker         path.quadTo(pts[i], pts[i + 1]);
68*c8dee2aaSAndroid Build Coastguard Worker     }
69*c8dee2aaSAndroid Build Coastguard Worker     SkPathMeasure meas(path, false);
70*c8dee2aaSAndroid Build Coastguard Worker     meas.getLength();
71*c8dee2aaSAndroid Build Coastguard Worker }
72*c8dee2aaSAndroid Build Coastguard Worker 
test_small_segment()73*c8dee2aaSAndroid Build Coastguard Worker static void test_small_segment() {
74*c8dee2aaSAndroid Build Coastguard Worker     SkPath path;
75*c8dee2aaSAndroid Build Coastguard Worker     const SkPoint pts[] = {
76*c8dee2aaSAndroid Build Coastguard Worker         { 100000, 100000},
77*c8dee2aaSAndroid Build Coastguard Worker         // big jump between these points, makes a big segment
78*c8dee2aaSAndroid Build Coastguard Worker         { 1.0005f, 0.9999f },
79*c8dee2aaSAndroid Build Coastguard Worker         // tiny (non-zero) jump between these points
80*c8dee2aaSAndroid Build Coastguard Worker         { SK_Scalar1, SK_Scalar1 },
81*c8dee2aaSAndroid Build Coastguard Worker     };
82*c8dee2aaSAndroid Build Coastguard Worker 
83*c8dee2aaSAndroid Build Coastguard Worker     path.moveTo(pts[0]);
84*c8dee2aaSAndroid Build Coastguard Worker     for (size_t i = 1; i < std::size(pts); ++i) {
85*c8dee2aaSAndroid Build Coastguard Worker         path.lineTo(pts[i]);
86*c8dee2aaSAndroid Build Coastguard Worker     }
87*c8dee2aaSAndroid Build Coastguard Worker     SkPathMeasure meas(path, false);
88*c8dee2aaSAndroid Build Coastguard Worker 
89*c8dee2aaSAndroid Build Coastguard Worker     /*  this would assert (before a fix) because we added a segment with
90*c8dee2aaSAndroid Build Coastguard Worker         the same length as the prev segment, due to the follow (bad) pattern
91*c8dee2aaSAndroid Build Coastguard Worker 
92*c8dee2aaSAndroid Build Coastguard Worker         d = distance(pts[0], pts[1]);
93*c8dee2aaSAndroid Build Coastguard Worker         distance += d;
94*c8dee2aaSAndroid Build Coastguard Worker         seg->fDistance = distance;
95*c8dee2aaSAndroid Build Coastguard Worker 
96*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(d > 0);    // TRUE
97*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(seg->fDistance > prevSeg->fDistance);  // FALSE
98*c8dee2aaSAndroid Build Coastguard Worker 
99*c8dee2aaSAndroid Build Coastguard Worker         This 2nd assert failes because (distance += d) didn't affect distance
100*c8dee2aaSAndroid Build Coastguard Worker         because distance >>> d.
101*c8dee2aaSAndroid Build Coastguard Worker      */
102*c8dee2aaSAndroid Build Coastguard Worker     meas.getLength();
103*c8dee2aaSAndroid Build Coastguard Worker }
104*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(PathMeasure,reporter)105*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(PathMeasure, reporter) {
106*c8dee2aaSAndroid Build Coastguard Worker     SkPath  path;
107*c8dee2aaSAndroid Build Coastguard Worker 
108*c8dee2aaSAndroid Build Coastguard Worker     path.moveTo(0, 0);
109*c8dee2aaSAndroid Build Coastguard Worker     path.lineTo(SK_Scalar1, 0);
110*c8dee2aaSAndroid Build Coastguard Worker     path.lineTo(SK_Scalar1, SK_Scalar1);
111*c8dee2aaSAndroid Build Coastguard Worker     path.lineTo(0, SK_Scalar1);
112*c8dee2aaSAndroid Build Coastguard Worker 
113*c8dee2aaSAndroid Build Coastguard Worker     SkPathMeasure   meas(path, true);
114*c8dee2aaSAndroid Build Coastguard Worker     SkScalar        length = meas.getLength();
115*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(length == SK_Scalar1*4);
116*c8dee2aaSAndroid Build Coastguard Worker 
117*c8dee2aaSAndroid Build Coastguard Worker     path.reset();
118*c8dee2aaSAndroid Build Coastguard Worker     path.moveTo(0, 0);
119*c8dee2aaSAndroid Build Coastguard Worker     path.lineTo(SK_Scalar1*3, SK_Scalar1*4);
120*c8dee2aaSAndroid Build Coastguard Worker     meas.setPath(&path, false);
121*c8dee2aaSAndroid Build Coastguard Worker     length = meas.getLength();
122*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, length == SK_Scalar1*5);
123*c8dee2aaSAndroid Build Coastguard Worker 
124*c8dee2aaSAndroid Build Coastguard Worker     path.reset();
125*c8dee2aaSAndroid Build Coastguard Worker     path.addCircle(0, 0, SK_Scalar1);
126*c8dee2aaSAndroid Build Coastguard Worker     meas.setPath(&path, true);
127*c8dee2aaSAndroid Build Coastguard Worker     length = meas.getLength();
128*c8dee2aaSAndroid Build Coastguard Worker //    SkDebugf("circle arc-length = %g\n", length);
129*c8dee2aaSAndroid Build Coastguard Worker 
130*c8dee2aaSAndroid Build Coastguard Worker     // Test the behavior following a close not followed by a move.
131*c8dee2aaSAndroid Build Coastguard Worker     path.reset();
132*c8dee2aaSAndroid Build Coastguard Worker     path.lineTo(SK_Scalar1, 0);
133*c8dee2aaSAndroid Build Coastguard Worker     path.lineTo(SK_Scalar1, SK_Scalar1);
134*c8dee2aaSAndroid Build Coastguard Worker     path.lineTo(0, SK_Scalar1);
135*c8dee2aaSAndroid Build Coastguard Worker     path.close();
136*c8dee2aaSAndroid Build Coastguard Worker     path.lineTo(-SK_Scalar1, 0);
137*c8dee2aaSAndroid Build Coastguard Worker     meas.setPath(&path, false);
138*c8dee2aaSAndroid Build Coastguard Worker     length = meas.getLength();
139*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, length == SK_Scalar1 * 4);
140*c8dee2aaSAndroid Build Coastguard Worker     meas.nextContour();
141*c8dee2aaSAndroid Build Coastguard Worker     length = meas.getLength();
142*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, length == SK_Scalar1);
143*c8dee2aaSAndroid Build Coastguard Worker     SkPoint position;
144*c8dee2aaSAndroid Build Coastguard Worker     SkVector tangent;
145*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent));
146*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter,
147*c8dee2aaSAndroid Build Coastguard Worker         SkScalarNearlyEqual(position.fX,
148*c8dee2aaSAndroid Build Coastguard Worker                             -SK_ScalarHalf,
149*c8dee2aaSAndroid Build Coastguard Worker                             0.0001f));
150*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, position.fY == 0);
151*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, tangent.fX == -SK_Scalar1);
152*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, tangent.fY == 0);
153*c8dee2aaSAndroid Build Coastguard Worker 
154*c8dee2aaSAndroid Build Coastguard Worker     // Test degenerate paths
155*c8dee2aaSAndroid Build Coastguard Worker     path.reset();
156*c8dee2aaSAndroid Build Coastguard Worker     path.moveTo(0, 0);
157*c8dee2aaSAndroid Build Coastguard Worker     path.lineTo(0, 0);
158*c8dee2aaSAndroid Build Coastguard Worker     path.lineTo(SK_Scalar1, 0);
159*c8dee2aaSAndroid Build Coastguard Worker     path.quadTo(SK_Scalar1, 0, SK_Scalar1, 0);
160*c8dee2aaSAndroid Build Coastguard Worker     path.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1 * 2);
161*c8dee2aaSAndroid Build Coastguard Worker     path.cubicTo(SK_Scalar1, SK_Scalar1 * 2,
162*c8dee2aaSAndroid Build Coastguard Worker                  SK_Scalar1, SK_Scalar1 * 2,
163*c8dee2aaSAndroid Build Coastguard Worker                  SK_Scalar1, SK_Scalar1 * 2);
164*c8dee2aaSAndroid Build Coastguard Worker     path.cubicTo(SK_Scalar1*2, SK_Scalar1 * 2,
165*c8dee2aaSAndroid Build Coastguard Worker                  SK_Scalar1*3, SK_Scalar1 * 2,
166*c8dee2aaSAndroid Build Coastguard Worker                  SK_Scalar1*4, SK_Scalar1 * 2);
167*c8dee2aaSAndroid Build Coastguard Worker     meas.setPath(&path, false);
168*c8dee2aaSAndroid Build Coastguard Worker     length = meas.getLength();
169*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, length == SK_Scalar1 * 6);
170*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent));
171*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter,
172*c8dee2aaSAndroid Build Coastguard Worker         SkScalarNearlyEqual(position.fX,
173*c8dee2aaSAndroid Build Coastguard Worker                             SK_ScalarHalf,
174*c8dee2aaSAndroid Build Coastguard Worker                             0.0001f));
175*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, position.fY == 0);
176*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, tangent.fX == SK_Scalar1);
177*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, tangent.fY == 0);
178*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, meas.getPosTan(2.5f, &position, &tangent));
179*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter,
180*c8dee2aaSAndroid Build Coastguard Worker         SkScalarNearlyEqual(position.fX, SK_Scalar1, 0.0001f));
181*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter,
182*c8dee2aaSAndroid Build Coastguard Worker         SkScalarNearlyEqual(position.fY, 1.5f));
183*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, tangent.fX == 0);
184*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, tangent.fY == SK_Scalar1);
185*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, meas.getPosTan(4.5f, &position, &tangent));
186*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter,
187*c8dee2aaSAndroid Build Coastguard Worker         SkScalarNearlyEqual(position.fX,
188*c8dee2aaSAndroid Build Coastguard Worker                             2.5f,
189*c8dee2aaSAndroid Build Coastguard Worker                             0.0001f));
190*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter,
191*c8dee2aaSAndroid Build Coastguard Worker         SkScalarNearlyEqual(position.fY,
192*c8dee2aaSAndroid Build Coastguard Worker                             2.0f,
193*c8dee2aaSAndroid Build Coastguard Worker                             0.0001f));
194*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, tangent.fX == SK_Scalar1);
195*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, tangent.fY == 0);
196*c8dee2aaSAndroid Build Coastguard Worker 
197*c8dee2aaSAndroid Build Coastguard Worker     path.reset();
198*c8dee2aaSAndroid Build Coastguard Worker     path.moveTo(0, 0);
199*c8dee2aaSAndroid Build Coastguard Worker     path.lineTo(SK_Scalar1, 0);
200*c8dee2aaSAndroid Build Coastguard Worker     path.moveTo(SK_Scalar1, SK_Scalar1);
201*c8dee2aaSAndroid Build Coastguard Worker     path.moveTo(SK_Scalar1 * 2, SK_Scalar1 * 2);
202*c8dee2aaSAndroid Build Coastguard Worker     path.lineTo(SK_Scalar1, SK_Scalar1 * 2);
203*c8dee2aaSAndroid Build Coastguard Worker     meas.setPath(&path, false);
204*c8dee2aaSAndroid Build Coastguard Worker     length = meas.getLength();
205*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, length == SK_Scalar1);
206*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent));
207*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter,
208*c8dee2aaSAndroid Build Coastguard Worker         SkScalarNearlyEqual(position.fX,
209*c8dee2aaSAndroid Build Coastguard Worker                             SK_ScalarHalf,
210*c8dee2aaSAndroid Build Coastguard Worker                             0.0001f));
211*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, position.fY == 0);
212*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, tangent.fX == SK_Scalar1);
213*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, tangent.fY == 0);
214*c8dee2aaSAndroid Build Coastguard Worker     meas.nextContour();
215*c8dee2aaSAndroid Build Coastguard Worker     length = meas.getLength();
216*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, length == SK_Scalar1);
217*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent));
218*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter,
219*c8dee2aaSAndroid Build Coastguard Worker         SkScalarNearlyEqual(position.fX,
220*c8dee2aaSAndroid Build Coastguard Worker                             1.5f,
221*c8dee2aaSAndroid Build Coastguard Worker                             0.0001f));
222*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter,
223*c8dee2aaSAndroid Build Coastguard Worker         SkScalarNearlyEqual(position.fY,
224*c8dee2aaSAndroid Build Coastguard Worker                             2.0f,
225*c8dee2aaSAndroid Build Coastguard Worker                             0.0001f));
226*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, tangent.fX == -SK_Scalar1);
227*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, tangent.fY == 0);
228*c8dee2aaSAndroid Build Coastguard Worker 
229*c8dee2aaSAndroid Build Coastguard Worker     test_small_segment();
230*c8dee2aaSAndroid Build Coastguard Worker     test_small_segment2();
231*c8dee2aaSAndroid Build Coastguard Worker     test_small_segment3(reporter);
232*c8dee2aaSAndroid Build Coastguard Worker 
233*c8dee2aaSAndroid Build Coastguard Worker     // SkPathMeasure isn't copyable, but it should be move-able
234*c8dee2aaSAndroid Build Coastguard Worker     SkPathMeasure meas2(std::move(meas));
235*c8dee2aaSAndroid Build Coastguard Worker     meas = std::move(meas2);
236*c8dee2aaSAndroid Build Coastguard Worker }
237*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(PathMeasureConic,reporter)238*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(PathMeasureConic, reporter) {
239*c8dee2aaSAndroid Build Coastguard Worker     SkPoint stdP, hiP, pts[] = {{0,0}, {100,0}, {100,0}};
240*c8dee2aaSAndroid Build Coastguard Worker     SkPath p;
241*c8dee2aaSAndroid Build Coastguard Worker     p.moveTo(0, 0);
242*c8dee2aaSAndroid Build Coastguard Worker     p.conicTo(pts[1], pts[2], 1);
243*c8dee2aaSAndroid Build Coastguard Worker     SkPathMeasure stdm(p, false);
244*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, stdm.getPosTan(20, &stdP, nullptr));
245*c8dee2aaSAndroid Build Coastguard Worker     p.reset();
246*c8dee2aaSAndroid Build Coastguard Worker     p.moveTo(0, 0);
247*c8dee2aaSAndroid Build Coastguard Worker     p.conicTo(pts[1], pts[2], 10);
248*c8dee2aaSAndroid Build Coastguard Worker     stdm.setPath(&p, false);
249*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, stdm.getPosTan(20, &hiP, nullptr));
250*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, 19.5f < stdP.fX && stdP.fX < 20.5f);
251*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, 19.5f < hiP.fX && hiP.fX < 20.5f);
252*c8dee2aaSAndroid Build Coastguard Worker }
253*c8dee2aaSAndroid Build Coastguard Worker 
254*c8dee2aaSAndroid Build Coastguard Worker // Regression test for b/26425223
DEF_TEST(PathMeasure_nextctr,reporter)255*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(PathMeasure_nextctr, reporter) {
256*c8dee2aaSAndroid Build Coastguard Worker     SkPath path;
257*c8dee2aaSAndroid Build Coastguard Worker     path.moveTo(0, 0); path.lineTo(100, 0);
258*c8dee2aaSAndroid Build Coastguard Worker 
259*c8dee2aaSAndroid Build Coastguard Worker     SkPathMeasure meas(path, false);
260*c8dee2aaSAndroid Build Coastguard Worker     // only expect 1 contour, even if we didn't explicitly call getLength() ourselves
261*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, !meas.nextContour());
262*c8dee2aaSAndroid Build Coastguard Worker }
263*c8dee2aaSAndroid Build Coastguard Worker 
test_90_degrees(const sk_sp<SkContourMeasure> & cm,SkScalar radius,skiatest::Reporter * reporter)264*c8dee2aaSAndroid Build Coastguard Worker static void test_90_degrees(const sk_sp<SkContourMeasure>& cm, SkScalar radius,
265*c8dee2aaSAndroid Build Coastguard Worker                             skiatest::Reporter* reporter) {
266*c8dee2aaSAndroid Build Coastguard Worker     SkPoint pos;
267*c8dee2aaSAndroid Build Coastguard Worker     SkVector tan;
268*c8dee2aaSAndroid Build Coastguard Worker     SkScalar distance = cm->length() / 4;
269*c8dee2aaSAndroid Build Coastguard Worker     bool success = cm->getPosTan(distance, &pos, &tan);
270*c8dee2aaSAndroid Build Coastguard Worker 
271*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, success);
272*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(pos.fX, 0));
273*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(pos.fY, radius));
274*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(tan.fX, -1));
275*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(tan.fY, 0));
276*c8dee2aaSAndroid Build Coastguard Worker }
277*c8dee2aaSAndroid Build Coastguard Worker 
test_empty_contours(skiatest::Reporter * reporter)278*c8dee2aaSAndroid Build Coastguard Worker static void test_empty_contours(skiatest::Reporter* reporter) {
279*c8dee2aaSAndroid Build Coastguard Worker     SkPath path;
280*c8dee2aaSAndroid Build Coastguard Worker 
281*c8dee2aaSAndroid Build Coastguard Worker     path.moveTo(0, 0).lineTo(100, 100).lineTo(200, 100);
282*c8dee2aaSAndroid Build Coastguard Worker     path.moveTo(2, 2).moveTo(3, 3);                 // zero-length(s)
283*c8dee2aaSAndroid Build Coastguard Worker     path.moveTo(4, 4).close().close().close();      // zero-length
284*c8dee2aaSAndroid Build Coastguard Worker     path.moveTo(5, 5).lineTo(5, 5);                 // zero-length
285*c8dee2aaSAndroid Build Coastguard Worker     path.moveTo(5, 5).lineTo(5, 5).close();         // zero-length
286*c8dee2aaSAndroid Build Coastguard Worker     path.moveTo(5, 5).lineTo(5, 5).close().close(); // zero-length
287*c8dee2aaSAndroid Build Coastguard Worker     path.moveTo(6, 6).lineTo(7, 7);
288*c8dee2aaSAndroid Build Coastguard Worker     path.moveTo(10, 10);                            // zero-length
289*c8dee2aaSAndroid Build Coastguard Worker 
290*c8dee2aaSAndroid Build Coastguard Worker     SkContourMeasureIter fact(path, false);
291*c8dee2aaSAndroid Build Coastguard Worker 
292*c8dee2aaSAndroid Build Coastguard Worker     // given the above construction, we expect only 2 contours (the rest are "empty")
293*c8dee2aaSAndroid Build Coastguard Worker 
294*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, fact.next());
295*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, fact.next());
296*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, !fact.next());
297*c8dee2aaSAndroid Build Coastguard Worker }
298*c8dee2aaSAndroid Build Coastguard Worker 
test_MLM_contours(skiatest::Reporter * reporter)299*c8dee2aaSAndroid Build Coastguard Worker static void test_MLM_contours(skiatest::Reporter* reporter) {
300*c8dee2aaSAndroid Build Coastguard Worker     SkPath path;
301*c8dee2aaSAndroid Build Coastguard Worker 
302*c8dee2aaSAndroid Build Coastguard Worker     // This odd sequence (with a trailing moveTo) used to return a 2nd contour, which is
303*c8dee2aaSAndroid Build Coastguard Worker     // wrong, since the contract for a measure is to only return non-zero length contours.
304*c8dee2aaSAndroid Build Coastguard Worker     path.moveTo(10, 10).lineTo(20, 20).moveTo(30, 30);
305*c8dee2aaSAndroid Build Coastguard Worker 
306*c8dee2aaSAndroid Build Coastguard Worker     for (bool forceClosed : {false, true}) {
307*c8dee2aaSAndroid Build Coastguard Worker         SkContourMeasureIter fact(path, forceClosed);
308*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, fact.next());
309*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, !fact.next());
310*c8dee2aaSAndroid Build Coastguard Worker     }
311*c8dee2aaSAndroid Build Coastguard Worker }
312*c8dee2aaSAndroid Build Coastguard Worker 
test_shrink(skiatest::Reporter * reporter)313*c8dee2aaSAndroid Build Coastguard Worker static void test_shrink(skiatest::Reporter* reporter) {
314*c8dee2aaSAndroid Build Coastguard Worker     SkPath path;
315*c8dee2aaSAndroid Build Coastguard Worker     path.addRect({1, 2, 3, 4});
316*c8dee2aaSAndroid Build Coastguard Worker     path.incReserve(100);   // give shrinkToFit() something to do
317*c8dee2aaSAndroid Build Coastguard Worker 
318*c8dee2aaSAndroid Build Coastguard Worker     SkContourMeasureIter iter(path, false);
319*c8dee2aaSAndroid Build Coastguard Worker 
320*c8dee2aaSAndroid Build Coastguard Worker     // shrinks the allocation, possibly relocating the underlying arrays.
321*c8dee2aaSAndroid Build Coastguard Worker     // The contouremasureiter needs to have safely copied path, to be unaffected by this
322*c8dee2aaSAndroid Build Coastguard Worker     // change to "path".
323*c8dee2aaSAndroid Build Coastguard Worker     SkPathPriv::ShrinkToFit(&path);
324*c8dee2aaSAndroid Build Coastguard Worker 
325*c8dee2aaSAndroid Build Coastguard Worker     // Note, this failed (before the fix) on an ASAN build, which notices that we were
326*c8dee2aaSAndroid Build Coastguard Worker     // using an internal iterator of the passed-in path, not our copy.
327*c8dee2aaSAndroid Build Coastguard Worker     while (iter.next())
328*c8dee2aaSAndroid Build Coastguard Worker         ;
329*c8dee2aaSAndroid Build Coastguard Worker }
330*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(contour_measure,reporter)331*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(contour_measure, reporter) {
332*c8dee2aaSAndroid Build Coastguard Worker     SkPath path;
333*c8dee2aaSAndroid Build Coastguard Worker     path.addCircle(0, 0, 100);
334*c8dee2aaSAndroid Build Coastguard Worker     path.addCircle(0, 0, 10);
335*c8dee2aaSAndroid Build Coastguard Worker 
336*c8dee2aaSAndroid Build Coastguard Worker     SkContourMeasureIter fact(path, false);
337*c8dee2aaSAndroid Build Coastguard Worker     path.reset();   // we should not need the path avert we created the factory
338*c8dee2aaSAndroid Build Coastguard Worker 
339*c8dee2aaSAndroid Build Coastguard Worker     auto cm0 = fact.next();
340*c8dee2aaSAndroid Build Coastguard Worker     auto cm1 = fact.next();
341*c8dee2aaSAndroid Build Coastguard Worker 
342*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, cm0->isClosed());
343*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(cm0->length(), 200 * SK_ScalarPI, 1.5f));
344*c8dee2aaSAndroid Build Coastguard Worker 
345*c8dee2aaSAndroid Build Coastguard Worker     test_90_degrees(cm0, 100, reporter);
346*c8dee2aaSAndroid Build Coastguard Worker 
347*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, cm1->isClosed());
348*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(cm1->length(), 20 * SK_ScalarPI, 0.5f));
349*c8dee2aaSAndroid Build Coastguard Worker 
350*c8dee2aaSAndroid Build Coastguard Worker     test_90_degrees(cm1, 10, reporter);
351*c8dee2aaSAndroid Build Coastguard Worker 
352*c8dee2aaSAndroid Build Coastguard Worker     auto cm2 = fact.next();
353*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, !cm2);
354*c8dee2aaSAndroid Build Coastguard Worker 
355*c8dee2aaSAndroid Build Coastguard Worker     test_empty_contours(reporter);
356*c8dee2aaSAndroid Build Coastguard Worker     test_MLM_contours(reporter);
357*c8dee2aaSAndroid Build Coastguard Worker 
358*c8dee2aaSAndroid Build Coastguard Worker     test_shrink(reporter);
359*c8dee2aaSAndroid Build Coastguard Worker }
360*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(contour_measure_verbs,reporter)361*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(contour_measure_verbs, reporter) {
362*c8dee2aaSAndroid Build Coastguard Worker     SkPath path;
363*c8dee2aaSAndroid Build Coastguard Worker     path.moveTo(10, 10);
364*c8dee2aaSAndroid Build Coastguard Worker     path.lineTo(10, 30);
365*c8dee2aaSAndroid Build Coastguard Worker     path.lineTo(30, 30);
366*c8dee2aaSAndroid Build Coastguard Worker     path.quadTo({40, 30}, {40, 40});
367*c8dee2aaSAndroid Build Coastguard Worker     path.cubicTo({50, 40}, {50, 50}, {40, 50});
368*c8dee2aaSAndroid Build Coastguard Worker     path.conicTo({50, 50}, {50, 60}, 1.2f);
369*c8dee2aaSAndroid Build Coastguard Worker 
370*c8dee2aaSAndroid Build Coastguard Worker     SkContourMeasureIter measure(path, false);
371*c8dee2aaSAndroid Build Coastguard Worker 
372*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkContourMeasure> cmeasure = measure.next();
373*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, cmeasure);
374*c8dee2aaSAndroid Build Coastguard Worker 
375*c8dee2aaSAndroid Build Coastguard Worker     SkContourMeasure::ForwardVerbIterator viter = cmeasure->begin();
376*c8dee2aaSAndroid Build Coastguard Worker     {
377*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, viter != cmeasure->end());
378*c8dee2aaSAndroid Build Coastguard Worker         const auto vmeasure = *viter;
379*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, vmeasure.fVerb == SkPathVerb::kLine);
380*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(vmeasure.fDistance, 20));
381*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, vmeasure.fPts.size() == 2);
382*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, vmeasure.fPts[0] == SkPoint::Make(10, 10));
383*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, vmeasure.fPts[1] == SkPoint::Make(10, 30));
384*c8dee2aaSAndroid Build Coastguard Worker     }
385*c8dee2aaSAndroid Build Coastguard Worker 
386*c8dee2aaSAndroid Build Coastguard Worker     ++viter;
387*c8dee2aaSAndroid Build Coastguard Worker     {
388*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, viter != cmeasure->end());
389*c8dee2aaSAndroid Build Coastguard Worker         const auto vmeasure = *viter;
390*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, vmeasure.fVerb == SkPathVerb::kLine);
391*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(vmeasure.fDistance, 40));
392*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, vmeasure.fPts.size() == 2);
393*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, vmeasure.fPts[0] == SkPoint::Make(10, 30));
394*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, vmeasure.fPts[1] == SkPoint::Make(30, 30));
395*c8dee2aaSAndroid Build Coastguard Worker     }
396*c8dee2aaSAndroid Build Coastguard Worker 
397*c8dee2aaSAndroid Build Coastguard Worker     ++viter;
398*c8dee2aaSAndroid Build Coastguard Worker     {
399*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, viter != cmeasure->end());
400*c8dee2aaSAndroid Build Coastguard Worker         const auto vmeasure = *viter;
401*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, vmeasure.fVerb == SkPathVerb::kQuad);
402*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(vmeasure.fDistance, 56.127525f));
403*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, vmeasure.fPts.size() == 3);
404*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, vmeasure.fPts[0] == SkPoint::Make(30, 30));
405*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, vmeasure.fPts[1] == SkPoint::Make(40, 30));
406*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, vmeasure.fPts[2] == SkPoint::Make(40, 40));
407*c8dee2aaSAndroid Build Coastguard Worker     }
408*c8dee2aaSAndroid Build Coastguard Worker 
409*c8dee2aaSAndroid Build Coastguard Worker     ++viter;
410*c8dee2aaSAndroid Build Coastguard Worker     {
411*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, viter != cmeasure->end());
412*c8dee2aaSAndroid Build Coastguard Worker         const auto vmeasure = *viter;
413*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, vmeasure.fVerb == SkPathVerb::kCubic);
414*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(vmeasure.fDistance, 76.004692f));
415*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, vmeasure.fPts.size() == 4);
416*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, vmeasure.fPts[0] == SkPoint::Make(40, 40));
417*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, vmeasure.fPts[1] == SkPoint::Make(50, 40));
418*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, vmeasure.fPts[2] == SkPoint::Make(50, 50));
419*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, vmeasure.fPts[3] == SkPoint::Make(40, 50));
420*c8dee2aaSAndroid Build Coastguard Worker     }
421*c8dee2aaSAndroid Build Coastguard Worker 
422*c8dee2aaSAndroid Build Coastguard Worker     ++viter;
423*c8dee2aaSAndroid Build Coastguard Worker     {
424*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, viter != cmeasure->end());
425*c8dee2aaSAndroid Build Coastguard Worker         const auto vmeasure = *viter;
426*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, vmeasure.fVerb == SkPathVerb::kConic);
427*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(vmeasure.fDistance, 92.428185f));
428*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, vmeasure.fPts.size() == 4);
429*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, vmeasure.fPts[0] == SkPoint::Make(40, 50));
430*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, vmeasure.fPts[1] == SkPoint::Make(1.2f, 0));
431*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, vmeasure.fPts[2] == SkPoint::Make(50, 50));
432*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, vmeasure.fPts[3] == SkPoint::Make(50, 60));
433*c8dee2aaSAndroid Build Coastguard Worker 
434*c8dee2aaSAndroid Build Coastguard Worker         // The last verb distance should also match the contour length.
435*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(vmeasure.fDistance, cmeasure->length()));
436*c8dee2aaSAndroid Build Coastguard Worker     }
437*c8dee2aaSAndroid Build Coastguard Worker 
438*c8dee2aaSAndroid Build Coastguard Worker     ++viter;
439*c8dee2aaSAndroid Build Coastguard Worker     {
440*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, viter == cmeasure->end());
441*c8dee2aaSAndroid Build Coastguard Worker     }
442*c8dee2aaSAndroid Build Coastguard Worker 
443*c8dee2aaSAndroid Build Coastguard Worker     // Exercise the range iterator form.
444*c8dee2aaSAndroid Build Coastguard Worker     float current_distance = 0;
445*c8dee2aaSAndroid Build Coastguard Worker     size_t verb_count = 0;
446*c8dee2aaSAndroid Build Coastguard Worker     for (const auto vmeasure : *cmeasure) {
447*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, vmeasure.fDistance > current_distance);
448*c8dee2aaSAndroid Build Coastguard Worker         current_distance = vmeasure.fDistance;
449*c8dee2aaSAndroid Build Coastguard Worker         verb_count++;
450*c8dee2aaSAndroid Build Coastguard Worker     }
451*c8dee2aaSAndroid Build Coastguard Worker     REPORTER_ASSERT(reporter, verb_count == 5);
452*c8dee2aaSAndroid Build Coastguard Worker }
453