• Home
  • History
  • Annotate
  • 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 "SkCanvas.h"
9 #include "SkPaint.h"
10 #include "SkParse.h"
11 #include "SkParsePath.h"
12 #include "SkPath.h"
13 #include "SkPathEffect.h"
14 #include "SkRRect.h"
15 #include "SkRandom.h"
16 #include "SkReader32.h"
17 #include "SkSize.h"
18 #include "SkSurface.h"
19 #include "SkTypes.h"
20 #include "SkWriter32.h"
21 #include "Test.h"
22 
make_path_crbug364224(SkPath * path)23 static void make_path_crbug364224(SkPath* path) {
24     path->reset();
25     path->moveTo(3.747501373f, 2.724499941f);
26     path->lineTo(3.747501373f, 3.75f);
27     path->cubicTo(3.747501373f, 3.88774991f, 3.635501385f, 4.0f, 3.497501373f, 4.0f);
28     path->lineTo(0.7475013733f, 4.0f);
29     path->cubicTo(0.6095013618f, 4.0f, 0.4975013733f, 3.88774991f, 0.4975013733f, 3.75f);
30     path->lineTo(0.4975013733f, 1.0f);
31     path->cubicTo(0.4975013733f, 0.8622499704f, 0.6095013618f, 0.75f, 0.7475013733f,0.75f);
32     path->lineTo(3.497501373f, 0.75f);
33     path->cubicTo(3.50275135f, 0.75f, 3.5070014f, 0.7527500391f, 3.513001442f, 0.753000021f);
34     path->lineTo(3.715001345f, 0.5512499809f);
35     path->cubicTo(3.648251295f, 0.5194999576f, 3.575501442f, 0.4999999702f, 3.497501373f, 0.4999999702f);
36     path->lineTo(0.7475013733f, 0.4999999702f);
37     path->cubicTo(0.4715013802f, 0.4999999702f, 0.2475013733f, 0.7239999771f, 0.2475013733f, 1.0f);
38     path->lineTo(0.2475013733f, 3.75f);
39     path->cubicTo(0.2475013733f, 4.026000023f, 0.4715013504f, 4.25f, 0.7475013733f, 4.25f);
40     path->lineTo(3.497501373f, 4.25f);
41     path->cubicTo(3.773501396f, 4.25f, 3.997501373f, 4.026000023f, 3.997501373f, 3.75f);
42     path->lineTo(3.997501373f, 2.474750042f);
43     path->lineTo(3.747501373f, 2.724499941f);
44     path->close();
45 }
46 
make_path_crbug364224_simplified(SkPath * path)47 static void make_path_crbug364224_simplified(SkPath* path) {
48     path->moveTo(3.747501373f, 2.724499941f);
49     path->cubicTo(3.648251295f, 0.5194999576f, 3.575501442f, 0.4999999702f, 3.497501373f, 0.4999999702f);
50     path->close();
51 }
52 
test_path_crbug364224()53 static void test_path_crbug364224() {
54     SkPath path;
55     SkPaint paint;
56     SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(84, 88));
57     SkCanvas* canvas = surface->getCanvas();
58 
59     make_path_crbug364224_simplified(&path);
60     canvas->drawPath(path, paint);
61 
62     make_path_crbug364224(&path);
63     canvas->drawPath(path, paint);
64 }
65 
make_path0(SkPath * path)66 static void make_path0(SkPath* path) {
67     // from  *  https://code.google.com/p/skia/issues/detail?id=1706
68 
69     path->moveTo(146.939f, 1012.84f);
70     path->lineTo(181.747f, 1009.18f);
71     path->lineTo(182.165f, 1013.16f);
72     path->lineTo(147.357f, 1016.82f);
73     path->lineTo(146.939f, 1012.84f);
74     path->close();
75 }
76 
make_path1(SkPath * path)77 static void make_path1(SkPath* path) {
78     path->addRect(SkRect::MakeXYWH(10, 10, 10, 1));
79 }
80 
81 typedef void (*PathProc)(SkPath*);
82 
83 /*
84  *  Regression test: we used to crash (overwrite internal storage) during
85  *  construction of the region when the path was INVERSE. That is now fixed,
86  *  so test these regions (which used to assert/crash).
87  *
88  *  https://code.google.com/p/skia/issues/detail?id=1706
89  */
test_path_to_region(skiatest::Reporter * reporter)90 static void test_path_to_region(skiatest::Reporter* reporter) {
91     PathProc procs[] = {
92         make_path0,
93         make_path1,
94     };
95 
96     SkRegion clip;
97     clip.setRect(0, 0, 1255, 1925);
98 
99     for (size_t i = 0; i < SK_ARRAY_COUNT(procs); ++i) {
100         SkPath path;
101         procs[i](&path);
102 
103         SkRegion rgn;
104         rgn.setPath(path, clip);
105         path.toggleInverseFillType();
106         rgn.setPath(path, clip);
107     }
108 }
109 
110 #if defined(WIN32)
111     #define SUPPRESS_VISIBILITY_WARNING
112 #else
113     #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden")))
114 #endif
115 
test_path_close_issue1474(skiatest::Reporter * reporter)116 static void test_path_close_issue1474(skiatest::Reporter* reporter) {
117     // This test checks that r{Line,Quad,Conic,Cubic}To following a close()
118     // are relative to the point we close to, not relative to the point we close from.
119     SkPath path;
120     SkPoint last;
121 
122     // Test rLineTo().
123     path.rLineTo(0, 100);
124     path.rLineTo(100, 0);
125     path.close();          // Returns us back to 0,0.
126     path.rLineTo(50, 50);  // This should go to 50,50.
127 
128     path.getLastPt(&last);
129     REPORTER_ASSERT(reporter, 50 == last.fX);
130     REPORTER_ASSERT(reporter, 50 == last.fY);
131 
132     // Test rQuadTo().
133     path.rewind();
134     path.rLineTo(0, 100);
135     path.rLineTo(100, 0);
136     path.close();
137     path.rQuadTo(50, 50, 75, 75);
138 
139     path.getLastPt(&last);
140     REPORTER_ASSERT(reporter, 75 == last.fX);
141     REPORTER_ASSERT(reporter, 75 == last.fY);
142 
143     // Test rConicTo().
144     path.rewind();
145     path.rLineTo(0, 100);
146     path.rLineTo(100, 0);
147     path.close();
148     path.rConicTo(50, 50, 85, 85, 2);
149 
150     path.getLastPt(&last);
151     REPORTER_ASSERT(reporter, 85 == last.fX);
152     REPORTER_ASSERT(reporter, 85 == last.fY);
153 
154     // Test rCubicTo().
155     path.rewind();
156     path.rLineTo(0, 100);
157     path.rLineTo(100, 0);
158     path.close();
159     path.rCubicTo(50, 50, 85, 85, 95, 95);
160 
161     path.getLastPt(&last);
162     REPORTER_ASSERT(reporter, 95 == last.fX);
163     REPORTER_ASSERT(reporter, 95 == last.fY);
164 }
165 
test_android_specific_behavior(skiatest::Reporter * reporter)166 static void test_android_specific_behavior(skiatest::Reporter* reporter) {
167 #ifdef SK_BUILD_FOR_ANDROID
168     // Make sure we treat fGenerationID and fSourcePath correctly for each of
169     // copy, assign, rewind, reset, and swap.
170     SkPath original, source, anotherSource;
171     original.setSourcePath(&source);
172     original.moveTo(0, 0);
173     original.lineTo(1, 1);
174     REPORTER_ASSERT(reporter, original.getSourcePath() == &source);
175 
176     uint32_t copyID, assignID;
177 
178     // Test copy constructor.  Copy generation ID, copy source path.
179     SkPath copy(original);
180     REPORTER_ASSERT(reporter, copy.getGenerationID() == original.getGenerationID());
181     REPORTER_ASSERT(reporter, copy.getSourcePath() == original.getSourcePath());
182 
183     // Test assigment operator.  Change generation ID, copy source path.
184     SkPath assign;
185     assignID = assign.getGenerationID();
186     assign = original;
187     REPORTER_ASSERT(reporter, assign.getGenerationID() != assignID);
188     REPORTER_ASSERT(reporter, assign.getSourcePath() == original.getSourcePath());
189 
190     // Test rewind.  Change generation ID, don't touch source path.
191     copyID = copy.getGenerationID();
192     copy.rewind();
193     REPORTER_ASSERT(reporter, copy.getGenerationID() != copyID);
194     REPORTER_ASSERT(reporter, copy.getSourcePath() == original.getSourcePath());
195 
196     // Test reset.  Change generation ID, don't touch source path.
197     assignID = assign.getGenerationID();
198     assign.reset();
199     REPORTER_ASSERT(reporter, assign.getGenerationID() != assignID);
200     REPORTER_ASSERT(reporter, assign.getSourcePath() == original.getSourcePath());
201 
202     // Test swap.  Swap the generation IDs, swap source paths.
203     copy.reset();
204     copy.moveTo(2, 2);
205     copy.setSourcePath(&anotherSource);
206     copyID = copy.getGenerationID();
207     assign.moveTo(3, 3);
208     assignID = assign.getGenerationID();
209     copy.swap(assign);
210     REPORTER_ASSERT(reporter, copy.getGenerationID() != copyID);
211     REPORTER_ASSERT(reporter, assign.getGenerationID() != assignID);
212     REPORTER_ASSERT(reporter, copy.getSourcePath() == original.getSourcePath());
213     REPORTER_ASSERT(reporter, assign.getSourcePath() == &anotherSource);
214 #endif
215 }
216 
test_gen_id(skiatest::Reporter * reporter)217 static void test_gen_id(skiatest::Reporter* reporter) {
218     SkPath a, b;
219     REPORTER_ASSERT(reporter, a.getGenerationID() == b.getGenerationID());
220 
221     a.moveTo(0, 0);
222     const uint32_t z = a.getGenerationID();
223     REPORTER_ASSERT(reporter, z != b.getGenerationID());
224 
225     a.reset();
226     REPORTER_ASSERT(reporter, a.getGenerationID() == b.getGenerationID());
227 
228     a.moveTo(1, 1);
229     const uint32_t y = a.getGenerationID();
230     REPORTER_ASSERT(reporter, z != y);
231 
232     b.moveTo(2, 2);
233     const uint32_t x = b.getGenerationID();
234     REPORTER_ASSERT(reporter, x != y && x != z);
235 
236     a.swap(b);
237     REPORTER_ASSERT(reporter, b.getGenerationID() == y && a.getGenerationID() == x);
238 
239     b = a;
240     REPORTER_ASSERT(reporter, b.getGenerationID() == x);
241 
242     SkPath c(a);
243     REPORTER_ASSERT(reporter, c.getGenerationID() == x);
244 
245     c.lineTo(3, 3);
246     const uint32_t w = c.getGenerationID();
247     REPORTER_ASSERT(reporter, b.getGenerationID() == x);
248     REPORTER_ASSERT(reporter, a.getGenerationID() == x);
249     REPORTER_ASSERT(reporter, w != x);
250 
251 #ifdef SK_BUILD_FOR_ANDROID
252     static bool kExpectGenIDToIgnoreFill = false;
253 #else
254     static bool kExpectGenIDToIgnoreFill = true;
255 #endif
256 
257     c.toggleInverseFillType();
258     const uint32_t v = c.getGenerationID();
259     REPORTER_ASSERT(reporter, (v == w) == kExpectGenIDToIgnoreFill);
260 
261     c.rewind();
262     REPORTER_ASSERT(reporter, v != c.getGenerationID());
263 }
264 
265 // This used to assert in the debug build, as the edges did not all line-up.
test_bad_cubic_crbug234190()266 static void test_bad_cubic_crbug234190() {
267     SkPath path;
268     path.moveTo(13.8509f, 3.16858f);
269     path.cubicTo(-2.35893e+08f, -4.21044e+08f,
270                  -2.38991e+08f, -4.26573e+08f,
271                  -2.41016e+08f, -4.30188e+08f);
272 
273     SkPaint paint;
274     paint.setAntiAlias(true);
275     SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(84, 88));
276     surface->getCanvas()->drawPath(path, paint);
277 }
278 
test_bad_cubic_crbug229478()279 static void test_bad_cubic_crbug229478() {
280     const SkPoint pts[] = {
281         { 4595.91064f,    -11596.9873f },
282         { 4597.2168f,    -11595.9414f },
283         { 4598.52344f,    -11594.8955f },
284         { 4599.83008f,    -11593.8496f },
285     };
286 
287     SkPath path;
288     path.moveTo(pts[0]);
289     path.cubicTo(pts[1], pts[2], pts[3]);
290 
291     SkPaint paint;
292     paint.setStyle(SkPaint::kStroke_Style);
293     paint.setStrokeWidth(20);
294 
295     SkPath dst;
296     // Before the fix, this would infinite-recurse, and run out of stack
297     // because we would keep trying to subdivide a degenerate cubic segment.
298     paint.getFillPath(path, &dst, NULL);
299 }
300 
build_path_170666(SkPath & path)301 static void build_path_170666(SkPath& path) {
302     path.moveTo(17.9459f, 21.6344f);
303     path.lineTo(139.545f, -47.8105f);
304     path.lineTo(139.545f, -47.8105f);
305     path.lineTo(131.07f, -47.3888f);
306     path.lineTo(131.07f, -47.3888f);
307     path.lineTo(122.586f, -46.9532f);
308     path.lineTo(122.586f, -46.9532f);
309     path.lineTo(18076.6f, 31390.9f);
310     path.lineTo(18076.6f, 31390.9f);
311     path.lineTo(18085.1f, 31390.5f);
312     path.lineTo(18085.1f, 31390.5f);
313     path.lineTo(18076.6f, 31390.9f);
314     path.lineTo(18076.6f, 31390.9f);
315     path.lineTo(17955, 31460.3f);
316     path.lineTo(17955, 31460.3f);
317     path.lineTo(17963.5f, 31459.9f);
318     path.lineTo(17963.5f, 31459.9f);
319     path.lineTo(17971.9f, 31459.5f);
320     path.lineTo(17971.9f, 31459.5f);
321     path.lineTo(17.9551f, 21.6205f);
322     path.lineTo(17.9551f, 21.6205f);
323     path.lineTo(9.47091f, 22.0561f);
324     path.lineTo(9.47091f, 22.0561f);
325     path.lineTo(17.9459f, 21.6344f);
326     path.lineTo(17.9459f, 21.6344f);
327     path.close();path.moveTo(0.995934f, 22.4779f);
328     path.lineTo(0.986725f, 22.4918f);
329     path.lineTo(0.986725f, 22.4918f);
330     path.lineTo(17955, 31460.4f);
331     path.lineTo(17955, 31460.4f);
332     path.lineTo(17971.9f, 31459.5f);
333     path.lineTo(17971.9f, 31459.5f);
334     path.lineTo(18093.6f, 31390.1f);
335     path.lineTo(18093.6f, 31390.1f);
336     path.lineTo(18093.6f, 31390);
337     path.lineTo(18093.6f, 31390);
338     path.lineTo(139.555f, -47.8244f);
339     path.lineTo(139.555f, -47.8244f);
340     path.lineTo(122.595f, -46.9671f);
341     path.lineTo(122.595f, -46.9671f);
342     path.lineTo(0.995934f, 22.4779f);
343     path.lineTo(0.995934f, 22.4779f);
344     path.close();
345     path.moveTo(5.43941f, 25.5223f);
346     path.lineTo(798267, -28871.1f);
347     path.lineTo(798267, -28871.1f);
348     path.lineTo(3.12512e+06f, -113102);
349     path.lineTo(3.12512e+06f, -113102);
350     path.cubicTo(5.16324e+06f, -186882, 8.15247e+06f, -295092, 1.1957e+07f, -432813);
351     path.cubicTo(1.95659e+07f, -708257, 3.04359e+07f, -1.10175e+06f, 4.34798e+07f, -1.57394e+06f);
352     path.cubicTo(6.95677e+07f, -2.51831e+06f, 1.04352e+08f, -3.77748e+06f, 1.39135e+08f, -5.03666e+06f);
353     path.cubicTo(1.73919e+08f, -6.29583e+06f, 2.08703e+08f, -7.555e+06f, 2.34791e+08f, -8.49938e+06f);
354     path.cubicTo(2.47835e+08f, -8.97157e+06f, 2.58705e+08f, -9.36506e+06f, 2.66314e+08f, -9.6405e+06f);
355     path.cubicTo(2.70118e+08f, -9.77823e+06f, 2.73108e+08f, -9.88644e+06f, 2.75146e+08f, -9.96022e+06f);
356     path.cubicTo(2.76165e+08f, -9.99711e+06f, 2.76946e+08f, -1.00254e+07f, 2.77473e+08f, -1.00444e+07f);
357     path.lineTo(2.78271e+08f, -1.00733e+07f);
358     path.lineTo(2.78271e+08f, -1.00733e+07f);
359     path.cubicTo(2.78271e+08f, -1.00733e+07f, 2.08703e+08f, -7.555e+06f, 135.238f, 23.3517f);
360     path.cubicTo(131.191f, 23.4981f, 125.995f, 23.7976f, 123.631f, 24.0206f);
361     path.cubicTo(121.267f, 24.2436f, 122.631f, 24.3056f, 126.677f, 24.1591f);
362     path.cubicTo(2.08703e+08f, -7.555e+06f, 2.78271e+08f, -1.00733e+07f, 2.78271e+08f, -1.00733e+07f);
363     path.lineTo(2.77473e+08f, -1.00444e+07f);
364     path.lineTo(2.77473e+08f, -1.00444e+07f);
365     path.cubicTo(2.76946e+08f, -1.00254e+07f, 2.76165e+08f, -9.99711e+06f, 2.75146e+08f, -9.96022e+06f);
366     path.cubicTo(2.73108e+08f, -9.88644e+06f, 2.70118e+08f, -9.77823e+06f, 2.66314e+08f, -9.6405e+06f);
367     path.cubicTo(2.58705e+08f, -9.36506e+06f, 2.47835e+08f, -8.97157e+06f, 2.34791e+08f, -8.49938e+06f);
368     path.cubicTo(2.08703e+08f, -7.555e+06f, 1.73919e+08f, -6.29583e+06f, 1.39135e+08f, -5.03666e+06f);
369     path.cubicTo(1.04352e+08f, -3.77749e+06f, 6.95677e+07f, -2.51831e+06f, 4.34798e+07f, -1.57394e+06f);
370     path.cubicTo(3.04359e+07f, -1.10175e+06f, 1.95659e+07f, -708258, 1.1957e+07f, -432814);
371     path.cubicTo(8.15248e+06f, -295092, 5.16324e+06f, -186883, 3.12513e+06f, -113103);
372     path.lineTo(798284, -28872);
373     path.lineTo(798284, -28872);
374     path.lineTo(22.4044f, 24.6677f);
375     path.lineTo(22.4044f, 24.6677f);
376     path.cubicTo(22.5186f, 24.5432f, 18.8134f, 24.6337f, 14.1287f, 24.8697f);
377     path.cubicTo(9.4439f, 25.1057f, 5.55359f, 25.3978f, 5.43941f, 25.5223f);
378     path.close();
379 }
380 
build_path_simple_170666(SkPath & path)381 static void build_path_simple_170666(SkPath& path) {
382     path.moveTo(126.677f, 24.1591f);
383     path.cubicTo(2.08703e+08f, -7.555e+06f, 2.78271e+08f, -1.00733e+07f, 2.78271e+08f, -1.00733e+07f);
384 }
385 
386 // This used to assert in the SK_DEBUG build, as the clip step would fail with
387 // too-few interations in our cubic-line intersection code. That code now runs
388 // 24 interations (instead of 16).
test_crbug_170666()389 static void test_crbug_170666() {
390     SkPath path;
391     SkPaint paint;
392     paint.setAntiAlias(true);
393 
394     SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(1000, 1000));
395 
396     build_path_simple_170666(path);
397     surface->getCanvas()->drawPath(path, paint);
398 
399     build_path_170666(path);
400     surface->getCanvas()->drawPath(path, paint);
401 }
402 
test_addrect(skiatest::Reporter * reporter)403 static void test_addrect(skiatest::Reporter* reporter) {
404     SkPath path;
405     path.lineTo(0, 0);
406     path.addRect(SkRect::MakeWH(50, 100));
407     REPORTER_ASSERT(reporter, path.isRect(NULL));
408 
409     path.reset();
410     path.lineTo(FLT_EPSILON, FLT_EPSILON);
411     path.addRect(SkRect::MakeWH(50, 100));
412     REPORTER_ASSERT(reporter, !path.isRect(NULL));
413 
414     path.reset();
415     path.quadTo(0, 0, 0, 0);
416     path.addRect(SkRect::MakeWH(50, 100));
417     REPORTER_ASSERT(reporter, !path.isRect(NULL));
418 
419     path.reset();
420     path.conicTo(0, 0, 0, 0, 0.5f);
421     path.addRect(SkRect::MakeWH(50, 100));
422     REPORTER_ASSERT(reporter, !path.isRect(NULL));
423 
424     path.reset();
425     path.cubicTo(0, 0, 0, 0, 0, 0);
426     path.addRect(SkRect::MakeWH(50, 100));
427     REPORTER_ASSERT(reporter, !path.isRect(NULL));
428 }
429 
430 // Make sure we stay non-finite once we get there (unless we reset or rewind).
test_addrect_isfinite(skiatest::Reporter * reporter)431 static void test_addrect_isfinite(skiatest::Reporter* reporter) {
432     SkPath path;
433 
434     path.addRect(SkRect::MakeWH(50, 100));
435     REPORTER_ASSERT(reporter, path.isFinite());
436 
437     path.moveTo(0, 0);
438     path.lineTo(SK_ScalarInfinity, 42);
439     REPORTER_ASSERT(reporter, !path.isFinite());
440 
441     path.addRect(SkRect::MakeWH(50, 100));
442     REPORTER_ASSERT(reporter, !path.isFinite());
443 
444     path.reset();
445     REPORTER_ASSERT(reporter, path.isFinite());
446 
447     path.addRect(SkRect::MakeWH(50, 100));
448     REPORTER_ASSERT(reporter, path.isFinite());
449 }
450 
build_big_path(SkPath * path,bool reducedCase)451 static void build_big_path(SkPath* path, bool reducedCase) {
452     if (reducedCase) {
453         path->moveTo(577330, 1971.72f);
454         path->cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f);
455     } else {
456         path->moveTo(60.1631f, 7.70567f);
457         path->quadTo(60.1631f, 7.70567f, 0.99474f, 0.901199f);
458         path->lineTo(577379, 1977.77f);
459         path->quadTo(577364, 1979.57f, 577325, 1980.26f);
460         path->quadTo(577286, 1980.95f, 577245, 1980.13f);
461         path->quadTo(577205, 1979.3f, 577187, 1977.45f);
462         path->quadTo(577168, 1975.6f, 577183, 1973.8f);
463         path->quadTo(577198, 1972, 577238, 1971.31f);
464         path->quadTo(577277, 1970.62f, 577317, 1971.45f);
465         path->quadTo(577330, 1971.72f, 577341, 1972.11f);
466         path->cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f);
467         path->moveTo(306.718f, -32.912f);
468         path->cubicTo(30.531f, 10.0005f, 1502.47f, 13.2804f, 84.3088f, 9.99601f);
469     }
470 }
471 
test_clipped_cubic()472 static void test_clipped_cubic() {
473     SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(640, 480));
474 
475     // This path used to assert, because our cubic-chopping code incorrectly
476     // moved control points after the chop. This test should be run in SK_DEBUG
477     // mode to ensure that we no long assert.
478     SkPath path;
479     for (int doReducedCase = 0; doReducedCase <= 1; ++doReducedCase) {
480         build_big_path(&path, SkToBool(doReducedCase));
481 
482         SkPaint paint;
483         for (int doAA = 0; doAA <= 1; ++doAA) {
484             paint.setAntiAlias(SkToBool(doAA));
485             surface->getCanvas()->drawPath(path, paint);
486         }
487     }
488 }
489 
490 // Inspired by http://ie.microsoft.com/testdrive/Performance/Chalkboard/
491 // which triggered an assert, from a tricky cubic. This test replicates that
492 // example, so we can ensure that we handle it (in SkEdge.cpp), and don't
493 // assert in the SK_DEBUG build.
test_tricky_cubic()494 static void test_tricky_cubic() {
495     const SkPoint pts[] = {
496         { SkDoubleToScalar(18.8943768),    SkDoubleToScalar(129.121277) },
497         { SkDoubleToScalar(18.8937435),    SkDoubleToScalar(129.121689) },
498         { SkDoubleToScalar(18.8950119),    SkDoubleToScalar(129.120422) },
499         { SkDoubleToScalar(18.5030727),    SkDoubleToScalar(129.13121)  },
500     };
501 
502     SkPath path;
503     path.moveTo(pts[0]);
504     path.cubicTo(pts[1], pts[2], pts[3]);
505 
506     SkPaint paint;
507     paint.setAntiAlias(true);
508 
509     SkSurface* surface = SkSurface::NewRasterPMColor(19, 130);
510     surface->getCanvas()->drawPath(path, paint);
511     surface->unref();
512 }
513 
514 // Inspired by http://code.google.com/p/chromium/issues/detail?id=141651
515 //
test_isfinite_after_transform(skiatest::Reporter * reporter)516 static void test_isfinite_after_transform(skiatest::Reporter* reporter) {
517     SkPath path;
518     path.quadTo(157, 366, 286, 208);
519     path.arcTo(37, 442, 315, 163, 957494590897113.0f);
520 
521     SkMatrix matrix;
522     matrix.setScale(1000*1000, 1000*1000);
523 
524     // Be sure that path::transform correctly updates isFinite and the bounds
525     // if the transformation overflows. The previous bug was that isFinite was
526     // set to true in this case, but the bounds were not set to empty (which
527     // they should be).
528     while (path.isFinite()) {
529         REPORTER_ASSERT(reporter, path.getBounds().isFinite());
530         REPORTER_ASSERT(reporter, !path.getBounds().isEmpty());
531         path.transform(matrix);
532     }
533     REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
534 
535     matrix.setTranslate(SK_Scalar1, SK_Scalar1);
536     path.transform(matrix);
537     // we need to still be non-finite
538     REPORTER_ASSERT(reporter, !path.isFinite());
539     REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
540 }
541 
add_corner_arc(SkPath * path,const SkRect & rect,SkScalar xIn,SkScalar yIn,int startAngle)542 static void add_corner_arc(SkPath* path, const SkRect& rect,
543                            SkScalar xIn, SkScalar yIn,
544                            int startAngle)
545 {
546 
547     SkScalar rx = SkMinScalar(rect.width(), xIn);
548     SkScalar ry = SkMinScalar(rect.height(), yIn);
549 
550     SkRect arcRect;
551     arcRect.set(-rx, -ry, rx, ry);
552     switch (startAngle) {
553     case 0:
554         arcRect.offset(rect.fRight - arcRect.fRight, rect.fBottom - arcRect.fBottom);
555         break;
556     case 90:
557         arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fBottom - arcRect.fBottom);
558         break;
559     case 180:
560         arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fTop - arcRect.fTop);
561         break;
562     case 270:
563         arcRect.offset(rect.fRight - arcRect.fRight, rect.fTop - arcRect.fTop);
564         break;
565     default:
566         break;
567     }
568 
569     path->arcTo(arcRect, SkIntToScalar(startAngle), SkIntToScalar(90), false);
570 }
571 
make_arb_round_rect(SkPath * path,const SkRect & r,SkScalar xCorner,SkScalar yCorner)572 static void make_arb_round_rect(SkPath* path, const SkRect& r,
573                                 SkScalar xCorner, SkScalar yCorner) {
574     // we are lazy here and use the same x & y for each corner
575     add_corner_arc(path, r, xCorner, yCorner, 270);
576     add_corner_arc(path, r, xCorner, yCorner, 0);
577     add_corner_arc(path, r, xCorner, yCorner, 90);
578     add_corner_arc(path, r, xCorner, yCorner, 180);
579     path->close();
580 }
581 
582 // Chrome creates its own round rects with each corner possibly being different.
583 // Performance will suffer if they are not convex.
584 // Note: PathBench::ArbRoundRectBench performs almost exactly
585 // the same test (but with drawing)
test_arb_round_rect_is_convex(skiatest::Reporter * reporter)586 static void test_arb_round_rect_is_convex(skiatest::Reporter* reporter) {
587     SkRandom rand;
588     SkRect r;
589 
590     for (int i = 0; i < 5000; ++i) {
591 
592         SkScalar size = rand.nextUScalar1() * 30;
593         if (size < SK_Scalar1) {
594             continue;
595         }
596         r.fLeft = rand.nextUScalar1() * 300;
597         r.fTop =  rand.nextUScalar1() * 300;
598         r.fRight =  r.fLeft + 2 * size;
599         r.fBottom = r.fTop + 2 * size;
600 
601         SkPath temp;
602 
603         make_arb_round_rect(&temp, r, r.width() / 10, r.height() / 15);
604 
605         REPORTER_ASSERT(reporter, temp.isConvex());
606     }
607 }
608 
609 // Chrome will sometimes create a 0 radius round rect. The degenerate
610 // quads prevent the path from being converted to a rect
611 // Note: PathBench::ArbRoundRectBench performs almost exactly
612 // the same test (but with drawing)
test_arb_zero_rad_round_rect_is_rect(skiatest::Reporter * reporter)613 static void test_arb_zero_rad_round_rect_is_rect(skiatest::Reporter* reporter) {
614     SkRandom rand;
615     SkRect r;
616 
617     for (int i = 0; i < 5000; ++i) {
618 
619         SkScalar size = rand.nextUScalar1() * 30;
620         if (size < SK_Scalar1) {
621             continue;
622         }
623         r.fLeft = rand.nextUScalar1() * 300;
624         r.fTop =  rand.nextUScalar1() * 300;
625         r.fRight =  r.fLeft + 2 * size;
626         r.fBottom = r.fTop + 2 * size;
627 
628         SkPath temp;
629 
630         make_arb_round_rect(&temp, r, 0, 0);
631 
632         SkRect result;
633         REPORTER_ASSERT(reporter, temp.isRect(&result));
634         REPORTER_ASSERT(reporter, r == result);
635     }
636 }
637 
test_rect_isfinite(skiatest::Reporter * reporter)638 static void test_rect_isfinite(skiatest::Reporter* reporter) {
639     const SkScalar inf = SK_ScalarInfinity;
640     const SkScalar negInf = SK_ScalarNegativeInfinity;
641     const SkScalar nan = SK_ScalarNaN;
642 
643     SkRect r;
644     r.setEmpty();
645     REPORTER_ASSERT(reporter, r.isFinite());
646     r.set(0, 0, inf, negInf);
647     REPORTER_ASSERT(reporter, !r.isFinite());
648     r.set(0, 0, nan, 0);
649     REPORTER_ASSERT(reporter, !r.isFinite());
650 
651     SkPoint pts[] = {
652         { 0, 0 },
653         { SK_Scalar1, 0 },
654         { 0, SK_Scalar1 },
655     };
656 
657     bool isFine = r.setBoundsCheck(pts, 3);
658     REPORTER_ASSERT(reporter, isFine);
659     REPORTER_ASSERT(reporter, !r.isEmpty());
660 
661     pts[1].set(inf, 0);
662     isFine = r.setBoundsCheck(pts, 3);
663     REPORTER_ASSERT(reporter, !isFine);
664     REPORTER_ASSERT(reporter, r.isEmpty());
665 
666     pts[1].set(nan, 0);
667     isFine = r.setBoundsCheck(pts, 3);
668     REPORTER_ASSERT(reporter, !isFine);
669     REPORTER_ASSERT(reporter, r.isEmpty());
670 }
671 
test_path_isfinite(skiatest::Reporter * reporter)672 static void test_path_isfinite(skiatest::Reporter* reporter) {
673     const SkScalar inf = SK_ScalarInfinity;
674     const SkScalar negInf = SK_ScalarNegativeInfinity;
675     const SkScalar nan = SK_ScalarNaN;
676 
677     SkPath path;
678     REPORTER_ASSERT(reporter, path.isFinite());
679 
680     path.reset();
681     REPORTER_ASSERT(reporter, path.isFinite());
682 
683     path.reset();
684     path.moveTo(SK_Scalar1, 0);
685     REPORTER_ASSERT(reporter, path.isFinite());
686 
687     path.reset();
688     path.moveTo(inf, negInf);
689     REPORTER_ASSERT(reporter, !path.isFinite());
690 
691     path.reset();
692     path.moveTo(nan, 0);
693     REPORTER_ASSERT(reporter, !path.isFinite());
694 }
695 
test_isfinite(skiatest::Reporter * reporter)696 static void test_isfinite(skiatest::Reporter* reporter) {
697     test_rect_isfinite(reporter);
698     test_path_isfinite(reporter);
699 }
700 
701 // assert that we always
702 //  start with a moveTo
703 //  only have 1 moveTo
704 //  only have Lines after that
705 //  end with a single close
706 //  only have (at most) 1 close
707 //
test_poly(skiatest::Reporter * reporter,const SkPath & path,const SkPoint srcPts[],bool expectClose)708 static void test_poly(skiatest::Reporter* reporter, const SkPath& path,
709                       const SkPoint srcPts[], bool expectClose) {
710     SkPath::RawIter iter(path);
711     SkPoint         pts[4];
712 
713     bool firstTime = true;
714     bool foundClose = false;
715     for (;;) {
716         switch (iter.next(pts)) {
717             case SkPath::kMove_Verb:
718                 REPORTER_ASSERT(reporter, firstTime);
719                 REPORTER_ASSERT(reporter, pts[0] == srcPts[0]);
720                 srcPts++;
721                 firstTime = false;
722                 break;
723             case SkPath::kLine_Verb:
724                 REPORTER_ASSERT(reporter, !firstTime);
725                 REPORTER_ASSERT(reporter, pts[1] == srcPts[0]);
726                 srcPts++;
727                 break;
728             case SkPath::kQuad_Verb:
729                 REPORTER_ASSERT_MESSAGE(reporter, false, "unexpected quad verb");
730                 break;
731             case SkPath::kConic_Verb:
732                 REPORTER_ASSERT_MESSAGE(reporter, false, "unexpected conic verb");
733                 break;
734             case SkPath::kCubic_Verb:
735                 REPORTER_ASSERT_MESSAGE(reporter, false, "unexpected cubic verb");
736                 break;
737             case SkPath::kClose_Verb:
738                 REPORTER_ASSERT(reporter, !firstTime);
739                 REPORTER_ASSERT(reporter, !foundClose);
740                 REPORTER_ASSERT(reporter, expectClose);
741                 foundClose = true;
742                 break;
743             case SkPath::kDone_Verb:
744                 goto DONE;
745         }
746     }
747 DONE:
748     REPORTER_ASSERT(reporter, foundClose == expectClose);
749 }
750 
test_addPoly(skiatest::Reporter * reporter)751 static void test_addPoly(skiatest::Reporter* reporter) {
752     SkPoint pts[32];
753     SkRandom rand;
754 
755     for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) {
756         pts[i].fX = rand.nextSScalar1();
757         pts[i].fY = rand.nextSScalar1();
758     }
759 
760     for (int doClose = 0; doClose <= 1; ++doClose) {
761         for (size_t count = 1; count <= SK_ARRAY_COUNT(pts); ++count) {
762             SkPath path;
763             path.addPoly(pts, count, SkToBool(doClose));
764             test_poly(reporter, path, pts, SkToBool(doClose));
765         }
766     }
767 }
768 
test_strokerec(skiatest::Reporter * reporter)769 static void test_strokerec(skiatest::Reporter* reporter) {
770     SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
771     REPORTER_ASSERT(reporter, rec.isFillStyle());
772 
773     rec.setHairlineStyle();
774     REPORTER_ASSERT(reporter, rec.isHairlineStyle());
775 
776     rec.setStrokeStyle(SK_Scalar1, false);
777     REPORTER_ASSERT(reporter, SkStrokeRec::kStroke_Style == rec.getStyle());
778 
779     rec.setStrokeStyle(SK_Scalar1, true);
780     REPORTER_ASSERT(reporter, SkStrokeRec::kStrokeAndFill_Style == rec.getStyle());
781 
782     rec.setStrokeStyle(0, false);
783     REPORTER_ASSERT(reporter, SkStrokeRec::kHairline_Style == rec.getStyle());
784 
785     rec.setStrokeStyle(0, true);
786     REPORTER_ASSERT(reporter, SkStrokeRec::kFill_Style == rec.getStyle());
787 }
788 
789 // Set this for paths that don't have a consistent direction such as a bowtie.
790 // (cheapComputeDirection is not expected to catch these.)
791 static const SkPath::Direction kDontCheckDir = static_cast<SkPath::Direction>(-1);
792 
check_direction(skiatest::Reporter * reporter,const SkPath & path,SkPath::Direction expected)793 static void check_direction(skiatest::Reporter* reporter, const SkPath& path,
794                             SkPath::Direction expected) {
795     if (expected == kDontCheckDir) {
796         return;
797     }
798     SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
799 
800     SkPath::Direction dir;
801     if (copy.cheapComputeDirection(&dir)) {
802         REPORTER_ASSERT(reporter, dir == expected);
803     } else {
804         REPORTER_ASSERT(reporter, SkPath::kUnknown_Direction == expected);
805     }
806 }
807 
test_direction(skiatest::Reporter * reporter)808 static void test_direction(skiatest::Reporter* reporter) {
809     size_t i;
810     SkPath path;
811     REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
812     REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction));
813     REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction));
814     REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kUnknown_Direction));
815 
816     static const char* gDegen[] = {
817         "M 10 10",
818         "M 10 10 M 20 20",
819         "M 10 10 L 20 20",
820         "M 10 10 L 10 10 L 10 10",
821         "M 10 10 Q 10 10 10 10",
822         "M 10 10 C 10 10 10 10 10 10",
823     };
824     for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) {
825         path.reset();
826         bool valid = SkParsePath::FromSVGString(gDegen[i], &path);
827         REPORTER_ASSERT(reporter, valid);
828         REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
829     }
830 
831     static const char* gCW[] = {
832         "M 10 10 L 10 10 Q 20 10 20 20",
833         "M 10 10 C 20 10 20 20 20 20",
834         "M 20 10 Q 20 20 30 20 L 10 20", // test double-back at y-max
835         // rect with top two corners replaced by cubics with identical middle
836         // control points
837         "M 10 10 C 10 0 10 0 20 0 L 40 0 C 50 0 50 0 50 10",
838         "M 20 10 L 0 10 Q 10 10 20 0",  // left, degenerate serif
839     };
840     for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) {
841         path.reset();
842         bool valid = SkParsePath::FromSVGString(gCW[i], &path);
843         REPORTER_ASSERT(reporter, valid);
844         check_direction(reporter, path, SkPath::kCW_Direction);
845     }
846 
847     static const char* gCCW[] = {
848         "M 10 10 L 10 10 Q 20 10 20 -20",
849         "M 10 10 C 20 10 20 -20 20 -20",
850         "M 20 10 Q 20 20 10 20 L 30 20", // test double-back at y-max
851         // rect with top two corners replaced by cubics with identical middle
852         // control points
853         "M 50 10 C 50 0 50 0 40 0 L 20 0 C 10 0 10 0 10 10",
854         "M 10 10 L 30 10 Q 20 10 10 0",  // right, degenerate serif
855     };
856     for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) {
857         path.reset();
858         bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
859         REPORTER_ASSERT(reporter, valid);
860         check_direction(reporter, path, SkPath::kCCW_Direction);
861     }
862 
863     // Test two donuts, each wound a different direction. Only the outer contour
864     // determines the cheap direction
865     path.reset();
866     path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
867     path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
868     check_direction(reporter, path, SkPath::kCW_Direction);
869 
870     path.reset();
871     path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
872     path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
873     check_direction(reporter, path, SkPath::kCCW_Direction);
874 
875     // triangle with one point really far from the origin.
876     path.reset();
877     // the first point is roughly 1.05e10, 1.05e10
878     path.moveTo(SkBits2Float(0x501c7652), SkBits2Float(0x501c7652));
879     path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1);
880     path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1);
881     check_direction(reporter, path, SkPath::kCCW_Direction);
882 
883     path.reset();
884     path.conicTo(20, 0, 20, 20, 0.5f);
885     path.close();
886     check_direction(reporter, path, SkPath::kCW_Direction);
887 
888     path.reset();
889     path.lineTo(1, 1e7f);
890     path.lineTo(1e7f, 2e7f);
891     path.close();
892     REPORTER_ASSERT(reporter, SkPath::kConvex_Convexity == path.getConvexity());
893     check_direction(reporter, path, SkPath::kCCW_Direction);
894 }
895 
add_rect(SkPath * path,const SkRect & r)896 static void add_rect(SkPath* path, const SkRect& r) {
897     path->moveTo(r.fLeft, r.fTop);
898     path->lineTo(r.fRight, r.fTop);
899     path->lineTo(r.fRight, r.fBottom);
900     path->lineTo(r.fLeft, r.fBottom);
901     path->close();
902 }
903 
test_bounds(skiatest::Reporter * reporter)904 static void test_bounds(skiatest::Reporter* reporter) {
905     static const SkRect rects[] = {
906         { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
907         { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
908         { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
909         { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
910     };
911 
912     SkPath path0, path1;
913     for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
914         path0.addRect(rects[i]);
915         add_rect(&path1, rects[i]);
916     }
917 
918     REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
919 }
920 
stroke_cubic(const SkPoint pts[4])921 static void stroke_cubic(const SkPoint pts[4]) {
922     SkPath path;
923     path.moveTo(pts[0]);
924     path.cubicTo(pts[1], pts[2], pts[3]);
925 
926     SkPaint paint;
927     paint.setStyle(SkPaint::kStroke_Style);
928     paint.setStrokeWidth(SK_Scalar1 * 2);
929 
930     SkPath fill;
931     paint.getFillPath(path, &fill);
932 }
933 
934 // just ensure this can run w/o any SkASSERTS firing in the debug build
935 // we used to assert due to differences in how we determine a degenerate vector
936 // but that was fixed with the introduction of SkPoint::CanNormalize
stroke_tiny_cubic()937 static void stroke_tiny_cubic() {
938     SkPoint p0[] = {
939         { 372.0f,   92.0f },
940         { 372.0f,   92.0f },
941         { 372.0f,   92.0f },
942         { 372.0f,   92.0f },
943     };
944 
945     stroke_cubic(p0);
946 
947     SkPoint p1[] = {
948         { 372.0f,       92.0f },
949         { 372.0007f,    92.000755f },
950         { 371.99927f,   92.003922f },
951         { 371.99826f,   92.003899f },
952     };
953 
954     stroke_cubic(p1);
955 }
956 
check_close(skiatest::Reporter * reporter,const SkPath & path)957 static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
958     for (int i = 0; i < 2; ++i) {
959         SkPath::Iter iter(path, SkToBool(i));
960         SkPoint mv;
961         SkPoint pts[4];
962         SkPath::Verb v;
963         int nMT = 0;
964         int nCL = 0;
965         mv.set(0, 0);
966         while (SkPath::kDone_Verb != (v = iter.next(pts))) {
967             switch (v) {
968                 case SkPath::kMove_Verb:
969                     mv = pts[0];
970                     ++nMT;
971                     break;
972                 case SkPath::kClose_Verb:
973                     REPORTER_ASSERT(reporter, mv == pts[0]);
974                     ++nCL;
975                     break;
976                 default:
977                     break;
978             }
979         }
980         // if we force a close on the interator we should have a close
981         // for every moveTo
982         REPORTER_ASSERT(reporter, !i || nMT == nCL);
983     }
984 }
985 
test_close(skiatest::Reporter * reporter)986 static void test_close(skiatest::Reporter* reporter) {
987     SkPath closePt;
988     closePt.moveTo(0, 0);
989     closePt.close();
990     check_close(reporter, closePt);
991 
992     SkPath openPt;
993     openPt.moveTo(0, 0);
994     check_close(reporter, openPt);
995 
996     SkPath empty;
997     check_close(reporter, empty);
998     empty.close();
999     check_close(reporter, empty);
1000 
1001     SkPath rect;
1002     rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
1003     check_close(reporter, rect);
1004     rect.close();
1005     check_close(reporter, rect);
1006 
1007     SkPath quad;
1008     quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
1009     check_close(reporter, quad);
1010     quad.close();
1011     check_close(reporter, quad);
1012 
1013     SkPath cubic;
1014     quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
1015                  10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
1016     check_close(reporter, cubic);
1017     cubic.close();
1018     check_close(reporter, cubic);
1019 
1020     SkPath line;
1021     line.moveTo(SK_Scalar1, SK_Scalar1);
1022     line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
1023     check_close(reporter, line);
1024     line.close();
1025     check_close(reporter, line);
1026 
1027     SkPath rect2;
1028     rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
1029     rect2.close();
1030     rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
1031     check_close(reporter, rect2);
1032     rect2.close();
1033     check_close(reporter, rect2);
1034 
1035     SkPath oval3;
1036     oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
1037     oval3.close();
1038     oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
1039     check_close(reporter, oval3);
1040     oval3.close();
1041     check_close(reporter, oval3);
1042 
1043     SkPath moves;
1044     moves.moveTo(SK_Scalar1, SK_Scalar1);
1045     moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
1046     moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
1047     moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
1048     check_close(reporter, moves);
1049 
1050     stroke_tiny_cubic();
1051 }
1052 
check_convexity(skiatest::Reporter * reporter,const SkPath & path,SkPath::Convexity expected)1053 static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
1054                             SkPath::Convexity expected) {
1055     SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
1056     SkPath::Convexity c = copy.getConvexity();
1057     REPORTER_ASSERT(reporter, c == expected);
1058 }
1059 
test_convexity2(skiatest::Reporter * reporter)1060 static void test_convexity2(skiatest::Reporter* reporter) {
1061     SkPath pt;
1062     pt.moveTo(0, 0);
1063     pt.close();
1064     check_convexity(reporter, pt, SkPath::kConvex_Convexity);
1065     check_direction(reporter, pt, SkPath::kUnknown_Direction);
1066 
1067     SkPath line;
1068     line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
1069     line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
1070     line.close();
1071     check_convexity(reporter, line, SkPath::kConvex_Convexity);
1072     check_direction(reporter, line, SkPath::kUnknown_Direction);
1073 
1074     SkPath triLeft;
1075     triLeft.moveTo(0, 0);
1076     triLeft.lineTo(SK_Scalar1, 0);
1077     triLeft.lineTo(SK_Scalar1, SK_Scalar1);
1078     triLeft.close();
1079     check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
1080     check_direction(reporter, triLeft, SkPath::kCW_Direction);
1081 
1082     SkPath triRight;
1083     triRight.moveTo(0, 0);
1084     triRight.lineTo(-SK_Scalar1, 0);
1085     triRight.lineTo(SK_Scalar1, SK_Scalar1);
1086     triRight.close();
1087     check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
1088     check_direction(reporter, triRight, SkPath::kCCW_Direction);
1089 
1090     SkPath square;
1091     square.moveTo(0, 0);
1092     square.lineTo(SK_Scalar1, 0);
1093     square.lineTo(SK_Scalar1, SK_Scalar1);
1094     square.lineTo(0, SK_Scalar1);
1095     square.close();
1096     check_convexity(reporter, square, SkPath::kConvex_Convexity);
1097     check_direction(reporter, square, SkPath::kCW_Direction);
1098 
1099     SkPath redundantSquare;
1100     redundantSquare.moveTo(0, 0);
1101     redundantSquare.lineTo(0, 0);
1102     redundantSquare.lineTo(0, 0);
1103     redundantSquare.lineTo(SK_Scalar1, 0);
1104     redundantSquare.lineTo(SK_Scalar1, 0);
1105     redundantSquare.lineTo(SK_Scalar1, 0);
1106     redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
1107     redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
1108     redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
1109     redundantSquare.lineTo(0, SK_Scalar1);
1110     redundantSquare.lineTo(0, SK_Scalar1);
1111     redundantSquare.lineTo(0, SK_Scalar1);
1112     redundantSquare.close();
1113     check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
1114     check_direction(reporter, redundantSquare, SkPath::kCW_Direction);
1115 
1116     SkPath bowTie;
1117     bowTie.moveTo(0, 0);
1118     bowTie.lineTo(0, 0);
1119     bowTie.lineTo(0, 0);
1120     bowTie.lineTo(SK_Scalar1, SK_Scalar1);
1121     bowTie.lineTo(SK_Scalar1, SK_Scalar1);
1122     bowTie.lineTo(SK_Scalar1, SK_Scalar1);
1123     bowTie.lineTo(SK_Scalar1, 0);
1124     bowTie.lineTo(SK_Scalar1, 0);
1125     bowTie.lineTo(SK_Scalar1, 0);
1126     bowTie.lineTo(0, SK_Scalar1);
1127     bowTie.lineTo(0, SK_Scalar1);
1128     bowTie.lineTo(0, SK_Scalar1);
1129     bowTie.close();
1130     check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
1131     check_direction(reporter, bowTie, kDontCheckDir);
1132 
1133     SkPath spiral;
1134     spiral.moveTo(0, 0);
1135     spiral.lineTo(100*SK_Scalar1, 0);
1136     spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
1137     spiral.lineTo(0, 100*SK_Scalar1);
1138     spiral.lineTo(0, 50*SK_Scalar1);
1139     spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
1140     spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
1141     spiral.close();
1142     check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
1143     check_direction(reporter, spiral, kDontCheckDir);
1144 
1145     SkPath dent;
1146     dent.moveTo(0, 0);
1147     dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
1148     dent.lineTo(0, 100*SK_Scalar1);
1149     dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
1150     dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
1151     dent.close();
1152     check_convexity(reporter, dent, SkPath::kConcave_Convexity);
1153     check_direction(reporter, dent, SkPath::kCW_Direction);
1154 
1155     // http://skbug.com/2235
1156     SkPath strokedSin;
1157     for (int i = 0; i < 2000; i++) {
1158         SkScalar x = SkIntToScalar(i) / 2;
1159         SkScalar y = 500 - (x + SkScalarSin(x / 100) * 40) / 3;
1160         if (0 == i) {
1161             strokedSin.moveTo(x, y);
1162         } else {
1163             strokedSin.lineTo(x, y);
1164         }
1165     }
1166     SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
1167     stroke.setStrokeStyle(2 * SK_Scalar1);
1168     stroke.applyToPath(&strokedSin, strokedSin);
1169     check_convexity(reporter, strokedSin, SkPath::kConcave_Convexity);
1170     check_direction(reporter, strokedSin, kDontCheckDir);
1171 }
1172 
check_convex_bounds(skiatest::Reporter * reporter,const SkPath & p,const SkRect & bounds)1173 static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
1174                                 const SkRect& bounds) {
1175     REPORTER_ASSERT(reporter, p.isConvex());
1176     REPORTER_ASSERT(reporter, p.getBounds() == bounds);
1177 
1178     SkPath p2(p);
1179     REPORTER_ASSERT(reporter, p2.isConvex());
1180     REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
1181 
1182     SkPath other;
1183     other.swap(p2);
1184     REPORTER_ASSERT(reporter, other.isConvex());
1185     REPORTER_ASSERT(reporter, other.getBounds() == bounds);
1186 }
1187 
setFromString(SkPath * path,const char str[])1188 static void setFromString(SkPath* path, const char str[]) {
1189     bool first = true;
1190     while (str) {
1191         SkScalar x, y;
1192         str = SkParse::FindScalar(str, &x);
1193         if (NULL == str) {
1194             break;
1195         }
1196         str = SkParse::FindScalar(str, &y);
1197         SkASSERT(str);
1198         if (first) {
1199             path->moveTo(x, y);
1200             first = false;
1201         } else {
1202             path->lineTo(x, y);
1203         }
1204     }
1205 }
1206 
test_convexity(skiatest::Reporter * reporter)1207 static void test_convexity(skiatest::Reporter* reporter) {
1208     SkPath path;
1209 
1210     check_convexity(reporter, path, SkPath::kConvex_Convexity);
1211     path.addCircle(0, 0, SkIntToScalar(10));
1212     check_convexity(reporter, path, SkPath::kConvex_Convexity);
1213     path.addCircle(0, 0, SkIntToScalar(10));   // 2nd circle
1214     check_convexity(reporter, path, SkPath::kConcave_Convexity);
1215 
1216     path.reset();
1217     path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
1218     check_convexity(reporter, path, SkPath::kConvex_Convexity);
1219     REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
1220 
1221     path.reset();
1222     path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
1223     check_convexity(reporter, path, SkPath::kConvex_Convexity);
1224     REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
1225 
1226     static const struct {
1227         const char*         fPathStr;
1228         SkPath::Convexity   fExpectedConvexity;
1229         SkPath::Direction   fExpectedDirection;
1230     } gRec[] = {
1231         { "", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
1232         { "0 0", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
1233         { "0 0 10 10", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
1234         { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity, SkPath::kUnknown_Direction },
1235         { "0 0 10 10 10 20", SkPath::kConvex_Convexity, SkPath::kCW_Direction },
1236         { "0 0 10 10 10 0", SkPath::kConvex_Convexity, SkPath::kCCW_Direction },
1237         { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity, kDontCheckDir },
1238         { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity, SkPath::kCW_Direction },
1239     };
1240 
1241     for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
1242         SkPath path;
1243         setFromString(&path, gRec[i].fPathStr);
1244         check_convexity(reporter, path, gRec[i].fExpectedConvexity);
1245         check_direction(reporter, path, gRec[i].fExpectedDirection);
1246         // check after setting the initial convex and direction
1247         if (kDontCheckDir != gRec[i].fExpectedDirection) {
1248             SkPath copy(path);
1249             SkPath::Direction dir;
1250             bool foundDir = copy.cheapComputeDirection(&dir);
1251             REPORTER_ASSERT(reporter, (gRec[i].fExpectedDirection == SkPath::kUnknown_Direction)
1252                     ^ foundDir);
1253             REPORTER_ASSERT(reporter, !foundDir || gRec[i].fExpectedDirection == dir);
1254             check_convexity(reporter, copy, gRec[i].fExpectedConvexity);
1255         }
1256         REPORTER_ASSERT(reporter, gRec[i].fExpectedConvexity == path.getConvexity());
1257         check_direction(reporter, path, gRec[i].fExpectedDirection);
1258     }
1259 }
1260 
test_isLine(skiatest::Reporter * reporter)1261 static void test_isLine(skiatest::Reporter* reporter) {
1262     SkPath path;
1263     SkPoint pts[2];
1264     const SkScalar value = SkIntToScalar(5);
1265 
1266     REPORTER_ASSERT(reporter, !path.isLine(NULL));
1267 
1268     // set some non-zero values
1269     pts[0].set(value, value);
1270     pts[1].set(value, value);
1271     REPORTER_ASSERT(reporter, !path.isLine(pts));
1272     // check that pts was untouched
1273     REPORTER_ASSERT(reporter, pts[0].equals(value, value));
1274     REPORTER_ASSERT(reporter, pts[1].equals(value, value));
1275 
1276     const SkScalar moveX = SkIntToScalar(1);
1277     const SkScalar moveY = SkIntToScalar(2);
1278     REPORTER_ASSERT(reporter, value != moveX && value != moveY);
1279 
1280     path.moveTo(moveX, moveY);
1281     REPORTER_ASSERT(reporter, !path.isLine(NULL));
1282     REPORTER_ASSERT(reporter, !path.isLine(pts));
1283     // check that pts was untouched
1284     REPORTER_ASSERT(reporter, pts[0].equals(value, value));
1285     REPORTER_ASSERT(reporter, pts[1].equals(value, value));
1286 
1287     const SkScalar lineX = SkIntToScalar(2);
1288     const SkScalar lineY = SkIntToScalar(2);
1289     REPORTER_ASSERT(reporter, value != lineX && value != lineY);
1290 
1291     path.lineTo(lineX, lineY);
1292     REPORTER_ASSERT(reporter, path.isLine(NULL));
1293 
1294     REPORTER_ASSERT(reporter, !pts[0].equals(moveX, moveY));
1295     REPORTER_ASSERT(reporter, !pts[1].equals(lineX, lineY));
1296     REPORTER_ASSERT(reporter, path.isLine(pts));
1297     REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
1298     REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
1299 
1300     path.lineTo(0, 0);  // too many points/verbs
1301     REPORTER_ASSERT(reporter, !path.isLine(NULL));
1302     REPORTER_ASSERT(reporter, !path.isLine(pts));
1303     REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
1304     REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
1305 
1306     path.reset();
1307     path.quadTo(1, 1, 2, 2);
1308     REPORTER_ASSERT(reporter, !path.isLine(NULL));
1309 }
1310 
test_conservativelyContains(skiatest::Reporter * reporter)1311 static void test_conservativelyContains(skiatest::Reporter* reporter) {
1312     SkPath path;
1313 
1314     // kBaseRect is used to construct most our test paths: a rect, a circle, and a round-rect.
1315     static const SkRect kBaseRect = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
1316 
1317     // A circle that bounds kBaseRect (with a significant amount of slop)
1318     SkScalar circleR = SkMaxScalar(kBaseRect.width(), kBaseRect.height());
1319     circleR = SkScalarMul(circleR, 1.75f) / 2;
1320     static const SkPoint kCircleC = {kBaseRect.centerX(), kBaseRect.centerY()};
1321 
1322     // round-rect radii
1323     static const SkScalar kRRRadii[] = {SkIntToScalar(5), SkIntToScalar(3)};
1324 
1325     static const struct SUPPRESS_VISIBILITY_WARNING {
1326         SkRect fQueryRect;
1327         bool   fInRect;
1328         bool   fInCircle;
1329         bool   fInRR;
1330         bool   fInCubicRR;
1331     } kQueries[] = {
1332         {kBaseRect, true, true, false, false},
1333 
1334         // rect well inside of kBaseRect
1335         {SkRect::MakeLTRB(kBaseRect.fLeft + 0.25f*kBaseRect.width(),
1336                           kBaseRect.fTop + 0.25f*kBaseRect.height(),
1337                           kBaseRect.fRight - 0.25f*kBaseRect.width(),
1338                           kBaseRect.fBottom - 0.25f*kBaseRect.height()),
1339                           true, true, true, true},
1340 
1341         // rects with edges off by one from kBaseRect's edges
1342         {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1343                           kBaseRect.width(), kBaseRect.height() + 1),
1344          false, true, false, false},
1345         {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1346                           kBaseRect.width() + 1, kBaseRect.height()),
1347          false, true, false, false},
1348         {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1349                           kBaseRect.width() + 1, kBaseRect.height() + 1),
1350          false, true, false, false},
1351         {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
1352                           kBaseRect.width(), kBaseRect.height()),
1353          false, true, false, false},
1354         {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
1355                           kBaseRect.width(), kBaseRect.height()),
1356          false, true, false, false},
1357         {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
1358                           kBaseRect.width() + 2, kBaseRect.height()),
1359          false, true, false, false},
1360         {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
1361                           kBaseRect.width() + 2, kBaseRect.height()),
1362          false, true, false, false},
1363 
1364         // zero-w/h rects at each corner of kBaseRect
1365         {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop, 0, 0), true, true, false, false},
1366         {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fTop, 0, 0), true, true, false, true},
1367         {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fBottom, 0, 0), true, true, false, true},
1368         {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fBottom, 0, 0), true, true, false, true},
1369 
1370         // far away rect
1371         {SkRect::MakeXYWH(10 * kBaseRect.fRight, 10 * kBaseRect.fBottom,
1372                           SkIntToScalar(10), SkIntToScalar(10)),
1373          false, false, false, false},
1374 
1375         // very large rect containing kBaseRect
1376         {SkRect::MakeXYWH(kBaseRect.fLeft - 5 * kBaseRect.width(),
1377                           kBaseRect.fTop - 5 * kBaseRect.height(),
1378                           11 * kBaseRect.width(), 11 * kBaseRect.height()),
1379          false, false, false, false},
1380 
1381         // skinny rect that spans same y-range as kBaseRect
1382         {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
1383                           SkIntToScalar(1), kBaseRect.height()),
1384          true, true, true, true},
1385 
1386         // short rect that spans same x-range as kBaseRect
1387         {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(), kBaseRect.width(), SkScalar(1)),
1388          true, true, true, true},
1389 
1390         // skinny rect that spans slightly larger y-range than kBaseRect
1391         {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
1392                           SkIntToScalar(1), kBaseRect.height() + 1),
1393          false, true, false, false},
1394 
1395         // short rect that spans slightly larger x-range than kBaseRect
1396         {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(),
1397                           kBaseRect.width() + 1, SkScalar(1)),
1398          false, true, false, false},
1399     };
1400 
1401     for (int inv = 0; inv < 4; ++inv) {
1402         for (size_t q = 0; q < SK_ARRAY_COUNT(kQueries); ++q) {
1403             SkRect qRect = kQueries[q].fQueryRect;
1404             if (inv & 0x1) {
1405                 SkTSwap(qRect.fLeft, qRect.fRight);
1406             }
1407             if (inv & 0x2) {
1408                 SkTSwap(qRect.fTop, qRect.fBottom);
1409             }
1410             for (int d = 0; d < 2; ++d) {
1411                 SkPath::Direction dir = d ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
1412                 path.reset();
1413                 path.addRect(kBaseRect, dir);
1414                 REPORTER_ASSERT(reporter, kQueries[q].fInRect ==
1415                                           path.conservativelyContainsRect(qRect));
1416 
1417                 path.reset();
1418                 path.addCircle(kCircleC.fX, kCircleC.fY, circleR, dir);
1419                 REPORTER_ASSERT(reporter, kQueries[q].fInCircle ==
1420                                           path.conservativelyContainsRect(qRect));
1421 
1422                 path.reset();
1423                 path.addRoundRect(kBaseRect, kRRRadii[0], kRRRadii[1], dir);
1424                 REPORTER_ASSERT(reporter, kQueries[q].fInRR ==
1425                                           path.conservativelyContainsRect(qRect));
1426 
1427                 path.reset();
1428                 path.moveTo(kBaseRect.fLeft + kRRRadii[0], kBaseRect.fTop);
1429                 path.cubicTo(kBaseRect.fLeft + kRRRadii[0] / 2, kBaseRect.fTop,
1430                              kBaseRect.fLeft, kBaseRect.fTop + kRRRadii[1] / 2,
1431                              kBaseRect.fLeft, kBaseRect.fTop + kRRRadii[1]);
1432                 path.lineTo(kBaseRect.fLeft, kBaseRect.fBottom);
1433                 path.lineTo(kBaseRect.fRight, kBaseRect.fBottom);
1434                 path.lineTo(kBaseRect.fRight, kBaseRect.fTop);
1435                 path.close();
1436                 REPORTER_ASSERT(reporter, kQueries[q].fInCubicRR ==
1437                                           path.conservativelyContainsRect(qRect));
1438 
1439             }
1440             // Slightly non-convex shape, shouldn't contain any rects.
1441             path.reset();
1442             path.moveTo(0, 0);
1443             path.lineTo(SkIntToScalar(50), 0.05f);
1444             path.lineTo(SkIntToScalar(100), 0);
1445             path.lineTo(SkIntToScalar(100), SkIntToScalar(100));
1446             path.lineTo(0, SkIntToScalar(100));
1447             path.close();
1448             REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(qRect));
1449         }
1450     }
1451 
1452     // make sure a minimal convex shape works, a right tri with edges along pos x and y axes.
1453     path.reset();
1454     path.moveTo(0, 0);
1455     path.lineTo(SkIntToScalar(100), 0);
1456     path.lineTo(0, SkIntToScalar(100));
1457 
1458     // inside, on along top edge
1459     REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
1460                                                                                SkIntToScalar(10),
1461                                                                                SkIntToScalar(10))));
1462     // above
1463     REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(
1464         SkRect::MakeXYWH(SkIntToScalar(50),
1465                          SkIntToScalar(-10),
1466                          SkIntToScalar(10),
1467                          SkIntToScalar(10))));
1468     // to the left
1469     REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(-10),
1470                                                                                 SkIntToScalar(5),
1471                                                                                 SkIntToScalar(5),
1472                                                                                 SkIntToScalar(5))));
1473 
1474     // outside the diagonal edge
1475     REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(10),
1476                                                                                 SkIntToScalar(200),
1477                                                                                 SkIntToScalar(20),
1478                                                                                 SkIntToScalar(5))));
1479 
1480     // same as above path and first test but with an extra moveTo.
1481     path.reset();
1482     path.moveTo(100, 100);
1483     path.moveTo(0, 0);
1484     path.lineTo(SkIntToScalar(100), 0);
1485     path.lineTo(0, SkIntToScalar(100));
1486 
1487     REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
1488                                                                                SkIntToScalar(10),
1489                                                                                SkIntToScalar(10))));
1490 
1491     path.reset();
1492     path.lineTo(100, 100);
1493     REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(0, 0, 1, 1)));
1494 }
1495 
test_isRect_open_close(skiatest::Reporter * reporter)1496 static void test_isRect_open_close(skiatest::Reporter* reporter) {
1497     SkPath path;
1498     bool isClosed;
1499 
1500     path.moveTo(0, 0); path.lineTo(1, 0); path.lineTo(1, 1); path.lineTo(0, 1);
1501     path.close();
1502 
1503     REPORTER_ASSERT(reporter, path.isRect(NULL, NULL));
1504     REPORTER_ASSERT(reporter, path.isRect(&isClosed, NULL));
1505     REPORTER_ASSERT(reporter, isClosed);
1506     REPORTER_ASSERT(reporter, SkPath::kStroke_PathAsRect == path.asRect(NULL));
1507 }
1508 
1509 // Simple isRect test is inline TestPath, below.
1510 // test_isRect provides more extensive testing.
test_isRect(skiatest::Reporter * reporter)1511 static void test_isRect(skiatest::Reporter* reporter) {
1512     test_isRect_open_close(reporter);
1513 
1514     // passing tests (all moveTo / lineTo...
1515     SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
1516     SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1517     SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1518     SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
1519     SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
1520     SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1521     SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1522     SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1523     SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1524     SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f}, {1, 0}, {.5f, 0}};
1525     SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1}, {0, 1}, {0, .5f}};
1526     SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
1527     SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
1528     SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
1529     SkPoint rf[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}};
1530 
1531     // failing tests
1532     SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1533     SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1534     SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1535     SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1536     SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1537     SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1538     SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1539     SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
1540     SkPoint f9[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}, {2, 0}}; // overlaps
1541     SkPoint fa[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, -1}, {1, -1}}; // non colinear gap
1542     SkPoint fb[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 1}}; // falls short
1543 
1544     // no close, but we should detect them as fillably the same as a rect
1545     SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
1546     SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}};
1547     SkPoint c3[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}, {0, 0}}; // hit the start
1548 
1549     // like c2, but we double-back on ourselves
1550     SkPoint d1[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}, {0, 2}};
1551     // like c2, but we overshoot the start point
1552     SkPoint d2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, -1}};
1553     SkPoint d3[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, -1}, {0, 0}};
1554 
1555     struct IsRectTest {
1556         SkPoint *fPoints;
1557         size_t fPointCount;
1558         bool fClose;
1559         bool fIsRect;
1560     } tests[] = {
1561         { r1, SK_ARRAY_COUNT(r1), true, true },
1562         { r2, SK_ARRAY_COUNT(r2), true, true },
1563         { r3, SK_ARRAY_COUNT(r3), true, true },
1564         { r4, SK_ARRAY_COUNT(r4), true, true },
1565         { r5, SK_ARRAY_COUNT(r5), true, true },
1566         { r6, SK_ARRAY_COUNT(r6), true, true },
1567         { r7, SK_ARRAY_COUNT(r7), true, true },
1568         { r8, SK_ARRAY_COUNT(r8), true, true },
1569         { r9, SK_ARRAY_COUNT(r9), true, true },
1570         { ra, SK_ARRAY_COUNT(ra), true, true },
1571         { rb, SK_ARRAY_COUNT(rb), true, true },
1572         { rc, SK_ARRAY_COUNT(rc), true, true },
1573         { rd, SK_ARRAY_COUNT(rd), true, true },
1574         { re, SK_ARRAY_COUNT(re), true, true },
1575         { rf, SK_ARRAY_COUNT(rf), true, true },
1576 
1577         { f1, SK_ARRAY_COUNT(f1), true, false },
1578         { f2, SK_ARRAY_COUNT(f2), true, false },
1579         { f3, SK_ARRAY_COUNT(f3), true, false },
1580         { f4, SK_ARRAY_COUNT(f4), true, false },
1581         { f5, SK_ARRAY_COUNT(f5), true, false },
1582         { f6, SK_ARRAY_COUNT(f6), true, false },
1583         { f7, SK_ARRAY_COUNT(f7), true, false },
1584         { f8, SK_ARRAY_COUNT(f8), true, false },
1585         { f9, SK_ARRAY_COUNT(f9), true, false },
1586         { fa, SK_ARRAY_COUNT(fa), true, false },
1587         { fb, SK_ARRAY_COUNT(fb), true, false },
1588 
1589         { c1, SK_ARRAY_COUNT(c1), false, true },
1590         { c2, SK_ARRAY_COUNT(c2), false, true },
1591         { c3, SK_ARRAY_COUNT(c3), false, true },
1592 
1593         { d1, SK_ARRAY_COUNT(d1), false, false },
1594         { d2, SK_ARRAY_COUNT(d2), false, false },
1595         { d3, SK_ARRAY_COUNT(d3), false, false },
1596     };
1597 
1598     const size_t testCount = SK_ARRAY_COUNT(tests);
1599     size_t index;
1600     for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1601         SkPath path;
1602         path.moveTo(tests[testIndex].fPoints[0].fX, tests[testIndex].fPoints[0].fY);
1603         for (index = 1; index < tests[testIndex].fPointCount; ++index) {
1604             path.lineTo(tests[testIndex].fPoints[index].fX, tests[testIndex].fPoints[index].fY);
1605         }
1606         if (tests[testIndex].fClose) {
1607             path.close();
1608         }
1609         REPORTER_ASSERT(reporter, tests[testIndex].fIsRect == path.isRect(NULL));
1610         REPORTER_ASSERT(reporter, tests[testIndex].fIsRect == path.isRect(NULL, NULL));
1611 
1612         if (tests[testIndex].fIsRect) {
1613             SkRect computed, expected;
1614             expected.set(tests[testIndex].fPoints, tests[testIndex].fPointCount);
1615             REPORTER_ASSERT(reporter, path.isRect(&computed));
1616             REPORTER_ASSERT(reporter, expected == computed);
1617 
1618             bool isClosed;
1619             SkPath::Direction direction, cheapDirection;
1620             REPORTER_ASSERT(reporter, path.cheapComputeDirection(&cheapDirection));
1621             REPORTER_ASSERT(reporter, path.isRect(&isClosed, &direction));
1622             REPORTER_ASSERT(reporter, isClosed == tests[testIndex].fClose);
1623             REPORTER_ASSERT(reporter, direction == cheapDirection);
1624             direction = (SkPath::Direction) -1;
1625             if (!tests[testIndex].fClose) {
1626                 REPORTER_ASSERT(reporter, SkPath::kFill_PathAsRect == path.asRect());
1627                 REPORTER_ASSERT(reporter, SkPath::kFill_PathAsRect == path.asRect(&direction));
1628             } else {
1629                 REPORTER_ASSERT(reporter, SkPath::kStroke_PathAsRect == path.asRect());
1630                 REPORTER_ASSERT(reporter, SkPath::kStroke_PathAsRect == path.asRect(&direction));
1631             }
1632             REPORTER_ASSERT(reporter, direction == cheapDirection);
1633         } else {
1634             SkRect computed;
1635             computed.set(123, 456, 789, 1011);
1636             REPORTER_ASSERT(reporter, !path.isRect(&computed));
1637             REPORTER_ASSERT(reporter, computed.fLeft == 123 && computed.fTop == 456);
1638             REPORTER_ASSERT(reporter, computed.fRight == 789 && computed.fBottom == 1011);
1639 
1640             bool isClosed = (bool) -1;
1641             SkPath::Direction direction = (SkPath::Direction) -1;
1642             REPORTER_ASSERT(reporter, !path.isRect(&isClosed, &direction));
1643             REPORTER_ASSERT(reporter, isClosed == (bool) -1);
1644             REPORTER_ASSERT(reporter, direction == (SkPath::Direction) -1);
1645             REPORTER_ASSERT(reporter, SkPath::kNone_PathAsRect == path.asRect());
1646             REPORTER_ASSERT(reporter, SkPath::kNone_PathAsRect == path.asRect(&direction));
1647             REPORTER_ASSERT(reporter, direction == (SkPath::Direction) -1);
1648         }
1649     }
1650 
1651     // fail, close then line
1652     SkPath path1;
1653     path1.moveTo(r1[0].fX, r1[0].fY);
1654     for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) {
1655         path1.lineTo(r1[index].fX, r1[index].fY);
1656     }
1657     path1.close();
1658     path1.lineTo(1, 0);
1659     REPORTER_ASSERT(reporter, !path1.isRect(NULL));
1660 
1661     // fail, move in the middle
1662     path1.reset();
1663     path1.moveTo(r1[0].fX, r1[0].fY);
1664     for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) {
1665         if (index == 2) {
1666             path1.moveTo(1, .5f);
1667         }
1668         path1.lineTo(r1[index].fX, r1[index].fY);
1669     }
1670     path1.close();
1671     REPORTER_ASSERT(reporter, !path1.isRect(NULL));
1672 
1673     // fail, move on the edge
1674     path1.reset();
1675     for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) {
1676         path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1677         path1.lineTo(r1[index].fX, r1[index].fY);
1678     }
1679     path1.close();
1680     REPORTER_ASSERT(reporter, !path1.isRect(NULL));
1681 
1682     // fail, quad
1683     path1.reset();
1684     path1.moveTo(r1[0].fX, r1[0].fY);
1685     for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) {
1686         if (index == 2) {
1687             path1.quadTo(1, .5f, 1, .5f);
1688         }
1689         path1.lineTo(r1[index].fX, r1[index].fY);
1690     }
1691     path1.close();
1692     REPORTER_ASSERT(reporter, !path1.isRect(NULL));
1693 
1694     // fail, cubic
1695     path1.reset();
1696     path1.moveTo(r1[0].fX, r1[0].fY);
1697     for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) {
1698         if (index == 2) {
1699             path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1700         }
1701         path1.lineTo(r1[index].fX, r1[index].fY);
1702     }
1703     path1.close();
1704     REPORTER_ASSERT(reporter, !path1.isRect(NULL));
1705 }
1706 
test_isNestedRects(skiatest::Reporter * reporter)1707 static void test_isNestedRects(skiatest::Reporter* reporter) {
1708     // passing tests (all moveTo / lineTo...
1709     SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // CW
1710     SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1711     SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1712     SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
1713     SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}}; // CCW
1714     SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1715     SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1716     SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1717     SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1718     SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f}, {1, 0}, {.5f, 0}}; // CCW
1719     SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1}, {0, 1}, {0, .5f}}; // CW
1720     SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}; // CW
1721     SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}}; // CCW
1722     SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}}; // CW
1723 
1724     // failing tests
1725     SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1726     SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1727     SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1728     SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1729     SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1730     SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1731     SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1732     SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
1733 
1734     // failing, no close
1735     SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1736     SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1737 
1738     struct IsNestedRectTest {
1739         SkPoint *fPoints;
1740         size_t fPointCount;
1741         SkPath::Direction fDirection;
1742         bool fClose;
1743         bool fIsNestedRect; // nests with path.addRect(-1, -1, 2, 2);
1744     } tests[] = {
1745         { r1, SK_ARRAY_COUNT(r1), SkPath::kCW_Direction , true, true },
1746         { r2, SK_ARRAY_COUNT(r2), SkPath::kCW_Direction , true, true },
1747         { r3, SK_ARRAY_COUNT(r3), SkPath::kCW_Direction , true, true },
1748         { r4, SK_ARRAY_COUNT(r4), SkPath::kCW_Direction , true, true },
1749         { r5, SK_ARRAY_COUNT(r5), SkPath::kCCW_Direction, true, true },
1750         { r6, SK_ARRAY_COUNT(r6), SkPath::kCCW_Direction, true, true },
1751         { r7, SK_ARRAY_COUNT(r7), SkPath::kCCW_Direction, true, true },
1752         { r8, SK_ARRAY_COUNT(r8), SkPath::kCCW_Direction, true, true },
1753         { r9, SK_ARRAY_COUNT(r9), SkPath::kCCW_Direction, true, true },
1754         { ra, SK_ARRAY_COUNT(ra), SkPath::kCCW_Direction, true, true },
1755         { rb, SK_ARRAY_COUNT(rb), SkPath::kCW_Direction,  true, true },
1756         { rc, SK_ARRAY_COUNT(rc), SkPath::kCW_Direction,  true, true },
1757         { rd, SK_ARRAY_COUNT(rd), SkPath::kCCW_Direction, true, true },
1758         { re, SK_ARRAY_COUNT(re), SkPath::kCW_Direction,  true, true },
1759 
1760         { f1, SK_ARRAY_COUNT(f1), SkPath::kUnknown_Direction, true, false },
1761         { f2, SK_ARRAY_COUNT(f2), SkPath::kUnknown_Direction, true, false },
1762         { f3, SK_ARRAY_COUNT(f3), SkPath::kUnknown_Direction, true, false },
1763         { f4, SK_ARRAY_COUNT(f4), SkPath::kUnknown_Direction, true, false },
1764         { f5, SK_ARRAY_COUNT(f5), SkPath::kUnknown_Direction, true, false },
1765         { f6, SK_ARRAY_COUNT(f6), SkPath::kUnknown_Direction, true, false },
1766         { f7, SK_ARRAY_COUNT(f7), SkPath::kUnknown_Direction, true, false },
1767         { f8, SK_ARRAY_COUNT(f8), SkPath::kUnknown_Direction, true, false },
1768 
1769         { c1, SK_ARRAY_COUNT(c1), SkPath::kUnknown_Direction, false, false },
1770         { c2, SK_ARRAY_COUNT(c2), SkPath::kUnknown_Direction, false, false },
1771     };
1772 
1773     const size_t testCount = SK_ARRAY_COUNT(tests);
1774     size_t index;
1775     for (int rectFirst = 0; rectFirst <= 1; ++rectFirst) {
1776         for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1777             SkPath path;
1778             if (rectFirst) {
1779                 path.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1780             }
1781             path.moveTo(tests[testIndex].fPoints[0].fX, tests[testIndex].fPoints[0].fY);
1782             for (index = 1; index < tests[testIndex].fPointCount; ++index) {
1783                 path.lineTo(tests[testIndex].fPoints[index].fX, tests[testIndex].fPoints[index].fY);
1784             }
1785             if (tests[testIndex].fClose) {
1786                 path.close();
1787             }
1788             if (!rectFirst) {
1789                 path.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1790             }
1791             REPORTER_ASSERT(reporter, tests[testIndex].fIsNestedRect == path.isNestedRects(NULL));
1792             if (tests[testIndex].fIsNestedRect) {
1793                 SkRect expected[2], computed[2];
1794                 SkPath::Direction expectedDirs[2], computedDirs[2];
1795                 SkRect testBounds;
1796                 testBounds.set(tests[testIndex].fPoints, tests[testIndex].fPointCount);
1797                 expected[0] = SkRect::MakeLTRB(-1, -1, 2, 2);
1798                 expected[1] = testBounds;
1799                 if (rectFirst) {
1800                     expectedDirs[0] = SkPath::kCW_Direction;
1801                 } else {
1802                     expectedDirs[0] = SkPath::kCCW_Direction;
1803                 }
1804                 expectedDirs[1] = tests[testIndex].fDirection;
1805                 REPORTER_ASSERT(reporter, path.isNestedRects(computed, computedDirs));
1806                 REPORTER_ASSERT(reporter, expected[0] == computed[0]);
1807                 REPORTER_ASSERT(reporter, expected[1] == computed[1]);
1808                 REPORTER_ASSERT(reporter, expectedDirs[0] == computedDirs[0]);
1809                 REPORTER_ASSERT(reporter, expectedDirs[1] == computedDirs[1]);
1810             }
1811         }
1812 
1813         // fail, close then line
1814         SkPath path1;
1815         if (rectFirst) {
1816             path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1817         }
1818         path1.moveTo(r1[0].fX, r1[0].fY);
1819         for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) {
1820             path1.lineTo(r1[index].fX, r1[index].fY);
1821         }
1822         path1.close();
1823         path1.lineTo(1, 0);
1824         if (!rectFirst) {
1825             path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1826         }
1827         REPORTER_ASSERT(reporter, !path1.isNestedRects(NULL));
1828 
1829         // fail, move in the middle
1830         path1.reset();
1831         if (rectFirst) {
1832             path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1833         }
1834         path1.moveTo(r1[0].fX, r1[0].fY);
1835         for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) {
1836             if (index == 2) {
1837                 path1.moveTo(1, .5f);
1838             }
1839             path1.lineTo(r1[index].fX, r1[index].fY);
1840         }
1841         path1.close();
1842         if (!rectFirst) {
1843             path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1844         }
1845         REPORTER_ASSERT(reporter, !path1.isNestedRects(NULL));
1846 
1847         // fail, move on the edge
1848         path1.reset();
1849         if (rectFirst) {
1850             path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1851         }
1852         for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) {
1853             path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1854             path1.lineTo(r1[index].fX, r1[index].fY);
1855         }
1856         path1.close();
1857         if (!rectFirst) {
1858             path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1859         }
1860         REPORTER_ASSERT(reporter, !path1.isNestedRects(NULL));
1861 
1862         // fail, quad
1863         path1.reset();
1864         if (rectFirst) {
1865             path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1866         }
1867         path1.moveTo(r1[0].fX, r1[0].fY);
1868         for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) {
1869             if (index == 2) {
1870                 path1.quadTo(1, .5f, 1, .5f);
1871             }
1872             path1.lineTo(r1[index].fX, r1[index].fY);
1873         }
1874         path1.close();
1875         if (!rectFirst) {
1876             path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1877         }
1878         REPORTER_ASSERT(reporter, !path1.isNestedRects(NULL));
1879 
1880         // fail, cubic
1881         path1.reset();
1882         if (rectFirst) {
1883             path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1884         }
1885         path1.moveTo(r1[0].fX, r1[0].fY);
1886         for (index = 1; index < SK_ARRAY_COUNT(r1); ++index) {
1887             if (index == 2) {
1888                 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1889             }
1890             path1.lineTo(r1[index].fX, r1[index].fY);
1891         }
1892         path1.close();
1893         if (!rectFirst) {
1894             path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1895         }
1896         REPORTER_ASSERT(reporter, !path1.isNestedRects(NULL));
1897 
1898         // fail,  not nested
1899         path1.reset();
1900         path1.addRect(1, 1, 3, 3, SkPath::kCW_Direction);
1901         path1.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
1902         REPORTER_ASSERT(reporter, !path1.isNestedRects(NULL));
1903     }
1904 
1905     // pass, stroke rect
1906     SkPath src, dst;
1907     src.addRect(1, 1, 7, 7, SkPath::kCW_Direction);
1908     SkPaint strokePaint;
1909     strokePaint.setStyle(SkPaint::kStroke_Style);
1910     strokePaint.setStrokeWidth(2);
1911     strokePaint.getFillPath(src, &dst);
1912     REPORTER_ASSERT(reporter, dst.isNestedRects(NULL));
1913 }
1914 
write_and_read_back(skiatest::Reporter * reporter,const SkPath & p)1915 static void write_and_read_back(skiatest::Reporter* reporter,
1916                                 const SkPath& p) {
1917     SkWriter32 writer;
1918     writer.writePath(p);
1919     size_t size = writer.bytesWritten();
1920     SkAutoMalloc storage(size);
1921     writer.flatten(storage.get());
1922     SkReader32 reader(storage.get(), size);
1923 
1924     SkPath readBack;
1925     REPORTER_ASSERT(reporter, readBack != p);
1926     reader.readPath(&readBack);
1927     REPORTER_ASSERT(reporter, readBack == p);
1928 
1929     REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() ==
1930                               p.getConvexityOrUnknown());
1931 
1932     REPORTER_ASSERT(reporter, readBack.isOval(NULL) == p.isOval(NULL));
1933 
1934     const SkRect& origBounds = p.getBounds();
1935     const SkRect& readBackBounds = readBack.getBounds();
1936 
1937     REPORTER_ASSERT(reporter, origBounds == readBackBounds);
1938 }
1939 
test_flattening(skiatest::Reporter * reporter)1940 static void test_flattening(skiatest::Reporter* reporter) {
1941     SkPath p;
1942 
1943     static const SkPoint pts[] = {
1944         { 0, 0 },
1945         { SkIntToScalar(10), SkIntToScalar(10) },
1946         { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1947         { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1948     };
1949     p.moveTo(pts[0]);
1950     p.lineTo(pts[1]);
1951     p.quadTo(pts[2], pts[3]);
1952     p.cubicTo(pts[4], pts[5], pts[6]);
1953 
1954     write_and_read_back(reporter, p);
1955 
1956     // create a buffer that should be much larger than the path so we don't
1957     // kill our stack if writer goes too far.
1958     char buffer[1024];
1959     size_t size1 = p.writeToMemory(NULL);
1960     size_t size2 = p.writeToMemory(buffer);
1961     REPORTER_ASSERT(reporter, size1 == size2);
1962 
1963     SkPath p2;
1964     size_t size3 = p2.readFromMemory(buffer, 1024);
1965     REPORTER_ASSERT(reporter, size1 == size3);
1966     REPORTER_ASSERT(reporter, p == p2);
1967 
1968     size3 = p2.readFromMemory(buffer, 0);
1969     REPORTER_ASSERT(reporter, !size3);
1970 
1971     SkPath tooShort;
1972     size3 = tooShort.readFromMemory(buffer, size1 - 1);
1973     REPORTER_ASSERT(reporter, tooShort.isEmpty());
1974 
1975     char buffer2[1024];
1976     size3 = p2.writeToMemory(buffer2);
1977     REPORTER_ASSERT(reporter, size1 == size3);
1978     REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
1979 
1980     // test persistence of the oval flag & convexity
1981     {
1982         SkPath oval;
1983         SkRect rect = SkRect::MakeWH(10, 10);
1984         oval.addOval(rect);
1985 
1986         write_and_read_back(reporter, oval);
1987     }
1988 }
1989 
test_transform(skiatest::Reporter * reporter)1990 static void test_transform(skiatest::Reporter* reporter) {
1991     SkPath p;
1992 
1993 #define CONIC_PERSPECTIVE_BUG_FIXED 0
1994     static const SkPoint pts[] = {
1995         { 0, 0 },  // move
1996         { SkIntToScalar(10), SkIntToScalar(10) },  // line
1997         { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },  // quad
1998         { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) },  // cubic
1999 #if CONIC_PERSPECTIVE_BUG_FIXED
2000         { 0, 0 }, { SkIntToScalar(20), SkIntToScalar(10) },  // conic
2001 #endif
2002     };
2003     const int kPtCount = SK_ARRAY_COUNT(pts);
2004 
2005     p.moveTo(pts[0]);
2006     p.lineTo(pts[1]);
2007     p.quadTo(pts[2], pts[3]);
2008     p.cubicTo(pts[4], pts[5], pts[6]);
2009 #if CONIC_PERSPECTIVE_BUG_FIXED
2010     p.conicTo(pts[4], pts[5], 0.5f);
2011 #endif
2012     p.close();
2013 
2014     {
2015         SkMatrix matrix;
2016         matrix.reset();
2017         SkPath p1;
2018         p.transform(matrix, &p1);
2019         REPORTER_ASSERT(reporter, p == p1);
2020     }
2021 
2022 
2023     {
2024         SkMatrix matrix;
2025         matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
2026 
2027         SkPath p1;      // Leave p1 non-unique (i.e., the empty path)
2028 
2029         p.transform(matrix, &p1);
2030         SkPoint pts1[kPtCount];
2031         int count = p1.getPoints(pts1, kPtCount);
2032         REPORTER_ASSERT(reporter, kPtCount == count);
2033         for (int i = 0; i < count; ++i) {
2034             SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
2035             REPORTER_ASSERT(reporter, newPt == pts1[i]);
2036         }
2037     }
2038 
2039     {
2040         SkMatrix matrix;
2041         matrix.reset();
2042         matrix.setPerspX(SkScalarToPersp(4));
2043 
2044         SkPath p1;
2045         p1.moveTo(SkPoint::Make(0, 0));
2046 
2047         p.transform(matrix, &p1);
2048         REPORTER_ASSERT(reporter, matrix.invert(&matrix));
2049         p1.transform(matrix, NULL);
2050         SkRect pBounds = p.getBounds();
2051         SkRect p1Bounds = p1.getBounds();
2052         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(pBounds.fLeft, p1Bounds.fLeft));
2053         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(pBounds.fTop, p1Bounds.fTop));
2054         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(pBounds.fRight, p1Bounds.fRight));
2055         REPORTER_ASSERT(reporter, SkScalarNearlyEqual(pBounds.fBottom, p1Bounds.fBottom));
2056     }
2057 
2058     p.reset();
2059     p.addCircle(0, 0, 1, SkPath::kCW_Direction);
2060 
2061     {
2062         SkMatrix matrix;
2063         matrix.reset();
2064         SkPath p1;
2065         p1.moveTo(SkPoint::Make(0, 0));
2066 
2067         p.transform(matrix, &p1);
2068         REPORTER_ASSERT(reporter, p1.cheapIsDirection(SkPath::kCW_Direction));
2069     }
2070 
2071 
2072     {
2073         SkMatrix matrix;
2074         matrix.reset();
2075         matrix.setScaleX(-1);
2076         SkPath p1;
2077         p1.moveTo(SkPoint::Make(0, 0)); // Make p1 unique (i.e., not empty path)
2078 
2079         p.transform(matrix, &p1);
2080         REPORTER_ASSERT(reporter, p1.cheapIsDirection(SkPath::kCCW_Direction));
2081     }
2082 
2083     {
2084         SkMatrix matrix;
2085         matrix.setAll(1, 1, 0, 1, 1, 0, 0, 0, 1);
2086         SkPath p1;
2087         p1.moveTo(SkPoint::Make(0, 0)); // Make p1 unique (i.e., not empty path)
2088 
2089         p.transform(matrix, &p1);
2090         REPORTER_ASSERT(reporter, p1.cheapIsDirection(SkPath::kUnknown_Direction));
2091     }
2092 }
2093 
test_zero_length_paths(skiatest::Reporter * reporter)2094 static void test_zero_length_paths(skiatest::Reporter* reporter) {
2095     SkPath  p;
2096     uint8_t verbs[32];
2097 
2098     struct SUPPRESS_VISIBILITY_WARNING zeroPathTestData {
2099         const char* testPath;
2100         const size_t numResultPts;
2101         const SkRect resultBound;
2102         const SkPath::Verb* resultVerbs;
2103         const size_t numResultVerbs;
2104     };
2105 
2106     static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb };
2107     static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb };
2108     static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb };
2109     static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb };
2110     static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb };
2111     static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb };
2112     static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb };
2113     static const SkPath::Verb resultVerbs8[] = {
2114         SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb
2115     };
2116     static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb };
2117     static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb };
2118     static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb };
2119     static const SkPath::Verb resultVerbs12[] = {
2120         SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb
2121     };
2122     static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb };
2123     static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb };
2124     static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb };
2125     static const SkPath::Verb resultVerbs16[] = {
2126         SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb
2127     };
2128     static const struct zeroPathTestData gZeroLengthTests[] = {
2129         { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
2130         { "M 1 1 M 2 1", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
2131         { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
2132         { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
2133         { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) },
2134         { "M 1 1 L 1 1 M 2 1 L 2 1", 4, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs6, SK_ARRAY_COUNT(resultVerbs6) },
2135         { "M 1 1 L 1 1 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) },
2136         { "M 1 1 L 1 1 z M 2 1 L 2 1 z", 4, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs8, SK_ARRAY_COUNT(resultVerbs8) },
2137         { "M 1 1 Q 1 1 1 1", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) },
2138         { "M 1 1 Q 1 1 1 1 M 2 1 Q 2 1 2 1", 6, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs10, SK_ARRAY_COUNT(resultVerbs10) },
2139         { "M 1 1 Q 1 1 1 1 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) },
2140         { "M 1 1 Q 1 1 1 1 z M 2 1 Q 2 1 2 1 z", 6, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs12, SK_ARRAY_COUNT(resultVerbs12) },
2141         { "M 1 1 C 1 1 1 1 1 1", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) },
2142         { "M 1 1 C 1 1 1 1 1 1 M 2 1 C 2 1 2 1 2 1", 8, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs14,
2143             SK_ARRAY_COUNT(resultVerbs14)
2144         },
2145         { "M 1 1 C 1 1 1 1 1 1 z", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs15, SK_ARRAY_COUNT(resultVerbs15) },
2146         { "M 1 1 C 1 1 1 1 1 1 z M 2 1 C 2 1 2 1 2 1 z", 8, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs16,
2147             SK_ARRAY_COUNT(resultVerbs16)
2148         }
2149     };
2150 
2151     for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) {
2152         p.reset();
2153         bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p);
2154         REPORTER_ASSERT(reporter, valid);
2155         REPORTER_ASSERT(reporter, !p.isEmpty());
2156         REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints());
2157         REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds());
2158         REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
2159         for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) {
2160             REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]);
2161         }
2162     }
2163 }
2164 
2165 struct SegmentInfo {
2166     SkPath fPath;
2167     int    fPointCount;
2168 };
2169 
2170 #define kCurveSegmentMask   (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
2171 
test_segment_masks(skiatest::Reporter * reporter)2172 static void test_segment_masks(skiatest::Reporter* reporter) {
2173     SkPath p, p2;
2174 
2175     p.moveTo(0, 0);
2176     p.quadTo(100, 100, 200, 200);
2177     REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
2178     REPORTER_ASSERT(reporter, !p.isEmpty());
2179     p2 = p;
2180     REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
2181     p.cubicTo(100, 100, 200, 200, 300, 300);
2182     REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
2183     REPORTER_ASSERT(reporter, !p.isEmpty());
2184     p2 = p;
2185     REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
2186 
2187     p.reset();
2188     p.moveTo(0, 0);
2189     p.cubicTo(100, 100, 200, 200, 300, 300);
2190     REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
2191     p2 = p;
2192     REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
2193 
2194     REPORTER_ASSERT(reporter, !p.isEmpty());
2195 }
2196 
test_iter(skiatest::Reporter * reporter)2197 static void test_iter(skiatest::Reporter* reporter) {
2198     SkPath  p;
2199     SkPoint pts[4];
2200 
2201     // Test an iterator with no path
2202     SkPath::Iter noPathIter;
2203     REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
2204 
2205     // Test that setting an empty path works
2206     noPathIter.setPath(p, false);
2207     REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
2208 
2209     // Test that close path makes no difference for an empty path
2210     noPathIter.setPath(p, true);
2211     REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
2212 
2213     // Test an iterator with an initial empty path
2214     SkPath::Iter iter(p, false);
2215     REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
2216 
2217     // Test that close path makes no difference
2218     iter.setPath(p, true);
2219     REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
2220 
2221 
2222     struct iterTestData {
2223         const char* testPath;
2224         const bool forceClose;
2225         const bool consumeDegenerates;
2226         const size_t* numResultPtsPerVerb;
2227         const SkPoint* resultPts;
2228         const SkPath::Verb* resultVerbs;
2229         const size_t numResultVerbs;
2230     };
2231 
2232     static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb };
2233     static const SkPath::Verb resultVerbs2[] = {
2234         SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb
2235     };
2236     static const SkPath::Verb resultVerbs3[] = {
2237         SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
2238     };
2239     static const SkPath::Verb resultVerbs4[] = {
2240         SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
2241     };
2242     static const SkPath::Verb resultVerbs5[] = {
2243         SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
2244     };
2245     static const size_t resultPtsSizes1[] = { 0 };
2246     static const size_t resultPtsSizes2[] = { 1, 2, 2, 0 };
2247     static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1, 0 };
2248     static const size_t resultPtsSizes4[] = { 1, 2, 1, 1, 0 };
2249     static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1, 0 };
2250     static const SkPoint* resultPts1 = 0;
2251     static const SkPoint resultPts2[] = {
2252         { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }
2253     };
2254     static const SkPoint resultPts3[] = {
2255         { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 },
2256         { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }
2257     };
2258     static const SkPoint resultPts4[] = {
2259         { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
2260     };
2261     static const SkPoint resultPts5[] = {
2262         { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
2263     };
2264     static const struct iterTestData gIterTests[] = {
2265         { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
2266         { "M 1 0 M 2 0 M 3 0 M 4 0 M 5 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
2267         { "M 1 0 M 1 0 M 3 0 M 4 0 M 5 0", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
2268         { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
2269         { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
2270         { "z M 1 0 z z M 2 0 z M 3 0 M 4 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
2271         { "z M 1 0 z z M 2 0 z M 3 0 M 4 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
2272         { "M 1 0 L 1 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
2273         { "M 1 0 L 1 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
2274         { "M 1 0 L 1 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
2275         { "M 1 0 L 1 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
2276         { "M 1 0 L 1 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
2277         { "M 1 0 L 1 0 M 0 0 z", true, false, resultPtsSizes5, resultPts5, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) }
2278     };
2279 
2280     for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) {
2281         p.reset();
2282         bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p);
2283         REPORTER_ASSERT(reporter, valid);
2284         iter.setPath(p, gIterTests[i].forceClose);
2285         int j = 0, l = 0;
2286         do {
2287             REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]);
2288             for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) {
2289                 REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]);
2290             }
2291         } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb);
2292         REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs);
2293     }
2294 
2295     p.reset();
2296     iter.setPath(p, false);
2297     REPORTER_ASSERT(reporter, !iter.isClosedContour());
2298     p.lineTo(1, 1);
2299     p.close();
2300     iter.setPath(p, false);
2301     REPORTER_ASSERT(reporter, iter.isClosedContour());
2302     p.reset();
2303     iter.setPath(p, true);
2304     REPORTER_ASSERT(reporter, !iter.isClosedContour());
2305     p.lineTo(1, 1);
2306     iter.setPath(p, true);
2307     REPORTER_ASSERT(reporter, iter.isClosedContour());
2308     p.moveTo(0, 0);
2309     p.lineTo(2, 2);
2310     iter.setPath(p, false);
2311     REPORTER_ASSERT(reporter, !iter.isClosedContour());
2312 
2313     // this checks to see if the NaN logic is executed in SkPath::autoClose(), but does not
2314     // check to see if the result is correct.
2315     for (int setNaN = 0; setNaN < 4; ++setNaN) {
2316         p.reset();
2317         p.moveTo(setNaN == 0 ? SK_ScalarNaN : 0, setNaN == 1 ? SK_ScalarNaN : 0);
2318         p.lineTo(setNaN == 2 ? SK_ScalarNaN : 1, setNaN == 3 ? SK_ScalarNaN : 1);
2319         iter.setPath(p, true);
2320         iter.next(pts, false);
2321         iter.next(pts, false);
2322         REPORTER_ASSERT(reporter, SkPath::kClose_Verb == iter.next(pts, false));
2323     }
2324 
2325     p.reset();
2326     p.quadTo(0, 0, 0, 0);
2327     iter.setPath(p, false);
2328     iter.next(pts, false);
2329     REPORTER_ASSERT(reporter, SkPath::kQuad_Verb == iter.next(pts, false));
2330     iter.setPath(p, false);
2331     iter.next(pts, false);
2332     REPORTER_ASSERT(reporter, SkPath::kDone_Verb == iter.next(pts, true));
2333 
2334     p.reset();
2335     p.conicTo(0, 0, 0, 0, 0.5f);
2336     iter.setPath(p, false);
2337     iter.next(pts, false);
2338     REPORTER_ASSERT(reporter, SkPath::kConic_Verb == iter.next(pts, false));
2339     iter.setPath(p, false);
2340     iter.next(pts, false);
2341     REPORTER_ASSERT(reporter, SkPath::kDone_Verb == iter.next(pts, true));
2342 
2343     p.reset();
2344     p.cubicTo(0, 0, 0, 0, 0, 0);
2345     iter.setPath(p, false);
2346     iter.next(pts, false);
2347     REPORTER_ASSERT(reporter, SkPath::kCubic_Verb == iter.next(pts, false));
2348     iter.setPath(p, false);
2349     iter.next(pts, false);
2350     REPORTER_ASSERT(reporter, SkPath::kDone_Verb == iter.next(pts, true));
2351 
2352     p.moveTo(1, 1);  // add a trailing moveto
2353     iter.setPath(p, false);
2354     iter.next(pts, false);
2355     REPORTER_ASSERT(reporter, SkPath::kCubic_Verb == iter.next(pts, false));
2356     iter.setPath(p, false);
2357     iter.next(pts, false);
2358     REPORTER_ASSERT(reporter, SkPath::kDone_Verb == iter.next(pts, true));
2359 
2360     // The GM degeneratesegments.cpp test is more extensive
2361 }
2362 
test_raw_iter(skiatest::Reporter * reporter)2363 static void test_raw_iter(skiatest::Reporter* reporter) {
2364     SkPath p;
2365     SkPoint pts[4];
2366 
2367     // Test an iterator with no path
2368     SkPath::RawIter noPathIter;
2369     REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
2370     // Test that setting an empty path works
2371     noPathIter.setPath(p);
2372     REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
2373 
2374     // Test an iterator with an initial empty path
2375     SkPath::RawIter iter(p);
2376     REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
2377 
2378     // Test that a move-only path returns the move.
2379     p.moveTo(SK_Scalar1, 0);
2380     iter.setPath(p);
2381     REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
2382     REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
2383     REPORTER_ASSERT(reporter, pts[0].fY == 0);
2384     REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
2385 
2386     // No matter how many moves we add, we should get them all back
2387     p.moveTo(SK_Scalar1*2, SK_Scalar1);
2388     p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
2389     iter.setPath(p);
2390     REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
2391     REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
2392     REPORTER_ASSERT(reporter, pts[0].fY == 0);
2393     REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
2394     REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
2395     REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
2396     REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
2397     REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
2398     REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
2399     REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
2400 
2401     // Initial close is never ever stored
2402     p.reset();
2403     p.close();
2404     iter.setPath(p);
2405     REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
2406 
2407     // Move/close sequences
2408     p.reset();
2409     p.close(); // Not stored, no purpose
2410     p.moveTo(SK_Scalar1, 0);
2411     p.close();
2412     p.close(); // Not stored, no purpose
2413     p.moveTo(SK_Scalar1*2, SK_Scalar1);
2414     p.close();
2415     p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
2416     p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
2417     p.close();
2418     iter.setPath(p);
2419     REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
2420     REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
2421     REPORTER_ASSERT(reporter, pts[0].fY == 0);
2422     REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
2423     REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
2424     REPORTER_ASSERT(reporter, pts[0].fY == 0);
2425     REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
2426     REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
2427     REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
2428     REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
2429     REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
2430     REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
2431     REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
2432     REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
2433     REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
2434     REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
2435     REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
2436     REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
2437     REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
2438     REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
2439     REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
2440     REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
2441 
2442     // Generate random paths and verify
2443     SkPoint randomPts[25];
2444     for (int i = 0; i < 5; ++i) {
2445         for (int j = 0; j < 5; ++j) {
2446             randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
2447         }
2448     }
2449 
2450     // Max of 10 segments, max 3 points per segment
2451     SkRandom rand(9876543);
2452     SkPoint          expectedPts[31]; // May have leading moveTo
2453     SkPath::Verb     expectedVerbs[22]; // May have leading moveTo
2454     SkPath::Verb     nextVerb;
2455 
2456     for (int i = 0; i < 500; ++i) {
2457         p.reset();
2458         bool lastWasClose = true;
2459         bool haveMoveTo = false;
2460         SkPoint lastMoveToPt = { 0, 0 };
2461         int numPoints = 0;
2462         int numVerbs = (rand.nextU() >> 16) % 10;
2463         int numIterVerbs = 0;
2464         for (int j = 0; j < numVerbs; ++j) {
2465             do {
2466                 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
2467             } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
2468             switch (nextVerb) {
2469                 case SkPath::kMove_Verb:
2470                     expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
2471                     p.moveTo(expectedPts[numPoints]);
2472                     lastMoveToPt = expectedPts[numPoints];
2473                     numPoints += 1;
2474                     lastWasClose = false;
2475                     haveMoveTo = true;
2476                     break;
2477                 case SkPath::kLine_Verb:
2478                     if (!haveMoveTo) {
2479                         expectedPts[numPoints++] = lastMoveToPt;
2480                         expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
2481                         haveMoveTo = true;
2482                     }
2483                     expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
2484                     p.lineTo(expectedPts[numPoints]);
2485                     numPoints += 1;
2486                     lastWasClose = false;
2487                     break;
2488                 case SkPath::kQuad_Verb:
2489                     if (!haveMoveTo) {
2490                         expectedPts[numPoints++] = lastMoveToPt;
2491                         expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
2492                         haveMoveTo = true;
2493                     }
2494                     expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
2495                     expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
2496                     p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
2497                     numPoints += 2;
2498                     lastWasClose = false;
2499                     break;
2500                 case SkPath::kConic_Verb:
2501                     if (!haveMoveTo) {
2502                         expectedPts[numPoints++] = lastMoveToPt;
2503                         expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
2504                         haveMoveTo = true;
2505                     }
2506                     expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
2507                     expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
2508                     p.conicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
2509                               rand.nextUScalar1() * 4);
2510                     numPoints += 2;
2511                     lastWasClose = false;
2512                     break;
2513                 case SkPath::kCubic_Verb:
2514                     if (!haveMoveTo) {
2515                         expectedPts[numPoints++] = lastMoveToPt;
2516                         expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
2517                         haveMoveTo = true;
2518                     }
2519                     expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
2520                     expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
2521                     expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
2522                     p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
2523                               expectedPts[numPoints + 2]);
2524                     numPoints += 3;
2525                     lastWasClose = false;
2526                     break;
2527                 case SkPath::kClose_Verb:
2528                     p.close();
2529                     haveMoveTo = false;
2530                     lastWasClose = true;
2531                     break;
2532                 default:
2533                     SkDEBUGFAIL("unexpected verb");
2534             }
2535             expectedVerbs[numIterVerbs++] = nextVerb;
2536         }
2537 
2538         iter.setPath(p);
2539         numVerbs = numIterVerbs;
2540         numIterVerbs = 0;
2541         int numIterPts = 0;
2542         SkPoint lastMoveTo;
2543         SkPoint lastPt;
2544         lastMoveTo.set(0, 0);
2545         lastPt.set(0, 0);
2546         while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
2547             REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
2548             numIterVerbs++;
2549             switch (nextVerb) {
2550                 case SkPath::kMove_Verb:
2551                     REPORTER_ASSERT(reporter, numIterPts < numPoints);
2552                     REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
2553                     lastPt = lastMoveTo = pts[0];
2554                     numIterPts += 1;
2555                     break;
2556                 case SkPath::kLine_Verb:
2557                     REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
2558                     REPORTER_ASSERT(reporter, pts[0] == lastPt);
2559                     REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
2560                     lastPt = pts[1];
2561                     numIterPts += 1;
2562                     break;
2563                 case SkPath::kQuad_Verb:
2564                 case SkPath::kConic_Verb:
2565                     REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
2566                     REPORTER_ASSERT(reporter, pts[0] == lastPt);
2567                     REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
2568                     REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
2569                     lastPt = pts[2];
2570                     numIterPts += 2;
2571                     break;
2572                 case SkPath::kCubic_Verb:
2573                     REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
2574                     REPORTER_ASSERT(reporter, pts[0] == lastPt);
2575                     REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
2576                     REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
2577                     REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
2578                     lastPt = pts[3];
2579                     numIterPts += 3;
2580                     break;
2581                 case SkPath::kClose_Verb:
2582                     REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
2583                     lastPt = lastMoveTo;
2584                     break;
2585                 default:
2586                     SkDEBUGFAIL("unexpected verb");
2587             }
2588         }
2589         REPORTER_ASSERT(reporter, numIterPts == numPoints);
2590         REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
2591     }
2592 }
2593 
check_for_circle(skiatest::Reporter * reporter,const SkPath & path,bool expectedCircle,SkPath::Direction expectedDir)2594 static void check_for_circle(skiatest::Reporter* reporter,
2595                              const SkPath& path,
2596                              bool expectedCircle,
2597                              SkPath::Direction expectedDir) {
2598     SkRect rect = SkRect::MakeEmpty();
2599     REPORTER_ASSERT(reporter, path.isOval(&rect) == expectedCircle);
2600     REPORTER_ASSERT(reporter, path.cheapIsDirection(expectedDir));
2601 
2602     if (expectedCircle) {
2603         REPORTER_ASSERT(reporter, rect.height() == rect.width());
2604     }
2605 }
2606 
test_circle_skew(skiatest::Reporter * reporter,const SkPath & path,SkPath::Direction dir)2607 static void test_circle_skew(skiatest::Reporter* reporter,
2608                              const SkPath& path,
2609                              SkPath::Direction dir) {
2610     SkPath tmp;
2611 
2612     SkMatrix m;
2613     m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
2614     path.transform(m, &tmp);
2615     // this matrix reverses the direction.
2616     if (SkPath::kCCW_Direction == dir) {
2617         dir = SkPath::kCW_Direction;
2618     } else {
2619         REPORTER_ASSERT(reporter, SkPath::kCW_Direction == dir);
2620         dir = SkPath::kCCW_Direction;
2621     }
2622     check_for_circle(reporter, tmp, false, dir);
2623 }
2624 
test_circle_translate(skiatest::Reporter * reporter,const SkPath & path,SkPath::Direction dir)2625 static void test_circle_translate(skiatest::Reporter* reporter,
2626                                   const SkPath& path,
2627                                   SkPath::Direction dir) {
2628     SkPath tmp;
2629 
2630     // translate at small offset
2631     SkMatrix m;
2632     m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
2633     path.transform(m, &tmp);
2634     check_for_circle(reporter, tmp, true, dir);
2635 
2636     tmp.reset();
2637     m.reset();
2638 
2639     // translate at a relatively big offset
2640     m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
2641     path.transform(m, &tmp);
2642     check_for_circle(reporter, tmp, true, dir);
2643 }
2644 
test_circle_rotate(skiatest::Reporter * reporter,const SkPath & path,SkPath::Direction dir)2645 static void test_circle_rotate(skiatest::Reporter* reporter,
2646                                const SkPath& path,
2647                                SkPath::Direction dir) {
2648     for (int angle = 0; angle < 360; ++angle) {
2649         SkPath tmp;
2650         SkMatrix m;
2651         m.setRotate(SkIntToScalar(angle));
2652         path.transform(m, &tmp);
2653 
2654         // TODO: a rotated circle whose rotated angle is not a multiple of 90
2655         // degrees is not an oval anymore, this can be improved.  we made this
2656         // for the simplicity of our implementation.
2657         if (angle % 90 == 0) {
2658             check_for_circle(reporter, tmp, true, dir);
2659         } else {
2660             check_for_circle(reporter, tmp, false, dir);
2661         }
2662     }
2663 }
2664 
test_circle_mirror_x(skiatest::Reporter * reporter,const SkPath & path,SkPath::Direction dir)2665 static void test_circle_mirror_x(skiatest::Reporter* reporter,
2666                                  const SkPath& path,
2667                                  SkPath::Direction dir) {
2668     SkPath tmp;
2669     SkMatrix m;
2670     m.reset();
2671     m.setScaleX(-SK_Scalar1);
2672     path.transform(m, &tmp);
2673 
2674     if (SkPath::kCW_Direction == dir) {
2675         dir = SkPath::kCCW_Direction;
2676     } else {
2677         REPORTER_ASSERT(reporter, SkPath::kCCW_Direction == dir);
2678         dir = SkPath::kCW_Direction;
2679     }
2680 
2681     check_for_circle(reporter, tmp, true, dir);
2682 }
2683 
test_circle_mirror_y(skiatest::Reporter * reporter,const SkPath & path,SkPath::Direction dir)2684 static void test_circle_mirror_y(skiatest::Reporter* reporter,
2685                                  const SkPath& path,
2686                                  SkPath::Direction dir) {
2687     SkPath tmp;
2688     SkMatrix m;
2689     m.reset();
2690     m.setScaleY(-SK_Scalar1);
2691     path.transform(m, &tmp);
2692 
2693     if (SkPath::kCW_Direction == dir) {
2694         dir = SkPath::kCCW_Direction;
2695     } else {
2696         REPORTER_ASSERT(reporter, SkPath::kCCW_Direction == dir);
2697         dir = SkPath::kCW_Direction;
2698     }
2699 
2700     check_for_circle(reporter, tmp, true, dir);
2701 }
2702 
test_circle_mirror_xy(skiatest::Reporter * reporter,const SkPath & path,SkPath::Direction dir)2703 static void test_circle_mirror_xy(skiatest::Reporter* reporter,
2704                                  const SkPath& path,
2705                                  SkPath::Direction dir) {
2706     SkPath tmp;
2707     SkMatrix m;
2708     m.reset();
2709     m.setScaleX(-SK_Scalar1);
2710     m.setScaleY(-SK_Scalar1);
2711     path.transform(m, &tmp);
2712 
2713     check_for_circle(reporter, tmp, true, dir);
2714 }
2715 
test_circle_with_direction(skiatest::Reporter * reporter,SkPath::Direction dir)2716 static void test_circle_with_direction(skiatest::Reporter* reporter,
2717                                        SkPath::Direction dir) {
2718     SkPath path;
2719 
2720     // circle at origin
2721     path.addCircle(0, 0, SkIntToScalar(20), dir);
2722     check_for_circle(reporter, path, true, dir);
2723     test_circle_rotate(reporter, path, dir);
2724     test_circle_translate(reporter, path, dir);
2725     test_circle_skew(reporter, path, dir);
2726 
2727     // circle at an offset at (10, 10)
2728     path.reset();
2729     path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
2730                    SkIntToScalar(20), dir);
2731     check_for_circle(reporter, path, true, dir);
2732     test_circle_rotate(reporter, path, dir);
2733     test_circle_translate(reporter, path, dir);
2734     test_circle_skew(reporter, path, dir);
2735     test_circle_mirror_x(reporter, path, dir);
2736     test_circle_mirror_y(reporter, path, dir);
2737     test_circle_mirror_xy(reporter, path, dir);
2738 }
2739 
test_circle_with_add_paths(skiatest::Reporter * reporter)2740 static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
2741     SkPath path;
2742     SkPath circle;
2743     SkPath rect;
2744     SkPath empty;
2745 
2746     static const SkPath::Direction kCircleDir = SkPath::kCW_Direction;
2747     static const SkPath::Direction kCircleDirOpposite = SkPath::kCCW_Direction;
2748 
2749     circle.addCircle(0, 0, SkIntToScalar(10), kCircleDir);
2750     rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
2751                  SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
2752 
2753     SkMatrix translate;
2754     translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
2755 
2756     // Although all the path concatenation related operations leave
2757     // the path a circle, most mark it as a non-circle for simplicity
2758 
2759     // empty + circle (translate)
2760     path = empty;
2761     path.addPath(circle, translate);
2762     check_for_circle(reporter, path, false, kCircleDir);
2763 
2764     // circle + empty (translate)
2765     path = circle;
2766     path.addPath(empty, translate);
2767     check_for_circle(reporter, path, true, kCircleDir);
2768 
2769     // test reverseAddPath
2770     path = circle;
2771     path.reverseAddPath(rect);
2772     check_for_circle(reporter, path, false, kCircleDirOpposite);
2773 }
2774 
test_circle(skiatest::Reporter * reporter)2775 static void test_circle(skiatest::Reporter* reporter) {
2776     test_circle_with_direction(reporter, SkPath::kCW_Direction);
2777     test_circle_with_direction(reporter, SkPath::kCCW_Direction);
2778 
2779     // multiple addCircle()
2780     SkPath path;
2781     path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2782     path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
2783     check_for_circle(reporter, path, false, SkPath::kCW_Direction);
2784 
2785     // some extra lineTo() would make isOval() fail
2786     path.reset();
2787     path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2788     path.lineTo(0, 0);
2789     check_for_circle(reporter, path, false, SkPath::kCW_Direction);
2790 
2791     // not back to the original point
2792     path.reset();
2793     path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2794     path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
2795     check_for_circle(reporter, path, false, SkPath::kCW_Direction);
2796 
2797     test_circle_with_add_paths(reporter);
2798 
2799     // test negative radius
2800     path.reset();
2801     path.addCircle(0, 0, -1, SkPath::kCW_Direction);
2802     REPORTER_ASSERT(reporter, path.isEmpty());
2803 }
2804 
test_oval(skiatest::Reporter * reporter)2805 static void test_oval(skiatest::Reporter* reporter) {
2806     SkRect rect;
2807     SkMatrix m;
2808     SkPath path;
2809 
2810     rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
2811     path.addOval(rect);
2812 
2813     REPORTER_ASSERT(reporter, path.isOval(NULL));
2814 
2815     m.setRotate(SkIntToScalar(90));
2816     SkPath tmp;
2817     path.transform(m, &tmp);
2818     // an oval rotated 90 degrees is still an oval.
2819     REPORTER_ASSERT(reporter, tmp.isOval(NULL));
2820 
2821     m.reset();
2822     m.setRotate(SkIntToScalar(30));
2823     tmp.reset();
2824     path.transform(m, &tmp);
2825     // an oval rotated 30 degrees is not an oval anymore.
2826     REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2827 
2828     // since empty path being transformed.
2829     path.reset();
2830     tmp.reset();
2831     m.reset();
2832     path.transform(m, &tmp);
2833     REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2834 
2835     // empty path is not an oval
2836     tmp.reset();
2837     REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2838 
2839     // only has moveTo()s
2840     tmp.reset();
2841     tmp.moveTo(0, 0);
2842     tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
2843     REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2844 
2845     // mimic WebKit's calling convention,
2846     // call moveTo() first and then call addOval()
2847     path.reset();
2848     path.moveTo(0, 0);
2849     path.addOval(rect);
2850     REPORTER_ASSERT(reporter, path.isOval(NULL));
2851 
2852     // copy path
2853     path.reset();
2854     tmp.reset();
2855     tmp.addOval(rect);
2856     path = tmp;
2857     REPORTER_ASSERT(reporter, path.isOval(NULL));
2858 }
2859 
test_empty(skiatest::Reporter * reporter,const SkPath & p)2860 static void test_empty(skiatest::Reporter* reporter, const SkPath& p) {
2861     SkPath  empty;
2862 
2863     REPORTER_ASSERT(reporter, p.isEmpty());
2864     REPORTER_ASSERT(reporter, 0 == p.countPoints());
2865     REPORTER_ASSERT(reporter, 0 == p.countVerbs());
2866     REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
2867     REPORTER_ASSERT(reporter, p.isConvex());
2868     REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
2869     REPORTER_ASSERT(reporter, !p.isInverseFillType());
2870     REPORTER_ASSERT(reporter, p == empty);
2871     REPORTER_ASSERT(reporter, !(p != empty));
2872 }
2873 
test_rrect_is_convex(skiatest::Reporter * reporter,SkPath * path,SkPath::Direction dir)2874 static void test_rrect_is_convex(skiatest::Reporter* reporter, SkPath* path,
2875                                  SkPath::Direction dir) {
2876     REPORTER_ASSERT(reporter, path->isConvex());
2877     REPORTER_ASSERT(reporter, path->cheapIsDirection(dir));
2878     path->setConvexity(SkPath::kUnknown_Convexity);
2879     REPORTER_ASSERT(reporter, path->isConvex());
2880     path->reset();
2881 }
2882 
test_rrect(skiatest::Reporter * reporter)2883 static void test_rrect(skiatest::Reporter* reporter) {
2884     SkPath p;
2885     SkRRect rr;
2886     SkVector radii[] = {{1, 2}, {3, 4}, {5, 6}, {7, 8}};
2887     SkRect r = {10, 20, 30, 40};
2888     rr.setRectRadii(r, radii);
2889     p.addRRect(rr);
2890     test_rrect_is_convex(reporter, &p, SkPath::kCW_Direction);
2891     p.addRRect(rr, SkPath::kCCW_Direction);
2892     test_rrect_is_convex(reporter, &p, SkPath::kCCW_Direction);
2893     p.addRoundRect(r, &radii[0].fX);
2894     test_rrect_is_convex(reporter, &p, SkPath::kCW_Direction);
2895     p.addRoundRect(r, &radii[0].fX, SkPath::kCCW_Direction);
2896     test_rrect_is_convex(reporter, &p, SkPath::kCCW_Direction);
2897     p.addRoundRect(r, radii[1].fX, radii[1].fY);
2898     test_rrect_is_convex(reporter, &p, SkPath::kCW_Direction);
2899     p.addRoundRect(r, radii[1].fX, radii[1].fY, SkPath::kCCW_Direction);
2900     test_rrect_is_convex(reporter, &p, SkPath::kCCW_Direction);
2901     for (size_t i = 0; i < SK_ARRAY_COUNT(radii); ++i) {
2902         SkVector save = radii[i];
2903         radii[i].set(0, 0);
2904         rr.setRectRadii(r, radii);
2905         p.addRRect(rr);
2906         test_rrect_is_convex(reporter, &p, SkPath::kCW_Direction);
2907         radii[i] = save;
2908     }
2909     p.addRoundRect(r, 0, 0);
2910     SkRect returnedRect;
2911     REPORTER_ASSERT(reporter, p.isRect(&returnedRect));
2912     REPORTER_ASSERT(reporter, returnedRect == r);
2913     test_rrect_is_convex(reporter, &p, SkPath::kCW_Direction);
2914     SkVector zeroRadii[] = {{0, 0}, {0, 0}, {0, 0}, {0, 0}};
2915     rr.setRectRadii(r, zeroRadii);
2916     p.addRRect(rr);
2917     bool closed;
2918     SkPath::Direction dir;
2919     REPORTER_ASSERT(reporter, p.isRect(&closed, &dir));
2920     REPORTER_ASSERT(reporter, closed);
2921     REPORTER_ASSERT(reporter, SkPath::kCW_Direction == dir);
2922     test_rrect_is_convex(reporter, &p, SkPath::kCW_Direction);
2923     p.addRRect(rr, SkPath::kCW_Direction);
2924     p.addRRect(rr, SkPath::kCW_Direction);
2925     REPORTER_ASSERT(reporter, !p.isConvex());
2926     p.reset();
2927     p.addRRect(rr, SkPath::kCCW_Direction);
2928     p.addRRect(rr, SkPath::kCCW_Direction);
2929     REPORTER_ASSERT(reporter, !p.isConvex());
2930     p.reset();
2931     SkRect emptyR = {10, 20, 10, 30};
2932     rr.setRectRadii(emptyR, radii);
2933     p.addRRect(rr);
2934     REPORTER_ASSERT(reporter, p.isEmpty());
2935     SkRect largeR = {0, 0, SK_ScalarMax, SK_ScalarMax};
2936     rr.setRectRadii(largeR, radii);
2937     p.addRRect(rr);
2938     test_rrect_is_convex(reporter, &p, SkPath::kCW_Direction);
2939     SkRect infR = {0, 0, SK_ScalarMax, SK_ScalarInfinity};
2940     rr.setRectRadii(infR, radii);
2941     p.addRRect(rr);
2942     test_rrect_is_convex(reporter, &p, SkPath::kCW_Direction);
2943     SkRect tinyR = {0, 0, 1e-9f, 1e-9f};
2944     p.addRoundRect(tinyR, 5e-11f, 5e-11f);
2945     test_rrect_is_convex(reporter, &p, SkPath::kCW_Direction);
2946 }
2947 
test_arc(skiatest::Reporter * reporter)2948 static void test_arc(skiatest::Reporter* reporter) {
2949     SkPath p;
2950     SkRect emptyOval = {10, 20, 30, 20};
2951     REPORTER_ASSERT(reporter, emptyOval.isEmpty());
2952     p.addArc(emptyOval, 1, 2);
2953     REPORTER_ASSERT(reporter, p.isEmpty());
2954     p.reset();
2955     SkRect oval = {10, 20, 30, 40};
2956     p.addArc(oval, 1, 0);
2957     REPORTER_ASSERT(reporter, p.isEmpty());
2958     p.reset();
2959     SkPath cwOval;
2960     cwOval.addOval(oval);
2961     p.addArc(oval, 1, 360);
2962     REPORTER_ASSERT(reporter, p == cwOval);
2963     p.reset();
2964     SkPath ccwOval;
2965     ccwOval.addOval(oval, SkPath::kCCW_Direction);
2966     p.addArc(oval, 1, -360);
2967     REPORTER_ASSERT(reporter, p == ccwOval);
2968     p.reset();
2969     p.addArc(oval, 1, 180);
2970     REPORTER_ASSERT(reporter, p.isConvex());
2971     REPORTER_ASSERT(reporter, p.cheapIsDirection(SkPath::kCW_Direction));
2972     p.setConvexity(SkPath::kUnknown_Convexity);
2973     REPORTER_ASSERT(reporter, p.isConvex());
2974 }
2975 
check_move(skiatest::Reporter * reporter,SkPath::RawIter * iter,SkScalar x0,SkScalar y0)2976 static void check_move(skiatest::Reporter* reporter, SkPath::RawIter* iter,
2977                        SkScalar x0, SkScalar y0) {
2978     SkPoint pts[4];
2979     SkPath::Verb v = iter->next(pts);
2980     REPORTER_ASSERT(reporter, v == SkPath::kMove_Verb);
2981     REPORTER_ASSERT(reporter, pts[0].fX == x0);
2982     REPORTER_ASSERT(reporter, pts[0].fY == y0);
2983 }
2984 
check_line(skiatest::Reporter * reporter,SkPath::RawIter * iter,SkScalar x1,SkScalar y1)2985 static void check_line(skiatest::Reporter* reporter, SkPath::RawIter* iter,
2986                        SkScalar x1, SkScalar y1) {
2987     SkPoint pts[4];
2988     SkPath::Verb v = iter->next(pts);
2989     REPORTER_ASSERT(reporter, v == SkPath::kLine_Verb);
2990     REPORTER_ASSERT(reporter, pts[1].fX == x1);
2991     REPORTER_ASSERT(reporter, pts[1].fY == y1);
2992 }
2993 
check_quad(skiatest::Reporter * reporter,SkPath::RawIter * iter,SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2)2994 static void check_quad(skiatest::Reporter* reporter, SkPath::RawIter* iter,
2995                        SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
2996     SkPoint pts[4];
2997     SkPath::Verb v = iter->next(pts);
2998     REPORTER_ASSERT(reporter, v == SkPath::kQuad_Verb);
2999     REPORTER_ASSERT(reporter, pts[1].fX == x1);
3000     REPORTER_ASSERT(reporter, pts[1].fY == y1);
3001     REPORTER_ASSERT(reporter, pts[2].fX == x2);
3002     REPORTER_ASSERT(reporter, pts[2].fY == y2);
3003 }
3004 
check_done(skiatest::Reporter * reporter,SkPath * p,SkPath::RawIter * iter)3005 static void check_done(skiatest::Reporter* reporter, SkPath* p, SkPath::RawIter* iter) {
3006     SkPoint pts[4];
3007     SkPath::Verb v = iter->next(pts);
3008     REPORTER_ASSERT(reporter, v == SkPath::kDone_Verb);
3009 }
3010 
check_done_and_reset(skiatest::Reporter * reporter,SkPath * p,SkPath::RawIter * iter)3011 static void check_done_and_reset(skiatest::Reporter* reporter, SkPath* p, SkPath::RawIter* iter) {
3012     check_done(reporter, p, iter);
3013     p->reset();
3014 }
3015 
check_path_is_move_and_reset(skiatest::Reporter * reporter,SkPath * p,SkScalar x0,SkScalar y0)3016 static void check_path_is_move_and_reset(skiatest::Reporter* reporter, SkPath* p,
3017                                          SkScalar x0, SkScalar y0) {
3018     SkPath::RawIter iter(*p);
3019     check_move(reporter, &iter, x0, y0);
3020     check_done_and_reset(reporter, p, &iter);
3021 }
3022 
check_path_is_line_and_reset(skiatest::Reporter * reporter,SkPath * p,SkScalar x1,SkScalar y1)3023 static void check_path_is_line_and_reset(skiatest::Reporter* reporter, SkPath* p,
3024                                          SkScalar x1, SkScalar y1) {
3025     SkPath::RawIter iter(*p);
3026     check_move(reporter, &iter, 0, 0);
3027     check_line(reporter, &iter, x1, y1);
3028     check_done_and_reset(reporter, p, &iter);
3029 }
3030 
check_path_is_line(skiatest::Reporter * reporter,SkPath * p,SkScalar x1,SkScalar y1)3031 static void check_path_is_line(skiatest::Reporter* reporter, SkPath* p,
3032                                          SkScalar x1, SkScalar y1) {
3033     SkPath::RawIter iter(*p);
3034     check_move(reporter, &iter, 0, 0);
3035     check_line(reporter, &iter, x1, y1);
3036     check_done(reporter, p, &iter);
3037 }
3038 
check_path_is_line_pair_and_reset(skiatest::Reporter * reporter,SkPath * p,SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2)3039 static void check_path_is_line_pair_and_reset(skiatest::Reporter* reporter, SkPath* p,
3040                                     SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
3041     SkPath::RawIter iter(*p);
3042     check_move(reporter, &iter, 0, 0);
3043     check_line(reporter, &iter, x1, y1);
3044     check_line(reporter, &iter, x2, y2);
3045     check_done_and_reset(reporter, p, &iter);
3046 }
3047 
check_path_is_quad_and_reset(skiatest::Reporter * reporter,SkPath * p,SkScalar x1,SkScalar y1,SkScalar x2,SkScalar y2)3048 static void check_path_is_quad_and_reset(skiatest::Reporter* reporter, SkPath* p,
3049                                     SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
3050     SkPath::RawIter iter(*p);
3051     check_move(reporter, &iter, 0, 0);
3052     check_quad(reporter, &iter, x1, y1, x2, y2);
3053     check_done_and_reset(reporter, p, &iter);
3054 }
3055 
test_arcTo(skiatest::Reporter * reporter)3056 static void test_arcTo(skiatest::Reporter* reporter) {
3057     SkPath p;
3058     p.arcTo(0, 0, 1, 2, 1);
3059     check_path_is_line_and_reset(reporter, &p, 0, 0);
3060     p.arcTo(1, 2, 1, 2, 1);
3061     check_path_is_line_and_reset(reporter, &p, 1, 2);
3062     p.arcTo(1, 2, 3, 4, 0);
3063     check_path_is_line_and_reset(reporter, &p, 1, 2);
3064     p.arcTo(1, 2, 0, 0, 1);
3065     check_path_is_line_and_reset(reporter, &p, 1, 2);
3066     p.arcTo(1, 0, 1, 1, 1);
3067     SkPoint pt;
3068     REPORTER_ASSERT(reporter, p.getLastPt(&pt) && pt.fX == 1 && pt.fY == 1);
3069     p.reset();
3070     p.arcTo(1, 0, 1, -1, 1);
3071     REPORTER_ASSERT(reporter, p.getLastPt(&pt) && pt.fX == 1 && pt.fY == -1);
3072     p.reset();
3073     SkRect oval = {1, 2, 3, 4};
3074     p.arcTo(oval, 0, 0, true);
3075     check_path_is_move_and_reset(reporter, &p, oval.fRight, oval.centerY());
3076     p.arcTo(oval, 0, 0, false);
3077     check_path_is_move_and_reset(reporter, &p, oval.fRight, oval.centerY());
3078     p.arcTo(oval, 360, 0, true);
3079     check_path_is_move_and_reset(reporter, &p, oval.fRight, oval.centerY());
3080     p.arcTo(oval, 360, 0, false);
3081     check_path_is_move_and_reset(reporter, &p, oval.fRight, oval.centerY());
3082     for (float sweep = 359, delta = 0.5f; sweep != (float) (sweep + delta); ) {
3083         p.arcTo(oval, 0, sweep, false);
3084         REPORTER_ASSERT(reporter, p.getBounds() == oval);
3085         sweep += delta;
3086         delta /= 2;
3087     }
3088     for (float sweep = 361, delta = 0.5f; sweep != (float) (sweep - delta);) {
3089         p.arcTo(oval, 0, sweep, false);
3090         REPORTER_ASSERT(reporter, p.getBounds() == oval);
3091         sweep -= delta;
3092         delta /= 2;
3093     }
3094     SkRect noOvalWidth = {1, 2, 0, 3};
3095     p.reset();
3096     p.arcTo(noOvalWidth, 0, 360, false);
3097     REPORTER_ASSERT(reporter, p.isEmpty());
3098 
3099     SkRect noOvalHeight = {1, 2, 3, 1};
3100     p.reset();
3101     p.arcTo(noOvalHeight, 0, 360, false);
3102     REPORTER_ASSERT(reporter, p.isEmpty());
3103 }
3104 
test_addPath(skiatest::Reporter * reporter)3105 static void test_addPath(skiatest::Reporter* reporter) {
3106     SkPath p, q;
3107     p.lineTo(1, 2);
3108     q.moveTo(4, 4);
3109     q.lineTo(7, 8);
3110     q.conicTo(8, 7, 6, 5, 0.5f);
3111     q.quadTo(6, 7, 8, 6);
3112     q.cubicTo(5, 6, 7, 8, 7, 5);
3113     q.close();
3114     p.addPath(q, -4, -4);
3115     SkRect expected = {0, 0, 4, 4};
3116     REPORTER_ASSERT(reporter, p.getBounds() == expected);
3117     p.reset();
3118     p.reverseAddPath(q);
3119     SkRect reverseExpected = {4, 4, 8, 8};
3120     REPORTER_ASSERT(reporter, p.getBounds() == reverseExpected);
3121 }
3122 
test_addPathMode(skiatest::Reporter * reporter,bool explicitMoveTo,bool extend)3123 static void test_addPathMode(skiatest::Reporter* reporter, bool explicitMoveTo, bool extend) {
3124     SkPath p, q;
3125     if (explicitMoveTo) {
3126         p.moveTo(1, 1);
3127     }
3128     p.lineTo(1, 2);
3129     if (explicitMoveTo) {
3130         q.moveTo(2, 1);
3131     }
3132     q.lineTo(2, 2);
3133     p.addPath(q, extend ? SkPath::kExtend_AddPathMode : SkPath::kAppend_AddPathMode);
3134     uint8_t verbs[4];
3135     int verbcount = p.getVerbs(verbs, 4);
3136     REPORTER_ASSERT(reporter, verbcount == 4);
3137     REPORTER_ASSERT(reporter, verbs[0] == SkPath::kMove_Verb);
3138     REPORTER_ASSERT(reporter, verbs[1] == SkPath::kLine_Verb);
3139     REPORTER_ASSERT(reporter, verbs[2] == (extend ? SkPath::kLine_Verb : SkPath::kMove_Verb));
3140     REPORTER_ASSERT(reporter, verbs[3] == SkPath::kLine_Verb);
3141 }
3142 
test_extendClosedPath(skiatest::Reporter * reporter)3143 static void test_extendClosedPath(skiatest::Reporter* reporter) {
3144     SkPath p, q;
3145     p.moveTo(1, 1);
3146     p.lineTo(1, 2);
3147     p.lineTo(2, 2);
3148     p.close();
3149     q.moveTo(2, 1);
3150     q.lineTo(2, 3);
3151     p.addPath(q, SkPath::kExtend_AddPathMode);
3152     uint8_t verbs[7];
3153     int verbcount = p.getVerbs(verbs, 7);
3154     REPORTER_ASSERT(reporter, verbcount == 7);
3155     REPORTER_ASSERT(reporter, verbs[0] == SkPath::kMove_Verb);
3156     REPORTER_ASSERT(reporter, verbs[1] == SkPath::kLine_Verb);
3157     REPORTER_ASSERT(reporter, verbs[2] == SkPath::kLine_Verb);
3158     REPORTER_ASSERT(reporter, verbs[3] == SkPath::kClose_Verb);
3159     REPORTER_ASSERT(reporter, verbs[4] == SkPath::kMove_Verb);
3160     REPORTER_ASSERT(reporter, verbs[5] == SkPath::kLine_Verb);
3161     REPORTER_ASSERT(reporter, verbs[6] == SkPath::kLine_Verb);
3162 
3163     SkPoint pt;
3164     REPORTER_ASSERT(reporter, p.getLastPt(&pt));
3165     REPORTER_ASSERT(reporter, pt == SkPoint::Make(2, 3));
3166     REPORTER_ASSERT(reporter, p.getPoint(3) == SkPoint::Make(1, 1));
3167 }
3168 
test_addEmptyPath(skiatest::Reporter * reporter,SkPath::AddPathMode mode)3169 static void test_addEmptyPath(skiatest::Reporter* reporter, SkPath::AddPathMode mode) {
3170     SkPath p, q, r;
3171     // case 1: dst is empty
3172     p.moveTo(2, 1);
3173     p.lineTo(2, 3);
3174     q.addPath(p, mode);
3175     REPORTER_ASSERT(reporter, q == p);
3176     // case 2: src is empty
3177     p.addPath(r, mode);
3178     REPORTER_ASSERT(reporter, q == p);
3179     // case 3: src and dst are empty
3180     q.reset();
3181     q.addPath(r, mode);
3182     REPORTER_ASSERT(reporter, q.isEmpty());
3183 }
3184 
test_conicTo_special_case(skiatest::Reporter * reporter)3185 static void test_conicTo_special_case(skiatest::Reporter* reporter) {
3186     SkPath p;
3187     p.conicTo(1, 2, 3, 4, -1);
3188     check_path_is_line_and_reset(reporter, &p, 3, 4);
3189     p.conicTo(1, 2, 3, 4, SK_ScalarInfinity);
3190     check_path_is_line_pair_and_reset(reporter, &p, 1, 2, 3, 4);
3191     p.conicTo(1, 2, 3, 4, 1);
3192     check_path_is_quad_and_reset(reporter, &p, 1, 2, 3, 4);
3193 }
3194 
test_get_point(skiatest::Reporter * reporter)3195 static void test_get_point(skiatest::Reporter* reporter) {
3196     SkPath p;
3197     SkPoint pt = p.getPoint(0);
3198     REPORTER_ASSERT(reporter, pt == SkPoint::Make(0, 0));
3199     REPORTER_ASSERT(reporter, !p.getLastPt(NULL));
3200     REPORTER_ASSERT(reporter, !p.getLastPt(&pt) && pt == SkPoint::Make(0, 0));
3201     p.setLastPt(10, 10);
3202     pt = p.getPoint(0);
3203     REPORTER_ASSERT(reporter, pt == SkPoint::Make(10, 10));
3204     REPORTER_ASSERT(reporter, p.getLastPt(NULL));
3205     p.rMoveTo(10, 10);
3206     REPORTER_ASSERT(reporter, p.getLastPt(&pt) && pt == SkPoint::Make(20, 20));
3207 }
3208 
test_contains(skiatest::Reporter * reporter)3209 static void test_contains(skiatest::Reporter* reporter) {
3210     SkPath p;
3211     p.setFillType(SkPath::kInverseWinding_FillType);
3212     REPORTER_ASSERT(reporter, p.contains(0, 0));
3213     p.setFillType(SkPath::kWinding_FillType);
3214     REPORTER_ASSERT(reporter, !p.contains(0, 0));
3215     p.moveTo(4, 4);
3216     p.lineTo(6, 8);
3217     p.lineTo(8, 4);
3218     // test quick reject
3219     REPORTER_ASSERT(reporter, !p.contains(4, 0));
3220     REPORTER_ASSERT(reporter, !p.contains(0, 4));
3221     REPORTER_ASSERT(reporter, !p.contains(4, 10));
3222     REPORTER_ASSERT(reporter, !p.contains(10, 4));
3223     // test various crossings in x
3224     REPORTER_ASSERT(reporter, !p.contains(5, 7));
3225     REPORTER_ASSERT(reporter, p.contains(6, 7));
3226     REPORTER_ASSERT(reporter, !p.contains(7, 7));
3227     p.reset();
3228     p.moveTo(4, 4);
3229     p.lineTo(8, 6);
3230     p.lineTo(4, 8);
3231     // test various crossings in y
3232     REPORTER_ASSERT(reporter, !p.contains(7, 5));
3233     REPORTER_ASSERT(reporter, p.contains(7, 6));
3234     REPORTER_ASSERT(reporter, !p.contains(7, 7));
3235     // test quads
3236     p.reset();
3237     p.moveTo(4, 4);
3238     p.quadTo(6, 6, 8, 8);
3239     p.quadTo(6, 8, 4, 8);
3240     p.quadTo(4, 6, 4, 4);
3241     REPORTER_ASSERT(reporter, p.contains(5, 6));
3242     REPORTER_ASSERT(reporter, !p.contains(6, 5));
3243 
3244     p.reset();
3245     p.moveTo(6, 6);
3246     p.quadTo(8, 8, 6, 8);
3247     p.quadTo(4, 8, 4, 6);
3248     p.quadTo(4, 4, 6, 6);
3249     REPORTER_ASSERT(reporter, p.contains(5, 6));
3250     REPORTER_ASSERT(reporter, !p.contains(6, 5));
3251 
3252 #define CONIC_CONTAINS_BUG_FIXED 0
3253 #if CONIC_CONTAINS_BUG_FIXED
3254     p.reset();
3255     p.moveTo(4, 4);
3256     p.conicTo(6, 6, 8, 8, 0.5f);
3257     p.conicTo(6, 8, 4, 8, 0.5f);
3258     p.conicTo(4, 6, 4, 4, 0.5f);
3259     REPORTER_ASSERT(reporter, p.contains(5, 6));
3260     REPORTER_ASSERT(reporter, !p.contains(6, 5));
3261 #endif
3262 
3263     // test cubics
3264     SkPoint pts[] = {{5, 4}, {6, 5}, {7, 6}, {6, 6}, {4, 6}, {5, 7}, {5, 5}, {5, 4}, {6, 5}, {7, 6}};
3265     for (int i = 0; i < 3; ++i) {
3266         p.reset();
3267         p.setFillType(SkPath::kEvenOdd_FillType);
3268         p.moveTo(pts[i].fX, pts[i].fY);
3269         p.cubicTo(pts[i + 1].fX, pts[i + 1].fY, pts[i + 2].fX, pts[i + 2].fY, pts[i + 3].fX, pts[i + 3].fY);
3270         p.cubicTo(pts[i + 4].fX, pts[i + 4].fY, pts[i + 5].fX, pts[i + 5].fY, pts[i + 6].fX, pts[i + 6].fY);
3271         p.close();
3272         REPORTER_ASSERT(reporter, p.contains(5.5f, 5.5f));
3273         REPORTER_ASSERT(reporter, !p.contains(4.5f, 5.5f));
3274     }
3275 }
3276 
3277 class PathRefTest_Private {
3278 public:
TestPathRef(skiatest::Reporter * reporter)3279     static void TestPathRef(skiatest::Reporter* reporter) {
3280         static const int kRepeatCnt = 10;
3281 
3282         SkAutoTUnref<SkPathRef> pathRef(SkNEW(SkPathRef));
3283 
3284         SkPathRef::Editor ed(&pathRef);
3285 
3286         {
3287             ed.growForRepeatedVerb(SkPath::kMove_Verb, kRepeatCnt);
3288             REPORTER_ASSERT(reporter, kRepeatCnt == pathRef->countVerbs());
3289             REPORTER_ASSERT(reporter, kRepeatCnt == pathRef->countPoints());
3290             REPORTER_ASSERT(reporter, 0 == pathRef->getSegmentMasks());
3291             for (int i = 0; i < kRepeatCnt; ++i) {
3292                 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == pathRef->atVerb(i));
3293             }
3294             ed.resetToSize(0, 0, 0);
3295         }
3296 
3297         {
3298             ed.growForRepeatedVerb(SkPath::kLine_Verb, kRepeatCnt);
3299             REPORTER_ASSERT(reporter, kRepeatCnt == pathRef->countVerbs());
3300             REPORTER_ASSERT(reporter, kRepeatCnt == pathRef->countPoints());
3301             REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == pathRef->getSegmentMasks());
3302             for (int i = 0; i < kRepeatCnt; ++i) {
3303                 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == pathRef->atVerb(i));
3304             }
3305             ed.resetToSize(0, 0, 0);
3306         }
3307 
3308         {
3309             ed.growForRepeatedVerb(SkPath::kQuad_Verb, kRepeatCnt);
3310             REPORTER_ASSERT(reporter, kRepeatCnt == pathRef->countVerbs());
3311             REPORTER_ASSERT(reporter, 2*kRepeatCnt == pathRef->countPoints());
3312             REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == pathRef->getSegmentMasks());
3313             for (int i = 0; i < kRepeatCnt; ++i) {
3314                 REPORTER_ASSERT(reporter, SkPath::kQuad_Verb == pathRef->atVerb(i));
3315             }
3316             ed.resetToSize(0, 0, 0);
3317         }
3318 
3319         {
3320             SkScalar* weights = NULL;
3321             ed.growForRepeatedVerb(SkPath::kConic_Verb, kRepeatCnt, &weights);
3322             REPORTER_ASSERT(reporter, kRepeatCnt == pathRef->countVerbs());
3323             REPORTER_ASSERT(reporter, 2*kRepeatCnt == pathRef->countPoints());
3324             REPORTER_ASSERT(reporter, kRepeatCnt == pathRef->countWeights());
3325             REPORTER_ASSERT(reporter, SkPath::kConic_SegmentMask == pathRef->getSegmentMasks());
3326             REPORTER_ASSERT(reporter, NULL != weights);
3327             for (int i = 0; i < kRepeatCnt; ++i) {
3328                 REPORTER_ASSERT(reporter, SkPath::kConic_Verb == pathRef->atVerb(i));
3329             }
3330             ed.resetToSize(0, 0, 0);
3331         }
3332 
3333         {
3334             ed.growForRepeatedVerb(SkPath::kCubic_Verb, kRepeatCnt);
3335             REPORTER_ASSERT(reporter, kRepeatCnt == pathRef->countVerbs());
3336             REPORTER_ASSERT(reporter, 3*kRepeatCnt == pathRef->countPoints());
3337             REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == pathRef->getSegmentMasks());
3338             for (int i = 0; i < kRepeatCnt; ++i) {
3339                 REPORTER_ASSERT(reporter, SkPath::kCubic_Verb == pathRef->atVerb(i));
3340             }
3341             ed.resetToSize(0, 0, 0);
3342         }
3343     }
3344 };
3345 
test_operatorEqual(skiatest::Reporter * reporter)3346 static void test_operatorEqual(skiatest::Reporter* reporter) {
3347     SkPath a;
3348     SkPath b;
3349     REPORTER_ASSERT(reporter, a == a);
3350     REPORTER_ASSERT(reporter, a == b);
3351     a.setFillType(SkPath::kInverseWinding_FillType);
3352     REPORTER_ASSERT(reporter, a != b);
3353     a.reset();
3354     REPORTER_ASSERT(reporter, a == b);
3355     a.lineTo(1, 1);
3356     REPORTER_ASSERT(reporter, a != b);
3357     a.reset();
3358     REPORTER_ASSERT(reporter, a == b);
3359     a.lineTo(1, 1);
3360     b.lineTo(1, 2);
3361     REPORTER_ASSERT(reporter, a != b);
3362     a.reset();
3363     a.lineTo(1, 2);
3364     REPORTER_ASSERT(reporter, a == b);
3365 }
3366 
3367 class PathTest_Private {
3368 public:
TestPathTo(skiatest::Reporter * reporter)3369     static void TestPathTo(skiatest::Reporter* reporter) {
3370         SkPath p, q;
3371         p.lineTo(4, 4);
3372         p.reversePathTo(q);
3373         check_path_is_line(reporter, &p, 4, 4);
3374         q.moveTo(-4, -4);
3375         p.reversePathTo(q);
3376         check_path_is_line(reporter, &p, 4, 4);
3377         q.lineTo(7, 8);
3378         q.conicTo(8, 7, 6, 5, 0.5f);
3379         q.quadTo(6, 7, 8, 6);
3380         q.cubicTo(5, 6, 7, 8, 7, 5);
3381         q.close();
3382         p.reversePathTo(q);
3383         SkRect reverseExpected = {-4, -4, 8, 8};
3384         REPORTER_ASSERT(reporter, p.getBounds() == reverseExpected);
3385     }
3386 };
3387 
DEF_TEST(Paths,reporter)3388 DEF_TEST(Paths, reporter) {
3389     test_path_crbug364224();
3390 
3391     SkTSize<SkScalar>::Make(3,4);
3392 
3393     SkPath  p, empty;
3394     SkRect  bounds, bounds2;
3395     test_empty(reporter, p);
3396 
3397     REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
3398 
3399     // this triggers a code path in SkPath::operator= which is otherwise unexercised
3400     SkPath& self = p;
3401     p = self;
3402 
3403     // this triggers a code path in SkPath::swap which is otherwise unexercised
3404     p.swap(self);
3405 
3406     bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
3407 
3408     p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
3409     check_convex_bounds(reporter, p, bounds);
3410     // we have quads or cubics
3411     REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
3412     REPORTER_ASSERT(reporter, !p.isEmpty());
3413 
3414     p.reset();
3415     test_empty(reporter, p);
3416 
3417     p.addOval(bounds);
3418     check_convex_bounds(reporter, p, bounds);
3419     REPORTER_ASSERT(reporter, !p.isEmpty());
3420 
3421     p.rewind();
3422     test_empty(reporter, p);
3423 
3424     p.addRect(bounds);
3425     check_convex_bounds(reporter, p, bounds);
3426     // we have only lines
3427     REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
3428     REPORTER_ASSERT(reporter, !p.isEmpty());
3429 
3430     REPORTER_ASSERT(reporter, p != empty);
3431     REPORTER_ASSERT(reporter, !(p == empty));
3432 
3433     // do getPoints and getVerbs return the right result
3434     REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
3435     REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
3436     SkPoint pts[4];
3437     int count = p.getPoints(pts, 4);
3438     REPORTER_ASSERT(reporter, count == 4);
3439     uint8_t verbs[6];
3440     verbs[5] = 0xff;
3441     p.getVerbs(verbs, 5);
3442     REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
3443     REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
3444     REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
3445     REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
3446     REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
3447     REPORTER_ASSERT(reporter, 0xff == verbs[5]);
3448     bounds2.set(pts, 4);
3449     REPORTER_ASSERT(reporter, bounds == bounds2);
3450 
3451     bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
3452     p.offset(SK_Scalar1*3, SK_Scalar1*4);
3453     REPORTER_ASSERT(reporter, bounds == p.getBounds());
3454 
3455     REPORTER_ASSERT(reporter, p.isRect(NULL));
3456     bounds2.setEmpty();
3457     REPORTER_ASSERT(reporter, p.isRect(&bounds2));
3458     REPORTER_ASSERT(reporter, bounds == bounds2);
3459 
3460     // now force p to not be a rect
3461     bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
3462     p.addRect(bounds);
3463     REPORTER_ASSERT(reporter, !p.isRect(NULL));
3464 
3465     test_operatorEqual(reporter);
3466     test_isLine(reporter);
3467     test_isRect(reporter);
3468     test_isNestedRects(reporter);
3469     test_zero_length_paths(reporter);
3470     test_direction(reporter);
3471     test_convexity(reporter);
3472     test_convexity2(reporter);
3473     test_conservativelyContains(reporter);
3474     test_close(reporter);
3475     test_segment_masks(reporter);
3476     test_flattening(reporter);
3477     test_transform(reporter);
3478     test_bounds(reporter);
3479     test_iter(reporter);
3480     test_raw_iter(reporter);
3481     test_circle(reporter);
3482     test_oval(reporter);
3483     test_strokerec(reporter);
3484     test_addPoly(reporter);
3485     test_isfinite(reporter);
3486     test_isfinite_after_transform(reporter);
3487     test_arb_round_rect_is_convex(reporter);
3488     test_arb_zero_rad_round_rect_is_rect(reporter);
3489     test_addrect(reporter);
3490     test_addrect_isfinite(reporter);
3491     test_tricky_cubic();
3492     test_clipped_cubic();
3493     test_crbug_170666();
3494     test_bad_cubic_crbug229478();
3495     test_bad_cubic_crbug234190();
3496     test_android_specific_behavior(reporter);
3497     test_gen_id(reporter);
3498     test_path_close_issue1474(reporter);
3499     test_path_to_region(reporter);
3500     test_rrect(reporter);
3501     test_arc(reporter);
3502     test_arcTo(reporter);
3503     test_addPath(reporter);
3504     test_addPathMode(reporter, false, false);
3505     test_addPathMode(reporter, true, false);
3506     test_addPathMode(reporter, false, true);
3507     test_addPathMode(reporter, true, true);
3508     test_extendClosedPath(reporter);
3509     test_addEmptyPath(reporter, SkPath::kExtend_AddPathMode);
3510     test_addEmptyPath(reporter, SkPath::kAppend_AddPathMode);
3511     test_conicTo_special_case(reporter);
3512     test_get_point(reporter);
3513     test_contains(reporter);
3514     PathTest_Private::TestPathTo(reporter);
3515     PathRefTest_Private::TestPathRef(reporter);
3516 }
3517