blob: ff51edd1d8e6319a560afecdc00e9d5f36060153 [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
commit-bot@chromium.org9d54aeb2013-08-09 19:48:26 +000028static void test_path_close_issue1474(skiatest::Reporter* reporter) {
29 // This test checks that r{Line,Quad,Conic,Cubic}To following a close()
30 // are relative to the point we close to, not relative to the point we close from.
31 SkPath path;
32 SkPoint last;
33
34 // Test rLineTo().
35 path.rLineTo(0, 100);
36 path.rLineTo(100, 0);
37 path.close(); // Returns us back to 0,0.
38 path.rLineTo(50, 50); // This should go to 50,50.
39
40 path.getLastPt(&last);
41 REPORTER_ASSERT(reporter, 50 == last.fX);
42 REPORTER_ASSERT(reporter, 50 == last.fY);
43
44 // Test rQuadTo().
45 path.rewind();
46 path.rLineTo(0, 100);
47 path.rLineTo(100, 0);
48 path.close();
49 path.rQuadTo(50, 50, 75, 75);
50
51 path.getLastPt(&last);
52 REPORTER_ASSERT(reporter, 75 == last.fX);
53 REPORTER_ASSERT(reporter, 75 == last.fY);
54
55 // Test rConicTo().
56 path.rewind();
57 path.rLineTo(0, 100);
58 path.rLineTo(100, 0);
59 path.close();
60 path.rConicTo(50, 50, 85, 85, 2);
61
62 path.getLastPt(&last);
63 REPORTER_ASSERT(reporter, 85 == last.fX);
64 REPORTER_ASSERT(reporter, 85 == last.fY);
65
66 // Test rCubicTo().
67 path.rewind();
68 path.rLineTo(0, 100);
69 path.rLineTo(100, 0);
70 path.close();
71 path.rCubicTo(50, 50, 85, 85, 95, 95);
72
73 path.getLastPt(&last);
74 REPORTER_ASSERT(reporter, 95 == last.fX);
75 REPORTER_ASSERT(reporter, 95 == last.fY);
76}
77
mtklein@google.com9c9d4a72013-08-07 19:17:53 +000078static void test_android_specific_behavior(skiatest::Reporter* reporter) {
79#ifdef SK_BUILD_FOR_ANDROID
mtklein@google.comcb8b0ee2013-08-15 21:14:51 +000080 // Make sure we treat fGenerationID and fSourcePath correctly for each of
81 // copy, assign, rewind, reset, and swap.
82 SkPath original, source, anotherSource;
83 original.setSourcePath(&source);
mtklein@google.com9c9d4a72013-08-07 19:17:53 +000084 original.moveTo(0, 0);
85 original.lineTo(1, 1);
86 REPORTER_ASSERT(reporter, original.getGenerationID() > 0);
mtklein@google.comcb8b0ee2013-08-15 21:14:51 +000087 REPORTER_ASSERT(reporter, original.getSourcePath() == &source);
mtklein@google.com9c9d4a72013-08-07 19:17:53 +000088
mtklein@google.comcb8b0ee2013-08-15 21:14:51 +000089 uint32_t copyID, assignID;
90
91 // Test copy constructor. Copy generation ID, copy source path.
92 SkPath copy(original);
mtklein@google.com9c9d4a72013-08-07 19:17:53 +000093 REPORTER_ASSERT(reporter, copy.getGenerationID() == original.getGenerationID());
mtklein@google.comcb8b0ee2013-08-15 21:14:51 +000094 REPORTER_ASSERT(reporter, copy.getSourcePath() == original.getSourcePath());
mtklein@google.com9c9d4a72013-08-07 19:17:53 +000095
mtklein@google.comcb8b0ee2013-08-15 21:14:51 +000096 // Test assigment operator. Increment generation ID, copy source path.
mtklein@google.com9c9d4a72013-08-07 19:17:53 +000097 SkPath assign;
mtklein@google.comcb8b0ee2013-08-15 21:14:51 +000098 assignID = assign.getGenerationID();
mtklein@google.com9c9d4a72013-08-07 19:17:53 +000099 assign = original;
mtklein@google.comcb8b0ee2013-08-15 21:14:51 +0000100 REPORTER_ASSERT(reporter, assign.getGenerationID() > assignID);
101 REPORTER_ASSERT(reporter, assign.getSourcePath() == original.getSourcePath());
102
103 // Test rewind. Increment generation ID, don't touch source path.
104 copyID = copy.getGenerationID();
105 copy.rewind();
106 REPORTER_ASSERT(reporter, copy.getGenerationID() > copyID);
107 REPORTER_ASSERT(reporter, copy.getSourcePath() == original.getSourcePath());
108
109 // Test reset. Increment generation ID, don't touch source path.
110 assignID = assign.getGenerationID();
111 assign.reset();
112 REPORTER_ASSERT(reporter, assign.getGenerationID() > assignID);
113 REPORTER_ASSERT(reporter, assign.getSourcePath() == original.getSourcePath());
114
115 // Test swap. Increment both generation IDs, swap source paths.
116 copy.setSourcePath(&anotherSource);
117 copyID = copy.getGenerationID();
118 assignID = assign.getGenerationID();
119 copy.swap(assign);
120 REPORTER_ASSERT(reporter, copy.getGenerationID() > copyID);
121 REPORTER_ASSERT(reporter, assign.getGenerationID() > assignID);
122 REPORTER_ASSERT(reporter, copy.getSourcePath() == original.getSourcePath());
123 REPORTER_ASSERT(reporter, assign.getSourcePath() == &anotherSource);
mtklein@google.com9c9d4a72013-08-07 19:17:53 +0000124#endif
125}
126
reed@google.com3eff3592013-05-08 21:08:21 +0000127// This used to assert in the debug build, as the edges did not all line-up.
128static void test_bad_cubic_crbug234190() {
129 SkPath path;
130 path.moveTo(13.8509f, 3.16858f);
131 path.cubicTo(-2.35893e+08f, -4.21044e+08f,
132 -2.38991e+08f, -4.26573e+08f,
133 -2.41016e+08f, -4.30188e+08f);
134
135 SkPaint paint;
136 paint.setAntiAlias(true);
reed@google.comd28ba802013-09-20 19:33:52 +0000137 SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(84, 88));
reed@google.com3eff3592013-05-08 21:08:21 +0000138 surface->getCanvas()->drawPath(path, paint);
139}
140
reed@google.com7a90daf2013-04-10 18:44:00 +0000141static void test_bad_cubic_crbug229478() {
142 const SkPoint pts[] = {
skia.committer@gmail.com391ca662013-04-11 07:01:45 +0000143 { 4595.91064f, -11596.9873f },
144 { 4597.2168f, -11595.9414f },
145 { 4598.52344f, -11594.8955f },
146 { 4599.83008f, -11593.8496f },
reed@google.com7a90daf2013-04-10 18:44:00 +0000147 };
skia.committer@gmail.com391ca662013-04-11 07:01:45 +0000148
reed@google.com7a90daf2013-04-10 18:44:00 +0000149 SkPath path;
150 path.moveTo(pts[0]);
151 path.cubicTo(pts[1], pts[2], pts[3]);
skia.committer@gmail.com391ca662013-04-11 07:01:45 +0000152
reed@google.com7a90daf2013-04-10 18:44:00 +0000153 SkPaint paint;
154 paint.setStyle(SkPaint::kStroke_Style);
155 paint.setStrokeWidth(20);
skia.committer@gmail.com391ca662013-04-11 07:01:45 +0000156
reed@google.com7a90daf2013-04-10 18:44:00 +0000157 SkPath dst;
158 // Before the fix, this would infinite-recurse, and run out of stack
159 // because we would keep trying to subdivide a degenerate cubic segment.
160 paint.getFillPath(path, &dst, NULL);
161}
162
reed@google.com64d62952013-01-18 17:49:28 +0000163static void build_path_170666(SkPath& path) {
164 path.moveTo(17.9459f, 21.6344f);
165 path.lineTo(139.545f, -47.8105f);
166 path.lineTo(139.545f, -47.8105f);
167 path.lineTo(131.07f, -47.3888f);
168 path.lineTo(131.07f, -47.3888f);
169 path.lineTo(122.586f, -46.9532f);
170 path.lineTo(122.586f, -46.9532f);
171 path.lineTo(18076.6f, 31390.9f);
172 path.lineTo(18076.6f, 31390.9f);
173 path.lineTo(18085.1f, 31390.5f);
174 path.lineTo(18085.1f, 31390.5f);
175 path.lineTo(18076.6f, 31390.9f);
176 path.lineTo(18076.6f, 31390.9f);
177 path.lineTo(17955, 31460.3f);
178 path.lineTo(17955, 31460.3f);
179 path.lineTo(17963.5f, 31459.9f);
180 path.lineTo(17963.5f, 31459.9f);
181 path.lineTo(17971.9f, 31459.5f);
182 path.lineTo(17971.9f, 31459.5f);
183 path.lineTo(17.9551f, 21.6205f);
184 path.lineTo(17.9551f, 21.6205f);
185 path.lineTo(9.47091f, 22.0561f);
186 path.lineTo(9.47091f, 22.0561f);
187 path.lineTo(17.9459f, 21.6344f);
188 path.lineTo(17.9459f, 21.6344f);
189 path.close();path.moveTo(0.995934f, 22.4779f);
190 path.lineTo(0.986725f, 22.4918f);
191 path.lineTo(0.986725f, 22.4918f);
192 path.lineTo(17955, 31460.4f);
193 path.lineTo(17955, 31460.4f);
194 path.lineTo(17971.9f, 31459.5f);
195 path.lineTo(17971.9f, 31459.5f);
196 path.lineTo(18093.6f, 31390.1f);
197 path.lineTo(18093.6f, 31390.1f);
198 path.lineTo(18093.6f, 31390);
199 path.lineTo(18093.6f, 31390);
200 path.lineTo(139.555f, -47.8244f);
201 path.lineTo(139.555f, -47.8244f);
202 path.lineTo(122.595f, -46.9671f);
203 path.lineTo(122.595f, -46.9671f);
204 path.lineTo(0.995934f, 22.4779f);
205 path.lineTo(0.995934f, 22.4779f);
206 path.close();
207 path.moveTo(5.43941f, 25.5223f);
208 path.lineTo(798267, -28871.1f);
209 path.lineTo(798267, -28871.1f);
210 path.lineTo(3.12512e+06f, -113102);
211 path.lineTo(3.12512e+06f, -113102);
212 path.cubicTo(5.16324e+06f, -186882, 8.15247e+06f, -295092, 1.1957e+07f, -432813);
213 path.cubicTo(1.95659e+07f, -708257, 3.04359e+07f, -1.10175e+06f, 4.34798e+07f, -1.57394e+06f);
214 path.cubicTo(6.95677e+07f, -2.51831e+06f, 1.04352e+08f, -3.77748e+06f, 1.39135e+08f, -5.03666e+06f);
215 path.cubicTo(1.73919e+08f, -6.29583e+06f, 2.08703e+08f, -7.555e+06f, 2.34791e+08f, -8.49938e+06f);
216 path.cubicTo(2.47835e+08f, -8.97157e+06f, 2.58705e+08f, -9.36506e+06f, 2.66314e+08f, -9.6405e+06f);
217 path.cubicTo(2.70118e+08f, -9.77823e+06f, 2.73108e+08f, -9.88644e+06f, 2.75146e+08f, -9.96022e+06f);
218 path.cubicTo(2.76165e+08f, -9.99711e+06f, 2.76946e+08f, -1.00254e+07f, 2.77473e+08f, -1.00444e+07f);
219 path.lineTo(2.78271e+08f, -1.00733e+07f);
220 path.lineTo(2.78271e+08f, -1.00733e+07f);
221 path.cubicTo(2.78271e+08f, -1.00733e+07f, 2.08703e+08f, -7.555e+06f, 135.238f, 23.3517f);
222 path.cubicTo(131.191f, 23.4981f, 125.995f, 23.7976f, 123.631f, 24.0206f);
223 path.cubicTo(121.267f, 24.2436f, 122.631f, 24.3056f, 126.677f, 24.1591f);
224 path.cubicTo(2.08703e+08f, -7.555e+06f, 2.78271e+08f, -1.00733e+07f, 2.78271e+08f, -1.00733e+07f);
225 path.lineTo(2.77473e+08f, -1.00444e+07f);
226 path.lineTo(2.77473e+08f, -1.00444e+07f);
227 path.cubicTo(2.76946e+08f, -1.00254e+07f, 2.76165e+08f, -9.99711e+06f, 2.75146e+08f, -9.96022e+06f);
228 path.cubicTo(2.73108e+08f, -9.88644e+06f, 2.70118e+08f, -9.77823e+06f, 2.66314e+08f, -9.6405e+06f);
229 path.cubicTo(2.58705e+08f, -9.36506e+06f, 2.47835e+08f, -8.97157e+06f, 2.34791e+08f, -8.49938e+06f);
230 path.cubicTo(2.08703e+08f, -7.555e+06f, 1.73919e+08f, -6.29583e+06f, 1.39135e+08f, -5.03666e+06f);
231 path.cubicTo(1.04352e+08f, -3.77749e+06f, 6.95677e+07f, -2.51831e+06f, 4.34798e+07f, -1.57394e+06f);
232 path.cubicTo(3.04359e+07f, -1.10175e+06f, 1.95659e+07f, -708258, 1.1957e+07f, -432814);
233 path.cubicTo(8.15248e+06f, -295092, 5.16324e+06f, -186883, 3.12513e+06f, -113103);
234 path.lineTo(798284, -28872);
235 path.lineTo(798284, -28872);
236 path.lineTo(22.4044f, 24.6677f);
237 path.lineTo(22.4044f, 24.6677f);
238 path.cubicTo(22.5186f, 24.5432f, 18.8134f, 24.6337f, 14.1287f, 24.8697f);
239 path.cubicTo(9.4439f, 25.1057f, 5.55359f, 25.3978f, 5.43941f, 25.5223f);
240 path.close();
241}
242
243static void build_path_simple_170666(SkPath& path) {
244 path.moveTo(126.677f, 24.1591f);
245 path.cubicTo(2.08703e+08f, -7.555e+06f, 2.78271e+08f, -1.00733e+07f, 2.78271e+08f, -1.00733e+07f);
246}
247
248// This used to assert in the SK_DEBUG build, as the clip step would fail with
249// too-few interations in our cubic-line intersection code. That code now runs
250// 24 interations (instead of 16).
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000251static void test_crbug_170666() {
reed@google.com64d62952013-01-18 17:49:28 +0000252 SkPath path;
253 SkPaint paint;
254 paint.setAntiAlias(true);
255
reed@google.comd28ba802013-09-20 19:33:52 +0000256 SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(1000, 1000));
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000257
reed@google.com64d62952013-01-18 17:49:28 +0000258 build_path_simple_170666(path);
259 surface->getCanvas()->drawPath(path, paint);
skia.committer@gmail.com36df7ed2013-01-19 07:05:38 +0000260
reed@google.com64d62952013-01-18 17:49:28 +0000261 build_path_170666(path);
262 surface->getCanvas()->drawPath(path, paint);
263}
264
reed@google.coma8790de2012-10-24 21:04:04 +0000265// Make sure we stay non-finite once we get there (unless we reset or rewind).
266static void test_addrect_isfinite(skiatest::Reporter* reporter) {
267 SkPath path;
skia.committer@gmail.com8b0e2342012-10-25 02:01:20 +0000268
reed@google.coma8790de2012-10-24 21:04:04 +0000269 path.addRect(SkRect::MakeWH(50, 100));
270 REPORTER_ASSERT(reporter, path.isFinite());
271
272 path.moveTo(0, 0);
273 path.lineTo(SK_ScalarInfinity, 42);
274 REPORTER_ASSERT(reporter, !path.isFinite());
275
276 path.addRect(SkRect::MakeWH(50, 100));
277 REPORTER_ASSERT(reporter, !path.isFinite());
278
279 path.reset();
280 REPORTER_ASSERT(reporter, path.isFinite());
skia.committer@gmail.com8b0e2342012-10-25 02:01:20 +0000281
reed@google.coma8790de2012-10-24 21:04:04 +0000282 path.addRect(SkRect::MakeWH(50, 100));
283 REPORTER_ASSERT(reporter, path.isFinite());
284}
285
reed@google.com848148e2013-01-15 15:51:59 +0000286static void build_big_path(SkPath* path, bool reducedCase) {
287 if (reducedCase) {
288 path->moveTo(577330, 1971.72f);
289 path->cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f);
290 } else {
291 path->moveTo(60.1631f, 7.70567f);
292 path->quadTo(60.1631f, 7.70567f, 0.99474f, 0.901199f);
293 path->lineTo(577379, 1977.77f);
294 path->quadTo(577364, 1979.57f, 577325, 1980.26f);
295 path->quadTo(577286, 1980.95f, 577245, 1980.13f);
296 path->quadTo(577205, 1979.3f, 577187, 1977.45f);
297 path->quadTo(577168, 1975.6f, 577183, 1973.8f);
298 path->quadTo(577198, 1972, 577238, 1971.31f);
299 path->quadTo(577277, 1970.62f, 577317, 1971.45f);
300 path->quadTo(577330, 1971.72f, 577341, 1972.11f);
301 path->cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f);
302 path->moveTo(306.718f, -32.912f);
303 path->cubicTo(30.531f, 10.0005f, 1502.47f, 13.2804f, 84.3088f, 9.99601f);
304 }
305}
306
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000307static void test_clipped_cubic() {
reed@google.comd28ba802013-09-20 19:33:52 +0000308 SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterPMColor(640, 480));
reed@google.com848148e2013-01-15 15:51:59 +0000309
310 // This path used to assert, because our cubic-chopping code incorrectly
311 // moved control points after the chop. This test should be run in SK_DEBUG
312 // mode to ensure that we no long assert.
313 SkPath path;
314 for (int doReducedCase = 0; doReducedCase <= 1; ++doReducedCase) {
315 build_big_path(&path, SkToBool(doReducedCase));
skia.committer@gmail.comff21c2e2013-01-16 07:05:56 +0000316
reed@google.com848148e2013-01-15 15:51:59 +0000317 SkPaint paint;
318 for (int doAA = 0; doAA <= 1; ++doAA) {
319 paint.setAntiAlias(SkToBool(doAA));
320 surface->getCanvas()->drawPath(path, paint);
321 }
322 }
323}
324
reed@google.com8cae8352012-09-14 15:18:41 +0000325// Inspired by http://ie.microsoft.com/testdrive/Performance/Chalkboard/
326// which triggered an assert, from a tricky cubic. This test replicates that
327// example, so we can ensure that we handle it (in SkEdge.cpp), and don't
328// assert in the SK_DEBUG build.
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000329static void test_tricky_cubic() {
reed@google.com8cae8352012-09-14 15:18:41 +0000330 const SkPoint pts[] = {
skia.committer@gmail.com055c7c22012-09-15 02:01:41 +0000331 { SkDoubleToScalar(18.8943768), SkDoubleToScalar(129.121277) },
332 { SkDoubleToScalar(18.8937435), SkDoubleToScalar(129.121689) },
333 { SkDoubleToScalar(18.8950119), SkDoubleToScalar(129.120422) },
334 { SkDoubleToScalar(18.5030727), SkDoubleToScalar(129.13121) },
reed@google.com8cae8352012-09-14 15:18:41 +0000335 };
336
337 SkPath path;
338 path.moveTo(pts[0]);
339 path.cubicTo(pts[1], pts[2], pts[3]);
340
341 SkPaint paint;
342 paint.setAntiAlias(true);
343
reed@google.comd28ba802013-09-20 19:33:52 +0000344 SkSurface* surface = SkSurface::NewRasterPMColor(19, 130);
reed@google.com8cae8352012-09-14 15:18:41 +0000345 surface->getCanvas()->drawPath(path, paint);
346 surface->unref();
347}
reed@android.com3abec1d2009-03-02 05:36:20 +0000348
tomhudson@google.comed02c4d2012-08-10 14:10:45 +0000349// Inspired by http://code.google.com/p/chromium/issues/detail?id=141651
350//
351static void test_isfinite_after_transform(skiatest::Reporter* reporter) {
352 SkPath path;
353 path.quadTo(157, 366, 286, 208);
354 path.arcTo(37, 442, 315, 163, 957494590897113.0f);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000355
tomhudson@google.comed02c4d2012-08-10 14:10:45 +0000356 SkMatrix matrix;
357 matrix.setScale(1000*1000, 1000*1000);
358
359 // Be sure that path::transform correctly updates isFinite and the bounds
360 // if the transformation overflows. The previous bug was that isFinite was
361 // set to true in this case, but the bounds were not set to empty (which
362 // they should be).
363 while (path.isFinite()) {
364 REPORTER_ASSERT(reporter, path.getBounds().isFinite());
365 REPORTER_ASSERT(reporter, !path.getBounds().isEmpty());
366 path.transform(matrix);
367 }
368 REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
369
370 matrix.setTranslate(SK_Scalar1, SK_Scalar1);
371 path.transform(matrix);
372 // we need to still be non-finite
373 REPORTER_ASSERT(reporter, !path.isFinite());
374 REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
375}
376
skia.committer@gmail.com6a748ad2012-10-19 02:01:19 +0000377static void add_corner_arc(SkPath* path, const SkRect& rect,
378 SkScalar xIn, SkScalar yIn,
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000379 int startAngle)
380{
381
382 SkScalar rx = SkMinScalar(rect.width(), xIn);
383 SkScalar ry = SkMinScalar(rect.height(), yIn);
384
385 SkRect arcRect;
386 arcRect.set(-rx, -ry, rx, ry);
387 switch (startAngle) {
388 case 0:
389 arcRect.offset(rect.fRight - arcRect.fRight, rect.fBottom - arcRect.fBottom);
390 break;
391 case 90:
392 arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fBottom - arcRect.fBottom);
393 break;
394 case 180:
395 arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fTop - arcRect.fTop);
396 break;
397 case 270:
398 arcRect.offset(rect.fRight - arcRect.fRight, rect.fTop - arcRect.fTop);
399 break;
400 default:
401 break;
402 }
403
404 path->arcTo(arcRect, SkIntToScalar(startAngle), SkIntToScalar(90), false);
405}
406
skia.committer@gmail.com6a748ad2012-10-19 02:01:19 +0000407static void make_arb_round_rect(SkPath* path, const SkRect& r,
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000408 SkScalar xCorner, SkScalar yCorner) {
409 // we are lazy here and use the same x & y for each corner
410 add_corner_arc(path, r, xCorner, yCorner, 270);
411 add_corner_arc(path, r, xCorner, yCorner, 0);
412 add_corner_arc(path, r, xCorner, yCorner, 90);
413 add_corner_arc(path, r, xCorner, yCorner, 180);
robertphillips@google.com158618e2012-10-23 16:56:56 +0000414 path->close();
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000415}
416
417// Chrome creates its own round rects with each corner possibly being different.
418// Performance will suffer if they are not convex.
419// Note: PathBench::ArbRoundRectBench performs almost exactly
420// the same test (but with drawing)
421static void test_arb_round_rect_is_convex(skiatest::Reporter* reporter) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000422 SkRandom rand;
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000423 SkRect r;
424
425 for (int i = 0; i < 5000; ++i) {
426
robertphillips@google.com158618e2012-10-23 16:56:56 +0000427 SkScalar size = rand.nextUScalar1() * 30;
428 if (size < SK_Scalar1) {
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000429 continue;
430 }
431 r.fLeft = rand.nextUScalar1() * 300;
432 r.fTop = rand.nextUScalar1() * 300;
robertphillips@google.com158618e2012-10-23 16:56:56 +0000433 r.fRight = r.fLeft + 2 * size;
434 r.fBottom = r.fTop + 2 * size;
robertphillips@google.comb95eaa82012-10-18 15:26:12 +0000435
436 SkPath temp;
437
438 make_arb_round_rect(&temp, r, r.width() / 10, r.height() / 15);
439
440 REPORTER_ASSERT(reporter, temp.isConvex());
441 }
442}
443
robertphillips@google.com158618e2012-10-23 16:56:56 +0000444// Chrome will sometimes create a 0 radius round rect. The degenerate
445// quads prevent the path from being converted to a rect
446// Note: PathBench::ArbRoundRectBench performs almost exactly
447// the same test (but with drawing)
448static void test_arb_zero_rad_round_rect_is_rect(skiatest::Reporter* reporter) {
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000449 SkRandom rand;
robertphillips@google.com158618e2012-10-23 16:56:56 +0000450 SkRect r;
451
452 for (int i = 0; i < 5000; ++i) {
453
454 SkScalar size = rand.nextUScalar1() * 30;
455 if (size < SK_Scalar1) {
456 continue;
457 }
458 r.fLeft = rand.nextUScalar1() * 300;
459 r.fTop = rand.nextUScalar1() * 300;
460 r.fRight = r.fLeft + 2 * size;
461 r.fBottom = r.fTop + 2 * size;
462
463 SkPath temp;
464
465 make_arb_round_rect(&temp, r, 0, 0);
466
robertphillips@google.com158618e2012-10-23 16:56:56 +0000467 SkRect result;
468 REPORTER_ASSERT(reporter, temp.isRect(&result));
469 REPORTER_ASSERT(reporter, r == result);
robertphillips@google.com158618e2012-10-23 16:56:56 +0000470 }
471}
472
reed@google.com0bb18bb2012-07-26 15:20:36 +0000473static void test_rect_isfinite(skiatest::Reporter* reporter) {
474 const SkScalar inf = SK_ScalarInfinity;
caryclark@google.com7abfa492013-04-12 11:59:41 +0000475 const SkScalar negInf = SK_ScalarNegativeInfinity;
reed@google.com0bb18bb2012-07-26 15:20:36 +0000476 const SkScalar nan = SK_ScalarNaN;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000477
reed@google.com0bb18bb2012-07-26 15:20:36 +0000478 SkRect r;
479 r.setEmpty();
480 REPORTER_ASSERT(reporter, r.isFinite());
caryclark@google.com7abfa492013-04-12 11:59:41 +0000481 r.set(0, 0, inf, negInf);
reed@google.com0bb18bb2012-07-26 15:20:36 +0000482 REPORTER_ASSERT(reporter, !r.isFinite());
483 r.set(0, 0, nan, 0);
484 REPORTER_ASSERT(reporter, !r.isFinite());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000485
reed@google.com0bb18bb2012-07-26 15:20:36 +0000486 SkPoint pts[] = {
487 { 0, 0 },
488 { SK_Scalar1, 0 },
489 { 0, SK_Scalar1 },
490 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000491
reed@google.com0bb18bb2012-07-26 15:20:36 +0000492 bool isFine = r.setBoundsCheck(pts, 3);
493 REPORTER_ASSERT(reporter, isFine);
494 REPORTER_ASSERT(reporter, !r.isEmpty());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000495
reed@google.com0bb18bb2012-07-26 15:20:36 +0000496 pts[1].set(inf, 0);
497 isFine = r.setBoundsCheck(pts, 3);
498 REPORTER_ASSERT(reporter, !isFine);
499 REPORTER_ASSERT(reporter, r.isEmpty());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000500
reed@google.com0bb18bb2012-07-26 15:20:36 +0000501 pts[1].set(nan, 0);
502 isFine = r.setBoundsCheck(pts, 3);
503 REPORTER_ASSERT(reporter, !isFine);
504 REPORTER_ASSERT(reporter, r.isEmpty());
505}
506
507static void test_path_isfinite(skiatest::Reporter* reporter) {
508 const SkScalar inf = SK_ScalarInfinity;
bsalomon@google.com50c79d82013-01-08 20:31:53 +0000509 const SkScalar negInf = SK_ScalarNegativeInfinity;
reed@google.com0bb18bb2012-07-26 15:20:36 +0000510 const SkScalar nan = SK_ScalarNaN;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000511
reed@google.com0bb18bb2012-07-26 15:20:36 +0000512 SkPath path;
513 REPORTER_ASSERT(reporter, path.isFinite());
514
515 path.reset();
516 REPORTER_ASSERT(reporter, path.isFinite());
517
518 path.reset();
519 path.moveTo(SK_Scalar1, 0);
520 REPORTER_ASSERT(reporter, path.isFinite());
521
522 path.reset();
bsalomon@google.com50c79d82013-01-08 20:31:53 +0000523 path.moveTo(inf, negInf);
reed@google.com0bb18bb2012-07-26 15:20:36 +0000524 REPORTER_ASSERT(reporter, !path.isFinite());
525
526 path.reset();
527 path.moveTo(nan, 0);
528 REPORTER_ASSERT(reporter, !path.isFinite());
529}
530
531static void test_isfinite(skiatest::Reporter* reporter) {
532 test_rect_isfinite(reporter);
533 test_path_isfinite(reporter);
534}
535
reed@google.com744faba2012-05-29 19:54:52 +0000536// assert that we always
537// start with a moveTo
538// only have 1 moveTo
539// only have Lines after that
540// end with a single close
541// only have (at most) 1 close
542//
543static void test_poly(skiatest::Reporter* reporter, const SkPath& path,
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000544 const SkPoint srcPts[], bool expectClose) {
reed@google.com744faba2012-05-29 19:54:52 +0000545 SkPath::RawIter iter(path);
546 SkPoint pts[4];
reed@google.com744faba2012-05-29 19:54:52 +0000547
548 bool firstTime = true;
549 bool foundClose = false;
550 for (;;) {
551 switch (iter.next(pts)) {
552 case SkPath::kMove_Verb:
553 REPORTER_ASSERT(reporter, firstTime);
554 REPORTER_ASSERT(reporter, pts[0] == srcPts[0]);
555 srcPts++;
556 firstTime = false;
557 break;
558 case SkPath::kLine_Verb:
559 REPORTER_ASSERT(reporter, !firstTime);
560 REPORTER_ASSERT(reporter, pts[1] == srcPts[0]);
561 srcPts++;
562 break;
563 case SkPath::kQuad_Verb:
mtklein@google.com330313a2013-08-22 15:37:26 +0000564 REPORTER_ASSERT_MESSAGE(reporter, false, "unexpected quad verb");
reed@google.com744faba2012-05-29 19:54:52 +0000565 break;
reed@google.com277c3f82013-05-31 15:17:50 +0000566 case SkPath::kConic_Verb:
mtklein@google.com330313a2013-08-22 15:37:26 +0000567 REPORTER_ASSERT_MESSAGE(reporter, false, "unexpected conic verb");
reed@google.com277c3f82013-05-31 15:17:50 +0000568 break;
reed@google.com744faba2012-05-29 19:54:52 +0000569 case SkPath::kCubic_Verb:
mtklein@google.com330313a2013-08-22 15:37:26 +0000570 REPORTER_ASSERT_MESSAGE(reporter, false, "unexpected cubic verb");
reed@google.com744faba2012-05-29 19:54:52 +0000571 break;
572 case SkPath::kClose_Verb:
573 REPORTER_ASSERT(reporter, !firstTime);
574 REPORTER_ASSERT(reporter, !foundClose);
575 REPORTER_ASSERT(reporter, expectClose);
576 foundClose = true;
577 break;
578 case SkPath::kDone_Verb:
579 goto DONE;
580 }
581 }
582DONE:
583 REPORTER_ASSERT(reporter, foundClose == expectClose);
584}
585
586static void test_addPoly(skiatest::Reporter* reporter) {
587 SkPoint pts[32];
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000588 SkRandom rand;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000589
reed@google.com744faba2012-05-29 19:54:52 +0000590 for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) {
591 pts[i].fX = rand.nextSScalar1();
592 pts[i].fY = rand.nextSScalar1();
593 }
594
595 for (int doClose = 0; doClose <= 1; ++doClose) {
596 for (size_t count = 1; count <= SK_ARRAY_COUNT(pts); ++count) {
597 SkPath path;
598 path.addPoly(pts, count, SkToBool(doClose));
sugoi@google.com54f0d1b2013-02-27 19:17:41 +0000599 test_poly(reporter, path, pts, SkToBool(doClose));
reed@google.com744faba2012-05-29 19:54:52 +0000600 }
601 }
602}
603
reed@google.com8b06f1a2012-05-29 12:03:46 +0000604static void test_strokerec(skiatest::Reporter* reporter) {
605 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
606 REPORTER_ASSERT(reporter, rec.isFillStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000607
reed@google.com8b06f1a2012-05-29 12:03:46 +0000608 rec.setHairlineStyle();
609 REPORTER_ASSERT(reporter, rec.isHairlineStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000610
reed@google.com8b06f1a2012-05-29 12:03:46 +0000611 rec.setStrokeStyle(SK_Scalar1, false);
612 REPORTER_ASSERT(reporter, SkStrokeRec::kStroke_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000613
reed@google.com8b06f1a2012-05-29 12:03:46 +0000614 rec.setStrokeStyle(SK_Scalar1, true);
615 REPORTER_ASSERT(reporter, SkStrokeRec::kStrokeAndFill_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000616
reed@google.com8b06f1a2012-05-29 12:03:46 +0000617 rec.setStrokeStyle(0, false);
618 REPORTER_ASSERT(reporter, SkStrokeRec::kHairline_Style == rec.getStyle());
rmistry@google.comd6176b02012-08-23 18:14:13 +0000619
reed@google.com8b06f1a2012-05-29 12:03:46 +0000620 rec.setStrokeStyle(0, true);
621 REPORTER_ASSERT(reporter, SkStrokeRec::kFill_Style == rec.getStyle());
622}
623
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000624// Set this for paths that don't have a consistent direction such as a bowtie.
625// (cheapComputeDirection is not expected to catch these.)
626static const SkPath::Direction kDontCheckDir = static_cast<SkPath::Direction>(-1);
627
628static void check_direction(skiatest::Reporter* reporter, const SkPath& path,
629 SkPath::Direction expected) {
630 if (expected == kDontCheckDir) {
631 return;
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000632 }
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000633 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
634
635 SkPath::Direction dir;
636 if (copy.cheapComputeDirection(&dir)) {
637 REPORTER_ASSERT(reporter, dir == expected);
638 } else {
639 REPORTER_ASSERT(reporter, SkPath::kUnknown_Direction == expected);
640 }
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000641}
642
reed@google.com3e71a882012-01-10 18:44:37 +0000643static void test_direction(skiatest::Reporter* reporter) {
644 size_t i;
645 SkPath path;
646 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
647 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction));
648 REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction));
reed@google.coma8a3b3d2012-11-26 18:16:27 +0000649 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kUnknown_Direction));
reed@google.com3e71a882012-01-10 18:44:37 +0000650
651 static const char* gDegen[] = {
652 "M 10 10",
653 "M 10 10 M 20 20",
654 "M 10 10 L 20 20",
655 "M 10 10 L 10 10 L 10 10",
656 "M 10 10 Q 10 10 10 10",
657 "M 10 10 C 10 10 10 10 10 10",
658 };
659 for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) {
660 path.reset();
661 bool valid = SkParsePath::FromSVGString(gDegen[i], &path);
662 REPORTER_ASSERT(reporter, valid);
663 REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
664 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000665
reed@google.com3e71a882012-01-10 18:44:37 +0000666 static const char* gCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000667 "M 10 10 L 10 10 Q 20 10 20 20",
reed@google.com3e71a882012-01-10 18:44:37 +0000668 "M 10 10 C 20 10 20 20 20 20",
reed@google.comd4146662012-01-31 15:42:29 +0000669 "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 +0000670 // rect with top two corners replaced by cubics with identical middle
671 // control points
reed@google.com20fb0c72012-11-13 13:47:39 +0000672 "M 10 10 C 10 0 10 0 20 0 L 40 0 C 50 0 50 0 50 10",
673 "M 20 10 L 0 10 Q 10 10 20 0", // left, degenerate serif
reed@google.com3e71a882012-01-10 18:44:37 +0000674 };
675 for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) {
676 path.reset();
677 bool valid = SkParsePath::FromSVGString(gCW[i], &path);
678 REPORTER_ASSERT(reporter, valid);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000679 check_direction(reporter, path, SkPath::kCW_Direction);
reed@google.com3e71a882012-01-10 18:44:37 +0000680 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000681
reed@google.com3e71a882012-01-10 18:44:37 +0000682 static const char* gCCW[] = {
reed@google.comcabaf1d2012-01-11 21:03:05 +0000683 "M 10 10 L 10 10 Q 20 10 20 -20",
reed@google.com3e71a882012-01-10 18:44:37 +0000684 "M 10 10 C 20 10 20 -20 20 -20",
reed@google.comd4146662012-01-31 15:42:29 +0000685 "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 +0000686 // rect with top two corners replaced by cubics with identical middle
687 // control points
reed@google.com20fb0c72012-11-13 13:47:39 +0000688 "M 50 10 C 50 0 50 0 40 0 L 20 0 C 10 0 10 0 10 10",
689 "M 10 10 L 30 10 Q 20 10 10 0", // right, degenerate serif
reed@google.com3e71a882012-01-10 18:44:37 +0000690 };
691 for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) {
692 path.reset();
693 bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
694 REPORTER_ASSERT(reporter, valid);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000695 check_direction(reporter, path, SkPath::kCCW_Direction);
reed@google.com3e71a882012-01-10 18:44:37 +0000696 }
reed@google.comac8543f2012-01-30 20:51:25 +0000697
698 // Test two donuts, each wound a different direction. Only the outer contour
699 // determines the cheap direction
700 path.reset();
701 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
702 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000703 check_direction(reporter, path, SkPath::kCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000704
reed@google.comac8543f2012-01-30 20:51:25 +0000705 path.reset();
706 path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
707 path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000708 check_direction(reporter, path, SkPath::kCCW_Direction);
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000709
bsalomon@google.com6843ac42012-02-17 13:49:03 +0000710#ifdef SK_SCALAR_IS_FLOAT
bsalomon@google.comf0ed80a2012-02-17 13:38:26 +0000711 // triangle with one point really far from the origin.
712 path.reset();
713 // the first point is roughly 1.05e10, 1.05e10
bsalomon@google.com53aab782012-02-23 14:54:49 +0000714 path.moveTo(SkFloatToScalar(SkBits2Float(0x501c7652)), SkFloatToScalar(SkBits2Float(0x501c7652)));
715 path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1);
716 path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000717 check_direction(reporter, path, SkPath::kCCW_Direction);
bsalomon@google.com53aab782012-02-23 14:54:49 +0000718#endif
reed@google.com3e71a882012-01-10 18:44:37 +0000719}
720
reed@google.comffdb0182011-11-14 19:29:14 +0000721static void add_rect(SkPath* path, const SkRect& r) {
722 path->moveTo(r.fLeft, r.fTop);
723 path->lineTo(r.fRight, r.fTop);
724 path->lineTo(r.fRight, r.fBottom);
725 path->lineTo(r.fLeft, r.fBottom);
726 path->close();
727}
728
729static void test_bounds(skiatest::Reporter* reporter) {
730 static const SkRect rects[] = {
reed@google.com3563c9e2011-11-14 19:34:57 +0000731 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
732 { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
733 { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
734 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
reed@google.comffdb0182011-11-14 19:29:14 +0000735 };
736
737 SkPath path0, path1;
738 for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
739 path0.addRect(rects[i]);
740 add_rect(&path1, rects[i]);
741 }
742
743 REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
744}
745
reed@google.com55b5f4b2011-09-07 12:23:41 +0000746static void stroke_cubic(const SkPoint pts[4]) {
747 SkPath path;
748 path.moveTo(pts[0]);
749 path.cubicTo(pts[1], pts[2], pts[3]);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000750
reed@google.com55b5f4b2011-09-07 12:23:41 +0000751 SkPaint paint;
752 paint.setStyle(SkPaint::kStroke_Style);
753 paint.setStrokeWidth(SK_Scalar1 * 2);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000754
reed@google.com55b5f4b2011-09-07 12:23:41 +0000755 SkPath fill;
756 paint.getFillPath(path, &fill);
757}
758
759// just ensure this can run w/o any SkASSERTS firing in the debug build
760// we used to assert due to differences in how we determine a degenerate vector
761// but that was fixed with the introduction of SkPoint::CanNormalize
762static void stroke_tiny_cubic() {
763 SkPoint p0[] = {
764 { 372.0f, 92.0f },
765 { 372.0f, 92.0f },
766 { 372.0f, 92.0f },
767 { 372.0f, 92.0f },
768 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000769
reed@google.com55b5f4b2011-09-07 12:23:41 +0000770 stroke_cubic(p0);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000771
reed@google.com55b5f4b2011-09-07 12:23:41 +0000772 SkPoint p1[] = {
773 { 372.0f, 92.0f },
774 { 372.0007f, 92.000755f },
775 { 371.99927f, 92.003922f },
776 { 371.99826f, 92.003899f },
777 };
rmistry@google.comd6176b02012-08-23 18:14:13 +0000778
reed@google.com55b5f4b2011-09-07 12:23:41 +0000779 stroke_cubic(p1);
780}
781
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000782static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
783 for (int i = 0; i < 2; ++i) {
robertphillips@google.com09042b82012-04-06 20:01:46 +0000784 SkPath::Iter iter(path, SkToBool(i));
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000785 SkPoint mv;
786 SkPoint pts[4];
787 SkPath::Verb v;
788 int nMT = 0;
789 int nCL = 0;
tomhudson@google.com221db3c2011-07-28 21:10:29 +0000790 mv.set(0, 0);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000791 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
792 switch (v) {
793 case SkPath::kMove_Verb:
794 mv = pts[0];
795 ++nMT;
796 break;
797 case SkPath::kClose_Verb:
798 REPORTER_ASSERT(reporter, mv == pts[0]);
799 ++nCL;
800 break;
801 default:
802 break;
803 }
804 }
805 // if we force a close on the interator we should have a close
806 // for every moveTo
807 REPORTER_ASSERT(reporter, !i || nMT == nCL);
808 }
809}
810
811static void test_close(skiatest::Reporter* reporter) {
812 SkPath closePt;
813 closePt.moveTo(0, 0);
814 closePt.close();
815 check_close(reporter, closePt);
816
817 SkPath openPt;
818 openPt.moveTo(0, 0);
819 check_close(reporter, openPt);
820
821 SkPath empty;
822 check_close(reporter, empty);
823 empty.close();
824 check_close(reporter, empty);
825
826 SkPath rect;
827 rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
828 check_close(reporter, rect);
829 rect.close();
830 check_close(reporter, rect);
831
832 SkPath quad;
833 quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
834 check_close(reporter, quad);
835 quad.close();
836 check_close(reporter, quad);
837
838 SkPath cubic;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000839 quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000840 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
841 check_close(reporter, cubic);
842 cubic.close();
843 check_close(reporter, cubic);
844
845 SkPath line;
846 line.moveTo(SK_Scalar1, SK_Scalar1);
847 line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
848 check_close(reporter, line);
849 line.close();
850 check_close(reporter, line);
851
852 SkPath rect2;
853 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
854 rect2.close();
855 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
856 check_close(reporter, rect2);
857 rect2.close();
858 check_close(reporter, rect2);
859
860 SkPath oval3;
861 oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
862 oval3.close();
863 oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
864 check_close(reporter, oval3);
865 oval3.close();
866 check_close(reporter, oval3);
867
868 SkPath moves;
869 moves.moveTo(SK_Scalar1, SK_Scalar1);
870 moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
871 moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
872 moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
873 check_close(reporter, moves);
reed@google.com55b5f4b2011-09-07 12:23:41 +0000874
875 stroke_tiny_cubic();
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +0000876}
877
reed@google.com7c424812011-05-15 04:38:34 +0000878static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
879 SkPath::Convexity expected) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000880 SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
881 SkPath::Convexity c = copy.getConvexity();
reed@google.com7c424812011-05-15 04:38:34 +0000882 REPORTER_ASSERT(reporter, c == expected);
883}
884
885static void test_convexity2(skiatest::Reporter* reporter) {
886 SkPath pt;
887 pt.moveTo(0, 0);
888 pt.close();
reed@google.comb54455e2011-05-16 14:16:04 +0000889 check_convexity(reporter, pt, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000890 check_direction(reporter, pt, SkPath::kUnknown_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000891
reed@google.com7c424812011-05-15 04:38:34 +0000892 SkPath line;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000893 line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
894 line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000895 line.close();
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000896 check_convexity(reporter, line, SkPath::kConvex_Convexity);
897 check_direction(reporter, line, SkPath::kUnknown_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000898
reed@google.com7c424812011-05-15 04:38:34 +0000899 SkPath triLeft;
900 triLeft.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000901 triLeft.lineTo(SK_Scalar1, 0);
902 triLeft.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000903 triLeft.close();
904 check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000905 check_direction(reporter, triLeft, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000906
reed@google.com7c424812011-05-15 04:38:34 +0000907 SkPath triRight;
908 triRight.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000909 triRight.lineTo(-SK_Scalar1, 0);
910 triRight.lineTo(SK_Scalar1, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000911 triRight.close();
912 check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000913 check_direction(reporter, triRight, SkPath::kCCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000914
reed@google.com7c424812011-05-15 04:38:34 +0000915 SkPath square;
916 square.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000917 square.lineTo(SK_Scalar1, 0);
918 square.lineTo(SK_Scalar1, SK_Scalar1);
919 square.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000920 square.close();
921 check_convexity(reporter, square, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000922 check_direction(reporter, square, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000923
reed@google.com7c424812011-05-15 04:38:34 +0000924 SkPath redundantSquare;
925 redundantSquare.moveTo(0, 0);
926 redundantSquare.lineTo(0, 0);
927 redundantSquare.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000928 redundantSquare.lineTo(SK_Scalar1, 0);
929 redundantSquare.lineTo(SK_Scalar1, 0);
930 redundantSquare.lineTo(SK_Scalar1, 0);
931 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
932 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
933 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
934 redundantSquare.lineTo(0, SK_Scalar1);
935 redundantSquare.lineTo(0, SK_Scalar1);
936 redundantSquare.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000937 redundantSquare.close();
938 check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000939 check_direction(reporter, redundantSquare, SkPath::kCW_Direction);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000940
reed@google.com7c424812011-05-15 04:38:34 +0000941 SkPath bowTie;
942 bowTie.moveTo(0, 0);
943 bowTie.lineTo(0, 0);
944 bowTie.lineTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000945 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
946 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
947 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
948 bowTie.lineTo(SK_Scalar1, 0);
949 bowTie.lineTo(SK_Scalar1, 0);
950 bowTie.lineTo(SK_Scalar1, 0);
951 bowTie.lineTo(0, SK_Scalar1);
952 bowTie.lineTo(0, SK_Scalar1);
953 bowTie.lineTo(0, SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000954 bowTie.close();
955 check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000956 check_direction(reporter, bowTie, kDontCheckDir);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000957
reed@google.com7c424812011-05-15 04:38:34 +0000958 SkPath spiral;
959 spiral.moveTo(0, 0);
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000960 spiral.lineTo(100*SK_Scalar1, 0);
961 spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
962 spiral.lineTo(0, 100*SK_Scalar1);
963 spiral.lineTo(0, 50*SK_Scalar1);
964 spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
965 spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000966 spiral.close();
reed@google.com85b6e392011-05-15 20:25:17 +0000967 check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000968 check_direction(reporter, spiral, kDontCheckDir);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000969
reed@google.com7c424812011-05-15 04:38:34 +0000970 SkPath dent;
schenney@chromium.org6c31d9d2011-12-20 16:33:30 +0000971 dent.moveTo(0, 0);
972 dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
973 dent.lineTo(0, 100*SK_Scalar1);
974 dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
975 dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
reed@google.com7c424812011-05-15 04:38:34 +0000976 dent.close();
977 check_convexity(reporter, dent, SkPath::kConcave_Convexity);
bsalomon@google.com30c174b2012-11-13 14:36:42 +0000978 check_direction(reporter, dent, SkPath::kCW_Direction);
reed@google.com7c424812011-05-15 04:38:34 +0000979}
980
reed@android.com6b82d1a2009-06-03 02:35:01 +0000981static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
982 const SkRect& bounds) {
983 REPORTER_ASSERT(reporter, p.isConvex());
984 REPORTER_ASSERT(reporter, p.getBounds() == bounds);
reed@google.com62047cf2011-02-07 19:39:09 +0000985
reed@android.com6b82d1a2009-06-03 02:35:01 +0000986 SkPath p2(p);
987 REPORTER_ASSERT(reporter, p2.isConvex());
988 REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
989
990 SkPath other;
991 other.swap(p2);
992 REPORTER_ASSERT(reporter, other.isConvex());
993 REPORTER_ASSERT(reporter, other.getBounds() == bounds);
994}
995
reed@google.com04863fa2011-05-15 04:08:24 +0000996static void setFromString(SkPath* path, const char str[]) {
997 bool first = true;
998 while (str) {
999 SkScalar x, y;
1000 str = SkParse::FindScalar(str, &x);
1001 if (NULL == str) {
1002 break;
1003 }
1004 str = SkParse::FindScalar(str, &y);
1005 SkASSERT(str);
1006 if (first) {
1007 path->moveTo(x, y);
1008 first = false;
1009 } else {
1010 path->lineTo(x, y);
1011 }
1012 }
1013}
1014
1015static void test_convexity(skiatest::Reporter* reporter) {
reed@google.com04863fa2011-05-15 04:08:24 +00001016 SkPath path;
1017
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001018 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.come3543972012-01-10 18:59:22 +00001019 path.addCircle(0, 0, SkIntToScalar(10));
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001020 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.come3543972012-01-10 18:59:22 +00001021 path.addCircle(0, 0, SkIntToScalar(10)); // 2nd circle
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001022 check_convexity(reporter, path, SkPath::kConcave_Convexity);
1023
reed@google.com04863fa2011-05-15 04:08:24 +00001024 path.reset();
reed@google.come3543972012-01-10 18:59:22 +00001025 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001026 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.com3e71a882012-01-10 18:44:37 +00001027 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001028
reed@google.com04863fa2011-05-15 04:08:24 +00001029 path.reset();
reed@google.come3543972012-01-10 18:59:22 +00001030 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001031 check_convexity(reporter, path, SkPath::kConvex_Convexity);
reed@google.com3e71a882012-01-10 18:44:37 +00001032 REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001033
reed@google.com04863fa2011-05-15 04:08:24 +00001034 static const struct {
1035 const char* fPathStr;
1036 SkPath::Convexity fExpectedConvexity;
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001037 SkPath::Direction fExpectedDirection;
reed@google.com04863fa2011-05-15 04:08:24 +00001038 } gRec[] = {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001039 { "", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
1040 { "0 0", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
1041 { "0 0 10 10", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
1042 { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity, SkPath::kUnknown_Direction },
1043 { "0 0 10 10 10 20", SkPath::kConvex_Convexity, SkPath::kCW_Direction },
1044 { "0 0 10 10 10 0", SkPath::kConvex_Convexity, SkPath::kCCW_Direction },
1045 { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity, kDontCheckDir },
1046 { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity, SkPath::kCW_Direction },
reed@google.com04863fa2011-05-15 04:08:24 +00001047 };
1048
1049 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
1050 SkPath path;
1051 setFromString(&path, gRec[i].fPathStr);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00001052 check_convexity(reporter, path, gRec[i].fExpectedConvexity);
1053 check_direction(reporter, path, gRec[i].fExpectedDirection);
reed@google.com04863fa2011-05-15 04:08:24 +00001054 }
1055}
1056
reed@google.com7e6c4d12012-05-10 14:05:43 +00001057static void test_isLine(skiatest::Reporter* reporter) {
1058 SkPath path;
1059 SkPoint pts[2];
1060 const SkScalar value = SkIntToScalar(5);
1061
1062 REPORTER_ASSERT(reporter, !path.isLine(NULL));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001063
reed@google.com7e6c4d12012-05-10 14:05:43 +00001064 // set some non-zero values
1065 pts[0].set(value, value);
1066 pts[1].set(value, value);
1067 REPORTER_ASSERT(reporter, !path.isLine(pts));
1068 // check that pts was untouched
1069 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
1070 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
1071
1072 const SkScalar moveX = SkIntToScalar(1);
1073 const SkScalar moveY = SkIntToScalar(2);
1074 SkASSERT(value != moveX && value != moveY);
1075
1076 path.moveTo(moveX, moveY);
1077 REPORTER_ASSERT(reporter, !path.isLine(NULL));
1078 REPORTER_ASSERT(reporter, !path.isLine(pts));
1079 // check that pts was untouched
1080 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
1081 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
1082
1083 const SkScalar lineX = SkIntToScalar(2);
1084 const SkScalar lineY = SkIntToScalar(2);
1085 SkASSERT(value != lineX && value != lineY);
1086
1087 path.lineTo(lineX, lineY);
1088 REPORTER_ASSERT(reporter, path.isLine(NULL));
1089
1090 REPORTER_ASSERT(reporter, !pts[0].equals(moveX, moveY));
1091 REPORTER_ASSERT(reporter, !pts[1].equals(lineX, lineY));
1092 REPORTER_ASSERT(reporter, path.isLine(pts));
1093 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
1094 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
1095
1096 path.lineTo(0, 0); // too many points/verbs
1097 REPORTER_ASSERT(reporter, !path.isLine(NULL));
1098 REPORTER_ASSERT(reporter, !path.isLine(pts));
1099 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
1100 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
1101}
1102
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001103static void test_conservativelyContains(skiatest::Reporter* reporter) {
1104 SkPath path;
1105
1106 // kBaseRect is used to construct most our test paths: a rect, a circle, and a round-rect.
1107 static const SkRect kBaseRect = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
1108
1109 // A circle that bounds kBaseRect (with a significant amount of slop)
1110 SkScalar circleR = SkMaxScalar(kBaseRect.width(), kBaseRect.height());
1111 circleR = SkScalarMul(circleR, SkFloatToScalar(1.75f)) / 2;
1112 static const SkPoint kCircleC = {kBaseRect.centerX(), kBaseRect.centerY()};
1113
1114 // round-rect radii
1115 static const SkScalar kRRRadii[] = {SkIntToScalar(5), SkIntToScalar(3)};
skia.committer@gmail.comcec8de62012-11-14 02:01:22 +00001116
caryclark@google.com56f233a2012-11-19 13:06:06 +00001117 static const struct SUPPRESS_VISIBILITY_WARNING {
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001118 SkRect fQueryRect;
1119 bool fInRect;
1120 bool fInCircle;
1121 bool fInRR;
1122 } kQueries[] = {
1123 {kBaseRect, true, true, false},
1124
1125 // rect well inside of kBaseRect
1126 {SkRect::MakeLTRB(kBaseRect.fLeft + SkFloatToScalar(0.25f)*kBaseRect.width(),
1127 kBaseRect.fTop + SkFloatToScalar(0.25f)*kBaseRect.height(),
1128 kBaseRect.fRight - SkFloatToScalar(0.25f)*kBaseRect.width(),
1129 kBaseRect.fBottom - SkFloatToScalar(0.25f)*kBaseRect.height()),
1130 true, true, true},
1131
1132 // rects with edges off by one from kBaseRect's edges
1133 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1134 kBaseRect.width(), kBaseRect.height() + 1),
1135 false, true, false},
1136 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1137 kBaseRect.width() + 1, kBaseRect.height()),
1138 false, true, false},
1139 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1140 kBaseRect.width() + 1, kBaseRect.height() + 1),
1141 false, true, false},
1142 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
1143 kBaseRect.width(), kBaseRect.height()),
1144 false, true, false},
1145 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
1146 kBaseRect.width(), kBaseRect.height()),
1147 false, true, false},
1148 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
1149 kBaseRect.width() + 2, kBaseRect.height()),
1150 false, true, false},
1151 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
1152 kBaseRect.width() + 2, kBaseRect.height()),
1153 false, true, false},
1154
1155 // zero-w/h rects at each corner of kBaseRect
1156 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop, 0, 0), true, true, false},
1157 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fTop, 0, 0), true, true, false},
1158 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fBottom, 0, 0), true, true, false},
1159 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fBottom, 0, 0), true, true, false},
1160
1161 // far away rect
1162 {SkRect::MakeXYWH(10 * kBaseRect.fRight, 10 * kBaseRect.fBottom,
1163 SkIntToScalar(10), SkIntToScalar(10)),
1164 false, false, false},
1165
1166 // very large rect containing kBaseRect
1167 {SkRect::MakeXYWH(kBaseRect.fLeft - 5 * kBaseRect.width(),
1168 kBaseRect.fTop - 5 * kBaseRect.height(),
1169 11 * kBaseRect.width(), 11 * kBaseRect.height()),
1170 false, false, false},
1171
1172 // skinny rect that spans same y-range as kBaseRect
1173 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
1174 SkIntToScalar(1), kBaseRect.height()),
1175 true, true, true},
1176
1177 // short rect that spans same x-range as kBaseRect
1178 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(), kBaseRect.width(), SkScalar(1)),
1179 true, true, true},
1180
1181 // skinny rect that spans slightly larger y-range than kBaseRect
1182 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
1183 SkIntToScalar(1), kBaseRect.height() + 1),
1184 false, true, false},
1185
1186 // short rect that spans slightly larger x-range than kBaseRect
1187 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(),
1188 kBaseRect.width() + 1, SkScalar(1)),
1189 false, true, false},
1190 };
1191
1192 for (int inv = 0; inv < 4; ++inv) {
caryclark@google.com56f233a2012-11-19 13:06:06 +00001193 for (size_t q = 0; q < SK_ARRAY_COUNT(kQueries); ++q) {
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001194 SkRect qRect = kQueries[q].fQueryRect;
1195 if (inv & 0x1) {
1196 SkTSwap(qRect.fLeft, qRect.fRight);
1197 }
1198 if (inv & 0x2) {
1199 SkTSwap(qRect.fTop, qRect.fBottom);
1200 }
1201 for (int d = 0; d < 2; ++d) {
1202 SkPath::Direction dir = d ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
1203 path.reset();
1204 path.addRect(kBaseRect, dir);
1205 REPORTER_ASSERT(reporter, kQueries[q].fInRect ==
1206 path.conservativelyContainsRect(qRect));
1207
1208 path.reset();
1209 path.addCircle(kCircleC.fX, kCircleC.fY, circleR, dir);
1210 REPORTER_ASSERT(reporter, kQueries[q].fInCircle ==
1211 path.conservativelyContainsRect(qRect));
1212
1213 path.reset();
1214 path.addRoundRect(kBaseRect, kRRRadii[0], kRRRadii[1], dir);
1215 REPORTER_ASSERT(reporter, kQueries[q].fInRR ==
1216 path.conservativelyContainsRect(qRect));
1217 }
1218 // Slightly non-convex shape, shouldn't contain any rects.
1219 path.reset();
1220 path.moveTo(0, 0);
1221 path.lineTo(SkIntToScalar(50), SkFloatToScalar(0.05f));
1222 path.lineTo(SkIntToScalar(100), 0);
1223 path.lineTo(SkIntToScalar(100), SkIntToScalar(100));
1224 path.lineTo(0, SkIntToScalar(100));
1225 path.close();
1226 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(qRect));
1227 }
1228 }
1229
1230 // make sure a minimal convex shape works, a right tri with edges along pos x and y axes.
1231 path.reset();
1232 path.moveTo(0, 0);
1233 path.lineTo(SkIntToScalar(100), 0);
1234 path.lineTo(0, SkIntToScalar(100));
1235
1236 // inside, on along top edge
1237 REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
1238 SkIntToScalar(10),
1239 SkIntToScalar(10))));
1240 // above
1241 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(
1242 SkRect::MakeXYWH(SkIntToScalar(50),
1243 SkIntToScalar(-10),
1244 SkIntToScalar(10),
1245 SkIntToScalar(10))));
1246 // to the left
1247 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(-10),
1248 SkIntToScalar(5),
1249 SkIntToScalar(5),
1250 SkIntToScalar(5))));
1251
1252 // outside the diagonal edge
1253 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(10),
1254 SkIntToScalar(200),
1255 SkIntToScalar(20),
1256 SkIntToScalar(5))));
commit-bot@chromium.org62df5262013-08-01 15:35:06 +00001257
1258 // same as above path and first test but with an extra moveTo.
1259 path.reset();
1260 path.moveTo(100, 100);
1261 path.moveTo(0, 0);
1262 path.lineTo(SkIntToScalar(100), 0);
1263 path.lineTo(0, SkIntToScalar(100));
1264
1265 REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
1266 SkIntToScalar(10),
1267 SkIntToScalar(10))));
1268
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00001269}
1270
caryclark@google.comf1316942011-07-26 19:54:45 +00001271// Simple isRect test is inline TestPath, below.
1272// test_isRect provides more extensive testing.
1273static void test_isRect(skiatest::Reporter* reporter) {
1274 // passing tests (all moveTo / lineTo...
1275 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
1276 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1277 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1278 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
1279 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
1280 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1281 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1282 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1283 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1284 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
1285 {1, 0}, {.5f, 0}};
1286 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
1287 {0, 1}, {0, .5f}};
1288 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
1289 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
1290 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
caryclark@google.combfe90372012-11-21 13:56:20 +00001291 SkPoint rf[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}};
rmistry@google.comd6176b02012-08-23 18:14:13 +00001292
caryclark@google.comf1316942011-07-26 19:54:45 +00001293 // failing tests
1294 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1295 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1296 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1297 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1298 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1299 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1300 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1301 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
caryclark@google.combfe90372012-11-21 13:56:20 +00001302 SkPoint f9[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}, {2, 0}}; // overlaps
1303 SkPoint fa[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, -1}, {1, -1}}; // non colinear gap
1304 SkPoint fb[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 1}}; // falls short
rmistry@google.comd6176b02012-08-23 18:14:13 +00001305
caryclark@google.comf1316942011-07-26 19:54:45 +00001306 // failing, no close
1307 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1308 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1309
1310 size_t testLen[] = {
1311 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1312 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
caryclark@google.combfe90372012-11-21 13:56:20 +00001313 sizeof(rd), sizeof(re), sizeof(rf),
caryclark@google.comf1316942011-07-26 19:54:45 +00001314 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
caryclark@google.combfe90372012-11-21 13:56:20 +00001315 sizeof(f7), sizeof(f8), sizeof(f9), sizeof(fa), sizeof(fb),
rmistry@google.comd6176b02012-08-23 18:14:13 +00001316 sizeof(c1), sizeof(c2)
caryclark@google.comf1316942011-07-26 19:54:45 +00001317 };
1318 SkPoint* tests[] = {
caryclark@google.combfe90372012-11-21 13:56:20 +00001319 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re, rf,
1320 f1, f2, f3, f4, f5, f6, f7, f8, f9, fa, fb,
rmistry@google.comd6176b02012-08-23 18:14:13 +00001321 c1, c2
caryclark@google.comf1316942011-07-26 19:54:45 +00001322 };
caryclark@google.combfe90372012-11-21 13:56:20 +00001323 SkPoint* lastPass = rf;
1324 SkPoint* lastClose = fb;
caryclark@google.comf1316942011-07-26 19:54:45 +00001325 bool fail = false;
1326 bool close = true;
1327 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1328 size_t index;
1329 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1330 SkPath path;
1331 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1332 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1333 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1334 }
1335 if (close) {
1336 path.close();
1337 }
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001338 REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
1339 REPORTER_ASSERT(reporter, fail ^ path.isRect(NULL, NULL));
caryclark@google.comf68154a2012-11-21 15:18:06 +00001340
caryclark@google.com56f233a2012-11-19 13:06:06 +00001341 if (!fail) {
1342 SkRect computed, expected;
1343 expected.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1344 REPORTER_ASSERT(reporter, path.isRect(&computed));
1345 REPORTER_ASSERT(reporter, expected == computed);
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001346
caryclark@google.comf68154a2012-11-21 15:18:06 +00001347 bool isClosed;
1348 SkPath::Direction direction, cheapDirection;
1349 REPORTER_ASSERT(reporter, path.cheapComputeDirection(&cheapDirection));
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001350 REPORTER_ASSERT(reporter, path.isRect(&isClosed, &direction));
caryclark@google.comf68154a2012-11-21 15:18:06 +00001351 REPORTER_ASSERT(reporter, isClosed == close);
1352 REPORTER_ASSERT(reporter, direction == cheapDirection);
1353 } else {
1354 SkRect computed;
1355 computed.set(123, 456, 789, 1011);
1356 REPORTER_ASSERT(reporter, !path.isRect(&computed));
1357 REPORTER_ASSERT(reporter, computed.fLeft == 123 && computed.fTop == 456);
1358 REPORTER_ASSERT(reporter, computed.fRight == 789 && computed.fBottom == 1011);
1359
1360 bool isClosed = (bool) -1;
1361 SkPath::Direction direction = (SkPath::Direction) -1;
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001362 REPORTER_ASSERT(reporter, !path.isRect(&isClosed, &direction));
caryclark@google.comf68154a2012-11-21 15:18:06 +00001363 REPORTER_ASSERT(reporter, isClosed == (bool) -1);
1364 REPORTER_ASSERT(reporter, direction == (SkPath::Direction) -1);
caryclark@google.com56f233a2012-11-19 13:06:06 +00001365 }
skia.committer@gmail.com1c9c0d32012-11-22 02:02:41 +00001366
caryclark@google.comf1316942011-07-26 19:54:45 +00001367 if (tests[testIndex] == lastPass) {
1368 fail = true;
1369 }
1370 if (tests[testIndex] == lastClose) {
1371 close = false;
1372 }
1373 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00001374
caryclark@google.comf1316942011-07-26 19:54:45 +00001375 // fail, close then line
1376 SkPath path1;
1377 path1.moveTo(r1[0].fX, r1[0].fY);
1378 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1379 path1.lineTo(r1[index].fX, r1[index].fY);
1380 }
1381 path1.close();
1382 path1.lineTo(1, 0);
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001383 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001384
caryclark@google.comf1316942011-07-26 19:54:45 +00001385 // fail, move in the middle
1386 path1.reset();
1387 path1.moveTo(r1[0].fX, r1[0].fY);
1388 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1389 if (index == 2) {
1390 path1.moveTo(1, .5f);
1391 }
1392 path1.lineTo(r1[index].fX, r1[index].fY);
1393 }
1394 path1.close();
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001395 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
caryclark@google.comf1316942011-07-26 19:54:45 +00001396
1397 // fail, move on the edge
1398 path1.reset();
1399 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1400 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1401 path1.lineTo(r1[index].fX, r1[index].fY);
1402 }
1403 path1.close();
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001404 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001405
caryclark@google.comf1316942011-07-26 19:54:45 +00001406 // fail, quad
1407 path1.reset();
1408 path1.moveTo(r1[0].fX, r1[0].fY);
1409 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1410 if (index == 2) {
1411 path1.quadTo(1, .5f, 1, .5f);
1412 }
1413 path1.lineTo(r1[index].fX, r1[index].fY);
1414 }
1415 path1.close();
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001416 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
rmistry@google.comd6176b02012-08-23 18:14:13 +00001417
caryclark@google.comf1316942011-07-26 19:54:45 +00001418 // fail, cubic
1419 path1.reset();
1420 path1.moveTo(r1[0].fX, r1[0].fY);
1421 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1422 if (index == 2) {
1423 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1424 }
1425 path1.lineTo(r1[index].fX, r1[index].fY);
1426 }
1427 path1.close();
robertphillips@google.com8fd16032013-06-25 15:39:58 +00001428 REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
caryclark@google.comf1316942011-07-26 19:54:45 +00001429}
1430
caryclark@google.com56f233a2012-11-19 13:06:06 +00001431static void test_isNestedRects(skiatest::Reporter* reporter) {
1432 // passing tests (all moveTo / lineTo...
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001433 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // CW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001434 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
1435 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
1436 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001437 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}}; // CCW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001438 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
1439 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
1440 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
1441 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001442 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f}, // CCW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001443 {1, 0}, {.5f, 0}};
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001444 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1}, // CW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001445 {0, 1}, {0, .5f}};
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001446 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}; // CW
1447 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}}; // CCW
1448 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}}; // CW
caryclark@google.com56f233a2012-11-19 13:06:06 +00001449
1450 // failing tests
1451 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
1452 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
1453 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
1454 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
1455 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
1456 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
1457 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
1458 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
1459
1460 // failing, no close
1461 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
1462 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
1463
1464 size_t testLen[] = {
1465 sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
1466 sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
1467 sizeof(rd), sizeof(re),
1468 sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
1469 sizeof(f7), sizeof(f8),
1470 sizeof(c1), sizeof(c2)
1471 };
1472 SkPoint* tests[] = {
1473 r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
1474 f1, f2, f3, f4, f5, f6, f7, f8,
1475 c1, c2
1476 };
skia.committer@gmail.com845220b2013-05-20 11:51:35 +00001477 SkPath::Direction dirs[] = {
1478 SkPath::kCW_Direction, SkPath::kCW_Direction, SkPath::kCW_Direction,
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001479 SkPath::kCW_Direction, SkPath::kCCW_Direction, SkPath::kCCW_Direction,
skia.committer@gmail.com845220b2013-05-20 11:51:35 +00001480 SkPath::kCCW_Direction, SkPath::kCCW_Direction, SkPath::kCCW_Direction,
1481 SkPath::kCCW_Direction, SkPath::kCW_Direction, SkPath::kCW_Direction,
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001482 SkPath::kCCW_Direction, SkPath::kCW_Direction, SkPath::kUnknown_Direction,
1483 SkPath::kUnknown_Direction, SkPath::kUnknown_Direction, SkPath::kUnknown_Direction,
1484 SkPath::kUnknown_Direction, SkPath::kUnknown_Direction, SkPath::kUnknown_Direction,
1485 SkPath::kUnknown_Direction, SkPath::kUnknown_Direction, SkPath::kUnknown_Direction,
1486 };
1487 SkASSERT(SK_ARRAY_COUNT(tests) == SK_ARRAY_COUNT(dirs));
1488
caryclark@google.com56f233a2012-11-19 13:06:06 +00001489 const SkPoint* lastPass = re;
1490 const SkPoint* lastClose = f8;
1491 const size_t testCount = sizeof(tests) / sizeof(tests[0]);
1492 size_t index;
1493 for (int rectFirst = 0; rectFirst <= 1; ++rectFirst) {
1494 bool fail = false;
1495 bool close = true;
1496 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
1497 SkPath path;
1498 if (rectFirst) {
1499 path.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1500 }
1501 path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
1502 for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
1503 path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
1504 }
1505 if (close) {
1506 path.close();
1507 }
1508 if (!rectFirst) {
1509 path.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1510 }
1511 REPORTER_ASSERT(reporter, fail ^ path.isNestedRects(0));
1512 if (!fail) {
1513 SkRect expected[2], computed[2];
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001514 SkPath::Direction expectedDirs[2], computedDirs[2];
caryclark@google.com56f233a2012-11-19 13:06:06 +00001515 SkRect testBounds;
1516 testBounds.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
1517 expected[0] = SkRect::MakeLTRB(-1, -1, 2, 2);
1518 expected[1] = testBounds;
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001519 if (rectFirst) {
1520 expectedDirs[0] = SkPath::kCW_Direction;
1521 } else {
1522 expectedDirs[0] = SkPath::kCCW_Direction;
1523 }
1524 expectedDirs[1] = dirs[testIndex];
1525 REPORTER_ASSERT(reporter, path.isNestedRects(computed, computedDirs));
caryclark@google.com56f233a2012-11-19 13:06:06 +00001526 REPORTER_ASSERT(reporter, expected[0] == computed[0]);
1527 REPORTER_ASSERT(reporter, expected[1] == computed[1]);
robertphillips@google.com83d1a682013-05-17 12:50:27 +00001528 REPORTER_ASSERT(reporter, expectedDirs[0] == computedDirs[0]);
1529 REPORTER_ASSERT(reporter, expectedDirs[1] == computedDirs[1]);
caryclark@google.com56f233a2012-11-19 13:06:06 +00001530 }
1531 if (tests[testIndex] == lastPass) {
1532 fail = true;
1533 }
1534 if (tests[testIndex] == lastClose) {
1535 close = false;
1536 }
1537 }
1538
1539 // fail, close then line
1540 SkPath path1;
1541 if (rectFirst) {
1542 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1543 }
1544 path1.moveTo(r1[0].fX, r1[0].fY);
1545 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1546 path1.lineTo(r1[index].fX, r1[index].fY);
1547 }
1548 path1.close();
1549 path1.lineTo(1, 0);
1550 if (!rectFirst) {
1551 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1552 }
1553 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1554
1555 // fail, move in the middle
1556 path1.reset();
1557 if (rectFirst) {
1558 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1559 }
1560 path1.moveTo(r1[0].fX, r1[0].fY);
1561 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1562 if (index == 2) {
1563 path1.moveTo(1, .5f);
1564 }
1565 path1.lineTo(r1[index].fX, r1[index].fY);
1566 }
1567 path1.close();
1568 if (!rectFirst) {
1569 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1570 }
1571 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1572
1573 // fail, move on the edge
1574 path1.reset();
1575 if (rectFirst) {
1576 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1577 }
1578 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1579 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
1580 path1.lineTo(r1[index].fX, r1[index].fY);
1581 }
1582 path1.close();
1583 if (!rectFirst) {
1584 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1585 }
1586 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1587
1588 // fail, quad
1589 path1.reset();
1590 if (rectFirst) {
1591 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1592 }
1593 path1.moveTo(r1[0].fX, r1[0].fY);
1594 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1595 if (index == 2) {
1596 path1.quadTo(1, .5f, 1, .5f);
1597 }
1598 path1.lineTo(r1[index].fX, r1[index].fY);
1599 }
1600 path1.close();
1601 if (!rectFirst) {
1602 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1603 }
1604 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1605
1606 // fail, cubic
1607 path1.reset();
1608 if (rectFirst) {
1609 path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
1610 }
1611 path1.moveTo(r1[0].fX, r1[0].fY);
1612 for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
1613 if (index == 2) {
1614 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
1615 }
1616 path1.lineTo(r1[index].fX, r1[index].fY);
1617 }
1618 path1.close();
1619 if (!rectFirst) {
1620 path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
1621 }
1622 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
skia.committer@gmail.com34587162012-11-20 02:01:23 +00001623
caryclark@google.com56f233a2012-11-19 13:06:06 +00001624 // fail, not nested
1625 path1.reset();
1626 path1.addRect(1, 1, 3, 3, SkPath::kCW_Direction);
1627 path1.addRect(2, 2, 4, 4, SkPath::kCW_Direction);
1628 REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
1629 }
caryclark@google.combfe90372012-11-21 13:56:20 +00001630
1631 // pass, stroke rect
1632 SkPath src, dst;
1633 src.addRect(1, 1, 7, 7, SkPath::kCW_Direction);
1634 SkPaint strokePaint;
1635 strokePaint.setStyle(SkPaint::kStroke_Style);
1636 strokePaint.setStrokeWidth(2);
1637 strokePaint.getFillPath(src, &dst);
1638 REPORTER_ASSERT(reporter, dst.isNestedRects(0));
caryclark@google.com56f233a2012-11-19 13:06:06 +00001639}
1640
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001641static void write_and_read_back(skiatest::Reporter* reporter,
1642 const SkPath& p) {
1643 SkWriter32 writer(100);
1644 writer.writePath(p);
1645 size_t size = writer.size();
1646 SkAutoMalloc storage(size);
1647 writer.flatten(storage.get());
1648 SkReader32 reader(storage.get(), size);
1649
1650 SkPath readBack;
1651 REPORTER_ASSERT(reporter, readBack != p);
1652 reader.readPath(&readBack);
1653 REPORTER_ASSERT(reporter, readBack == p);
1654
rmistry@google.comd6176b02012-08-23 18:14:13 +00001655 REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() ==
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001656 p.getConvexityOrUnknown());
1657
1658 REPORTER_ASSERT(reporter, readBack.isOval(NULL) == p.isOval(NULL));
1659
1660 const SkRect& origBounds = p.getBounds();
1661 const SkRect& readBackBounds = readBack.getBounds();
1662
1663 REPORTER_ASSERT(reporter, origBounds == readBackBounds);
1664}
1665
reed@google.com53effc52011-09-21 19:05:12 +00001666static void test_flattening(skiatest::Reporter* reporter) {
1667 SkPath p;
1668
1669 static const SkPoint pts[] = {
1670 { 0, 0 },
1671 { SkIntToScalar(10), SkIntToScalar(10) },
1672 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1673 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1674 };
1675 p.moveTo(pts[0]);
1676 p.lineTo(pts[1]);
1677 p.quadTo(pts[2], pts[3]);
1678 p.cubicTo(pts[4], pts[5], pts[6]);
1679
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001680 write_and_read_back(reporter, p);
djsollen@google.com94e75ee2012-06-08 18:30:46 +00001681
1682 // create a buffer that should be much larger than the path so we don't
1683 // kill our stack if writer goes too far.
1684 char buffer[1024];
1685 uint32_t size1 = p.writeToMemory(NULL);
1686 uint32_t size2 = p.writeToMemory(buffer);
1687 REPORTER_ASSERT(reporter, size1 == size2);
1688
1689 SkPath p2;
1690 uint32_t size3 = p2.readFromMemory(buffer);
1691 REPORTER_ASSERT(reporter, size1 == size3);
1692 REPORTER_ASSERT(reporter, p == p2);
1693
1694 char buffer2[1024];
1695 size3 = p2.writeToMemory(buffer2);
1696 REPORTER_ASSERT(reporter, size1 == size3);
1697 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
robertphillips@google.com2972bb52012-08-07 17:32:51 +00001698
1699 // test persistence of the oval flag & convexity
1700 {
1701 SkPath oval;
1702 SkRect rect = SkRect::MakeWH(10, 10);
1703 oval.addOval(rect);
1704
1705 write_and_read_back(reporter, oval);
1706 }
reed@google.com53effc52011-09-21 19:05:12 +00001707}
1708
1709static void test_transform(skiatest::Reporter* reporter) {
1710 SkPath p, p1;
rmistry@google.comd6176b02012-08-23 18:14:13 +00001711
reed@google.com53effc52011-09-21 19:05:12 +00001712 static const SkPoint pts[] = {
1713 { 0, 0 },
1714 { SkIntToScalar(10), SkIntToScalar(10) },
1715 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
1716 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
1717 };
1718 p.moveTo(pts[0]);
1719 p.lineTo(pts[1]);
1720 p.quadTo(pts[2], pts[3]);
1721 p.cubicTo(pts[4], pts[5], pts[6]);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001722
reed@google.com53effc52011-09-21 19:05:12 +00001723 SkMatrix matrix;
1724 matrix.reset();
1725 p.transform(matrix, &p1);
1726 REPORTER_ASSERT(reporter, p == p1);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001727
reed@google.com53effc52011-09-21 19:05:12 +00001728 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
1729 p.transform(matrix, &p1);
1730 SkPoint pts1[7];
1731 int count = p1.getPoints(pts1, 7);
1732 REPORTER_ASSERT(reporter, 7 == count);
1733 for (int i = 0; i < count; ++i) {
1734 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
1735 REPORTER_ASSERT(reporter, newPt == pts1[i]);
1736 }
1737}
1738
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001739static void test_zero_length_paths(skiatest::Reporter* reporter) {
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001740 SkPath p;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001741 uint8_t verbs[32];
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001742
caryclark@google.com56f233a2012-11-19 13:06:06 +00001743 struct SUPPRESS_VISIBILITY_WARNING zeroPathTestData {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001744 const char* testPath;
1745 const size_t numResultPts;
1746 const SkRect resultBound;
1747 const SkPath::Verb* resultVerbs;
1748 const size_t numResultVerbs;
1749 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001750
schenney@chromium.org7e963602012-06-13 17:05:43 +00001751 static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb };
1752 static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb };
1753 static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb };
1754 static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb };
1755 static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb };
1756 static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb };
1757 static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb };
1758 static const SkPath::Verb resultVerbs8[] = {
1759 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb
1760 };
1761 static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb };
1762 static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb };
1763 static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb };
1764 static const SkPath::Verb resultVerbs12[] = {
1765 SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb
1766 };
1767 static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb };
1768 static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb };
1769 static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb };
1770 static const SkPath::Verb resultVerbs16[] = {
1771 SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb
1772 };
1773 static const struct zeroPathTestData gZeroLengthTests[] = {
1774 { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001775 { "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 +00001776 { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001777 { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1778 { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) },
1779 { "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) },
1780 { "M 1 1 L 1 1 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) },
1781 { "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) },
1782 { "M 1 1 Q 1 1 1 1", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) },
1783 { "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) },
1784 { "M 1 1 Q 1 1 1 1 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) },
1785 { "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) },
1786 { "M 1 1 C 1 1 1 1 1 1", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) },
1787 { "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 +00001788 SK_ARRAY_COUNT(resultVerbs14)
1789 },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001790 { "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) },
1791 { "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 +00001792 SK_ARRAY_COUNT(resultVerbs16)
1793 }
1794 };
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001795
schenney@chromium.org7e963602012-06-13 17:05:43 +00001796 for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) {
1797 p.reset();
1798 bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p);
1799 REPORTER_ASSERT(reporter, valid);
1800 REPORTER_ASSERT(reporter, !p.isEmpty());
1801 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints());
1802 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds());
1803 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
1804 for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) {
1805 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]);
1806 }
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00001807 }
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00001808}
1809
1810struct SegmentInfo {
1811 SkPath fPath;
1812 int fPointCount;
1813};
1814
reed@google.com10296cc2011-09-21 12:29:05 +00001815#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
1816
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001817static void test_segment_masks(skiatest::Reporter* reporter) {
reed@google.comeef938c2012-08-01 20:01:49 +00001818 SkPath p, p2;
1819
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001820 p.moveTo(0, 0);
1821 p.quadTo(100, 100, 200, 200);
1822 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
1823 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001824 p2 = p;
1825 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001826 p.cubicTo(100, 100, 200, 200, 300, 300);
1827 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
1828 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.comeef938c2012-08-01 20:01:49 +00001829 p2 = p;
1830 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
1831
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001832 p.reset();
1833 p.moveTo(0, 0);
1834 p.cubicTo(100, 100, 200, 200, 300, 300);
1835 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
reed@google.comeef938c2012-08-01 20:01:49 +00001836 p2 = p;
1837 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
rmistry@google.comd6176b02012-08-23 18:14:13 +00001838
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001839 REPORTER_ASSERT(reporter, !p.isEmpty());
1840}
1841
1842static void test_iter(skiatest::Reporter* reporter) {
schenney@chromium.org7e963602012-06-13 17:05:43 +00001843 SkPath p;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001844 SkPoint pts[4];
1845
1846 // Test an iterator with no path
1847 SkPath::Iter noPathIter;
1848 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001849
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001850 // Test that setting an empty path works
1851 noPathIter.setPath(p, false);
1852 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001853
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001854 // Test that close path makes no difference for an empty path
1855 noPathIter.setPath(p, true);
1856 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
schenney@chromium.org7e963602012-06-13 17:05:43 +00001857
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001858 // Test an iterator with an initial empty path
1859 SkPath::Iter iter(p, false);
1860 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1861
1862 // Test that close path makes no difference
schenney@chromium.org7e963602012-06-13 17:05:43 +00001863 iter.setPath(p, true);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001864 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1865
rmistry@google.comd6176b02012-08-23 18:14:13 +00001866
schenney@chromium.org7e963602012-06-13 17:05:43 +00001867 struct iterTestData {
1868 const char* testPath;
1869 const bool forceClose;
1870 const bool consumeDegenerates;
1871 const size_t* numResultPtsPerVerb;
1872 const SkPoint* resultPts;
1873 const SkPath::Verb* resultVerbs;
1874 const size_t numResultVerbs;
1875 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001876
schenney@chromium.org7e963602012-06-13 17:05:43 +00001877 static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb };
1878 static const SkPath::Verb resultVerbs2[] = {
1879 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb
1880 };
1881 static const SkPath::Verb resultVerbs3[] = {
1882 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1883 };
1884 static const SkPath::Verb resultVerbs4[] = {
1885 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1886 };
1887 static const SkPath::Verb resultVerbs5[] = {
1888 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
1889 };
1890 static const size_t resultPtsSizes1[] = { 0 };
schenney@chromium.orgfedd09b2012-06-13 18:29:20 +00001891 static const size_t resultPtsSizes2[] = { 1, 2, 2, 0 };
1892 static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1, 0 };
1893 static const size_t resultPtsSizes4[] = { 1, 2, 1, 1, 0 };
1894 static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1, 0 };
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001895 static const SkPoint* resultPts1 = 0;
schenney@chromium.org7e963602012-06-13 17:05:43 +00001896 static const SkPoint resultPts2[] = {
1897 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }
1898 };
1899 static const SkPoint resultPts3[] = {
1900 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 },
1901 { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }
1902 };
1903 static const SkPoint resultPts4[] = {
1904 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1905 };
1906 static const SkPoint resultPts5[] = {
1907 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
1908 };
1909 static const struct iterTestData gIterTests[] = {
1910 { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
schenney@chromium.orgaaf16882012-06-13 17:41:00 +00001911 { "M 1 0 M 2 0 M 3 0 M 4 0 M 5 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1912 { "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 +00001913 { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1914 { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1915 { "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) },
1916 { "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 +00001917 { "M 1 0 L 1 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
1918 { "M 1 0 L 1 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
1919 { "M 1 0 L 1 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1920 { "M 1 0 L 1 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
1921 { "M 1 0 L 1 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
1922 { "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 +00001923 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001924
schenney@chromium.org7e963602012-06-13 17:05:43 +00001925 for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) {
1926 p.reset();
1927 bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p);
1928 REPORTER_ASSERT(reporter, valid);
1929 iter.setPath(p, gIterTests[i].forceClose);
1930 int j = 0, l = 0;
1931 do {
1932 REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]);
1933 for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) {
1934 REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]);
1935 }
1936 } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb);
1937 REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs);
1938 }
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001939
1940 // The GM degeneratesegments.cpp test is more extensive
1941}
1942
1943static void test_raw_iter(skiatest::Reporter* reporter) {
1944 SkPath p;
1945 SkPoint pts[4];
1946
1947 // Test an iterator with no path
1948 SkPath::RawIter noPathIter;
1949 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
1950 // Test that setting an empty path works
1951 noPathIter.setPath(p);
1952 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
rmistry@google.comd6176b02012-08-23 18:14:13 +00001953
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00001954 // Test an iterator with an initial empty path
1955 SkPath::RawIter iter(p);
1956 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1957
1958 // Test that a move-only path returns the move.
1959 p.moveTo(SK_Scalar1, 0);
1960 iter.setPath(p);
1961 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1962 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1963 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1964 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1965
1966 // No matter how many moves we add, we should get them all back
1967 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1968 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1969 iter.setPath(p);
1970 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1971 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
1972 REPORTER_ASSERT(reporter, pts[0].fY == 0);
1973 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1974 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
1975 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
1976 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
1977 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
1978 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
1979 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1980
1981 // Initial close is never ever stored
1982 p.reset();
1983 p.close();
1984 iter.setPath(p);
1985 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
1986
1987 // Move/close sequences
1988 p.reset();
1989 p.close(); // Not stored, no purpose
1990 p.moveTo(SK_Scalar1, 0);
1991 p.close();
1992 p.close(); // Not stored, no purpose
1993 p.moveTo(SK_Scalar1*2, SK_Scalar1);
1994 p.close();
1995 p.moveTo(SK_Scalar1*3, SK_Scalar1*2);
1996 p.moveTo(SK_Scalar1*4, SK_Scalar1*3);
1997 p.close();
1998 iter.setPath(p);
1999 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
2000 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
2001 REPORTER_ASSERT(reporter, pts[0].fY == 0);
2002 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
2003 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
2004 REPORTER_ASSERT(reporter, pts[0].fY == 0);
2005 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
2006 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
2007 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
2008 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
2009 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
2010 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
2011 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
2012 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
2013 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
2014 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kMove_Verb);
2015 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
2016 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
2017 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kClose_Verb);
2018 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
2019 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
2020 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
2021
2022 // Generate random paths and verify
2023 SkPoint randomPts[25];
2024 for (int i = 0; i < 5; ++i) {
2025 for (int j = 0; j < 5; ++j) {
2026 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
2027 }
2028 }
2029
2030 // Max of 10 segments, max 3 points per segment
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +00002031 SkRandom rand(9876543);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002032 SkPoint expectedPts[31]; // May have leading moveTo
reed@google.comd335d1d2012-01-12 18:17:11 +00002033 SkPath::Verb expectedVerbs[22]; // May have leading moveTo
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002034 SkPath::Verb nextVerb;
reed@google.comd335d1d2012-01-12 18:17:11 +00002035
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002036 for (int i = 0; i < 500; ++i) {
2037 p.reset();
2038 bool lastWasClose = true;
2039 bool haveMoveTo = false;
reed@google.comd335d1d2012-01-12 18:17:11 +00002040 SkPoint lastMoveToPt = { 0, 0 };
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002041 int numPoints = 0;
2042 int numVerbs = (rand.nextU() >> 16) % 10;
2043 int numIterVerbs = 0;
2044 for (int j = 0; j < numVerbs; ++j) {
2045 do {
2046 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
2047 } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002048 switch (nextVerb) {
2049 case SkPath::kMove_Verb:
2050 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
2051 p.moveTo(expectedPts[numPoints]);
reed@google.comd335d1d2012-01-12 18:17:11 +00002052 lastMoveToPt = expectedPts[numPoints];
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002053 numPoints += 1;
2054 lastWasClose = false;
2055 haveMoveTo = true;
2056 break;
2057 case SkPath::kLine_Verb:
2058 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00002059 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002060 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
2061 haveMoveTo = true;
2062 }
2063 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
2064 p.lineTo(expectedPts[numPoints]);
2065 numPoints += 1;
2066 lastWasClose = false;
2067 break;
2068 case SkPath::kQuad_Verb:
2069 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00002070 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002071 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
2072 haveMoveTo = true;
2073 }
2074 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
2075 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
2076 p.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
2077 numPoints += 2;
2078 lastWasClose = false;
2079 break;
reed@google.com277c3f82013-05-31 15:17:50 +00002080 case SkPath::kConic_Verb:
2081 if (!haveMoveTo) {
2082 expectedPts[numPoints++] = lastMoveToPt;
2083 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
2084 haveMoveTo = true;
2085 }
2086 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
2087 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
2088 p.conicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
2089 rand.nextUScalar1() * 4);
2090 numPoints += 2;
2091 lastWasClose = false;
2092 break;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002093 case SkPath::kCubic_Verb:
2094 if (!haveMoveTo) {
reed@google.comd335d1d2012-01-12 18:17:11 +00002095 expectedPts[numPoints++] = lastMoveToPt;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002096 expectedVerbs[numIterVerbs++] = SkPath::kMove_Verb;
2097 haveMoveTo = true;
2098 }
2099 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
2100 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
2101 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
2102 p.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
2103 expectedPts[numPoints + 2]);
2104 numPoints += 3;
2105 lastWasClose = false;
2106 break;
2107 case SkPath::kClose_Verb:
2108 p.close();
reed@google.comd335d1d2012-01-12 18:17:11 +00002109 haveMoveTo = false;
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002110 lastWasClose = true;
2111 break;
reed@google.com277c3f82013-05-31 15:17:50 +00002112 default:
mtklein@google.com330313a2013-08-22 15:37:26 +00002113 SkDEBUGFAIL("unexpected verb");
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002114 }
2115 expectedVerbs[numIterVerbs++] = nextVerb;
2116 }
rmistry@google.comd6176b02012-08-23 18:14:13 +00002117
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002118 iter.setPath(p);
2119 numVerbs = numIterVerbs;
2120 numIterVerbs = 0;
2121 int numIterPts = 0;
2122 SkPoint lastMoveTo;
2123 SkPoint lastPt;
2124 lastMoveTo.set(0, 0);
2125 lastPt.set(0, 0);
2126 while ((nextVerb = iter.next(pts)) != SkPath::kDone_Verb) {
2127 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
2128 numIterVerbs++;
2129 switch (nextVerb) {
2130 case SkPath::kMove_Verb:
2131 REPORTER_ASSERT(reporter, numIterPts < numPoints);
2132 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
2133 lastPt = lastMoveTo = pts[0];
2134 numIterPts += 1;
2135 break;
2136 case SkPath::kLine_Verb:
2137 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
2138 REPORTER_ASSERT(reporter, pts[0] == lastPt);
2139 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
2140 lastPt = pts[1];
2141 numIterPts += 1;
2142 break;
2143 case SkPath::kQuad_Verb:
reed@google.com277c3f82013-05-31 15:17:50 +00002144 case SkPath::kConic_Verb:
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002145 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
2146 REPORTER_ASSERT(reporter, pts[0] == lastPt);
2147 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
2148 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
2149 lastPt = pts[2];
2150 numIterPts += 2;
2151 break;
2152 case SkPath::kCubic_Verb:
2153 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
2154 REPORTER_ASSERT(reporter, pts[0] == lastPt);
2155 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
2156 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
2157 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
2158 lastPt = pts[3];
2159 numIterPts += 3;
2160 break;
2161 case SkPath::kClose_Verb:
2162 REPORTER_ASSERT(reporter, pts[0] == lastMoveTo);
2163 lastPt = lastMoveTo;
2164 break;
reed@google.com277c3f82013-05-31 15:17:50 +00002165 default:
mtklein@google.com330313a2013-08-22 15:37:26 +00002166 SkDEBUGFAIL("unexpected verb");
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002167 }
2168 }
2169 REPORTER_ASSERT(reporter, numIterPts == numPoints);
2170 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
2171 }
2172}
2173
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002174static void check_for_circle(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002175 const SkPath& path,
2176 bool expectedCircle,
2177 SkPath::Direction expectedDir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002178 SkRect rect;
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002179 REPORTER_ASSERT(reporter, path.isOval(&rect) == expectedCircle);
2180 REPORTER_ASSERT(reporter, path.cheapIsDirection(expectedDir));
skia.committer@gmail.comfbb0ed92012-11-13 21:46:06 +00002181
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002182 if (expectedCircle) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002183 REPORTER_ASSERT(reporter, rect.height() == rect.width());
2184 }
2185}
2186
2187static void test_circle_skew(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002188 const SkPath& path,
2189 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002190 SkPath tmp;
2191
2192 SkMatrix m;
2193 m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
2194 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002195 // this matrix reverses the direction.
2196 if (SkPath::kCCW_Direction == dir) {
2197 dir = SkPath::kCW_Direction;
2198 } else {
2199 SkASSERT(SkPath::kCW_Direction == dir);
2200 dir = SkPath::kCCW_Direction;
2201 }
2202 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002203}
2204
2205static void test_circle_translate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002206 const SkPath& path,
2207 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002208 SkPath tmp;
2209
2210 // translate at small offset
2211 SkMatrix m;
2212 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
2213 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002214 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002215
2216 tmp.reset();
2217 m.reset();
2218
2219 // translate at a relatively big offset
2220 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
2221 path.transform(m, &tmp);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002222 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002223}
2224
2225static void test_circle_rotate(skiatest::Reporter* reporter,
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002226 const SkPath& path,
2227 SkPath::Direction dir) {
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002228 for (int angle = 0; angle < 360; ++angle) {
2229 SkPath tmp;
2230 SkMatrix m;
2231 m.setRotate(SkIntToScalar(angle));
2232 path.transform(m, &tmp);
2233
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002234 // TODO: a rotated circle whose rotated angle is not a multiple of 90
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002235 // degrees is not an oval anymore, this can be improved. we made this
2236 // for the simplicity of our implementation.
2237 if (angle % 90 == 0) {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002238 check_for_circle(reporter, tmp, true, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002239 } else {
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002240 check_for_circle(reporter, tmp, false, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002241 }
2242 }
2243}
2244
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002245static void test_circle_mirror_x(skiatest::Reporter* reporter,
2246 const SkPath& path,
2247 SkPath::Direction dir) {
2248 SkPath tmp;
2249 SkMatrix m;
2250 m.reset();
2251 m.setScaleX(-SK_Scalar1);
2252 path.transform(m, &tmp);
2253
2254 if (SkPath::kCW_Direction == dir) {
2255 dir = SkPath::kCCW_Direction;
2256 } else {
2257 SkASSERT(SkPath::kCCW_Direction == dir);
2258 dir = SkPath::kCW_Direction;
2259 }
2260
2261 check_for_circle(reporter, tmp, true, dir);
2262}
2263
2264static void test_circle_mirror_y(skiatest::Reporter* reporter,
2265 const SkPath& path,
2266 SkPath::Direction dir) {
2267 SkPath tmp;
2268 SkMatrix m;
2269 m.reset();
2270 m.setScaleY(-SK_Scalar1);
2271 path.transform(m, &tmp);
2272
2273 if (SkPath::kCW_Direction == dir) {
2274 dir = SkPath::kCCW_Direction;
2275 } else {
2276 SkASSERT(SkPath::kCCW_Direction == dir);
2277 dir = SkPath::kCW_Direction;
2278 }
2279
2280 check_for_circle(reporter, tmp, true, dir);
2281}
2282
2283static void test_circle_mirror_xy(skiatest::Reporter* reporter,
2284 const SkPath& path,
2285 SkPath::Direction dir) {
2286 SkPath tmp;
2287 SkMatrix m;
2288 m.reset();
2289 m.setScaleX(-SK_Scalar1);
2290 m.setScaleY(-SK_Scalar1);
2291 path.transform(m, &tmp);
2292
2293 check_for_circle(reporter, tmp, true, dir);
2294}
2295
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002296static void test_circle_with_direction(skiatest::Reporter* reporter,
2297 SkPath::Direction dir) {
2298 SkPath path;
2299
2300 // circle at origin
2301 path.addCircle(0, 0, SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002302 check_for_circle(reporter, path, true, dir);
2303 test_circle_rotate(reporter, path, dir);
2304 test_circle_translate(reporter, path, dir);
2305 test_circle_skew(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002306
2307 // circle at an offset at (10, 10)
2308 path.reset();
2309 path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
2310 SkIntToScalar(20), dir);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002311 check_for_circle(reporter, path, true, dir);
2312 test_circle_rotate(reporter, path, dir);
2313 test_circle_translate(reporter, path, dir);
2314 test_circle_skew(reporter, path, dir);
2315 test_circle_mirror_x(reporter, path, dir);
2316 test_circle_mirror_y(reporter, path, dir);
2317 test_circle_mirror_xy(reporter, path, dir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002318}
2319
2320static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
2321 SkPath path;
2322 SkPath circle;
2323 SkPath rect;
2324 SkPath empty;
2325
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002326 static const SkPath::Direction kCircleDir = SkPath::kCW_Direction;
2327 static const SkPath::Direction kCircleDirOpposite = SkPath::kCCW_Direction;
2328
2329 circle.addCircle(0, 0, SkIntToScalar(10), kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002330 rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
2331 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
2332
2333 SkMatrix translate;
2334 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
2335
2336 // For simplicity, all the path concatenation related operations
2337 // would mark it non-circle, though in theory it's still a circle.
2338
2339 // empty + circle (translate)
2340 path = empty;
2341 path.addPath(circle, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002342 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002343
2344 // circle + empty (translate)
2345 path = circle;
2346 path.addPath(empty, translate);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002347 check_for_circle(reporter, path, false, kCircleDir);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002348
2349 // test reverseAddPath
2350 path = circle;
2351 path.reverseAddPath(rect);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002352 check_for_circle(reporter, path, false, kCircleDirOpposite);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002353}
2354
2355static void test_circle(skiatest::Reporter* reporter) {
2356 test_circle_with_direction(reporter, SkPath::kCW_Direction);
2357 test_circle_with_direction(reporter, SkPath::kCCW_Direction);
2358
2359 // multiple addCircle()
2360 SkPath path;
2361 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2362 path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002363 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002364
2365 // some extra lineTo() would make isOval() fail
2366 path.reset();
2367 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2368 path.lineTo(0, 0);
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002369 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002370
2371 // not back to the original point
2372 path.reset();
2373 path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
2374 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
bsalomon@google.com30c174b2012-11-13 14:36:42 +00002375 check_for_circle(reporter, path, false, SkPath::kCW_Direction);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002376
2377 test_circle_with_add_paths(reporter);
2378}
2379
2380static void test_oval(skiatest::Reporter* reporter) {
2381 SkRect rect;
2382 SkMatrix m;
2383 SkPath path;
2384
2385 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
2386 path.addOval(rect);
2387
2388 REPORTER_ASSERT(reporter, path.isOval(NULL));
2389
2390 m.setRotate(SkIntToScalar(90));
2391 SkPath tmp;
2392 path.transform(m, &tmp);
2393 // an oval rotated 90 degrees is still an oval.
2394 REPORTER_ASSERT(reporter, tmp.isOval(NULL));
2395
2396 m.reset();
2397 m.setRotate(SkIntToScalar(30));
2398 tmp.reset();
2399 path.transform(m, &tmp);
2400 // an oval rotated 30 degrees is not an oval anymore.
2401 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2402
2403 // since empty path being transformed.
2404 path.reset();
2405 tmp.reset();
2406 m.reset();
2407 path.transform(m, &tmp);
2408 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2409
2410 // empty path is not an oval
2411 tmp.reset();
2412 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2413
2414 // only has moveTo()s
2415 tmp.reset();
2416 tmp.moveTo(0, 0);
2417 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
2418 REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
2419
2420 // mimic WebKit's calling convention,
2421 // call moveTo() first and then call addOval()
2422 path.reset();
2423 path.moveTo(0, 0);
2424 path.addOval(rect);
2425 REPORTER_ASSERT(reporter, path.isOval(NULL));
2426
2427 // copy path
2428 path.reset();
2429 tmp.reset();
2430 tmp.addOval(rect);
2431 path = tmp;
2432 REPORTER_ASSERT(reporter, path.isOval(NULL));
2433}
2434
bungeman@google.coma5809a32013-06-21 15:13:34 +00002435static void test_empty(skiatest::Reporter* reporter, const SkPath& p) {
2436 SkPath empty;
reed@android.com80e39a72009-04-02 16:59:40 +00002437
reed@android.com3abec1d2009-03-02 05:36:20 +00002438 REPORTER_ASSERT(reporter, p.isEmpty());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002439 REPORTER_ASSERT(reporter, 0 == p.countPoints());
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002440 REPORTER_ASSERT(reporter, 0 == p.countVerbs());
reed@google.com10296cc2011-09-21 12:29:05 +00002441 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
reed@google.comb54455e2011-05-16 14:16:04 +00002442 REPORTER_ASSERT(reporter, p.isConvex());
reed@android.com3abec1d2009-03-02 05:36:20 +00002443 REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
2444 REPORTER_ASSERT(reporter, !p.isInverseFillType());
bungeman@google.coma5809a32013-06-21 15:13:34 +00002445 REPORTER_ASSERT(reporter, p == empty);
2446 REPORTER_ASSERT(reporter, !(p != empty));
2447}
2448
2449static void TestPath(skiatest::Reporter* reporter) {
2450 SkTSize<SkScalar>::Make(3,4);
2451
2452 SkPath p, empty;
2453 SkRect bounds, bounds2;
2454 test_empty(reporter, p);
reed@android.com3abec1d2009-03-02 05:36:20 +00002455
reed@android.comd252db02009-04-01 18:31:44 +00002456 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
reed@android.com80e39a72009-04-02 16:59:40 +00002457
reed@android.com3abec1d2009-03-02 05:36:20 +00002458 bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002459
reed@android.com6b82d1a2009-06-03 02:35:01 +00002460 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
2461 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002462 // we have quads or cubics
2463 REPORTER_ASSERT(reporter, p.getSegmentMasks() & kCurveSegmentMask);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002464 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002465
reed@android.com6b82d1a2009-06-03 02:35:01 +00002466 p.reset();
bungeman@google.coma5809a32013-06-21 15:13:34 +00002467 test_empty(reporter, p);
reed@google.com10296cc2011-09-21 12:29:05 +00002468
reed@android.com6b82d1a2009-06-03 02:35:01 +00002469 p.addOval(bounds);
2470 check_convex_bounds(reporter, p, bounds);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002471 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@google.com62047cf2011-02-07 19:39:09 +00002472
bungeman@google.coma5809a32013-06-21 15:13:34 +00002473 p.rewind();
2474 test_empty(reporter, p);
2475
reed@android.com3abec1d2009-03-02 05:36:20 +00002476 p.addRect(bounds);
reed@android.com6b82d1a2009-06-03 02:35:01 +00002477 check_convex_bounds(reporter, p, bounds);
reed@google.com10296cc2011-09-21 12:29:05 +00002478 // we have only lines
2479 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002480 REPORTER_ASSERT(reporter, !p.isEmpty());
reed@android.com3abec1d2009-03-02 05:36:20 +00002481
bungeman@google.coma5809a32013-06-21 15:13:34 +00002482 REPORTER_ASSERT(reporter, p != empty);
2483 REPORTER_ASSERT(reporter, !(p == empty));
reed@android.com3abec1d2009-03-02 05:36:20 +00002484
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002485 // do getPoints and getVerbs return the right result
2486 REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
2487 REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
reed@android.com3abec1d2009-03-02 05:36:20 +00002488 SkPoint pts[4];
2489 int count = p.getPoints(pts, 4);
2490 REPORTER_ASSERT(reporter, count == 4);
bsalomon@google.comdf9d6562012-06-07 21:43:15 +00002491 uint8_t verbs[6];
2492 verbs[5] = 0xff;
2493 p.getVerbs(verbs, 5);
2494 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
2495 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
2496 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
2497 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
2498 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
2499 REPORTER_ASSERT(reporter, 0xff == verbs[5]);
reed@android.com3abec1d2009-03-02 05:36:20 +00002500 bounds2.set(pts, 4);
2501 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002502
reed@android.com3abec1d2009-03-02 05:36:20 +00002503 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
2504 p.offset(SK_Scalar1*3, SK_Scalar1*4);
reed@android.comd252db02009-04-01 18:31:44 +00002505 REPORTER_ASSERT(reporter, bounds == p.getBounds());
reed@android.com3abec1d2009-03-02 05:36:20 +00002506
reed@android.com3abec1d2009-03-02 05:36:20 +00002507 REPORTER_ASSERT(reporter, p.isRect(NULL));
caryclark@google.comf1316942011-07-26 19:54:45 +00002508 bounds2.setEmpty();
reed@android.com3abec1d2009-03-02 05:36:20 +00002509 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
2510 REPORTER_ASSERT(reporter, bounds == bounds2);
reed@android.com80e39a72009-04-02 16:59:40 +00002511
reed@android.com3abec1d2009-03-02 05:36:20 +00002512 // now force p to not be a rect
2513 bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
2514 p.addRect(bounds);
2515 REPORTER_ASSERT(reporter, !p.isRect(NULL));
reed@android.com3abec1d2009-03-02 05:36:20 +00002516
reed@google.com7e6c4d12012-05-10 14:05:43 +00002517 test_isLine(reporter);
2518 test_isRect(reporter);
caryclark@google.com56f233a2012-11-19 13:06:06 +00002519 test_isNestedRects(reporter);
schenney@chromium.org4da06ab2011-12-20 15:14:18 +00002520 test_zero_length_paths(reporter);
reed@google.comcabaf1d2012-01-11 21:03:05 +00002521 test_direction(reporter);
reed@google.com04863fa2011-05-15 04:08:24 +00002522 test_convexity(reporter);
reed@google.com7c424812011-05-15 04:38:34 +00002523 test_convexity2(reporter);
bsalomon@google.com9bee33a2012-11-13 21:51:38 +00002524 test_conservativelyContains(reporter);
bsalomon@google.comb3b8dfa2011-07-13 17:44:36 +00002525 test_close(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002526 test_segment_masks(reporter);
reed@google.com53effc52011-09-21 19:05:12 +00002527 test_flattening(reporter);
2528 test_transform(reporter);
reed@google.com3563c9e2011-11-14 19:34:57 +00002529 test_bounds(reporter);
schenney@chromium.org6630d8d2012-01-04 21:05:51 +00002530 test_iter(reporter);
2531 test_raw_iter(reporter);
bsalomon@google.com6aa29652012-04-18 13:29:52 +00002532 test_circle(reporter);
2533 test_oval(reporter);
reed@google.com8b06f1a2012-05-29 12:03:46 +00002534 test_strokerec(reporter);
reed@google.com744faba2012-05-29 19:54:52 +00002535 test_addPoly(reporter);
reed@google.com0bb18bb2012-07-26 15:20:36 +00002536 test_isfinite(reporter);
tomhudson@google.comed02c4d2012-08-10 14:10:45 +00002537 test_isfinite_after_transform(reporter);
robertphillips@google.comb95eaa82012-10-18 15:26:12 +00002538 test_arb_round_rect_is_convex(reporter);
robertphillips@google.com158618e2012-10-23 16:56:56 +00002539 test_arb_zero_rad_round_rect_is_rect(reporter);
reed@google.coma8790de2012-10-24 21:04:04 +00002540 test_addrect_isfinite(reporter);
sugoi@google.com54f0d1b2013-02-27 19:17:41 +00002541 test_tricky_cubic();
2542 test_clipped_cubic();
2543 test_crbug_170666();
reed@google.com7a90daf2013-04-10 18:44:00 +00002544 test_bad_cubic_crbug229478();
reed@google.com3eff3592013-05-08 21:08:21 +00002545 test_bad_cubic_crbug234190();
mtklein@google.com9c9d4a72013-08-07 19:17:53 +00002546 test_android_specific_behavior(reporter);
commit-bot@chromium.org9d54aeb2013-08-09 19:48:26 +00002547 test_path_close_issue1474(reporter);
reed@android.com3abec1d2009-03-02 05:36:20 +00002548}
2549
2550#include "TestClassDef.h"
2551DEFINE_TESTCLASS("Path", PathTestClass, TestPath)