blob: ed80d41aa2f4c999f02a8a4513ec989085ed060d [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
reed@android.com3abec1d2009-03-02 05:36:20 +00008#include "Test.h"
reed@google.com8cae8352012-09-14 15:18:41 +00009#include "SkCanvas.h"
reed@google.com55b5f4b2011-09-07 12:23:41 +000010#include "SkPaint.h"
reed@android.com3abec1d2009-03-02 05:36:20 +000011#include "SkPath.h"
reed@google.com04863fa2011-05-15 04:08:24 +000012#include "SkParse.h"
reed@google.com3e71a882012-01-10 18:44:37 +000013#include "SkParsePath.h"
reed@google.com8b06f1a2012-05-29 12:03:46 +000014#include "SkPathEffect.h"
schenney@chromium.org6630d8d2012-01-04 21:05:51 +000015#include "SkRandom.h"
reed@google.com53effc52011-09-21 19:05:12 +000016#include "SkReader32.h"
reed@android.com60bc6d52010-02-11 11:09:39 +000017#include "SkSize.h"
reed@google.com8cae8352012-09-14 15:18:41 +000018#include "SkSurface.h"
mtklein@google.com9c9d4a72013-08-07 19:17:53 +000019#include "SkTypes.h"
20#include "SkWriter32.h"
reed@google.com8cae8352012-09-14 15:18:41 +000021
caryclark@google.com56f233a2012-11-19 13:06:06 +000022#if defined(WIN32)
23 #define SUPPRESS_VISIBILITY_WARNING
24#else
25 #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden")))
26#endif
27
reed@google.com8cae8352012-09-14 15:18:41 +000028static SkSurface* new_surface(int w, int h) {
29 SkImage::Info info = {
30 w, h, SkImage::kPMColor_ColorType, SkImage::kPremul_AlphaType
31 };
mike@reedtribe.orgb9476252012-11-15 02:37:45 +000032 return SkSurface::NewRaster(info);
reed@google.com8cae8352012-09-14 15:18:41 +000033}
34
commit-bot@chromium.org9d54aeb2013-08-09 19:48:26 +000035static void test_path_close_issue1474(skiatest::Reporter* reporter) {
36 // This test checks that r{Line,Quad,Conic,Cubic}To following a close()
37 // are relative to the point we close to, not relative to the point we close from.
38 SkPath path;
39 SkPoint last;
40
41 // Test rLineTo().
42 path.rLineTo(0, 100);
43 path.rLineTo(100, 0);
44 path.close(); // Returns us back to 0,0.
45 path.rLineTo(50, 50); // This should go to 50,50.
46
47 path.getLastPt(&last);
48 REPORTER_ASSERT(reporter, 50 == last.fX);
49 REPORTER_ASSERT(reporter, 50 == last.fY);
50
51 // Test rQuadTo().
52 path.rewind();
53 path.rLineTo(0, 100);
54 path.rLineTo(100, 0);
55 path.close();
56 path.rQuadTo(50, 50, 75, 75);
57
58 path.getLastPt(&last);
59 REPORTER_ASSERT(reporter, 75 == last.fX);
60 REPORTER_ASSERT(reporter, 75 == last.fY);
61
62 // Test rConicTo().
63 path.rewind();
64 path.rLineTo(0, 100);
65 path.rLineTo(100, 0);
66 path.close();
67 path.rConicTo(50, 50, 85, 85, 2);
68
69 path.getLastPt(&last);
70 REPORTER_ASSERT(reporter, 85 == last.fX);
71 REPORTER_ASSERT(reporter, 85 == last.fY);
72
73 // Test rCubicTo().
74 path.rewind();
75 path.rLineTo(0, 100);
76 path.rLineTo(100, 0);
77 path.close();
78 path.rCubicTo(50, 50, 85, 85, 95, 95);
79
80 path.getLastPt(&last);
81 REPORTER_ASSERT(reporter, 95 == last.fX);
82 REPORTER_ASSERT(reporter, 95 == last.fY);
83}
84
mtklein@google.com9c9d4a72013-08-07 19:17:53 +000085static void test_android_specific_behavior(skiatest::Reporter* reporter) {
86#ifdef SK_BUILD_FOR_ANDROID
mtklein@google.comcb8b0ee2013-08-15 21:14:51 +000087 // Make sure we treat fGenerationID and fSourcePath correctly for each of
88 // copy, assign, rewind, reset, and swap.
89 SkPath original, source, anotherSource;
90 original.setSourcePath(&source);
mtklein@google.com9c9d4a72013-08-07 19:17:53 +000091 original.moveTo(0, 0);
92 original.lineTo(1, 1);
93 REPORTER_ASSERT(reporter, original.getGenerationID() > 0);
mtklein@google.comcb8b0ee2013-08-15 21:14:51 +000094 REPORTER_ASSERT(reporter, original.getSourcePath() == &source);
mtklein@google.com9c9d4a72013-08-07 19:17:53 +000095
mtklein@google.comcb8b0ee2013-08-15 21:14:51 +000096 uint32_t copyID, assignID;
97
98 // Test copy constructor. Copy generation ID, copy source path.
99 SkPath copy(original);
mtklein@google.com9c9d4a72013-08-07 19:17:53 +0000100 REPORTER_ASSERT(reporter, copy.getGenerationID() == original.getGenerationID());
mtklein@google.comcb8b0ee2013-08-15 21:14:51 +0000101 REPORTER_ASSERT(reporter, copy.getSourcePath() == original.getSourcePath());
mtklein@google.com9c9d4a72013-08-07 19:17:53 +0000102
mtklein@google.comcb8b0ee2013-08-15 21:14:51 +0000103 // Test assigment operator. Increment generation ID, copy source path.
mtklein@google.com9c9d4a72013-08-07 19:17:53 +0000104 SkPath assign;
mtklein@google.comcb8b0ee2013-08-15 21:14:51 +0000105 assignID = assign.getGenerationID();
mtklein@google.com9c9d4a72013-08-07 19:17:53 +0000106 assign = original;
mtklein@google.comcb8b0ee2013-08-15 21:14:51 +0000107 REPORTER_ASSERT(reporter, assign.getGenerationID() > assignID);
108 REPORTER_ASSERT(reporter, assign.getSourcePath() == original.getSourcePath());
109
110 // Test rewind. Increment generation ID, don't touch source path.
111 copyID = copy.getGenerationID();
112 copy.rewind();
113 REPORTER_ASSERT(reporter, copy.getGenerationID() > copyID);
114 REPORTER_ASSERT(reporter, copy.getSourcePath() == original.getSourcePath());
115
116 // Test reset. Increment generation ID, don't touch source path.
117 assignID = assign.getGenerationID();
118 assign.reset();
119 REPORTER_ASSERT(reporter, assign.getGenerationID() > assignID);
120 REPORTER_ASSERT(reporter, assign.getSourcePath() == original.getSourcePath());
121
122 // Test swap. Increment both generation IDs, swap source paths.
123 copy.setSourcePath(&anotherSource);
124 copyID = copy.getGenerationID();
125 assignID = assign.getGenerationID();
126 copy.swap(assign);
127 REPORTER_ASSERT(reporter, copy.getGenerationID() > copyID);
128 REPORTER_ASSERT(reporter, assign.getGenerationID() > assignID);
129 REPORTER_ASSERT(reporter, copy.getSourcePath() == original.getSourcePath());
130 REPORTER_ASSERT(reporter, assign.getSourcePath() == &anotherSource);
mtklein@google.com9c9d4a72013-08-07 19:17:53 +0000131#endif
132}
133
reed@google.com3eff3592013-05-08 21:08:21 +0000134// This used to assert in the debug build, as the edges did not all line-up.
135static void test_bad_cubic_crbug234190() {
136 SkPath path;
137 path.moveTo(13.8509f, 3.16858f);
138 path.cubicTo(-2.35893e+08f, -4.21044e+08f,
139 -2.38991e+08f, -4.26573e+08f,
140 -2.41016e+08f, -4.30188e+08f);
141
142 SkPaint paint;
143 paint.setAntiAlias(true);
144 SkAutoTUnref<SkSurface> surface(new_surface(84, 88));
145 surface->getCanvas()->drawPath(path, paint);
146}
147
reed@google.com7a90daf2013-04-10 18:44:00 +0000148static void test_bad_cubic_crbug229478() {
149 const SkPoint pts[] = {
skia.committer@gmail.com391ca662013-04-11 07:01:45 +0000150 { 4595.91064f, -11596.9873f },
151 { 4597.2168f, -11595.9414f },
152 { 4598.52344f, -11594.8955f },
153 { 4599.83008f, -11593.8496f },
reed@google.com7a90daf2013-04-10 18:44:00 +0000154 };
skia.committer@gmail.com391ca662013-04-11 07:01:45 +0000155
reed@google.com7a90daf2013-04-10 18:44:00 +0000156 SkPath path;
157 path.moveTo(pts[0]);
158 path.cubicTo(pts[1], pts[2], pts[3]);
skia.committer@gmail.com391ca662013-04-11 07:01:45 +0000159
reed@google.com7a90daf2013-04-10 18:44:00 +0000160 SkPaint paint;
161 paint.setStyle(SkPaint::kStroke_Style);
162 paint.setStrokeWidth(20);
skia.committer@gmail.com391ca662013-04-11 07:01:45 +0000163
reed@google.com7a90daf2013-04-10 18:44:00 +0000164 SkPath dst;
165 // Before the fix, this would infinite-recurse, and run out of stack
166 // because we would keep trying to subdivide a degenerate cubic segment.
167 paint.getFillPath(path, &dst, NULL);
168}
169
reed@google.com64d62952013-01-18 17:49:28 +0000170static void build_path_170666(SkPath& path) {
171 path.moveTo(17.9459f, 21.6344f);
172 path.lineTo(139.545f, -47.8105f);
173 path.lineTo(139.545f, -47.8105f);
174 path.lineTo(131.07f, -47.3888f);
175 path.lineTo(131.07f, -47.3888f);
176 path.lineTo(122.586f, -46.9532f);
177 path.lineTo(122.586f, -46.9532f);
178 path.lineTo(18076.6f, 31390.9f);
179 path.lineTo(18076.6f, 31390.9f);
180 path.lineTo(18085.1f, 31390.5f);
181 path.lineTo(18085.1f, 31390.5f);
182 path.lineTo(18076.6f, 31390.9f);
183 path.lineTo(18076.6f, 31390.9f);
184 path.lineTo(17955, 31460.3f);
185 path.lineTo(17955, 31460.3f);
186 path.lineTo(17963.5f, 31459.9f);
187 path.lineTo(17963.5f, 31459.9f);
188 path.lineTo(17971.9f, 31459.5f);
189 path.lineTo(17971.9f, 31459.5f);
190 path.lineTo(17.9551f, 21.6205f);
191 path.lineTo(17.9551f, 21.6205f);
192 path.lineTo(9.47091f, 22.0561f);
193 path.lineTo(9.47091f, 22.0561f);
194 path.lineTo(17.9459f, 21.6344f);
195 path.lineTo(17.9459f, 21.6344f);
196 path.close();path.moveTo(0.995934f, 22.4779f);
197 path.lineTo(0.986725f, 22.4918f);
198 path.lineTo(0.986725f, 22.4918f);
199 path.lineTo(17955, 31460.4f);
200 path.lineTo(17955, 31460.4f);
201 path.lineTo(17971.9f, 31459.5f);
202 path.lineTo(17971.9f, 31459.5f);
203 path.lineTo(18093.6f, 31390.1f);
204 path.lineTo(18093.6f, 31390.1f);
205 path.lineTo(18093.6f, 31390);
206 path.lineTo(18093.6f, 31390);
207 path.lineTo(139.555f, -47.8244f);
208 path.lineTo(139.555f, -47.8244f);
209 path.lineTo(122.595f, -46.9671f);
210 path.lineTo(122.595f, -46.9671f);
211 path.lineTo(0.995934f, 22.4779f);
212 path.lineTo(0.995934f, 22.4779f);
213 path.close();
214 path.moveTo(5.43941f, 25.5223f);
215 path.lineTo(798267, -28871.1f);
216 path.lineTo(798267, -28871.1f);
217 path.lineTo(3.12512e+06f, -113102);
218 path.lineTo(3.12512e+06f, -113102);
219 path.cubicTo(5.16324e+06f, -186882, 8.15247e+06f, -295092, 1.1957e+07f, -432813);
220 path.cubicTo(1.95659e+07f, -708257, 3.04359e+07f, -1.10175e+06f, 4.34798e+07f, -1.57394e+06f);
221 path.cubicTo(6.95677e+07f, -2.51831e+06f, 1.04352e+08f, -3.77748e+06f, 1.39135e+08f, -5.03666e+06f);
222 path.cubicTo(1.73919e+08f, -6.29583e+06f, 2.08703e+08f, -7.555e+06f, 2.34791e+08f, -8.49938e+06f);
223 path.cubicTo(2.47835e+08f, -8.97157e+06f, 2.58705e+08f, -9.36506e+06f, 2.66314e+08f, -9.6405e+06f);
224 path.cubicTo(2.70118e+08f, -9.77823e+06f, 2.73108e+08f, -9.88644e+06f, 2.75146e+08f, -9.96022e+06f);
225 path.cubicTo(2.76165e+08f, -9.99711e+06f, 2.76946e+08f, -1.00254e+07f, 2.77473e+08f, -1.00444e+07f);
226 path.lineTo(2.78271e+08f, -1.00733e+07f);
227 path.lineTo(2.78271e+08f, -1.00733e+07f);
228 path.cubicTo(2.78271e+08f, -1.00733e+07f, 2.08703e+08f, -7.555e+06f, 135.238f, 23.3517f);
229 path.cubicTo(131.191f, 23.4981f, 125.995f, 23.7976f, 123.631f, 24.0206f);
230 path.cubicTo(121.267f, 24.2436f, 122.631f, 24.3056f, 126.677f, 24.1591f);
231 path.cubicTo(2.08703e+08f, -7.555e+06f, 2.78271e+08f, -1.00733e+07f, 2.78271e+08f, -1.00733e+07f);
232 path.lineTo(2.77473e+08f, -1.00444e+07f);
233 path.lineTo(2.77473e+08f, -1.00444e+07f);
234 path.cubicTo(2.76946e+08f, -1.00254e+07f, 2.76165e+08f, -9.99711e+06f, 2.75146e+08f, -9.96022e+06f);
235 path.cubicTo(2.73108e+08f, -9.88644e+06f, 2.70118e+08f, -9.77823e+06f, 2.66314e+08f, -9.6405e+06f);
236 path.cubicTo(2.58705e+08f, -9.36506e+06f, 2.47835e+08f, -8.97157e+06f, 2.34791e+08f, -8.49938e+06f);
237 path.cubicTo(2.08703e+08f, -7.555e+06f, 1.73919e+08f, -6.29583e+06f, 1.39135e+08f, -5.03666e+06f);
238 path.cubicTo(1.04352e+08f, -3.77749e+06f, 6.95677e+07f, -2.51831e+06f, 4.34798e+07f, -1.57394e+06f);
239 path.cubicTo(3.04359e+07f, -1.10175e+06f, 1.95659e+07f, -708258, 1.1957e+07f, -432814);
240 path.cubicTo(8.15248e+06f, -295092, 5.16324e+06f, -186883, 3.12513e+06f, -113103);
241 path.lineTo(798284, -28872);
242 path.lineTo(798284, -28872);
243 path.lineTo(22.4044f, 24.6677f);
244 path.lineTo(22.4044f, 24.6677f);
245 path.cubicTo(22.5186f, 24.5432f, 18.8134f, 24.6337f, 14.1287f, 24.8697f);
246 path.cubicTo(9.4439f, 25.1057f, 5.55359f, 25.3978f, 5.43941f, 25.5223f);
247 path.close();
248}
249
250static void build_path_simple_170666(SkPath& path) {
251 path.moveTo(126.677f, 24.1591f);
252 path.cubicTo(2.08703e+08f, -7.555e+06f, 2.78271e+08f, -1.00733e+07f, 2.78271e+08f, -1.00733e+07f);
253}
254
255// This used to assert in the SK_DEBUG build, as the clip step would fail with
256// too-few interations in our cubic-line intersection code. That code now runs
257// 24 interations (instead of 16).
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000258static void test_crbug_170666() {
reed@google.com64d62952013-01-18 17:49:28 +0000259 SkPath path;
260 SkPaint paint;
261 paint.setAntiAlias(true);
262
263 SkAutoTUnref<SkSurface> surface(new_surface(1000, 1000));
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000264
reed@google.com64d62952013-01-18 17:49:28 +0000265 build_path_simple_170666(path);
266 surface->getCanvas()->drawPath(path, paint);
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000267
reed@google.com64d62952013-01-18 17:49:28 +0000268 build_path_170666(path);
269 surface->getCanvas()->drawPath(path, paint);
270}
271
reed@google.coma8790de2012-10-24 21:04:04 +0000272// Make sure we stay non-finite once we get there (unless we reset or rewind).
273static void test_addrect_isfinite(skiatest::Reporter* reporter) {
274 SkPath path;
skia.committer@gmail.com8b0e2342012-10-25 02:01:20 +0000275
reed@google.coma8790de2012-10-24 21:04:04 +0000276 path.addRect(SkRect::MakeWH(50, 100));
277 REPORTER_ASSERT(reporter, path.isFinite());
278
279 path.moveTo(0, 0);
280 path.lineTo(SK_ScalarInfinity, 42);
281 REPORTER_ASSERT(reporter, !path.isFinite());
282
283 path.addRect(SkRect::MakeWH(50, 100));
284 REPORTER_ASSERT(reporter, !path.isFinite());
285
286 path.reset();
287 REPORTER_ASSERT(reporter, path.isFinite());
skia.committer@gmail.com8b0e2342012-10-25 02:01:20 +0000288
reed@google.coma8790de2012-10-24 21:04:04 +0000289 path.addRect(SkRect::MakeWH(50, 100));
290 REPORTER_ASSERT(reporter, path.isFinite());
291}
292
reed@google.com848148e2013-01-15 15:51:59 +0000293static void build_big_path(SkPath* path, bool reducedCase) {
294 if (reducedCase) {
295 path->moveTo(577330, 1971.72f);
296 path->cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f);
297 } else {
298 path->moveTo(60.1631f, 7.70567f);
299 path->quadTo(60.1631f, 7.70567f, 0.99474f, 0.901199f);
300 path->lineTo(577379, 1977.77f);
301 path->quadTo(577364, 1979.57f, 577325, 1980.26f);
302 path->quadTo(577286, 1980.95f, 577245, 1980.13f);
303 path->quadTo(577205, 1979.3f, 577187, 1977.45f);
304 path->quadTo(577168, 1975.6f, 577183, 1973.8f);
305 path->quadTo(577198, 1972, 577238, 1971.31f);
306 path->quadTo(577277, 1970.62f, 577317, 1971.45f);
307 path->quadTo(577330, 1971.72f, 577341, 1972.11f);
308 path->cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f);
309 path->moveTo(306.718f, -32.912f);
310 path->cubicTo(30.531f, 10.0005f, 1502.47f, 13.2804f, 84.3088f, 9.99601f);
311 }
312}
313
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000314static void test_clipped_cubic() {
reed@google.com848148e2013-01-15 15:51:59 +0000315 SkAutoTUnref<SkSurface> surface(new_surface(640, 480));
316
317 // This path used to assert, because our cubic-chopping code incorrectly
318 // moved control points after the chop. This test should be run in SK_DEBUG
319 // mode to ensure that we no long assert.
320 SkPath path;
321 for (int doReducedCase = 0; doReducedCase <= 1; ++doReducedCase) {
322 build_big_path(&path, SkToBool(doReducedCase));
skia.committer@gmail.comff21c2e2013-01-16 07:05:56 +0000323
reed@google.com848148e2013-01-15 15:51:59 +0000324 SkPaint paint;
325 for (int doAA = 0; doAA <= 1; ++doAA) {
326 paint.setAntiAlias(SkToBool(doAA));
327 surface->getCanvas()->drawPath(path, paint);
328 }
329 }
330}
331
reed@google.com8cae8352012-09-14 15:18:41 +0000332// Inspired by http://ie.microsoft.com/testdrive/Performance/Chalkboard/
333// which triggered an assert, from a tricky cubic. This test replicates that
334// example, so we can ensure that we handle it (in SkEdge.cpp), and don't
335// assert in the SK_DEBUG build.
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000336static void test_tricky_cubic() {
reed@google.com8cae8352012-09-14 15:18:41 +0000337 const SkPoint pts[] = {
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +0000338 { SkDoubleToScalar(18.8943768), SkDoubleToScalar(129.121277) },
339 { SkDoubleToScalar(18.8937435), SkDoubleToScalar(129.121689) },
340 { SkDoubleToScalar(18.8950119), SkDoubleToScalar(129.120422) },
341 { SkDoubleToScalar(18.5030727), SkDoubleToScalar(129.13121) },
reed@google.com8cae8352012-09-14 15:18:41 +0000342 };
343
344 SkPath path;
345 path.moveTo(pts[0]);
346 path.cubicTo(pts[1], pts[2], pts[3]);
347
348 SkPaint paint;
349 paint.setAntiAlias(true);
350
351 SkSurface* surface = new_surface(19, 130);
352 surface->getCanvas()->drawPath(path, paint);
353 surface->unref();
354}
reed@android.com3abec1d2009-03-02 05:36:20 +0000355
tomhudson@google.comed02c4d2012-08-10 14:10:45 +0000356// Inspired by http://code.google.com/p/chromium/issues/detail?id=141651
357//
358static void test_isfinite_after_transform(skiatest::Reporter* reporter) {
359 SkPath path;
360 path.quadTo(157, 366, 286, 208);
361 path.arcTo(37, 442, 315, 163, 957494590897113.0f);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000362
tomhudson@google.comed02c4d2012-08-10 14:10:45 +0000363 SkMatrix matrix;
364 matrix.setScale(1000*1000, 1000*1000);
365
366 // Be sure that path::transform correctly updates isFinite and the bounds
367 // if the transformation overflows. The previous bug was that isFinite was
368 // set to true in this case, but the bounds were not set to empty (which
369 // they should be).
370 while (path.isFinite()) {
371 REPORTER_ASSERT(reporter, path.getBounds().isFinite());
372 REPORTER_ASSERT(reporter, !path.getBounds().isEmpty());
373 path.transform(matrix);
374 }
375 REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
376
377 matrix.setTranslate(SK_Scalar1, SK_Scalar1);
378 path.transform(matrix);
379 // we need to still be non-finite
380 REPORTER_ASSERT(reporter, !path.isFinite());
381 REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
382}
383
skia.committer@gmail.com6a748ad2012-10-19 02:01:19 +0000384static void add_corner_arc(SkPath* path, const SkRect& rect,
385 SkScalar xIn, SkScalar yIn,
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000386 int startAngle)
387{
388
389 SkScalar rx = SkMinScalar(rect.width(), xIn);
390 SkScalar ry = SkMinScalar(rect.height(), yIn);
391
392 SkRect arcRect;
393 arcRect.set(-rx, -ry, rx, ry);
394 switch (startAngle) {
395 case 0:
396 arcRect.offset(rect.fRight - arcRect.fRight, rect.fBottom - arcRect.fBottom);
397 break;
398 case 90:
399 arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fBottom - arcRect.fBottom);
400 break;
401 case 180:
402 arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fTop - arcRect.fTop);
403 break;
404 case 270:
405 arcRect.offset(rect.fRight - arcRect.fRight, rect.fTop - arcRect.fTop);
406 break;
407 default:
408 break;
409 }
410
411 path->arcTo(arcRect, SkIntToScalar(startAngle), SkIntToScalar(90), false);
412}
413
skia.committer@gmail.com6a748ad2012-10-19 02:01:19 +0000414static void make_arb_round_rect(SkPath* path, const SkRect& r,
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000415 SkScalar xCorner, SkScalar yCorner) {
416 // we are lazy here and use the same x & y for each corner
417 add_corner_arc(path, r, xCorner, yCorner, 270);
418 add_corner_arc(path, r, xCorner, yCorner, 0);
419 add_corner_arc(path, r, xCorner, yCorner, 90);
420 add_corner_arc(path, r, xCorner, yCorner, 180);
robertphillips@google.com158618e2012-10-23 16:56:56 +0000421 path->close();
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000422}
423
424// Chrome creates its own round rects with each corner possibly being different.
425// Performance will suffer if they are not convex.
426// Note: PathBench::ArbRoundRectBench performs almost exactly
427// the same test (but with drawing)
428static void test_arb_round_rect_is_convex(skiatest::Reporter* reporter) {
jvanverth@google.comc490f802013-03-04 13:56:38 +0000429 SkMWCRandom rand;
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000430 SkRect r;
431
432 for (int i = 0; i < 5000; ++i) {
433
robertphillips@google.com158618e2012-10-23 16:56:56 +0000434 SkScalar size = rand.nextUScalar1() * 30;
435 if (size < SK_Scalar1) {
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000436 continue;
437 }
438 r.fLeft = rand.nextUScalar1() * 300;
439 r.fTop = rand.nextUScalar1() * 300;
robertphillips@google.com158618e2012-10-23 16:56:56 +0000440 r.fRight = r.fLeft + 2 * size;
441 r.fBottom = r.fTop + 2 * size;
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000442
443 SkPath temp;
444
445 make_arb_round_rect(&temp, r, r.width() / 10, r.height() / 15);
446
447 REPORTER_ASSERT(reporter, temp.isConvex());
448 }
449}
450
robertphillips@google.com158618e2012-10-23 16:56:56 +0000451// Chrome will sometimes create a 0 radius round rect. The degenerate
452// quads prevent the path from being converted to a rect
453// Note: PathBench::ArbRoundRectBench performs almost exactly
454// the same test (but with drawing)
455static void test_arb_zero_rad_round_rect_is_rect(skiatest::Reporter* reporter) {
jvanverth@google.comc490f802013-03-04 13:56:38 +0000456 SkMWCRandom rand;
robertphillips@google.com158618e2012-10-23 16:56:56 +0000457 SkRect r;
458
459 for (int i = 0; i < 5000; ++i) {
460
461 SkScalar size = rand.nextUScalar1() * 30;
462 if (size < SK_Scalar1) {
463 continue;
464 }
465 r.fLeft = rand.nextUScalar1() * 300;
466 r.fTop = rand.nextUScalar1() * 300;
467 r.fRight = r.fLeft + 2 * size;
468 r.fBottom = r.fTop + 2 * size;
469
470 SkPath temp;
471
472 make_arb_round_rect(&temp, r, 0, 0);
473
robertphillips@google.com158618e2012-10-23 16:56:56 +0000474 SkRect result;
475 REPORTER_ASSERT(reporter, temp.isRect(&result));
476 REPORTER_ASSERT(reporter, r == result);
robertphillips@google.com158618e2012-10-23 16:56:56 +0000477 }
478}
479
reed@google.com0bb18bb2012-07-26 15:20:36 +0000480static void test_rect_isfinite(skiatest::Reporter* reporter) {
481 const SkScalar inf = SK_ScalarInfinity;
caryclark@google.com7abfa492013-04-12 11:59:41 +0000482 const SkScalar negInf = SK_ScalarNegativeInfinity;
reed@google.com0bb18bb2012-07-26 15:20:36 +0000483 const SkScalar nan = SK_ScalarNaN;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000484
reed@google.com0bb18bb2012-07-26 15:20:36 +0000485 SkRect r;
486 r.setEmpty();
487 REPORTER_ASSERT(reporter, r.isFinite());
caryclark@google.com7abfa492013-04-12 11:59:41 +0000488 r.set(0, 0, inf, negInf);
reed@google.com0bb18bb2012-07-26 15:20:36 +0000489 REPORTER_ASSERT(reporter, !r.isFinite());
490 r.set(0, 0, nan, 0);
491 REPORTER_ASSERT(reporter, !r.isFinite());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000492
reed@google.com0bb18bb2012-07-26 15:20:36 +0000493 SkPoint pts[] = {
494 { 0, 0 },
495 { SK_Scalar1, 0 },
496 { 0, SK_Scalar1 },
497 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000498
reed@google.com0bb18bb2012-07-26 15:20:36 +0000499 bool isFine = r.setBoundsCheck(pts, 3);
500 REPORTER_ASSERT(reporter, isFine);
501 REPORTER_ASSERT(reporter, !r.isEmpty());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000502
reed@google.com0bb18bb2012-07-26 15:20:36 +0000503 pts[1].set(inf, 0);
504 isFine = r.setBoundsCheck(pts, 3);
505 REPORTER_ASSERT(reporter, !isFine);
506 REPORTER_ASSERT(reporter, r.isEmpty());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000507
reed@google.com0bb18bb2012-07-26 15:20:36 +0000508 pts[1].set(nan, 0);
509 isFine = r.setBoundsCheck(pts, 3);
510 REPORTER_ASSERT(reporter, !isFine);
511 REPORTER_ASSERT(reporter, r.isEmpty());
512}
513
514static void test_path_isfinite(skiatest::Reporter* reporter) {
515 const SkScalar inf = SK_ScalarInfinity;
bsalomon@google.com50c79d82013-01-08 20:31:53 +0000516 const SkScalar negInf = SK_ScalarNegativeInfinity;
reed@google.com0bb18bb2012-07-26 15:20:36 +0000517 const SkScalar nan = SK_ScalarNaN;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000518
reed@google.com0bb18bb2012-07-26 15:20:36 +0000519 SkPath path;
520 REPORTER_ASSERT(reporter, path.isFinite());
521
522 path.reset();
523 REPORTER_ASSERT(reporter, path.isFinite());
524
525 path.reset();
526 path.moveTo(SK_Scalar1, 0);
527 REPORTER_ASSERT(reporter, path.isFinite());
528
529 path.reset();
bsalomon@google.com50c79d82013-01-08 20:31:53 +0000530 path.moveTo(inf, negInf);
reed@google.com0bb18bb2012-07-26 15:20:36 +0000531 REPORTER_ASSERT(reporter, !path.isFinite());
532
533 path.reset();
534 path.moveTo(nan, 0);
535 REPORTER_ASSERT(reporter, !path.isFinite());
536}
537
538static void test_isfinite(skiatest::Reporter* reporter) {
539 test_rect_isfinite(reporter);
540 test_path_isfinite(reporter);
541}
542
reed@google.com744faba2012-05-29 19:54:52 +0000543// assert that we always
544// start with a moveTo
545// only have 1 moveTo
546// only have Lines after that
547// end with a single close
548// only have (at most) 1 close
549//
550static void test_poly(skiatest::Reporter* reporter, const SkPath& path,
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000551 const SkPoint srcPts[], bool expectClose) {
reed@google.com744faba2012-05-29 19:54:52 +0000552 SkPath::RawIter iter(path);
553 SkPoint pts[4];
reed@google.com744faba2012-05-29 19:54:52 +0000554
555 bool firstTime = true;
556 bool foundClose = false;
557 for (;;) {
558 switch (iter.next(pts)) {
559 case SkPath::kMove_Verb:
560 REPORTER_ASSERT(reporter, firstTime);
561 REPORTER_ASSERT(reporter, pts[0] == srcPts[0]);
562 srcPts++;
563 firstTime = false;
564 break;
565 case SkPath::kLine_Verb:
566 REPORTER_ASSERT(reporter, !firstTime);
567 REPORTER_ASSERT(reporter, pts[1] == srcPts[0]);
568 srcPts++;
569 break;
570 case SkPath::kQuad_Verb:
mtklein@google.com330313a2013-08-22 15:37:26 +0000571 REPORTER_ASSERT_MESSAGE(reporter, false, "unexpected quad verb");
reed@google.com744faba2012-05-29 19:54:52 +0000572 break;
reed@google.com277c3f82013-05-31 15:17:50 +0000573 case SkPath::kConic_Verb:
mtklein@google.com330313a2013-08-22 15:37:26 +0000574 REPORTER_ASSERT_MESSAGE(reporter, false, "unexpected conic verb");
reed@google.com277c3f82013-05-31 15:17:50 +0000575 break;
reed@google.com744faba2012-05-29 19:54:52 +0000576 case SkPath::kCubic_Verb:
mtklein@google.com330313a2013-08-22 15:37:26 +0000577 REPORTER_ASSERT_MESSAGE(reporter, false, "unexpected cubic verb");
reed@google.com744faba2012-05-29 19:54:52 +0000578 break;
579 case SkPath::kClose_Verb:
580 REPORTER_ASSERT(reporter, !firstTime);
581 REPORTER_ASSERT(reporter, !foundClose);
582 REPORTER_ASSERT(reporter, expectClose);
583 foundClose = true;
584 break;
585 case SkPath::kDone_Verb:
586 goto DONE;
587 }
588 }
589DONE:
590 REPORTER_ASSERT(reporter, foundClose == expectClose);
591}
592
593static void test_addPoly(skiatest::Reporter* reporter) {
594 SkPoint pts[32];
jvanverth@google.comc490f802013-03-04 13:56:38 +0000595 SkMWCRandom rand;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000596
reed@google.com744faba2012-05-29 19:54:52 +0000597 for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) {
598 pts[i].fX = rand.nextSScalar1();
599 pts[i].fY = rand.nextSScalar1();
600 }
601
602 for (int doClose = 0; doClose <= 1; ++doClose) {
603 for (size_t count = 1; count <= SK_ARRAY_COUNT(pts); ++count) {
604 SkPath path;
605 path.addPoly(pts, count, SkToBool(doClose));
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000606 test_poly(reporter, path, pts, SkToBool(doClose));
reed@google.com744faba2012-05-29 19:54:52 +0000607 }
608 }
609}
610
reed@google.com8b06f1a2012-05-29 12:03:46 +0000611static void test_strokerec(skiatest::Reporter* reporter) {
612 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
613 REPORTER_ASSERT(reporter, rec.isFillStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000614
reed@google.com8b06f1a2012-05-29 12:03:46 +0000615 rec.setHairlineStyle();
616 REPORTER_ASSERT(reporter, rec.isHairlineStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000617
reed@google.com8b06f1a2012-05-29 12:03:46 +0000618 rec.setStrokeStyle(SK_Scalar1, false);
619 REPORTER_ASSERT(reporter, SkStrokeRec::kStroke_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000620
reed@google.com8b06f1a2012-05-29 12:03:46 +0000621 rec.setStrokeStyle(SK_Scalar1, true);
622 REPORTER_ASSERT(reporter, SkStrokeRec::kStrokeAndFill_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000623
reed@google.com8b06f1a2012-05-29 12:03:46 +0000624 rec.setStrokeStyle(0, false);
625 REPORTER_ASSERT(reporter, SkStrokeRec::kHairline_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000626
reed@google.com8b06f1a2012-05-29 12:03:46 +0000627 rec.setStrokeStyle(0, true);
628 REPORTER_ASSERT(reporter, SkStrokeRec::kFill_Style == rec.getStyle());
629}
630
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000631// Set this for paths that don't have a consistent direction such as a bowtie.
632// (cheapComputeDirection is not expected to catch these.)
633static const SkPath::Direction kDontCheckDir = static_cast<SkPath::Direction>(-1);
634
635static void check_direction(skiatest::Reporter* reporter, const SkPath& path,
636 SkPath::Direction expected) {
637 if (expected == kDontCheckDir) {
638 return;
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000639 }
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000640 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
641
642 SkPath::Direction dir;
643 if (copy.cheapComputeDirection(&dir)) {
644 REPORTER_ASSERT(reporter, dir == expected);
645 } else {
646 REPORTER_ASSERT(reporter, SkPath::kUnknown_Direction == expected);
647 }
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000648}
649
reed@google.com3e71a882012-01-10 18:44:37 +0000650static void test_direction(skiatest::Reporter* reporter) {
651 size_t i;
652 SkPath path;
653 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
654 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction));
655 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction));
reed@google.coma8a3b3d2012-11-26 18:16:27 +0000656 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kUnknown_Direction));
reed@google.com3e71a882012-01-10 18:44:37 +0000657
658 static const char* gDegen[] = {
659 "M 10 10",
660 "M 10 10 M 20 20",
661 "M 10 10 L 20 20",
662 "M 10 10 L 10 10 L 10 10",
663 "M 10 10 Q 10 10 10 10",
664 "M 10 10 C 10 10 10 10 10 10",
665 };
666 for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) {
667 path.reset();
668 bool valid = SkParsePath::FromSVGString(gDegen[i], &path);
669 REPORTER_ASSERT(reporter, valid);
670 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
671 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000672
reed@google.com3e71a882012-01-10 18:44:37 +0000673 static const char* gCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000674 "M 10 10 L 10 10 Q 20 10 20 20",
reed@google.com3e71a882012-01-10 18:44:37 +0000675 "M 10 10 C 20 10 20 20 20 20",
reed@google.comd4146662012-01-31 15:42:29 +0000676 "M 20 10 Q 20 20 30 20 L 10 20", // test double-back at y-max
bsalomon@google.com4eefe612012-07-10 18:28:12 +0000677 // rect with top two corners replaced by cubics with identical middle
678 // control points
reed@google.com20fb0c72012-11-13 13:47:39 +0000679 "M 10 10 C 10 0 10 0 20 0 L 40 0 C 50 0 50 0 50 10",
680 "M 20 10 L 0 10 Q 10 10 20 0", // left, degenerate serif
reed@google.com3e71a882012-01-10 18:44:37 +0000681 };
682 for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) {
683 path.reset();
684 bool valid = SkParsePath::FromSVGString(gCW[i], &path);
685 REPORTER_ASSERT(reporter, valid);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000686 check_direction(reporter, path, SkPath::kCW_Direction);
reed@google.com3e71a882012-01-10 18:44:37 +0000687 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000688
reed@google.com3e71a882012-01-10 18:44:37 +0000689 static const char* gCCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000690 "M 10 10 L 10 10 Q 20 10 20 -20",
reed@google.com3e71a882012-01-10 18:44:37 +0000691 "M 10 10 C 20 10 20 -20 20 -20",
reed@google.comd4146662012-01-31 15:42:29 +0000692 "M 20 10 Q 20 20 10 20 L 30 20", // test double-back at y-max
bsalomon@google.com4eefe612012-07-10 18:28:12 +0000693 // rect with top two corners replaced by cubics with identical middle
694 // control points
reed@google.com20fb0c72012-11-13 13:47:39 +0000695 "M 50 10 C 50 0 50 0 40 0 L 20 0 C 10 0 10 0 10 10",
696 "M 10 10 L 30 10 Q 20 10 10 0", // right, degenerate serif
reed@google.com3e71a882012-01-10 18:44:37 +0000697 };
698 for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) {
699 path.reset();
700 bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
701 REPORTER_ASSERT(reporter, valid);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000702 check_direction(reporter, path, SkPath::kCCW_Direction);
reed@google.com3e71a882012-01-10 18:44:37 +0000703 }
reed@google.comac8543f2012-01-30 20:51:25 +0000704
705 // Test two donuts, each wound a different direction. Only the outer contour
706 // determines the cheap direction
707 path.reset();
708 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
709 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000710 check_direction(reporter, path, SkPath::kCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000711
reed@google.comac8543f2012-01-30 20:51:25 +0000712 path.reset();
713 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
714 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000715 check_direction(reporter, path, SkPath::kCCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000716
bsalomon@google.com6843ac42012-02-17 13:49:03 +0000717#ifdef SK_SCALAR_IS_FLOAT
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000718 // triangle with one point really far from the origin.
719 path.reset();
720 // the first point is roughly 1.05e10, 1.05e10
bsalomon@google.com53aab782012-02-23 14:54:49 +0000721 path.moveTo(SkFloatToScalar(SkBits2Float(0x501c7652)), SkFloatToScalar(SkBits2Float(0x501c7652)));
722 path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1);
723 path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000724 check_direction(reporter, path, SkPath::kCCW_Direction);
bsalomon@google.com53aab782012-02-23 14:54:49 +0000725#endif
reed@google.com3e71a882012-01-10 18:44:37 +0000726}
727
reed@google.comffdb0182011-11-14 19:29:14 +0000728static void add_rect(SkPath* path, const SkRect& r) {
729 path->moveTo(r.fLeft, r.fTop);
730 path->lineTo(r.fRight, r.fTop);
731 path->lineTo(r.fRight, r.fBottom);
732 path->lineTo(r.fLeft, r.fBottom);
733 path->close();
734}
735
736static void test_bounds(skiatest::Reporter* reporter) {
737 static const SkRect rects[] = {
reed@google.com3563c9e2011-11-14 19:34:57 +0000738 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
739 { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
740 { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
741 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
reed@google.comffdb0182011-11-14 19:29:14 +0000742 };
743
744 SkPath path0, path1;
745 for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
746 path0.addRect(rects[i]);
747 add_rect(&path1, rects[i]);
748 }
749
750 REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
751}
752
reed@google.com55b5f4b2011-09-07 12:23:41 +0000753static void stroke_cubic(const SkPoint pts[4]) {
754 SkPath path;
755 path.moveTo(pts[0]);
756 path.cubicTo(pts[1], pts[2], pts[3]);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000757
reed@google.com55b5f4b2011-09-07 12:23:41 +0000758 SkPaint paint;
759 paint.setStyle(SkPaint::kStroke_Style);
760 paint.setStrokeWidth(SK_Scalar1 * 2);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000761
reed@google.com55b5f4b2011-09-07 12:23:41 +0000762 SkPath fill;
763 paint.getFillPath(path, &fill);
764}
765
766// just ensure this can run w/o any SkASSERTS firing in the debug build
767// we used to assert due to differences in how we determine a degenerate vector
768// but that was fixed with the introduction of SkPoint::CanNormalize
769static void stroke_tiny_cubic() {
770 SkPoint p0[] = {
771 { 372.0f, 92.0f },
772 { 372.0f, 92.0f },
773 { 372.0f, 92.0f },
774 { 372.0f, 92.0f },
775 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000776
reed@google.com55b5f4b2011-09-07 12:23:41 +0000777 stroke_cubic(p0);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000778
reed@google.com55b5f4b2011-09-07 12:23:41 +0000779 SkPoint p1[] = {
780 { 372.0f, 92.0f },
781 { 372.0007f, 92.000755f },
782 { 371.99927f, 92.003922f },
783 { 371.99826f, 92.003899f },
784 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000785
reed@google.com55b5f4b2011-09-07 12:23:41 +0000786 stroke_cubic(p1);
787}
788
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000789static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
790 for (int i = 0; i < 2; ++i) {
robertphillips@google.com09042b82012-04-06 20:01:46 +0000791 SkPath::Iter iter(path, SkToBool(i));
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000792 SkPoint mv;
793 SkPoint pts[4];
794 SkPath::Verb v;
795 int nMT = 0;
796 int nCL = 0;
tomhudson@google.com221db3c2011-07-28 21:10:29 +0000797 mv.set(0, 0);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000798 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
799 switch (v) {
800 case SkPath::kMove_Verb:
801 mv = pts[0];
802 ++nMT;
803 break;
804 case SkPath::kClose_Verb:
805 REPORTER_ASSERT(reporter, mv == pts[0]);
806 ++nCL;
807 break;
808 default:
809 break;
810 }
811 }
812 // if we force a close on the interator we should have a close
813 // for every moveTo
814 REPORTER_ASSERT(reporter, !i || nMT == nCL);
815 }
816}
817
818static void test_close(skiatest::Reporter* reporter) {
819 SkPath closePt;
820 closePt.moveTo(0, 0);
821 closePt.close();
822 check_close(reporter, closePt);
823
824 SkPath openPt;
825 openPt.moveTo(0, 0);
826 check_close(reporter, openPt);
827
828 SkPath empty;
829 check_close(reporter, empty);
830 empty.close();
831 check_close(reporter, empty);
832
833 SkPath rect;
834 rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
835 check_close(reporter, rect);
836 rect.close();
837 check_close(reporter, rect);
838
839 SkPath quad;
840 quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
841 check_close(reporter, quad);
842 quad.close();
843 check_close(reporter, quad);
844
845 SkPath cubic;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000846 quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000847 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
848 check_close(reporter, cubic);
849 cubic.close();
850 check_close(reporter, cubic);
851
852 SkPath line;
853 line.moveTo(SK_Scalar1, SK_Scalar1);
854 line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
855 check_close(reporter, line);
856 line.close();
857 check_close(reporter, line);
858
859 SkPath rect2;
860 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
861 rect2.close();
862 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
863 check_close(reporter, rect2);
864 rect2.close();
865 check_close(reporter, rect2);
866
867 SkPath oval3;
868 oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
869 oval3.close();
870 oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
871 check_close(reporter, oval3);
872 oval3.close();
873 check_close(reporter, oval3);
874
875 SkPath moves;
876 moves.moveTo(SK_Scalar1, SK_Scalar1);
877 moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
878 moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
879 moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
880 check_close(reporter, moves);
reed@google.com55b5f4b2011-09-07 12:23:41 +0000881
882 stroke_tiny_cubic();
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000883}
884
reed@google.com7c424812011-05-15 04:38:34 +0000885static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
886 SkPath::Convexity expected) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000887 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
888 SkPath::Convexity c = copy.getConvexity();
reed@google.com7c424812011-05-15 04:38:34 +0000889 REPORTER_ASSERT(reporter, c == expected);
890}
891
892static void test_convexity2(skiatest::Reporter* reporter) {
893 SkPath pt;
894 pt.moveTo(0, 0);
895 pt.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000896 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000897 check_direction(reporter, pt, SkPath::kUnknown_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000898
reed@google.com7c424812011-05-15 04:38:34 +0000899 SkPath line;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000900 line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
901 line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000902 line.close();
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000903 check_convexity(reporter, line, SkPath::kConvex_Convexity);
904 check_direction(reporter, line, SkPath::kUnknown_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000905
reed@google.com7c424812011-05-15 04:38:34 +0000906 SkPath triLeft;
907 triLeft.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000908 triLeft.lineTo(SK_Scalar1, 0);
909 triLeft.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000910 triLeft.close();
911 check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000912 check_direction(reporter, triLeft, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000913
reed@google.com7c424812011-05-15 04:38:34 +0000914 SkPath triRight;
915 triRight.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000916 triRight.lineTo(-SK_Scalar1, 0);
917 triRight.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000918 triRight.close();
919 check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000920 check_direction(reporter, triRight, SkPath::kCCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000921
reed@google.com7c424812011-05-15 04:38:34 +0000922 SkPath square;
923 square.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000924 square.lineTo(SK_Scalar1, 0);
925 square.lineTo(SK_Scalar1, SK_Scalar1);
926 square.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000927 square.close();
928 check_convexity(reporter, square, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000929 check_direction(reporter, square, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000930
reed@google.com7c424812011-05-15 04:38:34 +0000931 SkPath redundantSquare;
932 redundantSquare.moveTo(0, 0);
933 redundantSquare.lineTo(0, 0);
934 redundantSquare.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000935 redundantSquare.lineTo(SK_Scalar1, 0);
936 redundantSquare.lineTo(SK_Scalar1, 0);
937 redundantSquare.lineTo(SK_Scalar1, 0);
938 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
939 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
940 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
941 redundantSquare.lineTo(0, SK_Scalar1);
942 redundantSquare.lineTo(0, SK_Scalar1);
943 redundantSquare.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000944 redundantSquare.close();
945 check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000946 check_direction(reporter, redundantSquare, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000947
reed@google.com7c424812011-05-15 04:38:34 +0000948 SkPath bowTie;
949 bowTie.moveTo(0, 0);
950 bowTie.lineTo(0, 0);
951 bowTie.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000952 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
953 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
954 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
955 bowTie.lineTo(SK_Scalar1, 0);
956 bowTie.lineTo(SK_Scalar1, 0);
957 bowTie.lineTo(SK_Scalar1, 0);
958 bowTie.lineTo(0, SK_Scalar1);
959 bowTie.lineTo(0, SK_Scalar1);
960 bowTie.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000961 bowTie.close();
962 check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000963 check_direction(reporter, bowTie, kDontCheckDir);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000964
reed@google.com7c424812011-05-15 04:38:34 +0000965 SkPath spiral;
966 spiral.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000967 spiral.lineTo(100*SK_Scalar1, 0);
968 spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
969 spiral.lineTo(0, 100*SK_Scalar1);
970 spiral.lineTo(0, 50*SK_Scalar1);
971 spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
972 spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000973 spiral.close();
reed@google.com85b6e392011-05-15 20:25:17 +0000974 check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000975 check_direction(reporter, spiral, kDontCheckDir);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000976
reed@google.com7c424812011-05-15 04:38:34 +0000977 SkPath dent;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000978 dent.moveTo(0, 0);
979 dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
980 dent.lineTo(0, 100*SK_Scalar1);
981 dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
982 dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000983 dent.close();
984 check_convexity(reporter, dent, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000985 check_direction(reporter, dent, SkPath::kCW_Direction);
reed@google.com7c424812011-05-15 04:38:34 +0000986}
987
reed@android.com6b82d1a2009-06-03 02:35:01 +0000988static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
989 const SkRect& bounds) {
990 REPORTER_ASSERT(reporter, p.isConvex());
991 REPORTER_ASSERT(reporter, p.getBounds() == bounds);
reed@google.com62047cf2011-02-07 19:39:09 +0000992
reed@android.com6b82d1a2009-06-03 02:35:01 +0000993 SkPath p2(p);
994 REPORTER_ASSERT(reporter, p2.isConvex());
995 REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
996
997 SkPath other;
998 other.swap(p2);
999 REPORTER_ASSERT(reporter, other.isConvex());
1000 REPORTER_ASSERT(reporter, other.getBounds() == bounds);
1001}
1002
reed@google.com04863fa2011-05-15 04:08:24 +00001003static void setFromString(SkPath* path, const char str[]) {
1004 bool first = true;
1005 while (str) {
1006 SkScalar x, y;
1007 str = SkParse::FindScalar(str, &x);
1008 if (NULL == str) {
1009 break;
1010 }
1011 str = SkParse::FindScalar(str, &y);
1012 SkASSERT(str);
1013 if (first) {
1014 path->moveTo(x, y);
1015 first = false;
1016 } else {
1017 path->lineTo(x, y);
1018 }
1019 }
1020}
1021
1022static void test_convexity(skiatest::Reporter* reporter) {
reed@google.com04863fa2011-05-15 04:08:24 +00001023 SkPath path;
1024
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001025 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.come3543972012-01-10 18:59:22 +00001026 path.addCircle(0, 0, SkIntToScalar(10));
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001027 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.come3543972012-01-10 18:59:22 +00001028 path.addCircle(0, 0, SkIntToScalar(10)); // 2nd circle
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001029 check_convexity(reporter, path, SkPath::kConcave_Convexity);
1030
reed@google.com04863fa2011-05-15 04:08:24 +00001031 path.reset();
reed@google.come3543972012-01-10 18:59:22 +00001032 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001033 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.com3e71a882012-01-10 18:44:37 +00001034 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001035
reed@google.com04863fa2011-05-15 04:08:24 +00001036 path.reset();
reed@google.come3543972012-01-10 18:59:22 +00001037 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001038 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.com3e71a882012-01-10 18:44:37 +00001039 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001040
reed@google.com04863fa2011-05-15 04:08:24 +00001041 static const struct {
1042 const char* fPathStr;
1043 SkPath::Convexity fExpectedConvexity;
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001044 SkPath::Direction fExpectedDirection;
reed@google.com04863fa2011-05-15 04:08:24 +00001045 } gRec[] = {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001046 { "", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
1047 { "0 0", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
1048 { "0 0 10 10", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
1049 { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity, SkPath::kUnknown_Direction },
1050 { "0 0 10 10 10 20", SkPath::kConvex_Convexity, SkPath::kCW_Direction },
1051 { "0 0 10 10 10 0", SkPath::kConvex_Convexity, SkPath::kCCW_Direction },
1052 { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity, kDontCheckDir },
1053 { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity, SkPath::kCW_Direction },
reed@google.com04863fa2011-05-15 04:08:24 +00001054 };
1055
1056 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
1057 SkPath path;
1058 setFromString(&path, gRec[i].fPathStr);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001059 check_convexity(reporter, path, gRec[i].fExpectedConvexity);
1060 check_direction(reporter, path, gRec[i].fExpectedDirection);
reed@google.com04863fa2011-05-15 04:08:24 +00001061 }
1062}
1063
reed@google.com7e6c4d12012-05-10 14:05:43 +00001064static void test_isLine(skiatest::Reporter* reporter) {
1065 SkPath path;
1066 SkPoint pts[2];
1067 const SkScalar value = SkIntToScalar(5);
1068
1069 REPORTER_ASSERT(reporter, !path.isLine(NULL));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001070
reed@google.com7e6c4d12012-05-10 14:05:43 +00001071 // set some non-zero values
1072 pts[0].set(value, value);
1073 pts[1].set(value, value);
1074 REPORTER_ASSERT(reporter, !path.isLine(pts));
1075 // check that pts was untouched
1076 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
1077 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
1078
1079 const SkScalar moveX = SkIntToScalar(1);
1080 const SkScalar moveY = SkIntToScalar(2);
1081 SkASSERT(value != moveX && value != moveY);
1082
1083 path.moveTo(moveX, moveY);
1084 REPORTER_ASSERT(reporter, !path.isLine(NULL));
1085 REPORTER_ASSERT(reporter, !path.isLine(pts));
1086 // check that pts was untouched
1087 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
1088 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
1089
1090 const SkScalar lineX = SkIntToScalar(2);
1091 const SkScalar lineY = SkIntToScalar(2);
1092 SkASSERT(value != lineX && value != lineY);
1093
1094 path.lineTo(lineX, lineY);
1095 REPORTER_ASSERT(reporter, path.isLine(NULL));
1096
1097 REPORTER_ASSERT(reporter, !pts[0].equals(moveX, moveY));
1098 REPORTER_ASSERT(reporter, !pts[1].equals(lineX, lineY));
1099 REPORTER_ASSERT(reporter, path.isLine(pts));
1100 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
1101 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
1102
1103 path.lineTo(0, 0); // too many points/verbs
1104 REPORTER_ASSERT(reporter, !path.isLine(NULL));
1105 REPORTER_ASSERT(reporter, !path.isLine(pts));
1106 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
1107 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
1108}
1109
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001110static void test_conservativelyContains(skiatest::Reporter* reporter) {
1111 SkPath path;
1112
1113 // kBaseRect is used to construct most our test paths: a rect, a circle, and a round-rect.
1114 static const SkRect kBaseRect = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
1115
1116 // A circle that bounds kBaseRect (with a significant amount of slop)
1117 SkScalar circleR = SkMaxScalar(kBaseRect.width(), kBaseRect.height());
1118 circleR = SkScalarMul(circleR, SkFloatToScalar(1.75f)) / 2;
1119 static const SkPoint kCircleC = {kBaseRect.centerX(), kBaseRect.centerY()};
1120
1121 // round-rect radii
1122 static const SkScalar kRRRadii[] = {SkIntToScalar(5), SkIntToScalar(3)};
skia.committer@gmail.comcec8de62012-11-14 02:01:22 +00001123
caryclark@google.com56f233a2012-11-19 13:06:06 +00001124 static const struct SUPPRESS_VISIBILITY_WARNING {
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001125 SkRect fQueryRect;
1126 bool fInRect;
1127 bool fInCircle;
1128 bool fInRR;
1129 } kQueries[] = {
1130 {kBaseRect, true, true, false},
1131
1132 // rect well inside of kBaseRect
1133 {SkRect::MakeLTRB(kBaseRect.fLeft + SkFloatToScalar(0.25f)*kBaseRect.width(),
1134 kBaseRect.fTop + SkFloatToScalar(0.25f)*kBaseRect.height(),
1135 kBaseRect.fRight - SkFloatToScalar(0.25f)*kBaseRect.width(),
1136 kBaseRect.fBottom - SkFloatToScalar(0.25f)*kBaseRect.height()),
1137 true, true, true},
1138
1139 // rects with edges off by one from kBaseRect's edges
1140 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1141 kBaseRect.width(), kBaseRect.height() + 1),
1142 false, true, false},
1143 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1144 kBaseRect.width() + 1, kBaseRect.height()),
1145 false, true, false},
1146 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1147 kBaseRect.width() + 1, kBaseRect.height() + 1),
1148 false, true, false},
1149 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
1150 kBaseRect.width(), kBaseRect.height()),
1151 false, true, false},
1152 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
1153 kBaseRect.width(), kBaseRect.height()),
1154 false, true, false},
1155 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
1156 kBaseRect.width() + 2, kBaseRect.height()),
1157 false, true, false},
1158 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
1159 kBaseRect.width() + 2, kBaseRect.height()),
1160 false, true, false},
1161
1162 // zero-w/h rects at each corner of kBaseRect
1163 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop, 0, 0), true, true, false},
1164 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fTop, 0, 0), true, true, false},
1165 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fBottom, 0, 0), true, true, false},
1166 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fBottom, 0, 0), true, true, false},
1167
1168 // far away rect
1169 {SkRect::MakeXYWH(10 * kBaseRect.fRight, 10 * kBaseRect.fBottom,
1170 SkIntToScalar(10), SkIntToScalar(10)),
1171 false, false, false},
1172
1173 // very large rect containing kBaseRect
1174 {SkRect::MakeXYWH(kBaseRect.fLeft - 5 * kBaseRect.width(),
1175 kBaseRect.fTop - 5 * kBaseRect.height(),
1176 11 * kBaseRect.width(), 11 * kBaseRect.height()),
1177 false, false, false},
1178
1179 // skinny rect that spans same y-range as kBaseRect
1180 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
1181 SkIntToScalar(1), kBaseRect.height()),
1182 true, true, true},
1183
1184 // short rect that spans same x-range as kBaseRect
1185 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(), kBaseRect.width(), SkScalar(1)),
1186 true, true, true},
1187
1188 // skinny rect that spans slightly larger y-range than kBaseRect
1189 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
1190 SkIntToScalar(1), kBaseRect.height() + 1),
1191 false, true, false},
1192
1193 // short rect that spans slightly larger x-range than kBaseRect
1194 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(),
1195 kBaseRect.width() + 1, SkScalar(1)),
1196 false, true, false},
1197 };
1198
1199 for (int inv = 0; inv < 4; ++inv) {
caryclark@google.com56f233a2012-11-19 13:06:06 +00001200 for (size_t q = 0; q < SK_ARRAY_COUNT(kQueries); ++q) {
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001201 SkRect qRect = kQueries[q].fQueryRect;
1202 if (inv & 0x1) {
1203 SkTSwap(qRect.fLeft, qRect.fRight);
1204 }
1205 if (inv & 0x2) {
1206 SkTSwap(qRect.fTop, qRect.fBottom);
1207 }
1208 for (int d = 0; d < 2; ++d) {
1209 SkPath::Direction dir = d ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
1210 path.reset();
1211 path.addRect(kBaseRect, dir);
1212 REPORTER_ASSERT(reporter, kQueries[q].fInRect ==
1213 path.conservativelyContainsRect(qRect));
1214
1215 path.reset();
1216 path.addCircle(kCircleC.fX, kCircleC.fY, circleR, dir);
1217 REPORTER_ASSERT(reporter, kQueries[q].fInCircle ==
1218 path.conservativelyContainsRect(qRect));
1219
1220 path.reset();
1221 path.addRoundRect(kBaseRect, kRRRadii[0], kRRRadii[1], dir);
1222 REPORTER_ASSERT(reporter, kQueries[q].fInRR ==
1223 path.conservativelyContainsRect(qRect));
1224 }
1225 // Slightly non-convex shape, shouldn't contain any rects.
1226 path.reset();
1227 path.moveTo(0, 0);
1228 path.lineTo(SkIntToScalar(50), SkFloatToScalar(0.05f));
1229 path.lineTo(SkIntToScalar(100), 0);
1230 path.lineTo(SkIntToScalar(100), SkIntToScalar(100));
1231 path.lineTo(0, SkIntToScalar(100));
1232 path.close();
1233 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(qRect));
1234 }
1235 }
1236
1237 // make sure a minimal convex shape works, a right tri with edges along pos x and y axes.
1238 path.reset();
1239 path.moveTo(0, 0);
1240 path.lineTo(SkIntToScalar(100), 0);
1241 path.lineTo(0, SkIntToScalar(100));
1242
1243 // inside, on along top edge
1244 REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
1245 SkIntToScalar(10),
1246 SkIntToScalar(10))));
1247 // above
1248 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(
1249 SkRect::MakeXYWH(SkIntToScalar(50),
1250 SkIntToScalar(-10),
1251 SkIntToScalar(10),
1252 SkIntToScalar(10))));
1253 // to the left
1254 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(-10),
1255 SkIntToScalar(5),
1256 SkIntToScalar(5),
1257 SkIntToScalar(5))));
1258
1259 // outside the diagonal edge
1260 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(10),
1261 SkIntToScalar(200),
1262 SkIntToScalar(20),
1263 SkIntToScalar(5))));
commit-bot@chromium.org62df5262013-08-01 15:35:06 +00001264
1265 // same as above path and first test but with an extra moveTo.
1266 path.reset();
1267 path.moveTo(100, 100);
1268 path.moveTo(0, 0);
1269 path.lineTo(SkIntToScalar(100), 0);
1270 path.lineTo(0, SkIntToScalar(100));
1271
1272 REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
1273 SkIntToScalar(10),
1274 SkIntToScalar(10))));
1275
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001276}
1277
caryclark@google.comf1316942011-07-26 19:54:45 +00001278// Simple isRect test is inline TestPath, below.
1279// test_isRect provides more extensive testing.
1280static void test_isRect(skiatest::Reporter* reporter) {
1281 // passing tests (all moveTo / lineTo...
1282 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
1283 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1284 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1285 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
1286 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
1287 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1288 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1289 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1290 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1291 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
1292 {1, 0}, {.5f, 0}};
1293 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
1294 {0, 1}, {0, .5f}};
1295 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
1296 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
1297 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
caryclark@google.combfe90372012-11-21 13:56:20 +00001298 SkPoint rf[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}};
rmistry@google.comd6176b02012-08-23 18:14:13 +00001299
caryclark@google.comf1316942011-07-26 19:54:45 +00001300 // failing tests
1301 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1302 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1303 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1304 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1305 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1306 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1307 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1308 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
caryclark@google.combfe90372012-11-21 13:56:20 +00001309 SkPoint f9[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}, {2, 0}}; // overlaps
1310 SkPoint fa[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, -1}, {1, -1}}; // non colinear gap
1311 SkPoint fb[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 1}}; // falls short
rmistry@google.comd6176b02012-08-23 18:14:13 +00001312
caryclark@google.comf1316942011-07-26 19:54:45 +00001313 // failing, no close
1314 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1315 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1316
1317 size_t testLen[] = {
1318 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1319 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
caryclark@google.combfe90372012-11-21 13:56:20 +00001320 sizeof(rd), sizeof(re), sizeof(rf),
caryclark@google.comf1316942011-07-26 19:54:45 +00001321 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
caryclark@google.combfe90372012-11-21 13:56:20 +00001322 sizeof(f7), sizeof(f8), sizeof(f9), sizeof(fa), sizeof(fb),
rmistry@google.comd6176b02012-08-23 18:14:13 +00001323 sizeof(c1), sizeof(c2)
caryclark@google.comf1316942011-07-26 19:54:45 +00001324 };
1325 SkPoint* tests[] = {
caryclark@google.combfe90372012-11-21 13:56:20 +00001326 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re, rf,
1327 f1, f2, f3, f4, f5, f6, f7, f8, f9, fa, fb,
rmistry@google.comd6176b02012-08-23 18:14:13 +00001328 c1, c2
caryclark@google.comf1316942011-07-26 19:54:45 +00001329 };
caryclark@google.combfe90372012-11-21 13:56:20 +00001330 SkPoint* lastPass = rf;
1331 SkPoint* lastClose = fb;
caryclark@google.comf1316942011-07-26 19:54:45 +00001332 bool fail = false;
1333 bool close = true;
1334 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1335 size_t index;
1336 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1337 SkPath path;
1338 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1339 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1340 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1341 }
1342 if (close) {
1343 path.close();
1344 }
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001345 REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
1346 REPORTER_ASSERT(reporter, fail ^ path.isRect(NULL, NULL));
caryclark@google.comf68154a2012-11-21 15:18:06 +00001347
caryclark@google.com56f233a2012-11-19 13:06:06 +00001348 if (!fail) {
1349 SkRect computed, expected;
1350 expected.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1351 REPORTER_ASSERT(reporter, path.isRect(&computed));
1352 REPORTER_ASSERT(reporter, expected == computed);
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001353
caryclark@google.comf68154a2012-11-21 15:18:06 +00001354 bool isClosed;
1355 SkPath::Direction direction, cheapDirection;
1356 REPORTER_ASSERT(reporter, path.cheapComputeDirection(&cheapDirection));
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001357 REPORTER_ASSERT(reporter, path.isRect(&isClosed, &direction));
caryclark@google.comf68154a2012-11-21 15:18:06 +00001358 REPORTER_ASSERT(reporter, isClosed == close);
1359 REPORTER_ASSERT(reporter, direction == cheapDirection);
1360 } else {
1361 SkRect computed;
1362 computed.set(123, 456, 789, 1011);
1363 REPORTER_ASSERT(reporter, !path.isRect(&computed));
1364 REPORTER_ASSERT(reporter, computed.fLeft == 123 && computed.fTop == 456);
1365 REPORTER_ASSERT(reporter, computed.fRight == 789 && computed.fBottom == 1011);
1366
1367 bool isClosed = (bool) -1;
1368 SkPath::Direction direction = (SkPath::Direction) -1;
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001369 REPORTER_ASSERT(reporter, !path.isRect(&isClosed, &direction));
caryclark@google.comf68154a2012-11-21 15:18:06 +00001370 REPORTER_ASSERT(reporter, isClosed == (bool) -1);
1371 REPORTER_ASSERT(reporter, direction == (SkPath::Direction) -1);
caryclark@google.com56f233a2012-11-19 13:06:06 +00001372 }
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001373
caryclark@google.comf1316942011-07-26 19:54:45 +00001374 if (tests[testIndex] == lastPass) {
1375 fail = true;
1376 }
1377 if (tests[testIndex] == lastClose) {
1378 close = false;
1379 }
1380 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001381
caryclark@google.comf1316942011-07-26 19:54:45 +00001382 // fail, close then line
1383 SkPath path1;
1384 path1.moveTo(r1[0].fX, r1[0].fY);
1385 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1386 path1.lineTo(r1[index].fX, r1[index].fY);
1387 }
1388 path1.close();
1389 path1.lineTo(1, 0);
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001390 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001391
caryclark@google.comf1316942011-07-26 19:54:45 +00001392 // fail, move in the middle
1393 path1.reset();
1394 path1.moveTo(r1[0].fX, r1[0].fY);
1395 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1396 if (index == 2) {
1397 path1.moveTo(1, .5f);
1398 }
1399 path1.lineTo(r1[index].fX, r1[index].fY);
1400 }
1401 path1.close();
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001402 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
caryclark@google.comf1316942011-07-26 19:54:45 +00001403
1404 // fail, move on the edge
1405 path1.reset();
1406 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1407 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1408 path1.lineTo(r1[index].fX, r1[index].fY);
1409 }
1410 path1.close();
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001411 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001412
caryclark@google.comf1316942011-07-26 19:54:45 +00001413 // fail, quad
1414 path1.reset();
1415 path1.moveTo(r1[0].fX, r1[0].fY);
1416 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1417 if (index == 2) {
1418 path1.quadTo(1, .5f, 1, .5f);
1419 }
1420 path1.lineTo(r1[index].fX, r1[index].fY);
1421 }
1422 path1.close();
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001423 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001424
caryclark@google.comf1316942011-07-26 19:54:45 +00001425 // fail, cubic
1426 path1.reset();
1427 path1.moveTo(r1[0].fX, r1[0].fY);
1428 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1429 if (index == 2) {
1430 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1431 }
1432 path1.lineTo(r1[index].fX, r1[index].fY);
1433 }
1434 path1.close();
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001435 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
caryclark@google.comf1316942011-07-26 19:54:45 +00001436}
1437
caryclark@google.com56f233a2012-11-19 13:06:06 +00001438static void test_isNestedRects(skiatest::Reporter* reporter) {
1439 // passing tests (all moveTo / lineTo...
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001440 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // CW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001441 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1442 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1443 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001444 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}}; // CCW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001445 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1446 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1447 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1448 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001449 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f}, // CCW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001450 {1, 0}, {.5f, 0}};
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001451 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1}, // CW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001452 {0, 1}, {0, .5f}};
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001453 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}; // CW
1454 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}}; // CCW
1455 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}}; // CW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001456
1457 // failing tests
1458 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1459 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1460 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1461 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1462 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1463 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1464 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1465 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
1466
1467 // failing, no close
1468 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1469 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1470
1471 size_t testLen[] = {
1472 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1473 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
1474 sizeof(rd), sizeof(re),
1475 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
1476 sizeof(f7), sizeof(f8),
1477 sizeof(c1), sizeof(c2)
1478 };
1479 SkPoint* tests[] = {
1480 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
1481 f1, f2, f3, f4, f5, f6, f7, f8,
1482 c1, c2
1483 };
skia.committer@gmail.com845220b2013-05-20 11:51:35 +00001484 SkPath::Direction dirs[] = {
1485 SkPath::kCW_Direction, SkPath::kCW_Direction, SkPath::kCW_Direction,
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001486 SkPath::kCW_Direction, SkPath::kCCW_Direction, SkPath::kCCW_Direction,
skia.committer@gmail.com845220b2013-05-20 11:51:35 +00001487 SkPath::kCCW_Direction, SkPath::kCCW_Direction, SkPath::kCCW_Direction,
1488 SkPath::kCCW_Direction, SkPath::kCW_Direction, SkPath::kCW_Direction,
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001489 SkPath::kCCW_Direction, SkPath::kCW_Direction, SkPath::kUnknown_Direction,
1490 SkPath::kUnknown_Direction, SkPath::kUnknown_Direction, SkPath::kUnknown_Direction,
1491 SkPath::kUnknown_Direction, SkPath::kUnknown_Direction, SkPath::kUnknown_Direction,
1492 SkPath::kUnknown_Direction, SkPath::kUnknown_Direction, SkPath::kUnknown_Direction,
1493 };
1494 SkASSERT(SK_ARRAY_COUNT(tests) == SK_ARRAY_COUNT(dirs));
1495
caryclark@google.com56f233a2012-11-19 13:06:06 +00001496 const SkPoint* lastPass = re;
1497 const SkPoint* lastClose = f8;
1498 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1499 size_t index;
1500 for (int rectFirst = 0; rectFirst <= 1; ++rectFirst) {
1501 bool fail = false;
1502 bool close = true;
1503 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1504 SkPath path;
1505 if (rectFirst) {
1506 path.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1507 }
1508 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1509 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1510 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1511 }
1512 if (close) {
1513 path.close();
1514 }
1515 if (!rectFirst) {
1516 path.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1517 }
1518 REPORTER_ASSERT(reporter, fail ^ path.isNestedRects(0));
1519 if (!fail) {
1520 SkRect expected[2], computed[2];
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001521 SkPath::Direction expectedDirs[2], computedDirs[2];
caryclark@google.com56f233a2012-11-19 13:06:06 +00001522 SkRect testBounds;
1523 testBounds.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1524 expected[0] = SkRect::MakeLTRB(-1, -1, 2, 2);
1525 expected[1] = testBounds;
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001526 if (rectFirst) {
1527 expectedDirs[0] = SkPath::kCW_Direction;
1528 } else {
1529 expectedDirs[0] = SkPath::kCCW_Direction;
1530 }
1531 expectedDirs[1] = dirs[testIndex];
1532 REPORTER_ASSERT(reporter, path.isNestedRects(computed, computedDirs));
caryclark@google.com56f233a2012-11-19 13:06:06 +00001533 REPORTER_ASSERT(reporter, expected[0] == computed[0]);
1534 REPORTER_ASSERT(reporter, expected[1] == computed[1]);
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001535 REPORTER_ASSERT(reporter, expectedDirs[0] == computedDirs[0]);
1536 REPORTER_ASSERT(reporter, expectedDirs[1] == computedDirs[1]);
caryclark@google.com56f233a2012-11-19 13:06:06 +00001537 }
1538 if (tests[testIndex] == lastPass) {
1539 fail = true;
1540 }
1541 if (tests[testIndex] == lastClose) {
1542 close = false;
1543 }
1544 }
1545
1546 // fail, close then line
1547 SkPath path1;
1548 if (rectFirst) {
1549 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1550 }
1551 path1.moveTo(r1[0].fX, r1[0].fY);
1552 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1553 path1.lineTo(r1[index].fX, r1[index].fY);
1554 }
1555 path1.close();
1556 path1.lineTo(1, 0);
1557 if (!rectFirst) {
1558 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1559 }
1560 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1561
1562 // fail, move in the middle
1563 path1.reset();
1564 if (rectFirst) {
1565 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1566 }
1567 path1.moveTo(r1[0].fX, r1[0].fY);
1568 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1569 if (index == 2) {
1570 path1.moveTo(1, .5f);
1571 }
1572 path1.lineTo(r1[index].fX, r1[index].fY);
1573 }
1574 path1.close();
1575 if (!rectFirst) {
1576 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1577 }
1578 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1579
1580 // fail, move on the edge
1581 path1.reset();
1582 if (rectFirst) {
1583 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1584 }
1585 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1586 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1587 path1.lineTo(r1[index].fX, r1[index].fY);
1588 }
1589 path1.close();
1590 if (!rectFirst) {
1591 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1592 }
1593 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1594
1595 // fail, quad
1596 path1.reset();
1597 if (rectFirst) {
1598 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1599 }
1600 path1.moveTo(r1[0].fX, r1[0].fY);
1601 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1602 if (index == 2) {
1603 path1.quadTo(1, .5f, 1, .5f);
1604 }
1605 path1.lineTo(r1[index].fX, r1[index].fY);
1606 }
1607 path1.close();
1608 if (!rectFirst) {
1609 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1610 }
1611 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1612
1613 // fail, cubic
1614 path1.reset();
1615 if (rectFirst) {
1616 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1617 }
1618 path1.moveTo(r1[0].fX, r1[0].fY);
1619 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1620 if (index == 2) {
1621 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1622 }
1623 path1.lineTo(r1[index].fX, r1[index].fY);
1624 }
1625 path1.close();
1626 if (!rectFirst) {
1627 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1628 }
1629 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
skia.committer@gmail.com34587162012-11-20 02:01:23 +00001630
caryclark@google.com56f233a2012-11-19 13:06:06 +00001631 // fail, not nested
1632 path1.reset();
1633 path1.addRect(1, 1, 3, 3, SkPath::kCW_Direction);
1634 path1.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
1635 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1636 }
caryclark@google.combfe90372012-11-21 13:56:20 +00001637
1638 // pass, stroke rect
1639 SkPath src, dst;
1640 src.addRect(1, 1, 7, 7, SkPath::kCW_Direction);
1641 SkPaint strokePaint;
1642 strokePaint.setStyle(SkPaint::kStroke_Style);
1643 strokePaint.setStrokeWidth(2);
1644 strokePaint.getFillPath(src, &dst);
1645 REPORTER_ASSERT(reporter, dst.isNestedRects(0));
caryclark@google.com56f233a2012-11-19 13:06:06 +00001646}
1647
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001648static void write_and_read_back(skiatest::Reporter* reporter,
1649 const SkPath& p) {
1650 SkWriter32 writer(100);
1651 writer.writePath(p);
1652 size_t size = writer.size();
1653 SkAutoMalloc storage(size);
1654 writer.flatten(storage.get());
1655 SkReader32 reader(storage.get(), size);
1656
1657 SkPath readBack;
1658 REPORTER_ASSERT(reporter, readBack != p);
1659 reader.readPath(&readBack);
1660 REPORTER_ASSERT(reporter, readBack == p);
1661
rmistry@google.comd6176b02012-08-23 18:14:13 +00001662 REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() ==
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001663 p.getConvexityOrUnknown());
1664
1665 REPORTER_ASSERT(reporter, readBack.isOval(NULL) == p.isOval(NULL));
1666
1667 const SkRect& origBounds = p.getBounds();
1668 const SkRect& readBackBounds = readBack.getBounds();
1669
1670 REPORTER_ASSERT(reporter, origBounds == readBackBounds);
1671}
1672
reed@google.com53effc52011-09-21 19:05:12 +00001673static void test_flattening(skiatest::Reporter* reporter) {
1674 SkPath p;
1675
1676 static const SkPoint pts[] = {
1677 { 0, 0 },
1678 { SkIntToScalar(10), SkIntToScalar(10) },
1679 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1680 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1681 };
1682 p.moveTo(pts[0]);
1683 p.lineTo(pts[1]);
1684 p.quadTo(pts[2], pts[3]);
1685 p.cubicTo(pts[4], pts[5], pts[6]);
1686
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001687 write_and_read_back(reporter, p);
djsollen@google.com94e75ee2012-06-08 18:30:46 +00001688
1689 // create a buffer that should be much larger than the path so we don't
1690 // kill our stack if writer goes too far.
1691 char buffer[1024];
1692 uint32_t size1 = p.writeToMemory(NULL);
1693 uint32_t size2 = p.writeToMemory(buffer);
1694 REPORTER_ASSERT(reporter, size1 == size2);
1695
1696 SkPath p2;
1697 uint32_t size3 = p2.readFromMemory(buffer);
1698 REPORTER_ASSERT(reporter, size1 == size3);
1699 REPORTER_ASSERT(reporter, p == p2);
1700
1701 char buffer2[1024];
1702 size3 = p2.writeToMemory(buffer2);
1703 REPORTER_ASSERT(reporter, size1 == size3);
1704 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001705
1706 // test persistence of the oval flag & convexity
1707 {
1708 SkPath oval;
1709 SkRect rect = SkRect::MakeWH(10, 10);
1710 oval.addOval(rect);
1711
1712 write_and_read_back(reporter, oval);
1713 }
reed@google.com53effc52011-09-21 19:05:12 +00001714}
1715
1716static void test_transform(skiatest::Reporter* reporter) {
1717 SkPath p, p1;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001718
reed@google.com53effc52011-09-21 19:05:12 +00001719 static const SkPoint pts[] = {
1720 { 0, 0 },
1721 { SkIntToScalar(10), SkIntToScalar(10) },
1722 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1723 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1724 };
1725 p.moveTo(pts[0]);
1726 p.lineTo(pts[1]);
1727 p.quadTo(pts[2], pts[3]);
1728 p.cubicTo(pts[4], pts[5], pts[6]);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001729
reed@google.com53effc52011-09-21 19:05:12 +00001730 SkMatrix matrix;
1731 matrix.reset();
1732 p.transform(matrix, &p1);
1733 REPORTER_ASSERT(reporter, p == p1);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001734
reed@google.com53effc52011-09-21 19:05:12 +00001735 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
1736 p.transform(matrix, &p1);
1737 SkPoint pts1[7];
1738 int count = p1.getPoints(pts1, 7);
1739 REPORTER_ASSERT(reporter, 7 == count);
1740 for (int i = 0; i < count; ++i) {
1741 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
1742 REPORTER_ASSERT(reporter, newPt == pts1[i]);
1743 }
1744}
1745
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001746static void test_zero_length_paths(skiatest::Reporter* reporter) {
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001747 SkPath p;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001748 uint8_t verbs[32];
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001749
caryclark@google.com56f233a2012-11-19 13:06:06 +00001750 struct SUPPRESS_VISIBILITY_WARNING zeroPathTestData {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001751 const char* testPath;
1752 const size_t numResultPts;
1753 const SkRect resultBound;
1754 const SkPath::Verb* resultVerbs;
1755 const size_t numResultVerbs;
1756 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001757
schenney@chromium.org7e963602012-06-13 17:05:43 +00001758 static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb };
1759 static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb };
1760 static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb };
1761 static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb };
1762 static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb };
1763 static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb };
1764 static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb };
1765 static const SkPath::Verb resultVerbs8[] = {
1766 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb
1767 };
1768 static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb };
1769 static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb };
1770 static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb };
1771 static const SkPath::Verb resultVerbs12[] = {
1772 SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb
1773 };
1774 static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb };
1775 static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb };
1776 static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb };
1777 static const SkPath::Verb resultVerbs16[] = {
1778 SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb
1779 };
1780 static const struct zeroPathTestData gZeroLengthTests[] = {
1781 { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001782 { "M 1 1 M 2 1", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
schenney@chromium.org7e963602012-06-13 17:05:43 +00001783 { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001784 { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1785 { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) },
1786 { "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) },
1787 { "M 1 1 L 1 1 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) },
1788 { "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) },
1789 { "M 1 1 Q 1 1 1 1", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) },
1790 { "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) },
1791 { "M 1 1 Q 1 1 1 1 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) },
1792 { "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) },
1793 { "M 1 1 C 1 1 1 1 1 1", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) },
1794 { "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,
schenney@chromium.org7e963602012-06-13 17:05:43 +00001795 SK_ARRAY_COUNT(resultVerbs14)
1796 },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001797 { "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) },
1798 { "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,
schenney@chromium.org7e963602012-06-13 17:05:43 +00001799 SK_ARRAY_COUNT(resultVerbs16)
1800 }
1801 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001802
schenney@chromium.org7e963602012-06-13 17:05:43 +00001803 for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) {
1804 p.reset();
1805 bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p);
1806 REPORTER_ASSERT(reporter, valid);
1807 REPORTER_ASSERT(reporter, !p.isEmpty());
1808 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints());
1809 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds());
1810 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
1811 for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) {
1812 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]);
1813 }
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001814 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001815}
1816
1817struct SegmentInfo {
1818 SkPath fPath;
1819 int fPointCount;
1820};
1821
reed@google.com10296cc2011-09-21 12:29:05 +00001822#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
1823
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001824static void test_segment_masks(skiatest::Reporter* reporter) {
reed@google.comeef938c2012-08-01 20:01:49 +00001825 SkPath p, p2;
1826
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001827 p.moveTo(0, 0);
1828 p.quadTo(100, 100, 200, 200);
1829 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
1830 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001831 p2 = p;
1832 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001833 p.cubicTo(100, 100, 200, 200, 300, 300);
1834 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
1835 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001836 p2 = p;
1837 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
1838
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001839 p.reset();
1840 p.moveTo(0, 0);
1841 p.cubicTo(100, 100, 200, 200, 300, 300);
1842 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
reed@google.comeef938c2012-08-01 20:01:49 +00001843 p2 = p;
1844 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
rmistry@google.comd6176b02012-08-23 18:14:13 +00001845
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001846 REPORTER_ASSERT(reporter, !p.isEmpty());
1847}
1848
1849static void test_iter(skiatest::Reporter* reporter) {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001850 SkPath p;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001851 SkPoint pts[4];
1852
1853 // Test an iterator with no path
1854 SkPath::Iter noPathIter;
1855 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001856
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001857 // Test that setting an empty path works
1858 noPathIter.setPath(p, false);
1859 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001860
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001861 // Test that close path makes no difference for an empty path
1862 noPathIter.setPath(p, true);
1863 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001864
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001865 // Test an iterator with an initial empty path
1866 SkPath::Iter iter(p, false);
1867 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1868
1869 // Test that close path makes no difference
schenney@chromium.org7e963602012-06-13 17:05:43 +00001870 iter.setPath(p, true);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001871 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1872
rmistry@google.comd6176b02012-08-23 18:14:13 +00001873
schenney@chromium.org7e963602012-06-13 17:05:43 +00001874 struct iterTestData {
1875 const char* testPath;
1876 const bool forceClose;
1877 const bool consumeDegenerates;
1878 const size_t* numResultPtsPerVerb;
1879 const SkPoint* resultPts;
1880 const SkPath::Verb* resultVerbs;
1881 const size_t numResultVerbs;
1882 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001883
schenney@chromium.org7e963602012-06-13 17:05:43 +00001884 static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb };
1885 static const SkPath::Verb resultVerbs2[] = {
1886 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb
1887 };
1888 static const SkPath::Verb resultVerbs3[] = {
1889 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1890 };
1891 static const SkPath::Verb resultVerbs4[] = {
1892 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1893 };
1894 static const SkPath::Verb resultVerbs5[] = {
1895 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1896 };
1897 static const size_t resultPtsSizes1[] = { 0 };
schenney@chromium.orgfedd09b2012-06-13 18:29:20 +00001898 static const size_t resultPtsSizes2[] = { 1, 2, 2, 0 };
1899 static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1, 0 };
1900 static const size_t resultPtsSizes4[] = { 1, 2, 1, 1, 0 };
1901 static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1, 0 };
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001902 static const SkPoint* resultPts1 = 0;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001903 static const SkPoint resultPts2[] = {
1904 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }
1905 };
1906 static const SkPoint resultPts3[] = {
1907 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 },
1908 { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }
1909 };
1910 static const SkPoint resultPts4[] = {
1911 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1912 };
1913 static const SkPoint resultPts5[] = {
1914 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1915 };
1916 static const struct iterTestData gIterTests[] = {
1917 { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001918 { "M 1 0 M 2 0 M 3 0 M 4 0 M 5 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1919 { "M 1 0 M 1 0 M 3 0 M 4 0 M 5 0", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.org7e963602012-06-13 17:05:43 +00001920 { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1921 { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1922 { "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) },
1923 { "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) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001924 { "M 1 0 L 1 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
1925 { "M 1 0 L 1 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
1926 { "M 1 0 L 1 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1927 { "M 1 0 L 1 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1928 { "M 1 0 L 1 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1929 { "M 1 0 L 1 0 M 0 0 z", true, false, resultPtsSizes5, resultPts5, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) }
schenney@chromium.org7e963602012-06-13 17:05:43 +00001930 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001931
schenney@chromium.org7e963602012-06-13 17:05:43 +00001932 for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) {
1933 p.reset();
1934 bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p);
1935 REPORTER_ASSERT(reporter, valid);
1936 iter.setPath(p, gIterTests[i].forceClose);
1937 int j = 0, l = 0;
1938 do {
1939 REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]);
1940 for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) {
1941 REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]);
1942 }
1943 } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb);
1944 REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs);
1945 }
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001946
1947 // The GM degeneratesegments.cpp test is more extensive
1948}
1949
1950static void test_raw_iter(skiatest::Reporter* reporter) {
1951 SkPath p;
1952 SkPoint pts[4];
1953
1954 // Test an iterator with no path
1955 SkPath::RawIter noPathIter;
1956 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1957 // Test that setting an empty path works
1958 noPathIter.setPath(p);
1959 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001960
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001961 // Test an iterator with an initial empty path
1962 SkPath::RawIter iter(p);
1963 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1964
1965 // Test that a move-only path returns the move.
1966 p.moveTo(SK_Scalar1, 0);
1967 iter.setPath(p);
1968 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1969 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1970 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1971 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1972
1973 // No matter how many moves we add, we should get them all back
1974 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1975 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1976 iter.setPath(p);
1977 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1978 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1979 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1980 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1981 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1982 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1983 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1984 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1985 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1986 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1987
1988 // Initial close is never ever stored
1989 p.reset();
1990 p.close();
1991 iter.setPath(p);
1992 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1993
1994 // Move/close sequences
1995 p.reset();
1996 p.close(); // Not stored, no purpose
1997 p.moveTo(SK_Scalar1, 0);
1998 p.close();
1999 p.close(); // Not stored, no purpose
2000 p.moveTo(SK_Scalar1*2, SK_Scalar1);
2001 p.close();
2002 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
2003 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
2004 p.close();
2005 iter.setPath(p);
2006 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
2007 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
2008 REPORTER_ASSERT(reporter, pts[0].fY == 0);
2009 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
2010 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
2011 REPORTER_ASSERT(reporter, pts[0].fY == 0);
2012 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
2013 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
2014 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
2015 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
2016 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
2017 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
2018 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
2019 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
2020 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
2021 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
2022 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
2023 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
2024 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
2025 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
2026 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
2027 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
2028
2029 // Generate random paths and verify
2030 SkPoint randomPts[25];
2031 for (int i = 0; i < 5; ++i) {
2032 for (int j = 0; j < 5; ++j) {
2033 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
2034 }
2035 }
2036
2037 // Max of 10 segments, max 3 points per segment
jvanverth@google.comc490f802013-03-04 13:56:38 +00002038 SkMWCRandom rand(9876543);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002039 SkPoint expectedPts[31]; // May have leading moveTo
reed@google.comd335d1d2012-01-12 18:17:11 +00002040 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002041 SkPath::Verb nextVerb;
reed@google.comd335d1d2012-01-12 18:17:11 +00002042
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002043 for (int i = 0; i < 500; ++i) {
2044 p.reset();
2045 bool lastWasClose = true;
2046 bool haveMoveTo = false;
reed@google.comd335d1d2012-01-12 18:17:11 +00002047 SkPoint lastMoveToPt = { 0, 0 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002048 int numPoints = 0;
2049 int numVerbs = (rand.nextU() >> 16) % 10;
2050 int numIterVerbs = 0;
2051 for (int j = 0; j < numVerbs; ++j) {
2052 do {
2053 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
2054 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002055 switch (nextVerb) {
2056 case SkPath::kMove_Verb:
2057 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
2058 p.moveTo(expectedPts[numPoints]);
reed@google.comd335d1d2012-01-12 18:17:11 +00002059 lastMoveToPt = expectedPts[numPoints];
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002060 numPoints += 1;
2061 lastWasClose = false;
2062 haveMoveTo = true;
2063 break;
2064 case SkPath::kLine_Verb:
2065 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00002066 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002067 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
2068 haveMoveTo = true;
2069 }
2070 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
2071 p.lineTo(expectedPts[numPoints]);
2072 numPoints += 1;
2073 lastWasClose = false;
2074 break;
2075 case SkPath::kQuad_Verb:
2076 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00002077 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002078 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
2079 haveMoveTo = true;
2080 }
2081 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
2082 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
2083 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
2084 numPoints += 2;
2085 lastWasClose = false;
2086 break;
reed@google.com277c3f82013-05-31 15:17:50 +00002087 case SkPath::kConic_Verb:
2088 if (!haveMoveTo) {
2089 expectedPts[numPoints++] = lastMoveToPt;
2090 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
2091 haveMoveTo = true;
2092 }
2093 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
2094 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
2095 p.conicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
2096 rand.nextUScalar1() * 4);
2097 numPoints += 2;
2098 lastWasClose = false;
2099 break;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002100 case SkPath::kCubic_Verb:
2101 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00002102 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002103 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
2104 haveMoveTo = true;
2105 }
2106 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
2107 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
2108 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
2109 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
2110 expectedPts[numPoints + 2]);
2111 numPoints += 3;
2112 lastWasClose = false;
2113 break;
2114 case SkPath::kClose_Verb:
2115 p.close();
reed@google.comd335d1d2012-01-12 18:17:11 +00002116 haveMoveTo = false;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002117 lastWasClose = true;
2118 break;
reed@google.com277c3f82013-05-31 15:17:50 +00002119 default:
mtklein@google.com330313a2013-08-22 15:37:26 +00002120 SkDEBUGFAIL("unexpected verb");
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002121 }
2122 expectedVerbs[numIterVerbs++] = nextVerb;
2123 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002124
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002125 iter.setPath(p);
2126 numVerbs = numIterVerbs;
2127 numIterVerbs = 0;
2128 int numIterPts = 0;
2129 SkPoint lastMoveTo;
2130 SkPoint lastPt;
2131 lastMoveTo.set(0, 0);
2132 lastPt.set(0, 0);
2133 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
2134 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
2135 numIterVerbs++;
2136 switch (nextVerb) {
2137 case SkPath::kMove_Verb:
2138 REPORTER_ASSERT(reporter, numIterPts < numPoints);
2139 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
2140 lastPt = lastMoveTo = pts[0];
2141 numIterPts += 1;
2142 break;
2143 case SkPath::kLine_Verb:
2144 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
2145 REPORTER_ASSERT(reporter, pts[0] == lastPt);
2146 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
2147 lastPt = pts[1];
2148 numIterPts += 1;
2149 break;
2150 case SkPath::kQuad_Verb:
reed@google.com277c3f82013-05-31 15:17:50 +00002151 case SkPath::kConic_Verb:
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002152 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
2153 REPORTER_ASSERT(reporter, pts[0] == lastPt);
2154 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
2155 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
2156 lastPt = pts[2];
2157 numIterPts += 2;
2158 break;
2159 case SkPath::kCubic_Verb:
2160 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
2161 REPORTER_ASSERT(reporter, pts[0] == lastPt);
2162 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
2163 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
2164 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
2165 lastPt = pts[3];
2166 numIterPts += 3;
2167 break;
2168 case SkPath::kClose_Verb:
2169 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
2170 lastPt = lastMoveTo;
2171 break;
reed@google.com277c3f82013-05-31 15:17:50 +00002172 default:
mtklein@google.com330313a2013-08-22 15:37:26 +00002173 SkDEBUGFAIL("unexpected verb");
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002174 }
2175 }
2176 REPORTER_ASSERT(reporter, numIterPts == numPoints);
2177 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
2178 }
2179}
2180
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002181static void check_for_circle(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002182 const SkPath& path,
2183 bool expectedCircle,
2184 SkPath::Direction expectedDir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002185 SkRect rect;
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002186 REPORTER_ASSERT(reporter, path.isOval(&rect) == expectedCircle);
2187 REPORTER_ASSERT(reporter, path.cheapIsDirection(expectedDir));
skia.committer@gmail.comfbb0ed92012-11-13 21:46:06 +00002188
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002189 if (expectedCircle) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002190 REPORTER_ASSERT(reporter, rect.height() == rect.width());
2191 }
2192}
2193
2194static void test_circle_skew(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002195 const SkPath& path,
2196 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002197 SkPath tmp;
2198
2199 SkMatrix m;
2200 m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
2201 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002202 // this matrix reverses the direction.
2203 if (SkPath::kCCW_Direction == dir) {
2204 dir = SkPath::kCW_Direction;
2205 } else {
2206 SkASSERT(SkPath::kCW_Direction == dir);
2207 dir = SkPath::kCCW_Direction;
2208 }
2209 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002210}
2211
2212static void test_circle_translate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002213 const SkPath& path,
2214 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002215 SkPath tmp;
2216
2217 // translate at small offset
2218 SkMatrix m;
2219 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
2220 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002221 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002222
2223 tmp.reset();
2224 m.reset();
2225
2226 // translate at a relatively big offset
2227 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
2228 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002229 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002230}
2231
2232static void test_circle_rotate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002233 const SkPath& path,
2234 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002235 for (int angle = 0; angle < 360; ++angle) {
2236 SkPath tmp;
2237 SkMatrix m;
2238 m.setRotate(SkIntToScalar(angle));
2239 path.transform(m, &tmp);
2240
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002241 // TODO: a rotated circle whose rotated angle is not a multiple of 90
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002242 // degrees is not an oval anymore, this can be improved. we made this
2243 // for the simplicity of our implementation.
2244 if (angle % 90 == 0) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002245 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002246 } else {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002247 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002248 }
2249 }
2250}
2251
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002252static void test_circle_mirror_x(skiatest::Reporter* reporter,
2253 const SkPath& path,
2254 SkPath::Direction dir) {
2255 SkPath tmp;
2256 SkMatrix m;
2257 m.reset();
2258 m.setScaleX(-SK_Scalar1);
2259 path.transform(m, &tmp);
2260
2261 if (SkPath::kCW_Direction == dir) {
2262 dir = SkPath::kCCW_Direction;
2263 } else {
2264 SkASSERT(SkPath::kCCW_Direction == dir);
2265 dir = SkPath::kCW_Direction;
2266 }
2267
2268 check_for_circle(reporter, tmp, true, dir);
2269}
2270
2271static void test_circle_mirror_y(skiatest::Reporter* reporter,
2272 const SkPath& path,
2273 SkPath::Direction dir) {
2274 SkPath tmp;
2275 SkMatrix m;
2276 m.reset();
2277 m.setScaleY(-SK_Scalar1);
2278 path.transform(m, &tmp);
2279
2280 if (SkPath::kCW_Direction == dir) {
2281 dir = SkPath::kCCW_Direction;
2282 } else {
2283 SkASSERT(SkPath::kCCW_Direction == dir);
2284 dir = SkPath::kCW_Direction;
2285 }
2286
2287 check_for_circle(reporter, tmp, true, dir);
2288}
2289
2290static void test_circle_mirror_xy(skiatest::Reporter* reporter,
2291 const SkPath& path,
2292 SkPath::Direction dir) {
2293 SkPath tmp;
2294 SkMatrix m;
2295 m.reset();
2296 m.setScaleX(-SK_Scalar1);
2297 m.setScaleY(-SK_Scalar1);
2298 path.transform(m, &tmp);
2299
2300 check_for_circle(reporter, tmp, true, dir);
2301}
2302
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002303static void test_circle_with_direction(skiatest::Reporter* reporter,
2304 SkPath::Direction dir) {
2305 SkPath path;
2306
2307 // circle at origin
2308 path.addCircle(0, 0, SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002309 check_for_circle(reporter, path, true, dir);
2310 test_circle_rotate(reporter, path, dir);
2311 test_circle_translate(reporter, path, dir);
2312 test_circle_skew(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002313
2314 // circle at an offset at (10, 10)
2315 path.reset();
2316 path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
2317 SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002318 check_for_circle(reporter, path, true, dir);
2319 test_circle_rotate(reporter, path, dir);
2320 test_circle_translate(reporter, path, dir);
2321 test_circle_skew(reporter, path, dir);
2322 test_circle_mirror_x(reporter, path, dir);
2323 test_circle_mirror_y(reporter, path, dir);
2324 test_circle_mirror_xy(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002325}
2326
2327static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
2328 SkPath path;
2329 SkPath circle;
2330 SkPath rect;
2331 SkPath empty;
2332
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002333 static const SkPath::Direction kCircleDir = SkPath::kCW_Direction;
2334 static const SkPath::Direction kCircleDirOpposite = SkPath::kCCW_Direction;
2335
2336 circle.addCircle(0, 0, SkIntToScalar(10), kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002337 rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
2338 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
2339
2340 SkMatrix translate;
2341 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
2342
2343 // For simplicity, all the path concatenation related operations
2344 // would mark it non-circle, though in theory it's still a circle.
2345
2346 // empty + circle (translate)
2347 path = empty;
2348 path.addPath(circle, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002349 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002350
2351 // circle + empty (translate)
2352 path = circle;
2353 path.addPath(empty, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002354 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002355
2356 // test reverseAddPath
2357 path = circle;
2358 path.reverseAddPath(rect);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002359 check_for_circle(reporter, path, false, kCircleDirOpposite);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002360}
2361
2362static void test_circle(skiatest::Reporter* reporter) {
2363 test_circle_with_direction(reporter, SkPath::kCW_Direction);
2364 test_circle_with_direction(reporter, SkPath::kCCW_Direction);
2365
2366 // multiple addCircle()
2367 SkPath path;
2368 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2369 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002370 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002371
2372 // some extra lineTo() would make isOval() fail
2373 path.reset();
2374 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2375 path.lineTo(0, 0);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002376 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002377
2378 // not back to the original point
2379 path.reset();
2380 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2381 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002382 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002383
2384 test_circle_with_add_paths(reporter);
2385}
2386
2387static void test_oval(skiatest::Reporter* reporter) {
2388 SkRect rect;
2389 SkMatrix m;
2390 SkPath path;
2391
2392 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
2393 path.addOval(rect);
2394
2395 REPORTER_ASSERT(reporter, path.isOval(NULL));
2396
2397 m.setRotate(SkIntToScalar(90));
2398 SkPath tmp;
2399 path.transform(m, &tmp);
2400 // an oval rotated 90 degrees is still an oval.
2401 REPORTER_ASSERT(reporter, tmp.isOval(NULL));
2402
2403 m.reset();
2404 m.setRotate(SkIntToScalar(30));
2405 tmp.reset();
2406 path.transform(m, &tmp);
2407 // an oval rotated 30 degrees is not an oval anymore.
2408 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2409
2410 // since empty path being transformed.
2411 path.reset();
2412 tmp.reset();
2413 m.reset();
2414 path.transform(m, &tmp);
2415 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2416
2417 // empty path is not an oval
2418 tmp.reset();
2419 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2420
2421 // only has moveTo()s
2422 tmp.reset();
2423 tmp.moveTo(0, 0);
2424 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
2425 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2426
2427 // mimic WebKit's calling convention,
2428 // call moveTo() first and then call addOval()
2429 path.reset();
2430 path.moveTo(0, 0);
2431 path.addOval(rect);
2432 REPORTER_ASSERT(reporter, path.isOval(NULL));
2433
2434 // copy path
2435 path.reset();
2436 tmp.reset();
2437 tmp.addOval(rect);
2438 path = tmp;
2439 REPORTER_ASSERT(reporter, path.isOval(NULL));
2440}
2441
bungeman@google.coma5809a32013-06-21 15:13:34 +00002442static void test_empty(skiatest::Reporter* reporter, const SkPath& p) {
2443 SkPath empty;
reed@android.com80e39a72009-04-02 16:59:40 +00002444
reed@android.com3abec1d2009-03-02 05:36:20 +00002445 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002446 REPORTER_ASSERT(reporter, 0 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002447 REPORTER_ASSERT(reporter, 0 == p.countVerbs());
reed@google.com10296cc2011-09-21 12:29:05 +00002448 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00002449 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00002450 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
2451 REPORTER_ASSERT(reporter, !p.isInverseFillType());
bungeman@google.coma5809a32013-06-21 15:13:34 +00002452 REPORTER_ASSERT(reporter, p == empty);
2453 REPORTER_ASSERT(reporter, !(p != empty));
2454}
2455
2456static void TestPath(skiatest::Reporter* reporter) {
2457 SkTSize<SkScalar>::Make(3,4);
2458
2459 SkPath p, empty;
2460 SkRect bounds, bounds2;
2461 test_empty(reporter, p);
reed@android.com3abec1d2009-03-02 05:36:20 +00002462
reed@android.comd252db02009-04-01 18:31:44 +00002463 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00002464
reed@android.com3abec1d2009-03-02 05:36:20 +00002465 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002466
reed@android.com6b82d1a2009-06-03 02:35:01 +00002467 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
2468 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002469 // we have quads or cubics
2470 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002471 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002472
reed@android.com6b82d1a2009-06-03 02:35:01 +00002473 p.reset();
bungeman@google.coma5809a32013-06-21 15:13:34 +00002474 test_empty(reporter, p);
reed@google.com10296cc2011-09-21 12:29:05 +00002475
reed@android.com6b82d1a2009-06-03 02:35:01 +00002476 p.addOval(bounds);
2477 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002478 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002479
bungeman@google.coma5809a32013-06-21 15:13:34 +00002480 p.rewind();
2481 test_empty(reporter, p);
2482
reed@android.com3abec1d2009-03-02 05:36:20 +00002483 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002484 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002485 // we have only lines
2486 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002487 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00002488
bungeman@google.coma5809a32013-06-21 15:13:34 +00002489 REPORTER_ASSERT(reporter, p != empty);
2490 REPORTER_ASSERT(reporter, !(p == empty));
reed@android.com3abec1d2009-03-02 05:36:20 +00002491
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002492 // do getPoints and getVerbs return the right result
2493 REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
2494 REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
reed@android.com3abec1d2009-03-02 05:36:20 +00002495 SkPoint pts[4];
2496 int count = p.getPoints(pts, 4);
2497 REPORTER_ASSERT(reporter, count == 4);
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002498 uint8_t verbs[6];
2499 verbs[5] = 0xff;
2500 p.getVerbs(verbs, 5);
2501 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
2502 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
2503 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
2504 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
2505 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
2506 REPORTER_ASSERT(reporter, 0xff == verbs[5]);
reed@android.com3abec1d2009-03-02 05:36:20 +00002507 bounds2.set(pts, 4);
2508 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002509
reed@android.com3abec1d2009-03-02 05:36:20 +00002510 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
2511 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00002512 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00002513
reed@android.com3abec1d2009-03-02 05:36:20 +00002514 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00002515 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00002516 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
2517 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002518
reed@android.com3abec1d2009-03-02 05:36:20 +00002519 // now force p to not be a rect
2520 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
2521 p.addRect(bounds);
2522 REPORTER_ASSERT(reporter, !p.isRect(NULL));
reed@android.com3abec1d2009-03-02 05:36:20 +00002523
reed@google.com7e6c4d12012-05-10 14:05:43 +00002524 test_isLine(reporter);
2525 test_isRect(reporter);
caryclark@google.com56f233a2012-11-19 13:06:06 +00002526 test_isNestedRects(reporter);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002527 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00002528 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00002529 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00002530 test_convexity2(reporter);
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00002531 test_conservativelyContains(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00002532 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002533 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00002534 test_flattening(reporter);
2535 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00002536 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002537 test_iter(reporter);
2538 test_raw_iter(reporter);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002539 test_circle(reporter);
2540 test_oval(reporter);
reed@google.com8b06f1a2012-05-29 12:03:46 +00002541 test_strokerec(reporter);
reed@google.com744faba2012-05-29 19:54:52 +00002542 test_addPoly(reporter);
reed@google.com0bb18bb2012-07-26 15:20:36 +00002543 test_isfinite(reporter);
tomhudson@google.comed02c4d2012-08-10 14:10:45 +00002544 test_isfinite_after_transform(reporter);
robertphillips@google.comb95eaa82012-10-18 15:26:12 +00002545 test_arb_round_rect_is_convex(reporter);
robertphillips@google.com158618e2012-10-23 16:56:56 +00002546 test_arb_zero_rad_round_rect_is_rect(reporter);
reed@google.coma8790de2012-10-24 21:04:04 +00002547 test_addrect_isfinite(reporter);
sugoi@google.com54f0d1b2013-02-27 19:17:41 +00002548 test_tricky_cubic();
2549 test_clipped_cubic();
2550 test_crbug_170666();
reed@google.com7a90daf2013-04-10 18:44:00 +00002551 test_bad_cubic_crbug229478();
reed@google.com3eff3592013-05-08 21:08:21 +00002552 test_bad_cubic_crbug234190();
mtklein@google.com9c9d4a72013-08-07 19:17:53 +00002553 test_android_specific_behavior(reporter);
commit-bot@chromium.org9d54aeb2013-08-09 19:48:26 +00002554 test_path_close_issue1474(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +00002555}
2556
2557#include "TestClassDef.h"
2558DEFINE_TESTCLASS("Path", PathTestClass, TestPath)