blob: eac5ebafcd9d8b6a4d354357d6df667f448f734f [file] [log] [blame]
Yuqian Li3154a532017-09-06 13:33:30 -04001/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkCanvas.h"
9#include "include/core/SkFont.h"
10#include "include/core/SkPaint.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/core/SkRRect.h"
12#include "include/core/SkSize.h"
13#include "include/core/SkStream.h"
14#include "include/core/SkStrokeRec.h"
15#include "include/core/SkSurface.h"
Brian Salomon71fe9452020-03-02 16:59:40 -050016#include "include/private/SkIDChangeListener.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050017#include "include/private/SkTo.h"
18#include "include/utils/SkNullCanvas.h"
19#include "include/utils/SkParse.h"
20#include "include/utils/SkParsePath.h"
21#include "include/utils/SkRandom.h"
22#include "src/core/SkAutoMalloc.h"
23#include "src/core/SkGeometry.h"
24#include "src/core/SkPathPriv.h"
Mike Reed375721d2020-08-07 16:00:06 -040025#include "src/core/SkPathView.h"
Brian Osmanff7bee92020-06-09 16:04:18 -040026#include "src/core/SkReadBuffer.h"
27#include "src/core/SkWriteBuffer.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050028#include "tests/Test.h"
Yuqian Li3154a532017-09-06 13:33:30 -040029
Hal Canaryc640d0d2018-06-13 09:59:02 -040030#include <cmath>
Ben Wagnerf08d1d02018-06-18 15:11:00 -040031#include <utility>
Cary Clarkc9b7c722018-12-12 14:50:23 -050032#include <vector>
Hal Canaryfdcfb8b2018-06-13 09:42:32 -040033
Yuqian Li3154a532017-09-06 13:33:30 -040034static void set_radii(SkVector radii[4], int index, float rad) {
35 sk_bzero(radii, sizeof(SkVector) * 4);
36 radii[index].set(rad, rad);
37}
38
39static void test_add_rrect(skiatest::Reporter* reporter, const SkRect& bounds,
40 const SkVector radii[4]) {
41 SkRRect rrect;
42 rrect.setRectRadii(bounds, radii);
43 REPORTER_ASSERT(reporter, bounds == rrect.rect());
44
45 SkPath path;
46 // this line should not assert in the debug build (from validate)
47 path.addRRect(rrect);
48 REPORTER_ASSERT(reporter, bounds == path.getBounds());
49}
50
51static void test_skbug_3469(skiatest::Reporter* reporter) {
52 SkPath path;
53 path.moveTo(20, 20);
54 path.quadTo(20, 50, 80, 50);
55 path.quadTo(20, 50, 20, 80);
56 REPORTER_ASSERT(reporter, !path.isConvex());
57}
58
59static void test_skbug_3239(skiatest::Reporter* reporter) {
60 const float min = SkBits2Float(0xcb7f16c8); /* -16717512.000000 */
61 const float max = SkBits2Float(0x4b7f1c1d); /* 16718877.000000 */
62 const float big = SkBits2Float(0x4b7f1bd7); /* 16718807.000000 */
63
64 const float rad = 33436320;
65
66 const SkRect rectx = SkRect::MakeLTRB(min, min, max, big);
67 const SkRect recty = SkRect::MakeLTRB(min, min, big, max);
68
69 SkVector radii[4];
70 for (int i = 0; i < 4; ++i) {
71 set_radii(radii, i, rad);
72 test_add_rrect(reporter, rectx, radii);
73 test_add_rrect(reporter, recty, radii);
74 }
75}
76
77static void make_path_crbug364224(SkPath* path) {
78 path->reset();
79 path->moveTo(3.747501373f, 2.724499941f);
80 path->lineTo(3.747501373f, 3.75f);
81 path->cubicTo(3.747501373f, 3.88774991f, 3.635501385f, 4.0f, 3.497501373f, 4.0f);
82 path->lineTo(0.7475013733f, 4.0f);
83 path->cubicTo(0.6095013618f, 4.0f, 0.4975013733f, 3.88774991f, 0.4975013733f, 3.75f);
84 path->lineTo(0.4975013733f, 1.0f);
85 path->cubicTo(0.4975013733f, 0.8622499704f, 0.6095013618f, 0.75f, 0.7475013733f,0.75f);
86 path->lineTo(3.497501373f, 0.75f);
87 path->cubicTo(3.50275135f, 0.75f, 3.5070014f, 0.7527500391f, 3.513001442f, 0.753000021f);
88 path->lineTo(3.715001345f, 0.5512499809f);
89 path->cubicTo(3.648251295f, 0.5194999576f, 3.575501442f, 0.4999999702f, 3.497501373f, 0.4999999702f);
90 path->lineTo(0.7475013733f, 0.4999999702f);
91 path->cubicTo(0.4715013802f, 0.4999999702f, 0.2475013733f, 0.7239999771f, 0.2475013733f, 1.0f);
92 path->lineTo(0.2475013733f, 3.75f);
93 path->cubicTo(0.2475013733f, 4.026000023f, 0.4715013504f, 4.25f, 0.7475013733f, 4.25f);
94 path->lineTo(3.497501373f, 4.25f);
95 path->cubicTo(3.773501396f, 4.25f, 3.997501373f, 4.026000023f, 3.997501373f, 3.75f);
96 path->lineTo(3.997501373f, 2.474750042f);
97 path->lineTo(3.747501373f, 2.724499941f);
98 path->close();
99}
100
101static void make_path_crbug364224_simplified(SkPath* path) {
102 path->moveTo(3.747501373f, 2.724499941f);
103 path->cubicTo(3.648251295f, 0.5194999576f, 3.575501442f, 0.4999999702f, 3.497501373f, 0.4999999702f);
104 path->close();
105}
106
107static void test_sect_with_horizontal_needs_pinning() {
108 // Test that sect_with_horizontal in SkLineClipper.cpp needs to pin after computing the
109 // intersection.
110 SkPath path;
111 path.reset();
112 path.moveTo(-540000, -720000);
113 path.lineTo(-9.10000017e-05f, 9.99999996e-13f);
114 path.lineTo(1, 1);
115
116 // Without the pinning code in sect_with_horizontal(), this would assert in the lineclipper
117 SkPaint paint;
118 SkSurface::MakeRasterN32Premul(10, 10)->getCanvas()->drawPath(path, paint);
119}
120
121static void test_path_crbug364224() {
122 SkPath path;
123 SkPaint paint;
124 auto surface(SkSurface::MakeRasterN32Premul(84, 88));
125 SkCanvas* canvas = surface->getCanvas();
126
127 make_path_crbug364224_simplified(&path);
128 canvas->drawPath(path, paint);
129
130 make_path_crbug364224(&path);
131 canvas->drawPath(path, paint);
132}
133
Yuqian Lif13beef2017-09-14 17:15:04 -0400134static void test_draw_AA_path(int width, int height, const SkPath& path) {
135 auto surface(SkSurface::MakeRasterN32Premul(width, height));
136 SkCanvas* canvas = surface->getCanvas();
137 SkPaint paint;
138 paint.setAntiAlias(true);
139 canvas->drawPath(path, paint);
140}
141
Yuqian Li3154a532017-09-06 13:33:30 -0400142// this is a unit test instead of a GM because it doesn't draw anything
143static void test_fuzz_crbug_638223() {
Yuqian Li3154a532017-09-06 13:33:30 -0400144 SkPath path;
145 path.moveTo(SkBits2Float(0x47452a00), SkBits2Float(0x43211d01)); // 50474, 161.113f
146 path.conicTo(SkBits2Float(0x401c0000), SkBits2Float(0x40680000),
147 SkBits2Float(0x02c25a81), SkBits2Float(0x981a1fa0),
148 SkBits2Float(0x6bf9abea)); // 2.4375f, 3.625f, 2.85577e-37f, -1.992e-24f, 6.03669e+26f
Yuqian Lif13beef2017-09-14 17:15:04 -0400149 test_draw_AA_path(250, 250, path);
Yuqian Li3154a532017-09-06 13:33:30 -0400150}
151
152static void test_fuzz_crbug_643933() {
Yuqian Li3154a532017-09-06 13:33:30 -0400153 SkPath path;
154 path.moveTo(0, 0);
155 path.conicTo(SkBits2Float(0x002001f2), SkBits2Float(0x4161ffff), // 2.93943e-39f, 14.125f
156 SkBits2Float(0x49f7224d), SkBits2Float(0x45eec8df), // 2.02452e+06f, 7641.11f
157 SkBits2Float(0x721aee0c)); // 3.0687e+30f
Yuqian Lif13beef2017-09-14 17:15:04 -0400158 test_draw_AA_path(250, 250, path);
Yuqian Li3154a532017-09-06 13:33:30 -0400159 path.reset();
160 path.moveTo(0, 0);
161 path.conicTo(SkBits2Float(0x00007ff2), SkBits2Float(0x4169ffff), // 4.58981e-41f, 14.625f
162 SkBits2Float(0x43ff2261), SkBits2Float(0x41eeea04), // 510.269f, 29.8643f
163 SkBits2Float(0x5d06eff8)); // 6.07704e+17f
Yuqian Lif13beef2017-09-14 17:15:04 -0400164 test_draw_AA_path(250, 250, path);
Yuqian Li3154a532017-09-06 13:33:30 -0400165}
166
167static void test_fuzz_crbug_647922() {
Yuqian Li3154a532017-09-06 13:33:30 -0400168 SkPath path;
169 path.moveTo(0, 0);
170 path.conicTo(SkBits2Float(0x00003939), SkBits2Float(0x42487fff), // 2.05276e-41f, 50.125f
171 SkBits2Float(0x48082361), SkBits2Float(0x4408e8e9), // 139406, 547.639f
172 SkBits2Float(0x4d1ade0f)); // 1.6239e+08f
Yuqian Lif13beef2017-09-14 17:15:04 -0400173 test_draw_AA_path(250, 250, path);
Yuqian Li3154a532017-09-06 13:33:30 -0400174}
175
176static void test_fuzz_crbug_662780() {
177 auto surface(SkSurface::MakeRasterN32Premul(250, 250));
178 SkCanvas* canvas = surface->getCanvas();
179 SkPaint paint;
180 paint.setAntiAlias(true);
181 SkPath path;
182 path.moveTo(SkBits2Float(0x41000000), SkBits2Float(0x431e0000)); // 8, 158
183 path.lineTo(SkBits2Float(0x41000000), SkBits2Float(0x42f00000)); // 8, 120
184 // 8, 8, 8.00002f, 8, 0.707107f
185 path.conicTo(SkBits2Float(0x41000000), SkBits2Float(0x41000000),
186 SkBits2Float(0x41000010), SkBits2Float(0x41000000), SkBits2Float(0x3f3504f3));
187 path.lineTo(SkBits2Float(0x439a0000), SkBits2Float(0x41000000)); // 308, 8
188 // 308, 8, 308, 8, 0.707107f
189 path.conicTo(SkBits2Float(0x439a0000), SkBits2Float(0x41000000),
190 SkBits2Float(0x439a0000), SkBits2Float(0x41000000), SkBits2Float(0x3f3504f3));
191 path.lineTo(SkBits2Float(0x439a0000), SkBits2Float(0x431e0000)); // 308, 158
192 // 308, 158, 308, 158, 0.707107f
193 path.conicTo(SkBits2Float(0x439a0000), SkBits2Float(0x431e0000),
194 SkBits2Float(0x439a0000), SkBits2Float(0x431e0000), SkBits2Float(0x3f3504f3));
195 path.lineTo(SkBits2Float(0x41000000), SkBits2Float(0x431e0000)); // 8, 158
196 // 8, 158, 8, 158, 0.707107f
197 path.conicTo(SkBits2Float(0x41000000), SkBits2Float(0x431e0000),
198 SkBits2Float(0x41000000), SkBits2Float(0x431e0000), SkBits2Float(0x3f3504f3));
199 path.close();
200 canvas->clipPath(path, true);
201 canvas->drawRect(SkRect::MakeWH(250, 250), paint);
202}
203
204static void test_mask_overflow() {
Yuqian Li3154a532017-09-06 13:33:30 -0400205 SkPath path;
206 path.moveTo(SkBits2Float(0x43e28000), SkBits2Float(0x43aa8000)); // 453, 341
207 path.lineTo(SkBits2Float(0x43de6000), SkBits2Float(0x43aa8000)); // 444.75f, 341
208 // 440.47f, 341, 437, 344.47f, 437, 348.75f
209 path.cubicTo(SkBits2Float(0x43dc3c29), SkBits2Float(0x43aa8000),
210 SkBits2Float(0x43da8000), SkBits2Float(0x43ac3c29),
211 SkBits2Float(0x43da8000), SkBits2Float(0x43ae6000));
212 path.lineTo(SkBits2Float(0x43da8000), SkBits2Float(0x43b18000)); // 437, 355
213 path.lineTo(SkBits2Float(0x43e28000), SkBits2Float(0x43b18000)); // 453, 355
214 path.lineTo(SkBits2Float(0x43e28000), SkBits2Float(0x43aa8000)); // 453, 341
Yuqian Lif13beef2017-09-14 17:15:04 -0400215 test_draw_AA_path(500, 500, path);
Yuqian Li3154a532017-09-06 13:33:30 -0400216}
217
218static void test_fuzz_crbug_668907() {
Yuqian Li3154a532017-09-06 13:33:30 -0400219 SkPath path;
220 path.moveTo(SkBits2Float(0x46313741), SkBits2Float(0x3b00e540)); // 11341.8f, 0.00196679f
221 path.quadTo(SkBits2Float(0x41410041), SkBits2Float(0xc1414141), SkBits2Float(0x41414141),
222 SkBits2Float(0x414100ff)); // 12.0626f, -12.0784f, 12.0784f, 12.0627f
223 path.lineTo(SkBits2Float(0x46313741), SkBits2Float(0x3b00e540)); // 11341.8f, 0.00196679f
224 path.close();
Yuqian Lif13beef2017-09-14 17:15:04 -0400225 test_draw_AA_path(400, 500, path);
Yuqian Li3154a532017-09-06 13:33:30 -0400226}
227
228/**
229 * In debug mode, this path was causing an assertion to fail in
230 * SkPathStroker::preJoinTo() and, in Release, the use of an unitialized value.
231 */
232static void make_path_crbugskia2820(SkPath* path, skiatest::Reporter* reporter) {
233 SkPoint orig, p1, p2, p3;
234 orig = SkPoint::Make(1.f, 1.f);
235 p1 = SkPoint::Make(1.f - SK_ScalarNearlyZero, 1.f);
236 p2 = SkPoint::Make(1.f, 1.f + SK_ScalarNearlyZero);
237 p3 = SkPoint::Make(2.f, 2.f);
238
239 path->reset();
240 path->moveTo(orig);
241 path->cubicTo(p1, p2, p3);
242 path->close();
243}
244
Robert Phillips58adb342020-07-23 09:41:57 -0400245static void test_path_crbugskia2820(skiatest::Reporter* reporter) {
Yuqian Li3154a532017-09-06 13:33:30 -0400246 SkPath path;
247 make_path_crbugskia2820(&path, reporter);
248
249 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
250 stroke.setStrokeStyle(2 * SK_Scalar1);
251 stroke.applyToPath(&path, path);
252}
253
254static void test_path_crbugskia5995() {
Yuqian Li3154a532017-09-06 13:33:30 -0400255 SkPath path;
256 path.moveTo(SkBits2Float(0x40303030), SkBits2Float(0x3e303030)); // 2.75294f, 0.172059f
257 path.quadTo(SkBits2Float(0x41d63030), SkBits2Float(0x30303030), SkBits2Float(0x41013030),
258 SkBits2Float(0x00000000)); // 26.7735f, 6.40969e-10f, 8.07426f, 0
259 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
Yuqian Lif13beef2017-09-14 17:15:04 -0400260 test_draw_AA_path(500, 500, path);
Yuqian Li3154a532017-09-06 13:33:30 -0400261}
262
263static void make_path0(SkPath* path) {
264 // from * https://code.google.com/p/skia/issues/detail?id=1706
265
266 path->moveTo(146.939f, 1012.84f);
267 path->lineTo(181.747f, 1009.18f);
268 path->lineTo(182.165f, 1013.16f);
269 path->lineTo(147.357f, 1016.82f);
270 path->lineTo(146.939f, 1012.84f);
271 path->close();
272}
273
274static void make_path1(SkPath* path) {
275 path->addRect(SkRect::MakeXYWH(10, 10, 10, 1));
276}
277
278typedef void (*PathProc)(SkPath*);
279
280/*
281 * Regression test: we used to crash (overwrite internal storage) during
282 * construction of the region when the path was INVERSE. That is now fixed,
283 * so test these regions (which used to assert/crash).
284 *
285 * https://code.google.com/p/skia/issues/detail?id=1706
286 */
287static void test_path_to_region(skiatest::Reporter* reporter) {
288 PathProc procs[] = {
289 make_path0,
290 make_path1,
291 };
292
293 SkRegion clip;
Mike Reed92b33352019-08-24 19:39:13 -0400294 clip.setRect({0, 0, 1255, 1925});
Yuqian Li3154a532017-09-06 13:33:30 -0400295
296 for (size_t i = 0; i < SK_ARRAY_COUNT(procs); ++i) {
297 SkPath path;
298 procs[i](&path);
299
300 SkRegion rgn;
301 rgn.setPath(path, clip);
302 path.toggleInverseFillType();
303 rgn.setPath(path, clip);
304 }
305}
306
307#ifdef SK_BUILD_FOR_WIN
308 #define SUPPRESS_VISIBILITY_WARNING
309#else
310 #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden")))
311#endif
312
313static void test_path_close_issue1474(skiatest::Reporter* reporter) {
314 // This test checks that r{Line,Quad,Conic,Cubic}To following a close()
315 // are relative to the point we close to, not relative to the point we close from.
316 SkPath path;
317 SkPoint last;
318
319 // Test rLineTo().
320 path.rLineTo(0, 100);
321 path.rLineTo(100, 0);
322 path.close(); // Returns us back to 0,0.
323 path.rLineTo(50, 50); // This should go to 50,50.
324
325 path.getLastPt(&last);
326 REPORTER_ASSERT(reporter, 50 == last.fX);
327 REPORTER_ASSERT(reporter, 50 == last.fY);
328
329 // Test rQuadTo().
330 path.rewind();
331 path.rLineTo(0, 100);
332 path.rLineTo(100, 0);
333 path.close();
334 path.rQuadTo(50, 50, 75, 75);
335
336 path.getLastPt(&last);
337 REPORTER_ASSERT(reporter, 75 == last.fX);
338 REPORTER_ASSERT(reporter, 75 == last.fY);
339
340 // Test rConicTo().
341 path.rewind();
342 path.rLineTo(0, 100);
343 path.rLineTo(100, 0);
344 path.close();
345 path.rConicTo(50, 50, 85, 85, 2);
346
347 path.getLastPt(&last);
348 REPORTER_ASSERT(reporter, 85 == last.fX);
349 REPORTER_ASSERT(reporter, 85 == last.fY);
350
351 // Test rCubicTo().
352 path.rewind();
353 path.rLineTo(0, 100);
354 path.rLineTo(100, 0);
355 path.close();
356 path.rCubicTo(50, 50, 85, 85, 95, 95);
357
358 path.getLastPt(&last);
359 REPORTER_ASSERT(reporter, 95 == last.fX);
360 REPORTER_ASSERT(reporter, 95 == last.fY);
361}
362
363static void test_gen_id(skiatest::Reporter* reporter) {
364 SkPath a, b;
365 REPORTER_ASSERT(reporter, a.getGenerationID() == b.getGenerationID());
366
367 a.moveTo(0, 0);
368 const uint32_t z = a.getGenerationID();
369 REPORTER_ASSERT(reporter, z != b.getGenerationID());
370
371 a.reset();
372 REPORTER_ASSERT(reporter, a.getGenerationID() == b.getGenerationID());
373
374 a.moveTo(1, 1);
375 const uint32_t y = a.getGenerationID();
376 REPORTER_ASSERT(reporter, z != y);
377
378 b.moveTo(2, 2);
379 const uint32_t x = b.getGenerationID();
380 REPORTER_ASSERT(reporter, x != y && x != z);
381
382 a.swap(b);
383 REPORTER_ASSERT(reporter, b.getGenerationID() == y && a.getGenerationID() == x);
384
385 b = a;
386 REPORTER_ASSERT(reporter, b.getGenerationID() == x);
387
388 SkPath c(a);
389 REPORTER_ASSERT(reporter, c.getGenerationID() == x);
390
391 c.lineTo(3, 3);
392 const uint32_t w = c.getGenerationID();
393 REPORTER_ASSERT(reporter, b.getGenerationID() == x);
394 REPORTER_ASSERT(reporter, a.getGenerationID() == x);
395 REPORTER_ASSERT(reporter, w != x);
396
397#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
398 static bool kExpectGenIDToIgnoreFill = false;
399#else
400 static bool kExpectGenIDToIgnoreFill = true;
401#endif
402
403 c.toggleInverseFillType();
404 const uint32_t v = c.getGenerationID();
405 REPORTER_ASSERT(reporter, (v == w) == kExpectGenIDToIgnoreFill);
406
407 c.rewind();
408 REPORTER_ASSERT(reporter, v != c.getGenerationID());
409}
410
411// This used to assert in the debug build, as the edges did not all line-up.
412static void test_bad_cubic_crbug234190() {
413 SkPath path;
414 path.moveTo(13.8509f, 3.16858f);
415 path.cubicTo(-2.35893e+08f, -4.21044e+08f,
416 -2.38991e+08f, -4.26573e+08f,
417 -2.41016e+08f, -4.30188e+08f);
Yuqian Lif13beef2017-09-14 17:15:04 -0400418 test_draw_AA_path(84, 88, path);
Yuqian Li3154a532017-09-06 13:33:30 -0400419}
420
421static void test_bad_cubic_crbug229478() {
422 const SkPoint pts[] = {
423 { 4595.91064f, -11596.9873f },
424 { 4597.2168f, -11595.9414f },
425 { 4598.52344f, -11594.8955f },
426 { 4599.83008f, -11593.8496f },
427 };
428
429 SkPath path;
430 path.moveTo(pts[0]);
431 path.cubicTo(pts[1], pts[2], pts[3]);
432
433 SkPaint paint;
434 paint.setStyle(SkPaint::kStroke_Style);
435 paint.setStrokeWidth(20);
436
437 SkPath dst;
438 // Before the fix, this would infinite-recurse, and run out of stack
439 // because we would keep trying to subdivide a degenerate cubic segment.
440 paint.getFillPath(path, &dst, nullptr);
441}
442
443static void build_path_170666(SkPath& path) {
444 path.moveTo(17.9459f, 21.6344f);
445 path.lineTo(139.545f, -47.8105f);
446 path.lineTo(139.545f, -47.8105f);
447 path.lineTo(131.07f, -47.3888f);
448 path.lineTo(131.07f, -47.3888f);
449 path.lineTo(122.586f, -46.9532f);
450 path.lineTo(122.586f, -46.9532f);
451 path.lineTo(18076.6f, 31390.9f);
452 path.lineTo(18076.6f, 31390.9f);
453 path.lineTo(18085.1f, 31390.5f);
454 path.lineTo(18085.1f, 31390.5f);
455 path.lineTo(18076.6f, 31390.9f);
456 path.lineTo(18076.6f, 31390.9f);
457 path.lineTo(17955, 31460.3f);
458 path.lineTo(17955, 31460.3f);
459 path.lineTo(17963.5f, 31459.9f);
460 path.lineTo(17963.5f, 31459.9f);
461 path.lineTo(17971.9f, 31459.5f);
462 path.lineTo(17971.9f, 31459.5f);
463 path.lineTo(17.9551f, 21.6205f);
464 path.lineTo(17.9551f, 21.6205f);
465 path.lineTo(9.47091f, 22.0561f);
466 path.lineTo(9.47091f, 22.0561f);
467 path.lineTo(17.9459f, 21.6344f);
468 path.lineTo(17.9459f, 21.6344f);
469 path.close();path.moveTo(0.995934f, 22.4779f);
470 path.lineTo(0.986725f, 22.4918f);
471 path.lineTo(0.986725f, 22.4918f);
472 path.lineTo(17955, 31460.4f);
473 path.lineTo(17955, 31460.4f);
474 path.lineTo(17971.9f, 31459.5f);
475 path.lineTo(17971.9f, 31459.5f);
476 path.lineTo(18093.6f, 31390.1f);
477 path.lineTo(18093.6f, 31390.1f);
478 path.lineTo(18093.6f, 31390);
479 path.lineTo(18093.6f, 31390);
480 path.lineTo(139.555f, -47.8244f);
481 path.lineTo(139.555f, -47.8244f);
482 path.lineTo(122.595f, -46.9671f);
483 path.lineTo(122.595f, -46.9671f);
484 path.lineTo(0.995934f, 22.4779f);
485 path.lineTo(0.995934f, 22.4779f);
486 path.close();
487 path.moveTo(5.43941f, 25.5223f);
488 path.lineTo(798267, -28871.1f);
489 path.lineTo(798267, -28871.1f);
490 path.lineTo(3.12512e+06f, -113102);
491 path.lineTo(3.12512e+06f, -113102);
492 path.cubicTo(5.16324e+06f, -186882, 8.15247e+06f, -295092, 1.1957e+07f, -432813);
493 path.cubicTo(1.95659e+07f, -708257, 3.04359e+07f, -1.10175e+06f, 4.34798e+07f, -1.57394e+06f);
494 path.cubicTo(6.95677e+07f, -2.51831e+06f, 1.04352e+08f, -3.77748e+06f, 1.39135e+08f, -5.03666e+06f);
495 path.cubicTo(1.73919e+08f, -6.29583e+06f, 2.08703e+08f, -7.555e+06f, 2.34791e+08f, -8.49938e+06f);
496 path.cubicTo(2.47835e+08f, -8.97157e+06f, 2.58705e+08f, -9.36506e+06f, 2.66314e+08f, -9.6405e+06f);
497 path.cubicTo(2.70118e+08f, -9.77823e+06f, 2.73108e+08f, -9.88644e+06f, 2.75146e+08f, -9.96022e+06f);
498 path.cubicTo(2.76165e+08f, -9.99711e+06f, 2.76946e+08f, -1.00254e+07f, 2.77473e+08f, -1.00444e+07f);
499 path.lineTo(2.78271e+08f, -1.00733e+07f);
500 path.lineTo(2.78271e+08f, -1.00733e+07f);
501 path.cubicTo(2.78271e+08f, -1.00733e+07f, 2.08703e+08f, -7.555e+06f, 135.238f, 23.3517f);
502 path.cubicTo(131.191f, 23.4981f, 125.995f, 23.7976f, 123.631f, 24.0206f);
503 path.cubicTo(121.267f, 24.2436f, 122.631f, 24.3056f, 126.677f, 24.1591f);
504 path.cubicTo(2.08703e+08f, -7.555e+06f, 2.78271e+08f, -1.00733e+07f, 2.78271e+08f, -1.00733e+07f);
505 path.lineTo(2.77473e+08f, -1.00444e+07f);
506 path.lineTo(2.77473e+08f, -1.00444e+07f);
507 path.cubicTo(2.76946e+08f, -1.00254e+07f, 2.76165e+08f, -9.99711e+06f, 2.75146e+08f, -9.96022e+06f);
508 path.cubicTo(2.73108e+08f, -9.88644e+06f, 2.70118e+08f, -9.77823e+06f, 2.66314e+08f, -9.6405e+06f);
509 path.cubicTo(2.58705e+08f, -9.36506e+06f, 2.47835e+08f, -8.97157e+06f, 2.34791e+08f, -8.49938e+06f);
510 path.cubicTo(2.08703e+08f, -7.555e+06f, 1.73919e+08f, -6.29583e+06f, 1.39135e+08f, -5.03666e+06f);
511 path.cubicTo(1.04352e+08f, -3.77749e+06f, 6.95677e+07f, -2.51831e+06f, 4.34798e+07f, -1.57394e+06f);
512 path.cubicTo(3.04359e+07f, -1.10175e+06f, 1.95659e+07f, -708258, 1.1957e+07f, -432814);
513 path.cubicTo(8.15248e+06f, -295092, 5.16324e+06f, -186883, 3.12513e+06f, -113103);
514 path.lineTo(798284, -28872);
515 path.lineTo(798284, -28872);
516 path.lineTo(22.4044f, 24.6677f);
517 path.lineTo(22.4044f, 24.6677f);
518 path.cubicTo(22.5186f, 24.5432f, 18.8134f, 24.6337f, 14.1287f, 24.8697f);
519 path.cubicTo(9.4439f, 25.1057f, 5.55359f, 25.3978f, 5.43941f, 25.5223f);
520 path.close();
521}
522
523static void build_path_simple_170666(SkPath& path) {
524 path.moveTo(126.677f, 24.1591f);
525 path.cubicTo(2.08703e+08f, -7.555e+06f, 2.78271e+08f, -1.00733e+07f, 2.78271e+08f, -1.00733e+07f);
526}
527
528// This used to assert in the SK_DEBUG build, as the clip step would fail with
529// too-few interations in our cubic-line intersection code. That code now runs
530// 24 interations (instead of 16).
531static void test_crbug_170666() {
532 SkPath path;
Yuqian Li3154a532017-09-06 13:33:30 -0400533 build_path_simple_170666(path);
Yuqian Lif13beef2017-09-14 17:15:04 -0400534 test_draw_AA_path(1000, 1000, path);
Yuqian Li3154a532017-09-06 13:33:30 -0400535
536 build_path_170666(path);
Yuqian Lif13beef2017-09-14 17:15:04 -0400537 test_draw_AA_path(1000, 1000, path);
Yuqian Li3154a532017-09-06 13:33:30 -0400538}
539
540
541static void test_tiny_path_convexity(skiatest::Reporter* reporter, const char* pathBug,
542 SkScalar tx, SkScalar ty, SkScalar scale) {
543 SkPath smallPath;
544 SkAssertResult(SkParsePath::FromSVGString(pathBug, &smallPath));
545 bool smallConvex = smallPath.isConvex();
546 SkPath largePath;
547 SkAssertResult(SkParsePath::FromSVGString(pathBug, &largePath));
548 SkMatrix matrix;
549 matrix.reset();
550 matrix.preTranslate(100, 100);
551 matrix.preScale(scale, scale);
552 largePath.transform(matrix);
553 bool largeConvex = largePath.isConvex();
554 REPORTER_ASSERT(reporter, smallConvex == largeConvex);
555}
556
557static void test_crbug_493450(skiatest::Reporter* reporter) {
558 const char reducedCase[] =
559 "M0,0"
560 "L0.0002, 0"
561 "L0.0002, 0.0002"
562 "L0.0001, 0.0001"
563 "L0,0.0002"
564 "Z";
565 test_tiny_path_convexity(reporter, reducedCase, 100, 100, 100000);
566 const char originalFiddleData[] =
567 "M-0.3383152268862998,-0.11217565719203619L-0.33846085183212765,-0.11212264406895281"
568 "L-0.338509393480737,-0.11210607966681395L-0.33857792286700894,-0.1121889121487573"
569 "L-0.3383866116636664,-0.11228834570924921L-0.33842087635680235,-0.11246078673250548"
570 "L-0.33809536177201055,-0.11245415228342878L-0.33797257995493996,-0.11216571641452182"
571 "L-0.33802112160354925,-0.11201996164188659L-0.33819815585141844,-0.11218559834671019Z";
572 test_tiny_path_convexity(reporter, originalFiddleData, 280081.4116670522f, 93268.04618493588f,
573 826357.3384828606f);
574}
575
576static void test_crbug_495894(skiatest::Reporter* reporter) {
577 const char originalFiddleData[] =
578 "M-0.34004273849857214,-0.11332803232216355L-0.34008271397389744,-0.11324483772714951"
579 "L-0.3401940742265893,-0.11324483772714951L-0.34017694188002134,-0.11329807920275889"
580 "L-0.3402026403998733,-0.11333468903941245L-0.34029972369709194,-0.11334134592705701"
581 "L-0.3403054344792813,-0.11344121970007795L-0.3403140006525653,-0.11351115418399343"
582 "L-0.34024261587519866,-0.11353446986281181L-0.3402197727464413,-0.11360442946144192"
583 "L-0.34013696640469604,-0.11359110237029302L-0.34009128014718143,-0.1135877707043939"
584 "L-0.3400598708451401,-0.11360776134112742L-0.34004273849857214,-0.11355112520064405"
585 "L-0.3400113291965308,-0.11355112520064405L-0.3399970522410575,-0.11359110237029302"
586 "L-0.33997135372120546,-0.11355112520064405L-0.3399627875479215,-0.11353780084493197"
587 "L-0.3399485105924481,-0.11350782354357004L-0.3400027630232468,-0.11346452910331437"
588 "L-0.3399485105924481,-0.11340126558629839L-0.33993994441916414,-0.11340126558629839"
589 "L-0.33988283659727087,-0.11331804756574679L-0.33989140277055485,-0.11324483772714951"
590 "L-0.33997991989448945,-0.11324483772714951L-0.3399856306766788,-0.11324483772714951"
591 "L-0.34002560615200417,-0.11334467443478255ZM-0.3400684370184241,-0.11338461985124307"
592 "L-0.340154098751264,-0.11341791238732665L-0.340162664924548,-0.1134378899559977"
593 "L-0.34017979727111597,-0.11340126558629839L-0.3401655203156427,-0.11338129083212668"
594 "L-0.34012268944922275,-0.11332137577529414L-0.34007414780061346,-0.11334467443478255Z"
595 "M-0.3400027630232468,-0.11290567901106024L-0.3400113291965308,-0.11298876531245433"
596 "L-0.33997991989448945,-0.11301535852306784L-0.33990282433493346,-0.11296217481488612"
597 "L-0.33993994441916414,-0.11288906492739594Z";
598 test_tiny_path_convexity(reporter, originalFiddleData, 22682.240000000005f,7819.72220766405f,
599 65536);
600}
601
602static void test_crbug_613918() {
603 SkPath path;
604 path.conicTo(-6.62478e-08f, 4.13885e-08f, -6.36935e-08f, 3.97927e-08f, 0.729058f);
605 path.quadTo(2.28206e-09f, -1.42572e-09f, 3.91919e-09f, -2.44852e-09f);
606 path.cubicTo(-16752.2f, -26792.9f, -21.4673f, 10.9347f, -8.57322f, -7.22739f);
607
608 // This call could lead to an assert or uninitialized read due to a failure
609 // to check the return value from SkCubicClipper::ChopMonoAtY.
610 path.contains(-1.84817e-08f, 1.15465e-08f);
611}
612
613static void test_addrect(skiatest::Reporter* reporter) {
614 SkPath path;
615 path.lineTo(0, 0);
616 path.addRect(SkRect::MakeWH(50, 100));
617 REPORTER_ASSERT(reporter, path.isRect(nullptr));
618
619 path.reset();
620 path.lineTo(FLT_EPSILON, FLT_EPSILON);
621 path.addRect(SkRect::MakeWH(50, 100));
622 REPORTER_ASSERT(reporter, !path.isRect(nullptr));
623
624 path.reset();
625 path.quadTo(0, 0, 0, 0);
626 path.addRect(SkRect::MakeWH(50, 100));
627 REPORTER_ASSERT(reporter, !path.isRect(nullptr));
628
629 path.reset();
630 path.conicTo(0, 0, 0, 0, 0.5f);
631 path.addRect(SkRect::MakeWH(50, 100));
632 REPORTER_ASSERT(reporter, !path.isRect(nullptr));
633
634 path.reset();
635 path.cubicTo(0, 0, 0, 0, 0, 0);
636 path.addRect(SkRect::MakeWH(50, 100));
637 REPORTER_ASSERT(reporter, !path.isRect(nullptr));
638}
639
640// Make sure we stay non-finite once we get there (unless we reset or rewind).
641static void test_addrect_isfinite(skiatest::Reporter* reporter) {
642 SkPath path;
643
644 path.addRect(SkRect::MakeWH(50, 100));
645 REPORTER_ASSERT(reporter, path.isFinite());
646
647 path.moveTo(0, 0);
648 path.lineTo(SK_ScalarInfinity, 42);
649 REPORTER_ASSERT(reporter, !path.isFinite());
650
651 path.addRect(SkRect::MakeWH(50, 100));
652 REPORTER_ASSERT(reporter, !path.isFinite());
653
654 path.reset();
655 REPORTER_ASSERT(reporter, path.isFinite());
656
657 path.addRect(SkRect::MakeWH(50, 100));
658 REPORTER_ASSERT(reporter, path.isFinite());
659}
660
661static void build_big_path(SkPath* path, bool reducedCase) {
662 if (reducedCase) {
663 path->moveTo(577330, 1971.72f);
664 path->cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f);
665 } else {
666 path->moveTo(60.1631f, 7.70567f);
667 path->quadTo(60.1631f, 7.70567f, 0.99474f, 0.901199f);
668 path->lineTo(577379, 1977.77f);
669 path->quadTo(577364, 1979.57f, 577325, 1980.26f);
670 path->quadTo(577286, 1980.95f, 577245, 1980.13f);
671 path->quadTo(577205, 1979.3f, 577187, 1977.45f);
672 path->quadTo(577168, 1975.6f, 577183, 1973.8f);
673 path->quadTo(577198, 1972, 577238, 1971.31f);
674 path->quadTo(577277, 1970.62f, 577317, 1971.45f);
675 path->quadTo(577330, 1971.72f, 577341, 1972.11f);
676 path->cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f);
677 path->moveTo(306.718f, -32.912f);
678 path->cubicTo(30.531f, 10.0005f, 1502.47f, 13.2804f, 84.3088f, 9.99601f);
679 }
680}
681
682static void test_clipped_cubic() {
683 auto surface(SkSurface::MakeRasterN32Premul(640, 480));
684
685 // This path used to assert, because our cubic-chopping code incorrectly
686 // moved control points after the chop. This test should be run in SK_DEBUG
687 // mode to ensure that we no long assert.
688 SkPath path;
689 for (int doReducedCase = 0; doReducedCase <= 1; ++doReducedCase) {
690 build_big_path(&path, SkToBool(doReducedCase));
691
692 SkPaint paint;
693 for (int doAA = 0; doAA <= 1; ++doAA) {
694 paint.setAntiAlias(SkToBool(doAA));
695 surface->getCanvas()->drawPath(path, paint);
696 }
697 }
698}
699
700static void dump_if_ne(skiatest::Reporter* reporter, const SkRect& expected, const SkRect& bounds) {
701 if (expected != bounds) {
702 ERRORF(reporter, "path.getBounds() returned [%g %g %g %g], but expected [%g %g %g %g]",
703 bounds.left(), bounds.top(), bounds.right(), bounds.bottom(),
704 expected.left(), expected.top(), expected.right(), expected.bottom());
705 }
706}
707
708static void test_bounds_crbug_513799(skiatest::Reporter* reporter) {
709 SkPath path;
710#if 0
711 // As written these tests were failing on LLVM 4.2 MacMini Release mysteriously, so we've
712 // rewritten them to avoid this (compiler-bug?).
713 REPORTER_ASSERT(reporter, SkRect::MakeLTRB(0, 0, 0, 0) == path.getBounds());
714
715 path.moveTo(-5, -8);
716 REPORTER_ASSERT(reporter, SkRect::MakeLTRB(-5, -8, -5, -8) == path.getBounds());
717
718 path.addRect(SkRect::MakeLTRB(1, 2, 3, 4));
719 REPORTER_ASSERT(reporter, SkRect::MakeLTRB(-5, -8, 3, 4) == path.getBounds());
720
721 path.moveTo(1, 2);
722 REPORTER_ASSERT(reporter, SkRect::MakeLTRB(-5, -8, 3, 4) == path.getBounds());
723#else
724 dump_if_ne(reporter, SkRect::MakeLTRB(0, 0, 0, 0), path.getBounds());
725
726 path.moveTo(-5, -8); // should set the bounds
727 dump_if_ne(reporter, SkRect::MakeLTRB(-5, -8, -5, -8), path.getBounds());
728
729 path.addRect(SkRect::MakeLTRB(1, 2, 3, 4)); // should extend the bounds
730 dump_if_ne(reporter, SkRect::MakeLTRB(-5, -8, 3, 4), path.getBounds());
731
732 path.moveTo(1, 2); // don't expect this to have changed the bounds
733 dump_if_ne(reporter, SkRect::MakeLTRB(-5, -8, 3, 4), path.getBounds());
734#endif
735}
736
Mike Kleinc0bd9f92019-04-23 12:05:21 -0500737#include "include/core/SkSurface.h"
Yuqian Li3154a532017-09-06 13:33:30 -0400738static void test_fuzz_crbug_627414(skiatest::Reporter* reporter) {
739 SkPath path;
740 path.moveTo(0, 0);
741 path.conicTo(3.58732e-43f, 2.72084f, 3.00392f, 3.00392f, 8.46e+37f);
Yuqian Lif13beef2017-09-14 17:15:04 -0400742 test_draw_AA_path(100, 100, path);
Yuqian Li3154a532017-09-06 13:33:30 -0400743}
744
745// Inspired by http://ie.microsoft.com/testdrive/Performance/Chalkboard/
746// which triggered an assert, from a tricky cubic. This test replicates that
747// example, so we can ensure that we handle it (in SkEdge.cpp), and don't
748// assert in the SK_DEBUG build.
749static void test_tricky_cubic() {
750 const SkPoint pts[] = {
751 { SkDoubleToScalar(18.8943768), SkDoubleToScalar(129.121277) },
752 { SkDoubleToScalar(18.8937435), SkDoubleToScalar(129.121689) },
753 { SkDoubleToScalar(18.8950119), SkDoubleToScalar(129.120422) },
754 { SkDoubleToScalar(18.5030727), SkDoubleToScalar(129.13121) },
755 };
756
757 SkPath path;
758 path.moveTo(pts[0]);
759 path.cubicTo(pts[1], pts[2], pts[3]);
Yuqian Lif13beef2017-09-14 17:15:04 -0400760 test_draw_AA_path(19, 130, path);
Yuqian Li3154a532017-09-06 13:33:30 -0400761}
762
763// Inspired by http://code.google.com/p/chromium/issues/detail?id=141651
764//
765static void test_isfinite_after_transform(skiatest::Reporter* reporter) {
766 SkPath path;
767 path.quadTo(157, 366, 286, 208);
768 path.arcTo(37, 442, 315, 163, 957494590897113.0f);
769
770 SkMatrix matrix;
771 matrix.setScale(1000*1000, 1000*1000);
772
773 // Be sure that path::transform correctly updates isFinite and the bounds
774 // if the transformation overflows. The previous bug was that isFinite was
775 // set to true in this case, but the bounds were not set to empty (which
776 // they should be).
777 while (path.isFinite()) {
778 REPORTER_ASSERT(reporter, path.getBounds().isFinite());
779 REPORTER_ASSERT(reporter, !path.getBounds().isEmpty());
780 path.transform(matrix);
781 }
782 REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
783
784 matrix.setTranslate(SK_Scalar1, SK_Scalar1);
785 path.transform(matrix);
786 // we need to still be non-finite
787 REPORTER_ASSERT(reporter, !path.isFinite());
788 REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
789}
790
791static void add_corner_arc(SkPath* path, const SkRect& rect,
792 SkScalar xIn, SkScalar yIn,
793 int startAngle)
794{
795
Brian Osman116b33e2020-02-05 13:34:09 -0500796 SkScalar rx = std::min(rect.width(), xIn);
797 SkScalar ry = std::min(rect.height(), yIn);
Yuqian Li3154a532017-09-06 13:33:30 -0400798
799 SkRect arcRect;
Mike Reed92b33352019-08-24 19:39:13 -0400800 arcRect.setLTRB(-rx, -ry, rx, ry);
Yuqian Li3154a532017-09-06 13:33:30 -0400801 switch (startAngle) {
802 case 0:
803 arcRect.offset(rect.fRight - arcRect.fRight, rect.fBottom - arcRect.fBottom);
804 break;
805 case 90:
806 arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fBottom - arcRect.fBottom);
807 break;
808 case 180:
809 arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fTop - arcRect.fTop);
810 break;
811 case 270:
812 arcRect.offset(rect.fRight - arcRect.fRight, rect.fTop - arcRect.fTop);
813 break;
814 default:
815 break;
816 }
817
818 path->arcTo(arcRect, SkIntToScalar(startAngle), SkIntToScalar(90), false);
819}
820
821static void make_arb_round_rect(SkPath* path, const SkRect& r,
822 SkScalar xCorner, SkScalar yCorner) {
823 // we are lazy here and use the same x & y for each corner
824 add_corner_arc(path, r, xCorner, yCorner, 270);
825 add_corner_arc(path, r, xCorner, yCorner, 0);
826 add_corner_arc(path, r, xCorner, yCorner, 90);
827 add_corner_arc(path, r, xCorner, yCorner, 180);
828 path->close();
829}
830
831// Chrome creates its own round rects with each corner possibly being different.
832// Performance will suffer if they are not convex.
833// Note: PathBench::ArbRoundRectBench performs almost exactly
834// the same test (but with drawing)
835static void test_arb_round_rect_is_convex(skiatest::Reporter* reporter) {
836 SkRandom rand;
837 SkRect r;
838
839 for (int i = 0; i < 5000; ++i) {
840
841 SkScalar size = rand.nextUScalar1() * 30;
842 if (size < SK_Scalar1) {
843 continue;
844 }
845 r.fLeft = rand.nextUScalar1() * 300;
846 r.fTop = rand.nextUScalar1() * 300;
847 r.fRight = r.fLeft + 2 * size;
848 r.fBottom = r.fTop + 2 * size;
849
850 SkPath temp;
851
852 make_arb_round_rect(&temp, r, r.width() / 10, r.height() / 15);
853
854 REPORTER_ASSERT(reporter, temp.isConvex());
855 }
856}
857
858// Chrome will sometimes create a 0 radius round rect. The degenerate
859// quads prevent the path from being converted to a rect
860// Note: PathBench::ArbRoundRectBench performs almost exactly
861// the same test (but with drawing)
862static void test_arb_zero_rad_round_rect_is_rect(skiatest::Reporter* reporter) {
863 SkRandom rand;
864 SkRect r;
865
866 for (int i = 0; i < 5000; ++i) {
867
868 SkScalar size = rand.nextUScalar1() * 30;
869 if (size < SK_Scalar1) {
870 continue;
871 }
872 r.fLeft = rand.nextUScalar1() * 300;
873 r.fTop = rand.nextUScalar1() * 300;
874 r.fRight = r.fLeft + 2 * size;
875 r.fBottom = r.fTop + 2 * size;
876
877 SkPath temp;
878
879 make_arb_round_rect(&temp, r, 0, 0);
880
881 SkRect result;
882 REPORTER_ASSERT(reporter, temp.isRect(&result));
883 REPORTER_ASSERT(reporter, r == result);
884 }
885}
886
887static void test_rect_isfinite(skiatest::Reporter* reporter) {
888 const SkScalar inf = SK_ScalarInfinity;
889 const SkScalar negInf = SK_ScalarNegativeInfinity;
890 const SkScalar nan = SK_ScalarNaN;
891
892 SkRect r;
893 r.setEmpty();
894 REPORTER_ASSERT(reporter, r.isFinite());
Mike Reed92b33352019-08-24 19:39:13 -0400895 r.setLTRB(0, 0, inf, negInf);
Yuqian Li3154a532017-09-06 13:33:30 -0400896 REPORTER_ASSERT(reporter, !r.isFinite());
Mike Reed92b33352019-08-24 19:39:13 -0400897 r.setLTRB(0, 0, nan, 0);
Yuqian Li3154a532017-09-06 13:33:30 -0400898 REPORTER_ASSERT(reporter, !r.isFinite());
899
900 SkPoint pts[] = {
901 { 0, 0 },
902 { SK_Scalar1, 0 },
903 { 0, SK_Scalar1 },
904 };
905
906 bool isFine = r.setBoundsCheck(pts, 3);
907 REPORTER_ASSERT(reporter, isFine);
908 REPORTER_ASSERT(reporter, !r.isEmpty());
909
910 pts[1].set(inf, 0);
911 isFine = r.setBoundsCheck(pts, 3);
912 REPORTER_ASSERT(reporter, !isFine);
913 REPORTER_ASSERT(reporter, r.isEmpty());
914
915 pts[1].set(nan, 0);
916 isFine = r.setBoundsCheck(pts, 3);
917 REPORTER_ASSERT(reporter, !isFine);
918 REPORTER_ASSERT(reporter, r.isEmpty());
919}
920
921static void test_path_isfinite(skiatest::Reporter* reporter) {
922 const SkScalar inf = SK_ScalarInfinity;
923 const SkScalar negInf = SK_ScalarNegativeInfinity;
924 const SkScalar nan = SK_ScalarNaN;
925
926 SkPath path;
927 REPORTER_ASSERT(reporter, path.isFinite());
928
929 path.reset();
930 REPORTER_ASSERT(reporter, path.isFinite());
931
932 path.reset();
933 path.moveTo(SK_Scalar1, 0);
934 REPORTER_ASSERT(reporter, path.isFinite());
935
936 path.reset();
937 path.moveTo(inf, negInf);
938 REPORTER_ASSERT(reporter, !path.isFinite());
939
940 path.reset();
941 path.moveTo(nan, 0);
942 REPORTER_ASSERT(reporter, !path.isFinite());
943}
944
945static void test_isfinite(skiatest::Reporter* reporter) {
946 test_rect_isfinite(reporter);
947 test_path_isfinite(reporter);
948}
949
950static void test_islastcontourclosed(skiatest::Reporter* reporter) {
951 SkPath path;
952 REPORTER_ASSERT(reporter, !path.isLastContourClosed());
953 path.moveTo(0, 0);
954 REPORTER_ASSERT(reporter, !path.isLastContourClosed());
955 path.close();
956 REPORTER_ASSERT(reporter, path.isLastContourClosed());
957 path.lineTo(100, 100);
958 REPORTER_ASSERT(reporter, !path.isLastContourClosed());
959 path.moveTo(200, 200);
960 REPORTER_ASSERT(reporter, !path.isLastContourClosed());
961 path.close();
962 REPORTER_ASSERT(reporter, path.isLastContourClosed());
963 path.moveTo(0, 0);
964 REPORTER_ASSERT(reporter, !path.isLastContourClosed());
965}
966
967// assert that we always
968// start with a moveTo
969// only have 1 moveTo
970// only have Lines after that
971// end with a single close
972// only have (at most) 1 close
973//
974static void test_poly(skiatest::Reporter* reporter, const SkPath& path,
975 const SkPoint srcPts[], bool expectClose) {
Yuqian Li3154a532017-09-06 13:33:30 -0400976 bool firstTime = true;
977 bool foundClose = false;
Chris Daltonde500372020-05-05 15:06:30 -0600978 for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
979 switch (verb) {
980 case SkPathVerb::kMove:
Yuqian Li3154a532017-09-06 13:33:30 -0400981 REPORTER_ASSERT(reporter, firstTime);
982 REPORTER_ASSERT(reporter, pts[0] == srcPts[0]);
983 srcPts++;
984 firstTime = false;
985 break;
Chris Daltonde500372020-05-05 15:06:30 -0600986 case SkPathVerb::kLine:
Yuqian Li3154a532017-09-06 13:33:30 -0400987 REPORTER_ASSERT(reporter, !firstTime);
988 REPORTER_ASSERT(reporter, pts[1] == srcPts[0]);
989 srcPts++;
990 break;
Chris Daltonde500372020-05-05 15:06:30 -0600991 case SkPathVerb::kQuad:
Brian Salomon1c80e992018-01-29 09:50:47 -0500992 REPORTER_ASSERT(reporter, false, "unexpected quad verb");
Yuqian Li3154a532017-09-06 13:33:30 -0400993 break;
Chris Daltonde500372020-05-05 15:06:30 -0600994 case SkPathVerb::kConic:
Brian Salomon1c80e992018-01-29 09:50:47 -0500995 REPORTER_ASSERT(reporter, false, "unexpected conic verb");
Yuqian Li3154a532017-09-06 13:33:30 -0400996 break;
Chris Daltonde500372020-05-05 15:06:30 -0600997 case SkPathVerb::kCubic:
Brian Salomon1c80e992018-01-29 09:50:47 -0500998 REPORTER_ASSERT(reporter, false, "unexpected cubic verb");
Yuqian Li3154a532017-09-06 13:33:30 -0400999 break;
Chris Daltonde500372020-05-05 15:06:30 -06001000 case SkPathVerb::kClose:
Yuqian Li3154a532017-09-06 13:33:30 -04001001 REPORTER_ASSERT(reporter, !firstTime);
1002 REPORTER_ASSERT(reporter, !foundClose);
1003 REPORTER_ASSERT(reporter, expectClose);
1004 foundClose = true;
1005 break;
Yuqian Li3154a532017-09-06 13:33:30 -04001006 }
1007 }
Yuqian Li3154a532017-09-06 13:33:30 -04001008 REPORTER_ASSERT(reporter, foundClose == expectClose);
1009}
1010
1011static void test_addPoly(skiatest::Reporter* reporter) {
1012 SkPoint pts[32];
1013 SkRandom rand;
1014
1015 for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) {
1016 pts[i].fX = rand.nextSScalar1();
1017 pts[i].fY = rand.nextSScalar1();
1018 }
1019
1020 for (int doClose = 0; doClose <= 1; ++doClose) {
1021 for (size_t count = 1; count <= SK_ARRAY_COUNT(pts); ++count) {
1022 SkPath path;
1023 path.addPoly(pts, SkToInt(count), SkToBool(doClose));
1024 test_poly(reporter, path, pts, SkToBool(doClose));
1025 }
1026 }
1027}
1028
1029static void test_strokerec(skiatest::Reporter* reporter) {
1030 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
1031 REPORTER_ASSERT(reporter, rec.isFillStyle());
1032
1033 rec.setHairlineStyle();
1034 REPORTER_ASSERT(reporter, rec.isHairlineStyle());
1035
1036 rec.setStrokeStyle(SK_Scalar1, false);
1037 REPORTER_ASSERT(reporter, SkStrokeRec::kStroke_Style == rec.getStyle());
1038
1039 rec.setStrokeStyle(SK_Scalar1, true);
1040 REPORTER_ASSERT(reporter, SkStrokeRec::kStrokeAndFill_Style == rec.getStyle());
1041
1042 rec.setStrokeStyle(0, false);
1043 REPORTER_ASSERT(reporter, SkStrokeRec::kHairline_Style == rec.getStyle());
1044
1045 rec.setStrokeStyle(0, true);
1046 REPORTER_ASSERT(reporter, SkStrokeRec::kFill_Style == rec.getStyle());
1047}
1048
1049// Set this for paths that don't have a consistent direction such as a bowtie.
1050// (cheapComputeDirection is not expected to catch these.)
Mike Kleinf6c85402018-08-13 12:19:28 -04001051// Legal values are CW (0), CCW (1) and Unknown (2), leaving 3 as a convenient sentinel.
Mike Reed3872c982020-08-29 17:46:51 -04001052const SkPathFirstDirection kDontCheckDir = static_cast<SkPathFirstDirection>(3);
Yuqian Li3154a532017-09-06 13:33:30 -04001053
1054static void check_direction(skiatest::Reporter* reporter, const SkPath& path,
Mike Reed3872c982020-08-29 17:46:51 -04001055 SkPathFirstDirection expected) {
Yuqian Li3154a532017-09-06 13:33:30 -04001056 if (expected == kDontCheckDir) {
1057 return;
1058 }
John Stiles31954bf2020-08-07 17:35:54 -04001059 // We make a copy so that we don't cache the result on the passed in path.
1060 SkPath copy(path); // NOLINT(performance-unnecessary-copy-initialization)
Yuqian Li3154a532017-09-06 13:33:30 -04001061
Mike Reed85f51b22020-08-30 10:32:06 -04001062 SkPathFirstDirection dir = SkPathPriv::ComputeFirstDirection(copy);
1063 if (dir != SkPathFirstDirection::kUnknown) {
Yuqian Li3154a532017-09-06 13:33:30 -04001064 REPORTER_ASSERT(reporter, dir == expected);
Yuqian Li3154a532017-09-06 13:33:30 -04001065 }
1066}
1067
1068static void test_direction(skiatest::Reporter* reporter) {
1069 size_t i;
1070 SkPath path;
Mike Reed85f51b22020-08-30 10:32:06 -04001071 REPORTER_ASSERT(reporter,
1072 SkPathPriv::ComputeFirstDirection(path) == SkPathFirstDirection::kUnknown);
Yuqian Li3154a532017-09-06 13:33:30 -04001073
1074 static const char* gDegen[] = {
1075 "M 10 10",
1076 "M 10 10 M 20 20",
1077 "M 10 10 L 20 20",
1078 "M 10 10 L 10 10 L 10 10",
1079 "M 10 10 Q 10 10 10 10",
1080 "M 10 10 C 10 10 10 10 10 10",
1081 };
1082 for (i = 0; i < SK_ARRAY_COUNT(gDegen); ++i) {
1083 path.reset();
1084 bool valid = SkParsePath::FromSVGString(gDegen[i], &path);
1085 REPORTER_ASSERT(reporter, valid);
Mike Reed85f51b22020-08-30 10:32:06 -04001086 REPORTER_ASSERT(reporter,
1087 SkPathPriv::ComputeFirstDirection(path) == SkPathFirstDirection::kUnknown);
Yuqian Li3154a532017-09-06 13:33:30 -04001088 }
1089
1090 static const char* gCW[] = {
1091 "M 10 10 L 10 10 Q 20 10 20 20",
1092 "M 10 10 C 20 10 20 20 20 20",
1093 "M 20 10 Q 20 20 30 20 L 10 20", // test double-back at y-max
1094 // rect with top two corners replaced by cubics with identical middle
1095 // control points
1096 "M 10 10 C 10 0 10 0 20 0 L 40 0 C 50 0 50 0 50 10",
1097 "M 20 10 L 0 10 Q 10 10 20 0", // left, degenerate serif
1098 };
1099 for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) {
1100 path.reset();
1101 bool valid = SkParsePath::FromSVGString(gCW[i], &path);
1102 REPORTER_ASSERT(reporter, valid);
Mike Reed3872c982020-08-29 17:46:51 -04001103 check_direction(reporter, path, SkPathFirstDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04001104 }
1105
1106 static const char* gCCW[] = {
1107 "M 10 10 L 10 10 Q 20 10 20 -20",
1108 "M 10 10 C 20 10 20 -20 20 -20",
1109 "M 20 10 Q 20 20 10 20 L 30 20", // test double-back at y-max
1110 // rect with top two corners replaced by cubics with identical middle
1111 // control points
1112 "M 50 10 C 50 0 50 0 40 0 L 20 0 C 10 0 10 0 10 10",
1113 "M 10 10 L 30 10 Q 20 10 10 0", // right, degenerate serif
1114 };
1115 for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) {
1116 path.reset();
1117 bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
1118 REPORTER_ASSERT(reporter, valid);
Mike Reed3872c982020-08-29 17:46:51 -04001119 check_direction(reporter, path, SkPathFirstDirection::kCCW);
Yuqian Li3154a532017-09-06 13:33:30 -04001120 }
1121
1122 // Test two donuts, each wound a different direction. Only the outer contour
1123 // determines the cheap direction
1124 path.reset();
Mike Reed30bc5272019-11-22 18:34:02 +00001125 path.addCircle(0, 0, SkIntToScalar(2), SkPathDirection::kCW);
1126 path.addCircle(0, 0, SkIntToScalar(1), SkPathDirection::kCCW);
Mike Reed3872c982020-08-29 17:46:51 -04001127 check_direction(reporter, path, SkPathFirstDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04001128
1129 path.reset();
Mike Reed30bc5272019-11-22 18:34:02 +00001130 path.addCircle(0, 0, SkIntToScalar(1), SkPathDirection::kCW);
1131 path.addCircle(0, 0, SkIntToScalar(2), SkPathDirection::kCCW);
Mike Reed3872c982020-08-29 17:46:51 -04001132 check_direction(reporter, path, SkPathFirstDirection::kCCW);
Yuqian Li3154a532017-09-06 13:33:30 -04001133
1134 // triangle with one point really far from the origin.
1135 path.reset();
1136 // the first point is roughly 1.05e10, 1.05e10
1137 path.moveTo(SkBits2Float(0x501c7652), SkBits2Float(0x501c7652));
1138 path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1);
1139 path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1);
Mike Reed3872c982020-08-29 17:46:51 -04001140 check_direction(reporter, path, SkPathFirstDirection::kCCW);
Yuqian Li3154a532017-09-06 13:33:30 -04001141
1142 path.reset();
1143 path.conicTo(20, 0, 20, 20, 0.5f);
1144 path.close();
Mike Reed3872c982020-08-29 17:46:51 -04001145 check_direction(reporter, path, SkPathFirstDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04001146
1147 path.reset();
1148 path.lineTo(1, 1e7f);
1149 path.lineTo(1e7f, 2e7f);
1150 path.close();
Mike Reed30bc5272019-11-22 18:34:02 +00001151 REPORTER_ASSERT(reporter, path.isConvex());
Mike Reed3872c982020-08-29 17:46:51 -04001152 check_direction(reporter, path, SkPathFirstDirection::kCCW);
Yuqian Li3154a532017-09-06 13:33:30 -04001153}
1154
1155static void add_rect(SkPath* path, const SkRect& r) {
1156 path->moveTo(r.fLeft, r.fTop);
1157 path->lineTo(r.fRight, r.fTop);
1158 path->lineTo(r.fRight, r.fBottom);
1159 path->lineTo(r.fLeft, r.fBottom);
1160 path->close();
1161}
1162
1163static void test_bounds(skiatest::Reporter* reporter) {
1164 static const SkRect rects[] = {
1165 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(160) },
1166 { SkIntToScalar(610), SkIntToScalar(160), SkIntToScalar(610), SkIntToScalar(199) },
1167 { SkIntToScalar(10), SkIntToScalar(198), SkIntToScalar(610), SkIntToScalar(199) },
1168 { SkIntToScalar(10), SkIntToScalar(160), SkIntToScalar(10), SkIntToScalar(199) },
1169 };
1170
1171 SkPath path0, path1;
1172 for (size_t i = 0; i < SK_ARRAY_COUNT(rects); ++i) {
1173 path0.addRect(rects[i]);
1174 add_rect(&path1, rects[i]);
1175 }
1176
1177 REPORTER_ASSERT(reporter, path0.getBounds() == path1.getBounds());
1178}
1179
1180static void stroke_cubic(const SkPoint pts[4]) {
1181 SkPath path;
1182 path.moveTo(pts[0]);
1183 path.cubicTo(pts[1], pts[2], pts[3]);
1184
1185 SkPaint paint;
1186 paint.setStyle(SkPaint::kStroke_Style);
1187 paint.setStrokeWidth(SK_Scalar1 * 2);
1188
1189 SkPath fill;
1190 paint.getFillPath(path, &fill);
1191}
1192
1193// just ensure this can run w/o any SkASSERTS firing in the debug build
1194// we used to assert due to differences in how we determine a degenerate vector
1195// but that was fixed with the introduction of SkPoint::CanNormalize
1196static void stroke_tiny_cubic() {
1197 SkPoint p0[] = {
1198 { 372.0f, 92.0f },
1199 { 372.0f, 92.0f },
1200 { 372.0f, 92.0f },
1201 { 372.0f, 92.0f },
1202 };
1203
1204 stroke_cubic(p0);
1205
1206 SkPoint p1[] = {
1207 { 372.0f, 92.0f },
1208 { 372.0007f, 92.000755f },
1209 { 371.99927f, 92.003922f },
1210 { 371.99826f, 92.003899f },
1211 };
1212
1213 stroke_cubic(p1);
1214}
1215
1216static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
1217 for (int i = 0; i < 2; ++i) {
1218 SkPath::Iter iter(path, SkToBool(i));
1219 SkPoint mv;
1220 SkPoint pts[4];
1221 SkPath::Verb v;
1222 int nMT = 0;
1223 int nCL = 0;
1224 mv.set(0, 0);
1225 while (SkPath::kDone_Verb != (v = iter.next(pts))) {
1226 switch (v) {
1227 case SkPath::kMove_Verb:
1228 mv = pts[0];
1229 ++nMT;
1230 break;
1231 case SkPath::kClose_Verb:
1232 REPORTER_ASSERT(reporter, mv == pts[0]);
1233 ++nCL;
1234 break;
1235 default:
1236 break;
1237 }
1238 }
1239 // if we force a close on the interator we should have a close
1240 // for every moveTo
1241 REPORTER_ASSERT(reporter, !i || nMT == nCL);
1242 }
1243}
1244
1245static void test_close(skiatest::Reporter* reporter) {
1246 SkPath closePt;
1247 closePt.moveTo(0, 0);
1248 closePt.close();
1249 check_close(reporter, closePt);
1250
1251 SkPath openPt;
1252 openPt.moveTo(0, 0);
1253 check_close(reporter, openPt);
1254
1255 SkPath empty;
1256 check_close(reporter, empty);
1257 empty.close();
1258 check_close(reporter, empty);
1259
1260 SkPath rect;
1261 rect.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
1262 check_close(reporter, rect);
1263 rect.close();
1264 check_close(reporter, rect);
1265
1266 SkPath quad;
1267 quad.quadTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
1268 check_close(reporter, quad);
1269 quad.close();
1270 check_close(reporter, quad);
1271
1272 SkPath cubic;
1273 quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
1274 10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
1275 check_close(reporter, cubic);
1276 cubic.close();
1277 check_close(reporter, cubic);
1278
1279 SkPath line;
1280 line.moveTo(SK_Scalar1, SK_Scalar1);
1281 line.lineTo(10 * SK_Scalar1, 10*SK_Scalar1);
1282 check_close(reporter, line);
1283 line.close();
1284 check_close(reporter, line);
1285
1286 SkPath rect2;
1287 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
1288 rect2.close();
1289 rect2.addRect(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 10*SK_Scalar1);
1290 check_close(reporter, rect2);
1291 rect2.close();
1292 check_close(reporter, rect2);
1293
1294 SkPath oval3;
1295 oval3.addOval(SkRect::MakeWH(SK_Scalar1*100,SK_Scalar1*100));
1296 oval3.close();
1297 oval3.addOval(SkRect::MakeWH(SK_Scalar1*200,SK_Scalar1*200));
1298 check_close(reporter, oval3);
1299 oval3.close();
1300 check_close(reporter, oval3);
1301
1302 SkPath moves;
1303 moves.moveTo(SK_Scalar1, SK_Scalar1);
1304 moves.moveTo(5 * SK_Scalar1, SK_Scalar1);
1305 moves.moveTo(SK_Scalar1, 10 * SK_Scalar1);
1306 moves.moveTo(10 *SK_Scalar1, SK_Scalar1);
1307 check_close(reporter, moves);
1308
1309 stroke_tiny_cubic();
1310}
1311
1312static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
Mike Reed6052c0b2020-08-28 11:49:56 -04001313 bool expectedConvexity) {
John Stiles31954bf2020-08-07 17:35:54 -04001314 // We make a copy so that we don't cache the result on the passed in path.
1315 SkPath copy(path); // NOLINT(performance-unnecessary-copy-initialization)
Mike Reed6052c0b2020-08-28 11:49:56 -04001316 bool convexity = copy.isConvex();
1317 REPORTER_ASSERT(reporter, convexity == expectedConvexity);
Mike Reed8ee77d82019-11-22 12:59:01 -05001318
Cary Clarkc9b7c722018-12-12 14:50:23 -05001319 // test points-by-array interface
1320 SkPath::Iter iter(path, true);
1321 int initialMoves = 0;
1322 SkPoint pts[4];
Mike Reedba7e9a62019-08-16 13:30:34 -04001323 while (SkPath::kMove_Verb == iter.next(pts)) {
Cary Clarkc9b7c722018-12-12 14:50:23 -05001324 ++initialMoves;
1325 }
1326 if (initialMoves > 0) {
1327 std::vector<SkPoint> points;
1328 points.resize(path.getPoints(nullptr, 0));
1329 (void) path.getPoints(&points.front(), points.size());
1330 int skip = initialMoves - 1;
1331 bool isConvex = SkPathPriv::IsConvex(&points.front() + skip, points.size() - skip);
Mike Reed6052c0b2020-08-28 11:49:56 -04001332 REPORTER_ASSERT(reporter, isConvex == expectedConvexity);
Cary Clarkc9b7c722018-12-12 14:50:23 -05001333 }
Yuqian Li3154a532017-09-06 13:33:30 -04001334}
1335
1336static void test_path_crbug389050(skiatest::Reporter* reporter) {
1337 SkPath tinyConvexPolygon;
1338 tinyConvexPolygon.moveTo(600.131559f, 800.112512f);
1339 tinyConvexPolygon.lineTo(600.161735f, 800.118627f);
1340 tinyConvexPolygon.lineTo(600.148962f, 800.142338f);
1341 tinyConvexPolygon.lineTo(600.134891f, 800.137724f);
1342 tinyConvexPolygon.close();
Mike Reed6052c0b2020-08-28 11:49:56 -04001343 tinyConvexPolygon.isConvex();
Brian Osmancf4ec502019-04-19 12:58:21 -04001344 // This is convex, but so small that it fails many of our checks, and the three "backwards"
1345 // bends convince the checker that it's concave. That's okay though, we draw it correctly.
Mike Reed6052c0b2020-08-28 11:49:56 -04001346 check_convexity(reporter, tinyConvexPolygon, false);
Mike Reed3872c982020-08-29 17:46:51 -04001347 check_direction(reporter, tinyConvexPolygon, SkPathFirstDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04001348
1349 SkPath platTriangle;
1350 platTriangle.moveTo(0, 0);
1351 platTriangle.lineTo(200, 0);
1352 platTriangle.lineTo(100, 0.04f);
1353 platTriangle.close();
Mike Reed6052c0b2020-08-28 11:49:56 -04001354 platTriangle.isConvex();
Mike Reed3872c982020-08-29 17:46:51 -04001355 check_direction(reporter, platTriangle, SkPathFirstDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04001356
1357 platTriangle.reset();
1358 platTriangle.moveTo(0, 0);
1359 platTriangle.lineTo(200, 0);
1360 platTriangle.lineTo(100, 0.03f);
1361 platTriangle.close();
Mike Reed6052c0b2020-08-28 11:49:56 -04001362 platTriangle.isConvex();
Mike Reed3872c982020-08-29 17:46:51 -04001363 check_direction(reporter, platTriangle, SkPathFirstDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04001364}
1365
1366static void test_convexity2(skiatest::Reporter* reporter) {
1367 SkPath pt;
1368 pt.moveTo(0, 0);
1369 pt.close();
Mike Reed6052c0b2020-08-28 11:49:56 -04001370 check_convexity(reporter, pt, true);
Mike Reed3872c982020-08-29 17:46:51 -04001371 check_direction(reporter, pt, SkPathFirstDirection::kUnknown);
Yuqian Li3154a532017-09-06 13:33:30 -04001372
1373 SkPath line;
1374 line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
1375 line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
1376 line.close();
Mike Reed6052c0b2020-08-28 11:49:56 -04001377 check_convexity(reporter, line, true);
Mike Reed3872c982020-08-29 17:46:51 -04001378 check_direction(reporter, line, SkPathFirstDirection::kUnknown);
Yuqian Li3154a532017-09-06 13:33:30 -04001379
1380 SkPath triLeft;
1381 triLeft.moveTo(0, 0);
1382 triLeft.lineTo(SK_Scalar1, 0);
1383 triLeft.lineTo(SK_Scalar1, SK_Scalar1);
1384 triLeft.close();
Mike Reed6052c0b2020-08-28 11:49:56 -04001385 check_convexity(reporter, triLeft, true);
Mike Reed3872c982020-08-29 17:46:51 -04001386 check_direction(reporter, triLeft, SkPathFirstDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04001387
1388 SkPath triRight;
1389 triRight.moveTo(0, 0);
1390 triRight.lineTo(-SK_Scalar1, 0);
1391 triRight.lineTo(SK_Scalar1, SK_Scalar1);
1392 triRight.close();
Mike Reed6052c0b2020-08-28 11:49:56 -04001393 check_convexity(reporter, triRight, true);
Mike Reed3872c982020-08-29 17:46:51 -04001394 check_direction(reporter, triRight, SkPathFirstDirection::kCCW);
Yuqian Li3154a532017-09-06 13:33:30 -04001395
1396 SkPath square;
1397 square.moveTo(0, 0);
1398 square.lineTo(SK_Scalar1, 0);
1399 square.lineTo(SK_Scalar1, SK_Scalar1);
1400 square.lineTo(0, SK_Scalar1);
1401 square.close();
Mike Reed6052c0b2020-08-28 11:49:56 -04001402 check_convexity(reporter, square, true);
Mike Reed3872c982020-08-29 17:46:51 -04001403 check_direction(reporter, square, SkPathFirstDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04001404
1405 SkPath redundantSquare;
1406 redundantSquare.moveTo(0, 0);
1407 redundantSquare.lineTo(0, 0);
1408 redundantSquare.lineTo(0, 0);
1409 redundantSquare.lineTo(SK_Scalar1, 0);
1410 redundantSquare.lineTo(SK_Scalar1, 0);
1411 redundantSquare.lineTo(SK_Scalar1, 0);
1412 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
1413 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
1414 redundantSquare.lineTo(SK_Scalar1, SK_Scalar1);
1415 redundantSquare.lineTo(0, SK_Scalar1);
1416 redundantSquare.lineTo(0, SK_Scalar1);
1417 redundantSquare.lineTo(0, SK_Scalar1);
1418 redundantSquare.close();
Mike Reed6052c0b2020-08-28 11:49:56 -04001419 check_convexity(reporter, redundantSquare, true);
Mike Reed3872c982020-08-29 17:46:51 -04001420 check_direction(reporter, redundantSquare, SkPathFirstDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04001421
1422 SkPath bowTie;
1423 bowTie.moveTo(0, 0);
1424 bowTie.lineTo(0, 0);
1425 bowTie.lineTo(0, 0);
1426 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
1427 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
1428 bowTie.lineTo(SK_Scalar1, SK_Scalar1);
1429 bowTie.lineTo(SK_Scalar1, 0);
1430 bowTie.lineTo(SK_Scalar1, 0);
1431 bowTie.lineTo(SK_Scalar1, 0);
1432 bowTie.lineTo(0, SK_Scalar1);
1433 bowTie.lineTo(0, SK_Scalar1);
1434 bowTie.lineTo(0, SK_Scalar1);
1435 bowTie.close();
Mike Reed6052c0b2020-08-28 11:49:56 -04001436 check_convexity(reporter, bowTie, false);
Yuqian Li3154a532017-09-06 13:33:30 -04001437 check_direction(reporter, bowTie, kDontCheckDir);
1438
1439 SkPath spiral;
1440 spiral.moveTo(0, 0);
1441 spiral.lineTo(100*SK_Scalar1, 0);
1442 spiral.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
1443 spiral.lineTo(0, 100*SK_Scalar1);
1444 spiral.lineTo(0, 50*SK_Scalar1);
1445 spiral.lineTo(50*SK_Scalar1, 50*SK_Scalar1);
1446 spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
1447 spiral.close();
Mike Reed6052c0b2020-08-28 11:49:56 -04001448 check_convexity(reporter, spiral, false);
Yuqian Li3154a532017-09-06 13:33:30 -04001449 check_direction(reporter, spiral, kDontCheckDir);
1450
1451 SkPath dent;
1452 dent.moveTo(0, 0);
1453 dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
1454 dent.lineTo(0, 100*SK_Scalar1);
1455 dent.lineTo(-50*SK_Scalar1, 200*SK_Scalar1);
1456 dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
1457 dent.close();
Mike Reed6052c0b2020-08-28 11:49:56 -04001458 check_convexity(reporter, dent, false);
Mike Reed3872c982020-08-29 17:46:51 -04001459 check_direction(reporter, dent, SkPathFirstDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04001460
1461 // https://bug.skia.org/2235
1462 SkPath strokedSin;
1463 for (int i = 0; i < 2000; i++) {
1464 SkScalar x = SkIntToScalar(i) / 2;
1465 SkScalar y = 500 - (x + SkScalarSin(x / 100) * 40) / 3;
1466 if (0 == i) {
1467 strokedSin.moveTo(x, y);
1468 } else {
1469 strokedSin.lineTo(x, y);
1470 }
1471 }
1472 SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
1473 stroke.setStrokeStyle(2 * SK_Scalar1);
1474 stroke.applyToPath(&strokedSin, strokedSin);
Mike Reed6052c0b2020-08-28 11:49:56 -04001475 check_convexity(reporter, strokedSin, false);
Yuqian Li3154a532017-09-06 13:33:30 -04001476 check_direction(reporter, strokedSin, kDontCheckDir);
1477
1478 // http://crbug.com/412640
1479 SkPath degenerateConcave;
1480 degenerateConcave.moveTo(148.67912f, 191.875f);
1481 degenerateConcave.lineTo(470.37695f, 7.5f);
1482 degenerateConcave.lineTo(148.67912f, 191.875f);
1483 degenerateConcave.lineTo(41.446522f, 376.25f);
1484 degenerateConcave.lineTo(-55.971577f, 460.0f);
1485 degenerateConcave.lineTo(41.446522f, 376.25f);
Mike Reed6052c0b2020-08-28 11:49:56 -04001486 check_convexity(reporter, degenerateConcave, false);
Mike Reed3872c982020-08-29 17:46:51 -04001487 check_direction(reporter, degenerateConcave, SkPathFirstDirection::kUnknown);
Yuqian Li3154a532017-09-06 13:33:30 -04001488
1489 // http://crbug.com/433683
1490 SkPath badFirstVector;
1491 badFirstVector.moveTo(501.087708f, 319.610352f);
1492 badFirstVector.lineTo(501.087708f, 319.610352f);
1493 badFirstVector.cubicTo(501.087677f, 319.610321f, 449.271606f, 258.078674f, 395.084564f, 198.711182f);
1494 badFirstVector.cubicTo(358.967072f, 159.140717f, 321.910553f, 120.650436f, 298.442322f, 101.955399f);
1495 badFirstVector.lineTo(301.557678f, 98.044601f);
1496 badFirstVector.cubicTo(325.283844f, 116.945084f, 362.615204f, 155.720825f, 398.777557f, 195.340454f);
1497 badFirstVector.cubicTo(453.031860f, 254.781662f, 504.912262f, 316.389618f, 504.912292f, 316.389648f);
1498 badFirstVector.lineTo(504.912292f, 316.389648f);
1499 badFirstVector.lineTo(501.087708f, 319.610352f);
1500 badFirstVector.close();
Mike Reed6052c0b2020-08-28 11:49:56 -04001501 check_convexity(reporter, badFirstVector, false);
Brian Osman4be31e22019-08-20 15:35:26 -04001502
1503 // http://crbug.com/993330
1504 SkPath falseBackEdge;
1505 falseBackEdge.moveTo(-217.83430557928145f, -382.14948768484857f);
1506 falseBackEdge.lineTo(-227.73867866614847f, -399.52485512718323f);
1507 falseBackEdge.cubicTo(-158.3541047666846f, -439.0757140459542f,
1508 -79.8654464485281f, -459.875f,
1509 -1.1368683772161603e-13f, -459.875f);
1510 falseBackEdge.lineTo(-8.08037266162413e-14f, -439.875f);
1511 falseBackEdge.lineTo(-8.526512829121202e-14f, -439.87499999999994f);
1512 falseBackEdge.cubicTo(-76.39209188702645f, -439.87499999999994f,
1513 -151.46727226799754f, -419.98027663161537f,
1514 -217.83430557928145f, -382.14948768484857f);
1515 falseBackEdge.close();
Mike Reed6052c0b2020-08-28 11:49:56 -04001516 check_convexity(reporter, falseBackEdge, false);
Yuqian Li3154a532017-09-06 13:33:30 -04001517}
1518
Cary Clarkc9b7c722018-12-12 14:50:23 -05001519static void test_convexity_doubleback(skiatest::Reporter* reporter) {
1520 SkPath doubleback;
1521 doubleback.lineTo(1, 1);
Mike Reed6052c0b2020-08-28 11:49:56 -04001522 check_convexity(reporter, doubleback, true);
Cary Clarkc9b7c722018-12-12 14:50:23 -05001523 doubleback.lineTo(2, 2);
Mike Reed6052c0b2020-08-28 11:49:56 -04001524 check_convexity(reporter, doubleback, true);
Cary Clarkc9b7c722018-12-12 14:50:23 -05001525 doubleback.reset();
1526 doubleback.lineTo(1, 0);
Mike Reed6052c0b2020-08-28 11:49:56 -04001527 check_convexity(reporter, doubleback, true);
Cary Clarkc9b7c722018-12-12 14:50:23 -05001528 doubleback.lineTo(2, 0);
Mike Reed6052c0b2020-08-28 11:49:56 -04001529 check_convexity(reporter, doubleback, true);
Cary Clarkc9b7c722018-12-12 14:50:23 -05001530 doubleback.lineTo(1, 0);
Mike Reed6052c0b2020-08-28 11:49:56 -04001531 check_convexity(reporter, doubleback, true);
Cary Clarkc9b7c722018-12-12 14:50:23 -05001532 doubleback.reset();
1533 doubleback.quadTo(1, 1, 2, 2);
Mike Reed6052c0b2020-08-28 11:49:56 -04001534 check_convexity(reporter, doubleback, true);
Cary Clarkc9b7c722018-12-12 14:50:23 -05001535 doubleback.reset();
1536 doubleback.quadTo(1, 0, 2, 0);
Mike Reed6052c0b2020-08-28 11:49:56 -04001537 check_convexity(reporter, doubleback, true);
Cary Clarkc9b7c722018-12-12 14:50:23 -05001538 doubleback.quadTo(1, 0, 0, 0);
Mike Reed6052c0b2020-08-28 11:49:56 -04001539 check_convexity(reporter, doubleback, true);
Cary Clarkc9b7c722018-12-12 14:50:23 -05001540}
1541
Yuqian Li3154a532017-09-06 13:33:30 -04001542static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
1543 const SkRect& bounds) {
1544 REPORTER_ASSERT(reporter, p.isConvex());
1545 REPORTER_ASSERT(reporter, p.getBounds() == bounds);
1546
1547 SkPath p2(p);
1548 REPORTER_ASSERT(reporter, p2.isConvex());
1549 REPORTER_ASSERT(reporter, p2.getBounds() == bounds);
1550
1551 SkPath other;
1552 other.swap(p2);
1553 REPORTER_ASSERT(reporter, other.isConvex());
1554 REPORTER_ASSERT(reporter, other.getBounds() == bounds);
1555}
1556
1557static void setFromString(SkPath* path, const char str[]) {
1558 bool first = true;
1559 while (str) {
1560 SkScalar x, y;
1561 str = SkParse::FindScalar(str, &x);
1562 if (nullptr == str) {
1563 break;
1564 }
1565 str = SkParse::FindScalar(str, &y);
1566 SkASSERT(str);
1567 if (first) {
1568 path->moveTo(x, y);
1569 first = false;
1570 } else {
1571 path->lineTo(x, y);
1572 }
1573 }
1574}
1575
1576static void test_convexity(skiatest::Reporter* reporter) {
1577 SkPath path;
1578
Mike Reed6052c0b2020-08-28 11:49:56 -04001579 check_convexity(reporter, path, true);
Yuqian Li3154a532017-09-06 13:33:30 -04001580 path.addCircle(0, 0, SkIntToScalar(10));
Mike Reed6052c0b2020-08-28 11:49:56 -04001581 check_convexity(reporter, path, true);
Yuqian Li3154a532017-09-06 13:33:30 -04001582 path.addCircle(0, 0, SkIntToScalar(10)); // 2nd circle
Mike Reed6052c0b2020-08-28 11:49:56 -04001583 check_convexity(reporter, path, false);
Yuqian Li3154a532017-09-06 13:33:30 -04001584
1585 path.reset();
Mike Reed30bc5272019-11-22 18:34:02 +00001586 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPathDirection::kCCW);
Mike Reed6052c0b2020-08-28 11:49:56 -04001587 check_convexity(reporter, path, true);
Mike Reed85f51b22020-08-30 10:32:06 -04001588 REPORTER_ASSERT(reporter, SkPathPriv::ComputeFirstDirection(path) == SkPathFirstDirection::kCCW);
Yuqian Li3154a532017-09-06 13:33:30 -04001589
1590 path.reset();
Mike Reed30bc5272019-11-22 18:34:02 +00001591 path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPathDirection::kCW);
Mike Reed6052c0b2020-08-28 11:49:56 -04001592 check_convexity(reporter, path, true);
Mike Reed85f51b22020-08-30 10:32:06 -04001593 REPORTER_ASSERT(reporter, SkPathPriv::ComputeFirstDirection(path) == SkPathFirstDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04001594
1595 path.reset();
Cary Clarkc9b7c722018-12-12 14:50:23 -05001596 path.quadTo(100, 100, 50, 50); // This from GM:convexpaths
Mike Reed6052c0b2020-08-28 11:49:56 -04001597 check_convexity(reporter, path, true);
Yuqian Li3154a532017-09-06 13:33:30 -04001598
1599 static const struct {
Mike Reed3872c982020-08-29 17:46:51 -04001600 const char* fPathStr;
1601 bool fExpectedIsConvex;
1602 SkPathFirstDirection fExpectedDirection;
Yuqian Li3154a532017-09-06 13:33:30 -04001603 } gRec[] = {
Mike Reed3872c982020-08-29 17:46:51 -04001604 { "", true, SkPathFirstDirection::kUnknown },
1605 { "0 0", true, SkPathFirstDirection::kUnknown },
1606 { "0 0 10 10", true, SkPathFirstDirection::kUnknown },
1607 { "0 0 10 10 20 20 0 0 10 10", false, SkPathFirstDirection::kUnknown },
1608 { "0 0 10 10 10 20", true, SkPathFirstDirection::kCW },
1609 { "0 0 10 10 10 0", true, SkPathFirstDirection::kCCW },
Mike Reed6052c0b2020-08-28 11:49:56 -04001610 { "0 0 10 10 10 0 0 10", false, kDontCheckDir },
Mike Reed3872c982020-08-29 17:46:51 -04001611 { "0 0 10 0 0 10 -10 -10", false, SkPathFirstDirection::kCW },
Yuqian Li3154a532017-09-06 13:33:30 -04001612 };
1613
1614 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
Cary Clarkc9b7c722018-12-12 14:50:23 -05001615 path.reset();
Yuqian Li3154a532017-09-06 13:33:30 -04001616 setFromString(&path, gRec[i].fPathStr);
Mike Reed6052c0b2020-08-28 11:49:56 -04001617 check_convexity(reporter, path, gRec[i].fExpectedIsConvex);
Yuqian Li3154a532017-09-06 13:33:30 -04001618 check_direction(reporter, path, gRec[i].fExpectedDirection);
1619 // check after setting the initial convex and direction
1620 if (kDontCheckDir != gRec[i].fExpectedDirection) {
John Stiles31954bf2020-08-07 17:35:54 -04001621 // We make a copy so that we don't cache the result on the passed in path.
1622 SkPath copy(path); // NOLINT(performance-unnecessary-copy-initialization)
Mike Reed85f51b22020-08-30 10:32:06 -04001623 SkPathFirstDirection dir = SkPathPriv::ComputeFirstDirection(copy);
1624 bool foundDir = dir != SkPathFirstDirection::kUnknown;
Mike Reed3872c982020-08-29 17:46:51 -04001625 REPORTER_ASSERT(reporter, (gRec[i].fExpectedDirection == SkPathFirstDirection::kUnknown)
Yuqian Li3154a532017-09-06 13:33:30 -04001626 ^ foundDir);
1627 REPORTER_ASSERT(reporter, !foundDir || gRec[i].fExpectedDirection == dir);
Mike Reed6052c0b2020-08-28 11:49:56 -04001628 check_convexity(reporter, copy, gRec[i].fExpectedIsConvex);
Yuqian Li3154a532017-09-06 13:33:30 -04001629 }
Mike Reed6052c0b2020-08-28 11:49:56 -04001630 REPORTER_ASSERT(reporter, gRec[i].fExpectedIsConvex == path.isConvex());
Yuqian Li3154a532017-09-06 13:33:30 -04001631 check_direction(reporter, path, gRec[i].fExpectedDirection);
1632 }
1633
1634 static const SkPoint nonFinitePts[] = {
1635 { SK_ScalarInfinity, 0 },
1636 { 0, SK_ScalarInfinity },
1637 { SK_ScalarInfinity, SK_ScalarInfinity },
1638 { SK_ScalarNegativeInfinity, 0},
1639 { 0, SK_ScalarNegativeInfinity },
1640 { SK_ScalarNegativeInfinity, SK_ScalarNegativeInfinity },
1641 { SK_ScalarNegativeInfinity, SK_ScalarInfinity },
1642 { SK_ScalarInfinity, SK_ScalarNegativeInfinity },
1643 { SK_ScalarNaN, 0 },
1644 { 0, SK_ScalarNaN },
1645 { SK_ScalarNaN, SK_ScalarNaN },
1646 };
1647
1648 const size_t nonFinitePtsCount = sizeof(nonFinitePts) / sizeof(nonFinitePts[0]);
1649
Cary Clarkc9b7c722018-12-12 14:50:23 -05001650 static const SkPoint axisAlignedPts[] = {
Yuqian Li3154a532017-09-06 13:33:30 -04001651 { SK_ScalarMax, 0 },
1652 { 0, SK_ScalarMax },
Yuqian Li3154a532017-09-06 13:33:30 -04001653 { SK_ScalarMin, 0 },
1654 { 0, SK_ScalarMin },
Yuqian Li3154a532017-09-06 13:33:30 -04001655 };
1656
Cary Clarkc9b7c722018-12-12 14:50:23 -05001657 const size_t axisAlignedPtsCount = sizeof(axisAlignedPts) / sizeof(axisAlignedPts[0]);
Yuqian Li3154a532017-09-06 13:33:30 -04001658
Cary Clarkc9b7c722018-12-12 14:50:23 -05001659 for (int index = 0; index < (int) (13 * nonFinitePtsCount * axisAlignedPtsCount); ++index) {
Yuqian Li3154a532017-09-06 13:33:30 -04001660 int i = (int) (index % nonFinitePtsCount);
Cary Clarkc9b7c722018-12-12 14:50:23 -05001661 int f = (int) (index % axisAlignedPtsCount);
1662 int g = (int) ((f + 1) % axisAlignedPtsCount);
Yuqian Li3154a532017-09-06 13:33:30 -04001663 path.reset();
1664 switch (index % 13) {
1665 case 0: path.lineTo(nonFinitePts[i]); break;
1666 case 1: path.quadTo(nonFinitePts[i], nonFinitePts[i]); break;
Cary Clarkc9b7c722018-12-12 14:50:23 -05001667 case 2: path.quadTo(nonFinitePts[i], axisAlignedPts[f]); break;
1668 case 3: path.quadTo(axisAlignedPts[f], nonFinitePts[i]); break;
1669 case 4: path.cubicTo(nonFinitePts[i], axisAlignedPts[f], axisAlignedPts[f]); break;
1670 case 5: path.cubicTo(axisAlignedPts[f], nonFinitePts[i], axisAlignedPts[f]); break;
1671 case 6: path.cubicTo(axisAlignedPts[f], axisAlignedPts[f], nonFinitePts[i]); break;
1672 case 7: path.cubicTo(nonFinitePts[i], nonFinitePts[i], axisAlignedPts[f]); break;
1673 case 8: path.cubicTo(nonFinitePts[i], axisAlignedPts[f], nonFinitePts[i]); break;
1674 case 9: path.cubicTo(axisAlignedPts[f], nonFinitePts[i], nonFinitePts[i]); break;
Yuqian Li3154a532017-09-06 13:33:30 -04001675 case 10: path.cubicTo(nonFinitePts[i], nonFinitePts[i], nonFinitePts[i]); break;
Cary Clarkc9b7c722018-12-12 14:50:23 -05001676 case 11: path.cubicTo(nonFinitePts[i], axisAlignedPts[f], axisAlignedPts[g]); break;
Yuqian Li3154a532017-09-06 13:33:30 -04001677 case 12: path.moveTo(nonFinitePts[i]); break;
1678 }
Mike Reed6052c0b2020-08-28 11:49:56 -04001679 REPORTER_ASSERT(reporter,
Mike Reed3872c982020-08-29 17:46:51 -04001680 SkPathPriv::GetConvexityOrUnknown(path) == SkPathConvexity::kUnknown);
Yuqian Li3154a532017-09-06 13:33:30 -04001681 }
1682
Cary Clarkc9b7c722018-12-12 14:50:23 -05001683 for (int index = 0; index < (int) (11 * axisAlignedPtsCount); ++index) {
1684 int f = (int) (index % axisAlignedPtsCount);
1685 int g = (int) ((f + 1) % axisAlignedPtsCount);
Yuqian Li3154a532017-09-06 13:33:30 -04001686 path.reset();
1687 int curveSelect = index % 11;
1688 switch (curveSelect) {
Cary Clarkc9b7c722018-12-12 14:50:23 -05001689 case 0: path.moveTo(axisAlignedPts[f]); break;
1690 case 1: path.lineTo(axisAlignedPts[f]); break;
1691 case 2: path.quadTo(axisAlignedPts[f], axisAlignedPts[f]); break;
1692 case 3: path.quadTo(axisAlignedPts[f], axisAlignedPts[g]); break;
1693 case 4: path.quadTo(axisAlignedPts[g], axisAlignedPts[f]); break;
1694 case 5: path.cubicTo(axisAlignedPts[f], axisAlignedPts[f], axisAlignedPts[f]); break;
1695 case 6: path.cubicTo(axisAlignedPts[f], axisAlignedPts[f], axisAlignedPts[g]); break;
1696 case 7: path.cubicTo(axisAlignedPts[f], axisAlignedPts[g], axisAlignedPts[f]); break;
1697 case 8: path.cubicTo(axisAlignedPts[f], axisAlignedPts[g], axisAlignedPts[g]); break;
1698 case 9: path.cubicTo(axisAlignedPts[g], axisAlignedPts[f], axisAlignedPts[f]); break;
1699 case 10: path.cubicTo(axisAlignedPts[g], axisAlignedPts[f], axisAlignedPts[g]); break;
Yuqian Li3154a532017-09-06 13:33:30 -04001700 }
Cary Clarkc9b7c722018-12-12 14:50:23 -05001701 if (curveSelect == 0 || curveSelect == 1 || curveSelect == 2 || curveSelect == 5) {
Mike Reed6052c0b2020-08-28 11:49:56 -04001702 check_convexity(reporter, path, true);
Cary Clarkc9b7c722018-12-12 14:50:23 -05001703 } else {
John Stiles31954bf2020-08-07 17:35:54 -04001704 // We make a copy so that we don't cache the result on the passed in path.
1705 SkPath copy(path); // NOLINT(performance-unnecessary-copy-initialization)
Mike Reed6052c0b2020-08-28 11:49:56 -04001706 REPORTER_ASSERT(reporter, !copy.isConvex());
Cary Clarkc9b7c722018-12-12 14:50:23 -05001707 }
Yuqian Li3154a532017-09-06 13:33:30 -04001708 }
1709
Cary Clarkc9b7c722018-12-12 14:50:23 -05001710 static const SkPoint diagonalPts[] = {
1711 { SK_ScalarMax, SK_ScalarMax },
1712 { SK_ScalarMin, SK_ScalarMin },
1713 };
1714
1715 const size_t diagonalPtsCount = sizeof(diagonalPts) / sizeof(diagonalPts[0]);
1716
1717 for (int index = 0; index < (int) (7 * diagonalPtsCount); ++index) {
1718 int f = (int) (index % diagonalPtsCount);
1719 int g = (int) ((f + 1) % diagonalPtsCount);
1720 path.reset();
1721 int curveSelect = index % 11;
1722 switch (curveSelect) {
1723 case 0: path.moveTo(diagonalPts[f]); break;
1724 case 1: path.lineTo(diagonalPts[f]); break;
1725 case 2: path.quadTo(diagonalPts[f], diagonalPts[f]); break;
1726 case 3: path.quadTo(axisAlignedPts[f], diagonalPts[g]); break;
1727 case 4: path.quadTo(diagonalPts[g], axisAlignedPts[f]); break;
1728 case 5: path.cubicTo(diagonalPts[f], diagonalPts[f], diagonalPts[f]); break;
1729 case 6: path.cubicTo(diagonalPts[f], diagonalPts[f], axisAlignedPts[g]); break;
1730 case 7: path.cubicTo(diagonalPts[f], axisAlignedPts[g], diagonalPts[f]); break;
1731 case 8: path.cubicTo(axisAlignedPts[f], diagonalPts[g], diagonalPts[g]); break;
1732 case 9: path.cubicTo(diagonalPts[g], diagonalPts[f], axisAlignedPts[f]); break;
1733 case 10: path.cubicTo(diagonalPts[g], axisAlignedPts[f], diagonalPts[g]); break;
1734 }
1735 if (curveSelect == 0) {
Mike Reed6052c0b2020-08-28 11:49:56 -04001736 check_convexity(reporter, path, true);
Cary Clarkc9b7c722018-12-12 14:50:23 -05001737 } else {
John Stiles31954bf2020-08-07 17:35:54 -04001738 // We make a copy so that we don't cache the result on the passed in path.
1739 SkPath copy(path); // NOLINT(performance-unnecessary-copy-initialization)
Mike Reed6052c0b2020-08-28 11:49:56 -04001740 REPORTER_ASSERT(reporter, !copy.isConvex());
Cary Clarkc9b7c722018-12-12 14:50:23 -05001741 }
1742 }
1743
1744
Yuqian Li3154a532017-09-06 13:33:30 -04001745 path.reset();
1746 path.moveTo(SkBits2Float(0xbe9171db), SkBits2Float(0xbd7eeb5d)); // -0.284072f, -0.0622362f
1747 path.lineTo(SkBits2Float(0xbe9171db), SkBits2Float(0xbd7eea38)); // -0.284072f, -0.0622351f
1748 path.lineTo(SkBits2Float(0xbe9171a0), SkBits2Float(0xbd7ee5a7)); // -0.28407f, -0.0622307f
1749 path.lineTo(SkBits2Float(0xbe917147), SkBits2Float(0xbd7ed886)); // -0.284067f, -0.0622182f
1750 path.lineTo(SkBits2Float(0xbe917378), SkBits2Float(0xbd7ee1a9)); // -0.284084f, -0.0622269f
1751 path.lineTo(SkBits2Float(0xbe9171db), SkBits2Float(0xbd7eeb5d)); // -0.284072f, -0.0622362f
1752 path.close();
Mike Reed6052c0b2020-08-28 11:49:56 -04001753 check_convexity(reporter, path, false);
Yuqian Li3154a532017-09-06 13:33:30 -04001754
1755}
1756
1757static void test_isLine(skiatest::Reporter* reporter) {
1758 SkPath path;
1759 SkPoint pts[2];
1760 const SkScalar value = SkIntToScalar(5);
1761
1762 REPORTER_ASSERT(reporter, !path.isLine(nullptr));
1763
1764 // set some non-zero values
1765 pts[0].set(value, value);
1766 pts[1].set(value, value);
1767 REPORTER_ASSERT(reporter, !path.isLine(pts));
1768 // check that pts was untouched
1769 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
1770 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
1771
1772 const SkScalar moveX = SkIntToScalar(1);
1773 const SkScalar moveY = SkIntToScalar(2);
1774 REPORTER_ASSERT(reporter, value != moveX && value != moveY);
1775
1776 path.moveTo(moveX, moveY);
1777 REPORTER_ASSERT(reporter, !path.isLine(nullptr));
1778 REPORTER_ASSERT(reporter, !path.isLine(pts));
1779 // check that pts was untouched
1780 REPORTER_ASSERT(reporter, pts[0].equals(value, value));
1781 REPORTER_ASSERT(reporter, pts[1].equals(value, value));
1782
1783 const SkScalar lineX = SkIntToScalar(2);
1784 const SkScalar lineY = SkIntToScalar(2);
1785 REPORTER_ASSERT(reporter, value != lineX && value != lineY);
1786
1787 path.lineTo(lineX, lineY);
1788 REPORTER_ASSERT(reporter, path.isLine(nullptr));
1789
1790 REPORTER_ASSERT(reporter, !pts[0].equals(moveX, moveY));
1791 REPORTER_ASSERT(reporter, !pts[1].equals(lineX, lineY));
1792 REPORTER_ASSERT(reporter, path.isLine(pts));
1793 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
1794 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
1795
1796 path.lineTo(0, 0); // too many points/verbs
1797 REPORTER_ASSERT(reporter, !path.isLine(nullptr));
1798 REPORTER_ASSERT(reporter, !path.isLine(pts));
1799 REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
1800 REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
1801
1802 path.reset();
1803 path.quadTo(1, 1, 2, 2);
1804 REPORTER_ASSERT(reporter, !path.isLine(nullptr));
1805}
1806
1807static void test_conservativelyContains(skiatest::Reporter* reporter) {
1808 SkPath path;
1809
1810 // kBaseRect is used to construct most our test paths: a rect, a circle, and a round-rect.
1811 static const SkRect kBaseRect = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
1812
1813 // A circle that bounds kBaseRect (with a significant amount of slop)
Brian Osman116b33e2020-02-05 13:34:09 -05001814 SkScalar circleR = std::max(kBaseRect.width(), kBaseRect.height());
Yuqian Li3154a532017-09-06 13:33:30 -04001815 circleR *= 1.75f / 2;
1816 static const SkPoint kCircleC = {kBaseRect.centerX(), kBaseRect.centerY()};
1817
1818 // round-rect radii
1819 static const SkScalar kRRRadii[] = {SkIntToScalar(5), SkIntToScalar(3)};
1820
1821 static const struct SUPPRESS_VISIBILITY_WARNING {
1822 SkRect fQueryRect;
1823 bool fInRect;
1824 bool fInCircle;
1825 bool fInRR;
1826 bool fInCubicRR;
1827 } kQueries[] = {
1828 {kBaseRect, true, true, false, false},
1829
1830 // rect well inside of kBaseRect
1831 {SkRect::MakeLTRB(kBaseRect.fLeft + 0.25f*kBaseRect.width(),
1832 kBaseRect.fTop + 0.25f*kBaseRect.height(),
1833 kBaseRect.fRight - 0.25f*kBaseRect.width(),
1834 kBaseRect.fBottom - 0.25f*kBaseRect.height()),
1835 true, true, true, true},
1836
1837 // rects with edges off by one from kBaseRect's edges
1838 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1839 kBaseRect.width(), kBaseRect.height() + 1),
1840 false, true, false, false},
1841 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1842 kBaseRect.width() + 1, kBaseRect.height()),
1843 false, true, false, false},
1844 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
1845 kBaseRect.width() + 1, kBaseRect.height() + 1),
1846 false, true, false, false},
1847 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
1848 kBaseRect.width(), kBaseRect.height()),
1849 false, true, false, false},
1850 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
1851 kBaseRect.width(), kBaseRect.height()),
1852 false, true, false, false},
1853 {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
1854 kBaseRect.width() + 2, kBaseRect.height()),
1855 false, true, false, false},
1856 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
1857 kBaseRect.width() + 2, kBaseRect.height()),
1858 false, true, false, false},
1859
1860 // zero-w/h rects at each corner of kBaseRect
1861 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop, 0, 0), true, true, false, false},
1862 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fTop, 0, 0), true, true, false, true},
1863 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fBottom, 0, 0), true, true, false, true},
1864 {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fBottom, 0, 0), true, true, false, true},
1865
1866 // far away rect
1867 {SkRect::MakeXYWH(10 * kBaseRect.fRight, 10 * kBaseRect.fBottom,
1868 SkIntToScalar(10), SkIntToScalar(10)),
1869 false, false, false, false},
1870
1871 // very large rect containing kBaseRect
1872 {SkRect::MakeXYWH(kBaseRect.fLeft - 5 * kBaseRect.width(),
1873 kBaseRect.fTop - 5 * kBaseRect.height(),
1874 11 * kBaseRect.width(), 11 * kBaseRect.height()),
1875 false, false, false, false},
1876
1877 // skinny rect that spans same y-range as kBaseRect
1878 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
1879 SkIntToScalar(1), kBaseRect.height()),
1880 true, true, true, true},
1881
1882 // short rect that spans same x-range as kBaseRect
1883 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(), kBaseRect.width(), SkScalar(1)),
1884 true, true, true, true},
1885
1886 // skinny rect that spans slightly larger y-range than kBaseRect
1887 {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
1888 SkIntToScalar(1), kBaseRect.height() + 1),
1889 false, true, false, false},
1890
1891 // short rect that spans slightly larger x-range than kBaseRect
1892 {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(),
1893 kBaseRect.width() + 1, SkScalar(1)),
1894 false, true, false, false},
1895 };
1896
1897 for (int inv = 0; inv < 4; ++inv) {
1898 for (size_t q = 0; q < SK_ARRAY_COUNT(kQueries); ++q) {
1899 SkRect qRect = kQueries[q].fQueryRect;
1900 if (inv & 0x1) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04001901 using std::swap;
1902 swap(qRect.fLeft, qRect.fRight);
Yuqian Li3154a532017-09-06 13:33:30 -04001903 }
1904 if (inv & 0x2) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04001905 using std::swap;
1906 swap(qRect.fTop, qRect.fBottom);
Yuqian Li3154a532017-09-06 13:33:30 -04001907 }
1908 for (int d = 0; d < 2; ++d) {
Mike Reed30bc5272019-11-22 18:34:02 +00001909 SkPathDirection dir = d ? SkPathDirection::kCCW : SkPathDirection::kCW;
Yuqian Li3154a532017-09-06 13:33:30 -04001910 path.reset();
1911 path.addRect(kBaseRect, dir);
1912 REPORTER_ASSERT(reporter, kQueries[q].fInRect ==
1913 path.conservativelyContainsRect(qRect));
1914
1915 path.reset();
1916 path.addCircle(kCircleC.fX, kCircleC.fY, circleR, dir);
1917 REPORTER_ASSERT(reporter, kQueries[q].fInCircle ==
1918 path.conservativelyContainsRect(qRect));
1919
1920 path.reset();
Mike Reed4241f5e2019-09-14 19:13:23 +00001921 path.addRoundRect(kBaseRect, kRRRadii[0], kRRRadii[1], dir);
Yuqian Li3154a532017-09-06 13:33:30 -04001922 REPORTER_ASSERT(reporter, kQueries[q].fInRR ==
1923 path.conservativelyContainsRect(qRect));
1924
1925 path.reset();
1926 path.moveTo(kBaseRect.fLeft + kRRRadii[0], kBaseRect.fTop);
1927 path.cubicTo(kBaseRect.fLeft + kRRRadii[0] / 2, kBaseRect.fTop,
1928 kBaseRect.fLeft, kBaseRect.fTop + kRRRadii[1] / 2,
1929 kBaseRect.fLeft, kBaseRect.fTop + kRRRadii[1]);
1930 path.lineTo(kBaseRect.fLeft, kBaseRect.fBottom);
1931 path.lineTo(kBaseRect.fRight, kBaseRect.fBottom);
1932 path.lineTo(kBaseRect.fRight, kBaseRect.fTop);
1933 path.close();
1934 REPORTER_ASSERT(reporter, kQueries[q].fInCubicRR ==
1935 path.conservativelyContainsRect(qRect));
1936
1937 }
1938 // Slightly non-convex shape, shouldn't contain any rects.
1939 path.reset();
1940 path.moveTo(0, 0);
1941 path.lineTo(SkIntToScalar(50), 0.05f);
1942 path.lineTo(SkIntToScalar(100), 0);
1943 path.lineTo(SkIntToScalar(100), SkIntToScalar(100));
1944 path.lineTo(0, SkIntToScalar(100));
1945 path.close();
1946 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(qRect));
1947 }
1948 }
1949
1950 // make sure a minimal convex shape works, a right tri with edges along pos x and y axes.
1951 path.reset();
1952 path.moveTo(0, 0);
1953 path.lineTo(SkIntToScalar(100), 0);
1954 path.lineTo(0, SkIntToScalar(100));
1955
1956 // inside, on along top edge
1957 REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
1958 SkIntToScalar(10),
1959 SkIntToScalar(10))));
1960 // above
1961 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(
1962 SkRect::MakeXYWH(SkIntToScalar(50),
1963 SkIntToScalar(-10),
1964 SkIntToScalar(10),
1965 SkIntToScalar(10))));
1966 // to the left
1967 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(-10),
1968 SkIntToScalar(5),
1969 SkIntToScalar(5),
1970 SkIntToScalar(5))));
1971
1972 // outside the diagonal edge
1973 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(10),
1974 SkIntToScalar(200),
1975 SkIntToScalar(20),
1976 SkIntToScalar(5))));
1977
1978
1979 // Test that multiple move commands do not cause asserts.
Yuqian Li3154a532017-09-06 13:33:30 -04001980 path.moveTo(SkIntToScalar(100), SkIntToScalar(100));
Yuqian Li3154a532017-09-06 13:33:30 -04001981 REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
1982 SkIntToScalar(10),
1983 SkIntToScalar(10))));
Yuqian Li3154a532017-09-06 13:33:30 -04001984
1985 // Same as above path and first test but with an extra moveTo.
1986 path.reset();
1987 path.moveTo(100, 100);
1988 path.moveTo(0, 0);
1989 path.lineTo(SkIntToScalar(100), 0);
1990 path.lineTo(0, SkIntToScalar(100));
Brian Osman205a1262017-09-18 13:13:48 +00001991 // Convexity logic is now more conservative, so that multiple (non-trailing) moveTos make a
1992 // path non-convex.
1993 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(
1994 SkRect::MakeXYWH(SkIntToScalar(50), 0,
1995 SkIntToScalar(10),
1996 SkIntToScalar(10))));
Yuqian Li3154a532017-09-06 13:33:30 -04001997
1998 // Same as above path and first test but with the extra moveTo making a degenerate sub-path
1999 // following the non-empty sub-path. Verifies that this does not trigger assertions.
2000 path.reset();
2001 path.moveTo(0, 0);
2002 path.lineTo(SkIntToScalar(100), 0);
2003 path.lineTo(0, SkIntToScalar(100));
2004 path.moveTo(100, 100);
2005
2006 REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
2007 SkIntToScalar(10),
2008 SkIntToScalar(10))));
2009
2010 // Test that multiple move commands do not cause asserts and that the function
2011 // is not confused by the multiple moves.
2012 path.reset();
2013 path.moveTo(0, 0);
2014 path.lineTo(SkIntToScalar(100), 0);
2015 path.lineTo(0, SkIntToScalar(100));
2016 path.moveTo(0, SkIntToScalar(200));
2017 path.lineTo(SkIntToScalar(100), SkIntToScalar(200));
2018 path.lineTo(0, SkIntToScalar(300));
2019
2020 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(
2021 SkRect::MakeXYWH(SkIntToScalar(50), 0,
2022 SkIntToScalar(10),
2023 SkIntToScalar(10))));
2024
2025 path.reset();
2026 path.lineTo(100, 100);
2027 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(0, 0, 1, 1)));
2028
2029 // An empty path should not contain any rectangle. It's questionable whether an empty path
2030 // contains an empty rectangle. However, since it is a conservative test it is ok to
2031 // return false.
2032 path.reset();
2033 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeWH(1,1)));
2034 REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeWH(0,0)));
2035}
2036
2037static void test_isRect_open_close(skiatest::Reporter* reporter) {
2038 SkPath path;
2039 bool isClosed;
2040
2041 path.moveTo(0, 0); path.lineTo(1, 0); path.lineTo(1, 1); path.lineTo(0, 1);
2042 path.close();
2043
2044 REPORTER_ASSERT(reporter, path.isRect(nullptr, &isClosed, nullptr));
2045 REPORTER_ASSERT(reporter, isClosed);
2046}
2047
2048// Simple isRect test is inline TestPath, below.
2049// test_isRect provides more extensive testing.
2050static void test_isRect(skiatest::Reporter* reporter) {
2051 test_isRect_open_close(reporter);
2052
2053 // passing tests (all moveTo / lineTo...
2054 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
2055 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
2056 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
2057 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
2058 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
2059 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
2060 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
2061 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
2062 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
2063 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f}, {1, 0}, {.5f, 0}};
2064 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1}, {0, 1}, {0, .5f}};
2065 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
2066 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
2067 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
2068 SkPoint rf[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}};
2069
2070 // failing tests
2071 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
2072 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
2073 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
2074 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
2075 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
2076 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
2077 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
2078 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
2079 SkPoint f9[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}, {2, 0}}; // overlaps
2080 SkPoint fa[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, -1}, {1, -1}}; // non colinear gap
2081 SkPoint fb[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 1}}; // falls short
2082
2083 // no close, but we should detect them as fillably the same as a rect
2084 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
2085 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}};
2086 SkPoint c3[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}, {0, 0}}; // hit the start
2087
2088 // like c2, but we double-back on ourselves
2089 SkPoint d1[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}, {0, 2}};
2090 // like c2, but we overshoot the start point
2091 SkPoint d2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, -1}};
2092 SkPoint d3[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, -1}, {0, 0}};
2093
2094 struct IsRectTest {
2095 SkPoint *fPoints;
2096 int fPointCount;
2097 bool fClose;
2098 bool fIsRect;
2099 } tests[] = {
2100 { r1, SK_ARRAY_COUNT(r1), true, true },
2101 { r2, SK_ARRAY_COUNT(r2), true, true },
2102 { r3, SK_ARRAY_COUNT(r3), true, true },
2103 { r4, SK_ARRAY_COUNT(r4), true, true },
2104 { r5, SK_ARRAY_COUNT(r5), true, true },
2105 { r6, SK_ARRAY_COUNT(r6), true, true },
2106 { r7, SK_ARRAY_COUNT(r7), true, true },
2107 { r8, SK_ARRAY_COUNT(r8), true, true },
2108 { r9, SK_ARRAY_COUNT(r9), true, true },
2109 { ra, SK_ARRAY_COUNT(ra), true, true },
2110 { rb, SK_ARRAY_COUNT(rb), true, true },
2111 { rc, SK_ARRAY_COUNT(rc), true, true },
2112 { rd, SK_ARRAY_COUNT(rd), true, true },
2113 { re, SK_ARRAY_COUNT(re), true, true },
2114 { rf, SK_ARRAY_COUNT(rf), true, true },
2115
2116 { f1, SK_ARRAY_COUNT(f1), true, false },
2117 { f2, SK_ARRAY_COUNT(f2), true, false },
2118 { f3, SK_ARRAY_COUNT(f3), true, false },
2119 { f4, SK_ARRAY_COUNT(f4), true, false },
2120 { f5, SK_ARRAY_COUNT(f5), true, false },
2121 { f6, SK_ARRAY_COUNT(f6), true, false },
2122 { f7, SK_ARRAY_COUNT(f7), true, false },
2123 { f8, SK_ARRAY_COUNT(f8), true, false },
2124 { f9, SK_ARRAY_COUNT(f9), true, false },
2125 { fa, SK_ARRAY_COUNT(fa), true, false },
2126 { fb, SK_ARRAY_COUNT(fb), true, false },
2127
2128 { c1, SK_ARRAY_COUNT(c1), false, true },
2129 { c2, SK_ARRAY_COUNT(c2), false, true },
2130 { c3, SK_ARRAY_COUNT(c3), false, true },
2131
2132 { d1, SK_ARRAY_COUNT(d1), false, false },
Cary Clarkdbc59ba2018-04-19 07:37:29 -04002133 { d2, SK_ARRAY_COUNT(d2), false, true },
Yuqian Li3154a532017-09-06 13:33:30 -04002134 { d3, SK_ARRAY_COUNT(d3), false, false },
2135 };
2136
2137 const size_t testCount = SK_ARRAY_COUNT(tests);
2138 int index;
2139 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
2140 SkPath path;
2141 path.moveTo(tests[testIndex].fPoints[0].fX, tests[testIndex].fPoints[0].fY);
2142 for (index = 1; index < tests[testIndex].fPointCount; ++index) {
2143 path.lineTo(tests[testIndex].fPoints[index].fX, tests[testIndex].fPoints[index].fY);
2144 }
2145 if (tests[testIndex].fClose) {
2146 path.close();
2147 }
2148 REPORTER_ASSERT(reporter, tests[testIndex].fIsRect == path.isRect(nullptr));
2149
2150 if (tests[testIndex].fIsRect) {
2151 SkRect computed, expected;
2152 bool isClosed;
Mike Reed30bc5272019-11-22 18:34:02 +00002153 SkPathDirection direction;
Cary Clarkdbc59ba2018-04-19 07:37:29 -04002154 int pointCount = tests[testIndex].fPointCount - (d2 == tests[testIndex].fPoints);
Mike Reed92b33352019-08-24 19:39:13 -04002155 expected.setBounds(tests[testIndex].fPoints, pointCount);
Mike Reed85f51b22020-08-30 10:32:06 -04002156 SkPathFirstDirection cheapDirection = SkPathPriv::ComputeFirstDirection(path);
2157 REPORTER_ASSERT(reporter, cheapDirection != SkPathFirstDirection::kUnknown);
Yuqian Li3154a532017-09-06 13:33:30 -04002158 REPORTER_ASSERT(reporter, path.isRect(&computed, &isClosed, &direction));
2159 REPORTER_ASSERT(reporter, expected == computed);
2160 REPORTER_ASSERT(reporter, isClosed == tests[testIndex].fClose);
2161 REPORTER_ASSERT(reporter, SkPathPriv::AsFirstDirection(direction) == cheapDirection);
2162 } else {
2163 SkRect computed;
Mike Reed92b33352019-08-24 19:39:13 -04002164 computed.setLTRB(123, 456, 789, 1011);
Mike Kleinde5d6eb2018-08-13 12:09:41 -04002165 for (auto c : {true, false})
Mike Reed30bc5272019-11-22 18:34:02 +00002166 for (auto d : {SkPathDirection::kCW, SkPathDirection::kCCW}) {
Mike Kleinde5d6eb2018-08-13 12:09:41 -04002167 bool isClosed = c;
Mike Reed30bc5272019-11-22 18:34:02 +00002168 SkPathDirection direction = d;
Mike Kleinde5d6eb2018-08-13 12:09:41 -04002169 REPORTER_ASSERT(reporter, !path.isRect(&computed, &isClosed, &direction));
2170 REPORTER_ASSERT(reporter, computed.fLeft == 123 && computed.fTop == 456);
2171 REPORTER_ASSERT(reporter, computed.fRight == 789 && computed.fBottom == 1011);
2172 REPORTER_ASSERT(reporter, isClosed == c);
2173 REPORTER_ASSERT(reporter, direction == d);
2174 }
Yuqian Li3154a532017-09-06 13:33:30 -04002175 }
2176 }
2177
2178 // fail, close then line
2179 SkPath path1;
2180 path1.moveTo(r1[0].fX, r1[0].fY);
2181 for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) {
2182 path1.lineTo(r1[index].fX, r1[index].fY);
2183 }
2184 path1.close();
2185 path1.lineTo(1, 0);
2186 REPORTER_ASSERT(reporter, !path1.isRect(nullptr));
2187
2188 // fail, move in the middle
2189 path1.reset();
2190 path1.moveTo(r1[0].fX, r1[0].fY);
2191 for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) {
2192 if (index == 2) {
2193 path1.moveTo(1, .5f);
2194 }
2195 path1.lineTo(r1[index].fX, r1[index].fY);
2196 }
2197 path1.close();
2198 REPORTER_ASSERT(reporter, !path1.isRect(nullptr));
2199
2200 // fail, move on the edge
2201 path1.reset();
2202 for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) {
2203 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
2204 path1.lineTo(r1[index].fX, r1[index].fY);
2205 }
2206 path1.close();
2207 REPORTER_ASSERT(reporter, !path1.isRect(nullptr));
2208
2209 // fail, quad
2210 path1.reset();
2211 path1.moveTo(r1[0].fX, r1[0].fY);
2212 for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) {
2213 if (index == 2) {
2214 path1.quadTo(1, .5f, 1, .5f);
2215 }
2216 path1.lineTo(r1[index].fX, r1[index].fY);
2217 }
2218 path1.close();
2219 REPORTER_ASSERT(reporter, !path1.isRect(nullptr));
2220
2221 // fail, cubic
2222 path1.reset();
2223 path1.moveTo(r1[0].fX, r1[0].fY);
2224 for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) {
2225 if (index == 2) {
2226 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
2227 }
2228 path1.lineTo(r1[index].fX, r1[index].fY);
2229 }
2230 path1.close();
2231 REPORTER_ASSERT(reporter, !path1.isRect(nullptr));
2232}
2233
2234static void check_simple_closed_rect(skiatest::Reporter* reporter, const SkPath& path,
Mike Reed30bc5272019-11-22 18:34:02 +00002235 const SkRect& rect, SkPathDirection dir, unsigned start) {
Yuqian Li3154a532017-09-06 13:33:30 -04002236 SkRect r = SkRect::MakeEmpty();
Mike Reed30bc5272019-11-22 18:34:02 +00002237 SkPathDirection d = SkPathDirection::kCCW;
Yuqian Li3154a532017-09-06 13:33:30 -04002238 unsigned s = ~0U;
2239
2240 REPORTER_ASSERT(reporter, SkPathPriv::IsSimpleClosedRect(path, &r, &d, &s));
2241 REPORTER_ASSERT(reporter, r == rect);
2242 REPORTER_ASSERT(reporter, d == dir);
2243 REPORTER_ASSERT(reporter, s == start);
2244}
2245
2246static void test_is_simple_closed_rect(skiatest::Reporter* reporter) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -04002247 using std::swap;
Yuqian Li3154a532017-09-06 13:33:30 -04002248 SkRect r = SkRect::MakeEmpty();
Mike Reed30bc5272019-11-22 18:34:02 +00002249 SkPathDirection d = SkPathDirection::kCCW;
Yuqian Li3154a532017-09-06 13:33:30 -04002250 unsigned s = ~0U;
2251
2252 const SkRect testRect = SkRect::MakeXYWH(10, 10, 50, 70);
2253 const SkRect emptyRect = SkRect::MakeEmpty();
2254 SkPath path;
2255 for (int start = 0; start < 4; ++start) {
Mike Reed30bc5272019-11-22 18:34:02 +00002256 for (auto dir : {SkPathDirection::kCCW, SkPathDirection::kCW}) {
Yuqian Li3154a532017-09-06 13:33:30 -04002257 SkPath path;
2258 path.addRect(testRect, dir, start);
2259 check_simple_closed_rect(reporter, path, testRect, dir, start);
2260 path.close();
2261 check_simple_closed_rect(reporter, path, testRect, dir, start);
2262 SkPath path2 = path;
2263 path2.lineTo(10, 10);
2264 REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path2, &r, &d, &s));
2265 path2 = path;
2266 path2.moveTo(10, 10);
2267 REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path2, &r, &d, &s));
2268 path2 = path;
2269 path2.addRect(testRect, dir, start);
2270 REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path2, &r, &d, &s));
2271 // Make the path by hand, manually closing it.
2272 path2.reset();
Yuqian Li3154a532017-09-06 13:33:30 -04002273 SkPoint firstPt = {0.f, 0.f};
Chris Daltonde500372020-05-05 15:06:30 -06002274 for (auto [v, verbPts, w] : SkPathPriv::Iterate(path)) {
Yuqian Li3154a532017-09-06 13:33:30 -04002275 switch(v) {
Chris Daltonde500372020-05-05 15:06:30 -06002276 case SkPathVerb::kMove:
Yuqian Li3154a532017-09-06 13:33:30 -04002277 firstPt = verbPts[0];
2278 path2.moveTo(verbPts[0]);
2279 break;
Chris Daltonde500372020-05-05 15:06:30 -06002280 case SkPathVerb::kLine:
Yuqian Li3154a532017-09-06 13:33:30 -04002281 path2.lineTo(verbPts[1]);
2282 break;
2283 default:
2284 break;
2285 }
2286 }
2287 // We haven't closed it yet...
2288 REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path2, &r, &d, &s));
2289 // ... now we do and test again.
2290 path2.lineTo(firstPt);
2291 check_simple_closed_rect(reporter, path2, testRect, dir, start);
2292 // A redundant close shouldn't cause a failure.
2293 path2.close();
2294 check_simple_closed_rect(reporter, path2, testRect, dir, start);
2295 // Degenerate point and line rects are not allowed
2296 path2.reset();
2297 path2.addRect(emptyRect, dir, start);
2298 REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path2, &r, &d, &s));
2299 SkRect degenRect = testRect;
2300 degenRect.fLeft = degenRect.fRight;
2301 path2.reset();
2302 path2.addRect(degenRect, dir, start);
2303 REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path2, &r, &d, &s));
2304 degenRect = testRect;
2305 degenRect.fTop = degenRect.fBottom;
2306 path2.reset();
2307 path2.addRect(degenRect, dir, start);
2308 REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path2, &r, &d, &s));
2309 // An inverted rect makes a rect path, but changes the winding dir and start point.
Mike Reed30bc5272019-11-22 18:34:02 +00002310 SkPathDirection swapDir = (dir == SkPathDirection::kCW)
2311 ? SkPathDirection::kCCW
2312 : SkPathDirection::kCW;
Yuqian Li3154a532017-09-06 13:33:30 -04002313 static constexpr unsigned kXSwapStarts[] = { 1, 0, 3, 2 };
2314 static constexpr unsigned kYSwapStarts[] = { 3, 2, 1, 0 };
2315 SkRect swapRect = testRect;
Ben Wagnerf08d1d02018-06-18 15:11:00 -04002316 swap(swapRect.fLeft, swapRect.fRight);
Yuqian Li3154a532017-09-06 13:33:30 -04002317 path2.reset();
2318 path2.addRect(swapRect, dir, start);
2319 check_simple_closed_rect(reporter, path2, testRect, swapDir, kXSwapStarts[start]);
2320 swapRect = testRect;
Ben Wagnerf08d1d02018-06-18 15:11:00 -04002321 swap(swapRect.fTop, swapRect.fBottom);
Yuqian Li3154a532017-09-06 13:33:30 -04002322 path2.reset();
2323 path2.addRect(swapRect, dir, start);
2324 check_simple_closed_rect(reporter, path2, testRect, swapDir, kYSwapStarts[start]);
2325 }
2326 }
2327 // down, up, left, close
2328 path.reset();
2329 path.moveTo(1, 1);
2330 path.lineTo(1, 2);
2331 path.lineTo(1, 1);
2332 path.lineTo(0, 1);
2333 SkRect rect;
Mike Reed30bc5272019-11-22 18:34:02 +00002334 SkPathDirection dir;
Yuqian Li3154a532017-09-06 13:33:30 -04002335 unsigned start;
2336 path.close();
2337 REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path, &rect, &dir, &start));
2338 // right, left, up, close
2339 path.reset();
2340 path.moveTo(1, 1);
2341 path.lineTo(2, 1);
2342 path.lineTo(1, 1);
2343 path.lineTo(1, 0);
2344 path.close();
2345 REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path, &rect, &dir, &start));
2346 // parallelogram with horizontal edges
2347 path.reset();
2348 path.moveTo(1, 0);
2349 path.lineTo(3, 0);
2350 path.lineTo(2, 1);
2351 path.lineTo(0, 1);
2352 path.close();
2353 REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path, &rect, &dir, &start));
2354 // parallelogram with vertical edges
2355 path.reset();
2356 path.moveTo(0, 1);
2357 path.lineTo(0, 3);
2358 path.lineTo(1, 2);
2359 path.lineTo(1, 0);
2360 path.close();
2361 REPORTER_ASSERT(reporter, !SkPathPriv::IsSimpleClosedRect(path, &rect, &dir, &start));
2362
2363}
2364
2365static void test_isNestedFillRects(skiatest::Reporter* reporter) {
2366 // passing tests (all moveTo / lineTo...
2367 SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // CW
2368 SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
2369 SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
2370 SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
2371 SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}}; // CCW
2372 SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
2373 SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
2374 SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
2375 SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
2376 SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f}, {1, 0}, {.5f, 0}}; // CCW
2377 SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1}, {0, 1}, {0, .5f}}; // CW
2378 SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}}; // CW
2379 SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}}; // CCW
2380 SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}}; // CW
2381
2382 // failing tests
2383 SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
2384 SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
2385 SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
2386 SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
2387 SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
2388 SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
2389 SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
2390 SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
2391
2392 // success, no close is OK
2393 SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
2394 SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
2395
2396 struct IsNestedRectTest {
2397 SkPoint *fPoints;
2398 int fPointCount;
Mike Reed3872c982020-08-29 17:46:51 -04002399 SkPathFirstDirection fDirection;
Yuqian Li3154a532017-09-06 13:33:30 -04002400 bool fClose;
2401 bool fIsNestedRect; // nests with path.addRect(-1, -1, 2, 2);
2402 } tests[] = {
Mike Reed3872c982020-08-29 17:46:51 -04002403 { r1, SK_ARRAY_COUNT(r1), SkPathFirstDirection::kCW , true, true },
2404 { r2, SK_ARRAY_COUNT(r2), SkPathFirstDirection::kCW , true, true },
2405 { r3, SK_ARRAY_COUNT(r3), SkPathFirstDirection::kCW , true, true },
2406 { r4, SK_ARRAY_COUNT(r4), SkPathFirstDirection::kCW , true, true },
2407 { r5, SK_ARRAY_COUNT(r5), SkPathFirstDirection::kCCW, true, true },
2408 { r6, SK_ARRAY_COUNT(r6), SkPathFirstDirection::kCCW, true, true },
2409 { r7, SK_ARRAY_COUNT(r7), SkPathFirstDirection::kCCW, true, true },
2410 { r8, SK_ARRAY_COUNT(r8), SkPathFirstDirection::kCCW, true, true },
2411 { r9, SK_ARRAY_COUNT(r9), SkPathFirstDirection::kCCW, true, true },
2412 { ra, SK_ARRAY_COUNT(ra), SkPathFirstDirection::kCCW, true, true },
2413 { rb, SK_ARRAY_COUNT(rb), SkPathFirstDirection::kCW, true, true },
2414 { rc, SK_ARRAY_COUNT(rc), SkPathFirstDirection::kCW, true, true },
2415 { rd, SK_ARRAY_COUNT(rd), SkPathFirstDirection::kCCW, true, true },
2416 { re, SK_ARRAY_COUNT(re), SkPathFirstDirection::kCW, true, true },
Yuqian Li3154a532017-09-06 13:33:30 -04002417
Mike Reed3872c982020-08-29 17:46:51 -04002418 { f1, SK_ARRAY_COUNT(f1), SkPathFirstDirection::kUnknown, true, false },
2419 { f2, SK_ARRAY_COUNT(f2), SkPathFirstDirection::kUnknown, true, false },
2420 { f3, SK_ARRAY_COUNT(f3), SkPathFirstDirection::kUnknown, true, false },
2421 { f4, SK_ARRAY_COUNT(f4), SkPathFirstDirection::kUnknown, true, false },
2422 { f5, SK_ARRAY_COUNT(f5), SkPathFirstDirection::kUnknown, true, false },
2423 { f6, SK_ARRAY_COUNT(f6), SkPathFirstDirection::kUnknown, true, false },
2424 { f7, SK_ARRAY_COUNT(f7), SkPathFirstDirection::kUnknown, true, false },
2425 { f8, SK_ARRAY_COUNT(f8), SkPathFirstDirection::kUnknown, true, false },
Yuqian Li3154a532017-09-06 13:33:30 -04002426
Mike Reed3872c982020-08-29 17:46:51 -04002427 { c1, SK_ARRAY_COUNT(c1), SkPathFirstDirection::kCW, false, true },
2428 { c2, SK_ARRAY_COUNT(c2), SkPathFirstDirection::kCW, false, true },
Yuqian Li3154a532017-09-06 13:33:30 -04002429 };
2430
2431 const size_t testCount = SK_ARRAY_COUNT(tests);
2432 int index;
2433 for (int rectFirst = 0; rectFirst <= 1; ++rectFirst) {
2434 for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
2435 SkPath path;
2436 if (rectFirst) {
Mike Reed30bc5272019-11-22 18:34:02 +00002437 path.addRect(-1, -1, 2, 2, SkPathDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04002438 }
2439 path.moveTo(tests[testIndex].fPoints[0].fX, tests[testIndex].fPoints[0].fY);
2440 for (index = 1; index < tests[testIndex].fPointCount; ++index) {
2441 path.lineTo(tests[testIndex].fPoints[index].fX, tests[testIndex].fPoints[index].fY);
2442 }
2443 if (tests[testIndex].fClose) {
2444 path.close();
2445 }
2446 if (!rectFirst) {
Mike Reed30bc5272019-11-22 18:34:02 +00002447 path.addRect(-1, -1, 2, 2, SkPathDirection::kCCW);
Yuqian Li3154a532017-09-06 13:33:30 -04002448 }
2449 REPORTER_ASSERT(reporter,
Mike Reed430470d2019-09-12 17:09:12 -04002450 tests[testIndex].fIsNestedRect == SkPathPriv::IsNestedFillRects(path, nullptr));
Yuqian Li3154a532017-09-06 13:33:30 -04002451 if (tests[testIndex].fIsNestedRect) {
2452 SkRect expected[2], computed[2];
Mike Reed3872c982020-08-29 17:46:51 -04002453 SkPathFirstDirection expectedDirs[2];
Mike Reed30bc5272019-11-22 18:34:02 +00002454 SkPathDirection computedDirs[2];
Yuqian Li3154a532017-09-06 13:33:30 -04002455 SkRect testBounds;
Mike Reed92b33352019-08-24 19:39:13 -04002456 testBounds.setBounds(tests[testIndex].fPoints, tests[testIndex].fPointCount);
Yuqian Li3154a532017-09-06 13:33:30 -04002457 expected[0] = SkRect::MakeLTRB(-1, -1, 2, 2);
2458 expected[1] = testBounds;
2459 if (rectFirst) {
Mike Reed3872c982020-08-29 17:46:51 -04002460 expectedDirs[0] = SkPathFirstDirection::kCW;
Yuqian Li3154a532017-09-06 13:33:30 -04002461 } else {
Mike Reed3872c982020-08-29 17:46:51 -04002462 expectedDirs[0] = SkPathFirstDirection::kCCW;
Yuqian Li3154a532017-09-06 13:33:30 -04002463 }
2464 expectedDirs[1] = tests[testIndex].fDirection;
Mike Reed430470d2019-09-12 17:09:12 -04002465 REPORTER_ASSERT(reporter, SkPathPriv::IsNestedFillRects(path, computed, computedDirs));
Yuqian Li3154a532017-09-06 13:33:30 -04002466 REPORTER_ASSERT(reporter, expected[0] == computed[0]);
2467 REPORTER_ASSERT(reporter, expected[1] == computed[1]);
2468 REPORTER_ASSERT(reporter, expectedDirs[0] == SkPathPriv::AsFirstDirection(computedDirs[0]));
2469 REPORTER_ASSERT(reporter, expectedDirs[1] == SkPathPriv::AsFirstDirection(computedDirs[1]));
2470 }
2471 }
2472
2473 // fail, close then line
2474 SkPath path1;
2475 if (rectFirst) {
Mike Reed30bc5272019-11-22 18:34:02 +00002476 path1.addRect(-1, -1, 2, 2, SkPathDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04002477 }
2478 path1.moveTo(r1[0].fX, r1[0].fY);
2479 for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) {
2480 path1.lineTo(r1[index].fX, r1[index].fY);
2481 }
2482 path1.close();
2483 path1.lineTo(1, 0);
2484 if (!rectFirst) {
Mike Reed30bc5272019-11-22 18:34:02 +00002485 path1.addRect(-1, -1, 2, 2, SkPathDirection::kCCW);
Yuqian Li3154a532017-09-06 13:33:30 -04002486 }
Mike Reed430470d2019-09-12 17:09:12 -04002487 REPORTER_ASSERT(reporter, !SkPathPriv::IsNestedFillRects(path1, nullptr));
Yuqian Li3154a532017-09-06 13:33:30 -04002488
2489 // fail, move in the middle
2490 path1.reset();
2491 if (rectFirst) {
Mike Reed30bc5272019-11-22 18:34:02 +00002492 path1.addRect(-1, -1, 2, 2, SkPathDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04002493 }
2494 path1.moveTo(r1[0].fX, r1[0].fY);
2495 for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) {
2496 if (index == 2) {
2497 path1.moveTo(1, .5f);
2498 }
2499 path1.lineTo(r1[index].fX, r1[index].fY);
2500 }
2501 path1.close();
2502 if (!rectFirst) {
Mike Reed30bc5272019-11-22 18:34:02 +00002503 path1.addRect(-1, -1, 2, 2, SkPathDirection::kCCW);
Yuqian Li3154a532017-09-06 13:33:30 -04002504 }
Mike Reed430470d2019-09-12 17:09:12 -04002505 REPORTER_ASSERT(reporter, !SkPathPriv::IsNestedFillRects(path1, nullptr));
Yuqian Li3154a532017-09-06 13:33:30 -04002506
2507 // fail, move on the edge
2508 path1.reset();
2509 if (rectFirst) {
Mike Reed30bc5272019-11-22 18:34:02 +00002510 path1.addRect(-1, -1, 2, 2, SkPathDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04002511 }
2512 for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) {
2513 path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
2514 path1.lineTo(r1[index].fX, r1[index].fY);
2515 }
2516 path1.close();
2517 if (!rectFirst) {
Mike Reed30bc5272019-11-22 18:34:02 +00002518 path1.addRect(-1, -1, 2, 2, SkPathDirection::kCCW);
Yuqian Li3154a532017-09-06 13:33:30 -04002519 }
Mike Reed430470d2019-09-12 17:09:12 -04002520 REPORTER_ASSERT(reporter, !SkPathPriv::IsNestedFillRects(path1, nullptr));
Yuqian Li3154a532017-09-06 13:33:30 -04002521
2522 // fail, quad
2523 path1.reset();
2524 if (rectFirst) {
Mike Reed30bc5272019-11-22 18:34:02 +00002525 path1.addRect(-1, -1, 2, 2, SkPathDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04002526 }
2527 path1.moveTo(r1[0].fX, r1[0].fY);
2528 for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) {
2529 if (index == 2) {
2530 path1.quadTo(1, .5f, 1, .5f);
2531 }
2532 path1.lineTo(r1[index].fX, r1[index].fY);
2533 }
2534 path1.close();
2535 if (!rectFirst) {
Mike Reed30bc5272019-11-22 18:34:02 +00002536 path1.addRect(-1, -1, 2, 2, SkPathDirection::kCCW);
Yuqian Li3154a532017-09-06 13:33:30 -04002537 }
Mike Reed430470d2019-09-12 17:09:12 -04002538 REPORTER_ASSERT(reporter, !SkPathPriv::IsNestedFillRects(path1, nullptr));
Yuqian Li3154a532017-09-06 13:33:30 -04002539
2540 // fail, cubic
2541 path1.reset();
2542 if (rectFirst) {
Mike Reed30bc5272019-11-22 18:34:02 +00002543 path1.addRect(-1, -1, 2, 2, SkPathDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04002544 }
2545 path1.moveTo(r1[0].fX, r1[0].fY);
2546 for (index = 1; index < SkToInt(SK_ARRAY_COUNT(r1)); ++index) {
2547 if (index == 2) {
2548 path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
2549 }
2550 path1.lineTo(r1[index].fX, r1[index].fY);
2551 }
2552 path1.close();
2553 if (!rectFirst) {
Mike Reed30bc5272019-11-22 18:34:02 +00002554 path1.addRect(-1, -1, 2, 2, SkPathDirection::kCCW);
Yuqian Li3154a532017-09-06 13:33:30 -04002555 }
Mike Reed430470d2019-09-12 17:09:12 -04002556 REPORTER_ASSERT(reporter, !SkPathPriv::IsNestedFillRects(path1, nullptr));
Yuqian Li3154a532017-09-06 13:33:30 -04002557
2558 // fail, not nested
2559 path1.reset();
Mike Reed30bc5272019-11-22 18:34:02 +00002560 path1.addRect(1, 1, 3, 3, SkPathDirection::kCW);
2561 path1.addRect(2, 2, 4, 4, SkPathDirection::kCW);
Mike Reed430470d2019-09-12 17:09:12 -04002562 REPORTER_ASSERT(reporter, !SkPathPriv::IsNestedFillRects(path1, nullptr));
Yuqian Li3154a532017-09-06 13:33:30 -04002563 }
2564
2565 // pass, constructed explicitly from manually closed rects specified as moves/lines.
2566 SkPath path;
2567 path.moveTo(0, 0);
2568 path.lineTo(10, 0);
2569 path.lineTo(10, 10);
2570 path.lineTo(0, 10);
2571 path.lineTo(0, 0);
2572 path.moveTo(1, 1);
2573 path.lineTo(9, 1);
2574 path.lineTo(9, 9);
2575 path.lineTo(1, 9);
2576 path.lineTo(1, 1);
Mike Reed430470d2019-09-12 17:09:12 -04002577 REPORTER_ASSERT(reporter, SkPathPriv::IsNestedFillRects(path, nullptr));
Yuqian Li3154a532017-09-06 13:33:30 -04002578
2579 // pass, stroke rect
2580 SkPath src, dst;
Mike Reed30bc5272019-11-22 18:34:02 +00002581 src.addRect(1, 1, 7, 7, SkPathDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04002582 SkPaint strokePaint;
2583 strokePaint.setStyle(SkPaint::kStroke_Style);
2584 strokePaint.setStrokeWidth(2);
2585 strokePaint.getFillPath(src, &dst);
Mike Reed430470d2019-09-12 17:09:12 -04002586 REPORTER_ASSERT(reporter, SkPathPriv::IsNestedFillRects(dst, nullptr));
Yuqian Li3154a532017-09-06 13:33:30 -04002587}
2588
2589static void write_and_read_back(skiatest::Reporter* reporter,
2590 const SkPath& p) {
Brian Osmanff7bee92020-06-09 16:04:18 -04002591 SkBinaryWriteBuffer writer;
Yuqian Li3154a532017-09-06 13:33:30 -04002592 writer.writePath(p);
2593 size_t size = writer.bytesWritten();
2594 SkAutoMalloc storage(size);
Brian Osmanff7bee92020-06-09 16:04:18 -04002595 writer.writeToMemory(storage.get());
2596 SkReadBuffer reader(storage.get(), size);
Yuqian Li3154a532017-09-06 13:33:30 -04002597
2598 SkPath readBack;
2599 REPORTER_ASSERT(reporter, readBack != p);
2600 reader.readPath(&readBack);
2601 REPORTER_ASSERT(reporter, readBack == p);
2602
Mike Reed3872c982020-08-29 17:46:51 -04002603 REPORTER_ASSERT(reporter, SkPathPriv::GetConvexityOrUnknown(readBack) ==
2604 SkPathPriv::GetConvexityOrUnknown(p));
Yuqian Li3154a532017-09-06 13:33:30 -04002605
2606 SkRect oval0, oval1;
Mike Reed30bc5272019-11-22 18:34:02 +00002607 SkPathDirection dir0, dir1;
Yuqian Li3154a532017-09-06 13:33:30 -04002608 unsigned start0, start1;
2609 REPORTER_ASSERT(reporter, readBack.isOval(nullptr) == p.isOval(nullptr));
Mike Reed0c3137c2018-02-20 13:57:05 -05002610 if (SkPathPriv::IsOval(p, &oval0, &dir0, &start0) &&
2611 SkPathPriv::IsOval(readBack, &oval1, &dir1, &start1)) {
Yuqian Li3154a532017-09-06 13:33:30 -04002612 REPORTER_ASSERT(reporter, oval0 == oval1);
2613 REPORTER_ASSERT(reporter, dir0 == dir1);
2614 REPORTER_ASSERT(reporter, start0 == start1);
2615 }
2616 REPORTER_ASSERT(reporter, readBack.isRRect(nullptr) == p.isRRect(nullptr));
2617 SkRRect rrect0, rrect1;
Mike Reed0c3137c2018-02-20 13:57:05 -05002618 if (SkPathPriv::IsRRect(p, &rrect0, &dir0, &start0) &&
2619 SkPathPriv::IsRRect(readBack, &rrect1, &dir1, &start1)) {
Yuqian Li3154a532017-09-06 13:33:30 -04002620 REPORTER_ASSERT(reporter, rrect0 == rrect1);
2621 REPORTER_ASSERT(reporter, dir0 == dir1);
2622 REPORTER_ASSERT(reporter, start0 == start1);
2623 }
2624 const SkRect& origBounds = p.getBounds();
2625 const SkRect& readBackBounds = readBack.getBounds();
2626
2627 REPORTER_ASSERT(reporter, origBounds == readBackBounds);
2628}
2629
Yuqian Li3154a532017-09-06 13:33:30 -04002630static void test_flattening(skiatest::Reporter* reporter) {
2631 SkPath p;
2632
2633 static const SkPoint pts[] = {
2634 { 0, 0 },
2635 { SkIntToScalar(10), SkIntToScalar(10) },
2636 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 },
2637 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }
2638 };
2639 p.moveTo(pts[0]);
2640 p.lineTo(pts[1]);
2641 p.quadTo(pts[2], pts[3]);
2642 p.cubicTo(pts[4], pts[5], pts[6]);
2643
2644 write_and_read_back(reporter, p);
2645
2646 // create a buffer that should be much larger than the path so we don't
2647 // kill our stack if writer goes too far.
2648 char buffer[1024];
2649 size_t size1 = p.writeToMemory(nullptr);
2650 size_t size2 = p.writeToMemory(buffer);
2651 REPORTER_ASSERT(reporter, size1 == size2);
2652
2653 SkPath p2;
2654 size_t size3 = p2.readFromMemory(buffer, 1024);
2655 REPORTER_ASSERT(reporter, size1 == size3);
2656 REPORTER_ASSERT(reporter, p == p2);
2657
2658 size3 = p2.readFromMemory(buffer, 0);
2659 REPORTER_ASSERT(reporter, !size3);
2660
2661 SkPath tooShort;
2662 size3 = tooShort.readFromMemory(buffer, size1 - 1);
2663 REPORTER_ASSERT(reporter, tooShort.isEmpty());
2664
2665 char buffer2[1024];
2666 size3 = p2.writeToMemory(buffer2);
2667 REPORTER_ASSERT(reporter, size1 == size3);
2668 REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
2669
2670 // test persistence of the oval flag & convexity
2671 {
2672 SkPath oval;
2673 SkRect rect = SkRect::MakeWH(10, 10);
2674 oval.addOval(rect);
2675
2676 write_and_read_back(reporter, oval);
2677 }
Yuqian Li3154a532017-09-06 13:33:30 -04002678}
2679
2680static void test_transform(skiatest::Reporter* reporter) {
2681 SkPath p;
2682
2683#define CONIC_PERSPECTIVE_BUG_FIXED 0
2684 static const SkPoint pts[] = {
2685 { 0, 0 }, // move
2686 { SkIntToScalar(10), SkIntToScalar(10) }, // line
2687 { SkIntToScalar(20), SkIntToScalar(10) }, { SkIntToScalar(20), 0 }, // quad
2688 { 0, 0 }, { 0, SkIntToScalar(10) }, { SkIntToScalar(1), SkIntToScalar(10) }, // cubic
2689#if CONIC_PERSPECTIVE_BUG_FIXED
2690 { 0, 0 }, { SkIntToScalar(20), SkIntToScalar(10) }, // conic
2691#endif
2692 };
2693 const int kPtCount = SK_ARRAY_COUNT(pts);
2694
2695 p.moveTo(pts[0]);
2696 p.lineTo(pts[1]);
2697 p.quadTo(pts[2], pts[3]);
2698 p.cubicTo(pts[4], pts[5], pts[6]);
2699#if CONIC_PERSPECTIVE_BUG_FIXED
2700 p.conicTo(pts[4], pts[5], 0.5f);
2701#endif
2702 p.close();
2703
2704 {
2705 SkMatrix matrix;
2706 matrix.reset();
2707 SkPath p1;
2708 p.transform(matrix, &p1);
2709 REPORTER_ASSERT(reporter, p == p1);
2710 }
2711
2712
2713 {
2714 SkMatrix matrix;
2715 matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
2716
2717 SkPath p1; // Leave p1 non-unique (i.e., the empty path)
2718
2719 p.transform(matrix, &p1);
2720 SkPoint pts1[kPtCount];
2721 int count = p1.getPoints(pts1, kPtCount);
2722 REPORTER_ASSERT(reporter, kPtCount == count);
2723 for (int i = 0; i < count; ++i) {
2724 SkPoint newPt = SkPoint::Make(pts[i].fX * 2, pts[i].fY * 3);
2725 REPORTER_ASSERT(reporter, newPt == pts1[i]);
2726 }
2727 }
2728
2729 {
2730 SkMatrix matrix;
2731 matrix.reset();
2732 matrix.setPerspX(4);
2733
2734 SkPath p1;
2735 p1.moveTo(SkPoint::Make(0, 0));
2736
Mike Reedbb59dfa2019-12-12 14:48:28 -05002737 p.transform(matrix, &p1, SkApplyPerspectiveClip::kNo);
Yuqian Li3154a532017-09-06 13:33:30 -04002738 REPORTER_ASSERT(reporter, matrix.invert(&matrix));
Mike Reedbb59dfa2019-12-12 14:48:28 -05002739 p1.transform(matrix, nullptr, SkApplyPerspectiveClip::kNo);
Yuqian Li3154a532017-09-06 13:33:30 -04002740 SkRect pBounds = p.getBounds();
2741 SkRect p1Bounds = p1.getBounds();
2742 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(pBounds.fLeft, p1Bounds.fLeft));
2743 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(pBounds.fTop, p1Bounds.fTop));
2744 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(pBounds.fRight, p1Bounds.fRight));
2745 REPORTER_ASSERT(reporter, SkScalarNearlyEqual(pBounds.fBottom, p1Bounds.fBottom));
2746 }
2747
2748 p.reset();
Mike Reed30bc5272019-11-22 18:34:02 +00002749 p.addCircle(0, 0, 1, SkPathDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04002750
2751 {
2752 SkMatrix matrix;
2753 matrix.reset();
2754 SkPath p1;
2755 p1.moveTo(SkPoint::Make(0, 0));
2756
2757 p.transform(matrix, &p1);
Mike Reed85f51b22020-08-30 10:32:06 -04002758 REPORTER_ASSERT(reporter, SkPathPriv::ComputeFirstDirection(p1) == SkPathFirstDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04002759 }
2760
2761
2762 {
2763 SkMatrix matrix;
2764 matrix.reset();
2765 matrix.setScaleX(-1);
2766 SkPath p1;
2767 p1.moveTo(SkPoint::Make(0, 0)); // Make p1 unique (i.e., not empty path)
2768
2769 p.transform(matrix, &p1);
Mike Reed85f51b22020-08-30 10:32:06 -04002770 REPORTER_ASSERT(reporter, SkPathPriv::ComputeFirstDirection(p1) == SkPathFirstDirection::kCCW);
Yuqian Li3154a532017-09-06 13:33:30 -04002771 }
2772
2773 {
2774 SkMatrix matrix;
2775 matrix.setAll(1, 1, 0, 1, 1, 0, 0, 0, 1);
2776 SkPath p1;
2777 p1.moveTo(SkPoint::Make(0, 0)); // Make p1 unique (i.e., not empty path)
2778
2779 p.transform(matrix, &p1);
Mike Reed85f51b22020-08-30 10:32:06 -04002780 REPORTER_ASSERT(reporter, SkPathPriv::ComputeFirstDirection(p1) == SkPathFirstDirection::kUnknown);
Yuqian Li3154a532017-09-06 13:33:30 -04002781 }
Brian Osmandbfcd922019-03-07 16:50:30 -05002782
2783 {
2784 SkPath p1;
2785 p1.addRect({ 10, 20, 30, 40 });
2786 SkPath p2;
2787 p2.addRect({ 10, 20, 30, 40 });
2788 uint32_t id1 = p1.getGenerationID();
2789 uint32_t id2 = p2.getGenerationID();
Mike Reed8fda88e2019-09-05 14:14:38 -04002790 REPORTER_ASSERT(reporter, id1 != id2);
Brian Osmandbfcd922019-03-07 16:50:30 -05002791 SkMatrix matrix;
2792 matrix.setScale(2, 2);
2793 p1.transform(matrix, &p2);
Mike Reed8fda88e2019-09-05 14:14:38 -04002794 REPORTER_ASSERT(reporter, id1 == p1.getGenerationID());
2795 REPORTER_ASSERT(reporter, id2 != p2.getGenerationID());
Brian Osmandbfcd922019-03-07 16:50:30 -05002796 p1.transform(matrix);
2797 REPORTER_ASSERT(reporter, id1 != p1.getGenerationID());
Brian Osmandbfcd922019-03-07 16:50:30 -05002798 }
Yuqian Li3154a532017-09-06 13:33:30 -04002799}
2800
2801static void test_zero_length_paths(skiatest::Reporter* reporter) {
2802 SkPath p;
2803 uint8_t verbs[32];
2804
2805 struct SUPPRESS_VISIBILITY_WARNING zeroPathTestData {
2806 const char* testPath;
2807 const size_t numResultPts;
2808 const SkRect resultBound;
2809 const SkPath::Verb* resultVerbs;
2810 const size_t numResultVerbs;
2811 };
2812
2813 static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb };
2814 static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb };
2815 static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb };
2816 static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb };
2817 static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb };
2818 static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb };
2819 static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb };
2820 static const SkPath::Verb resultVerbs8[] = {
2821 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb
2822 };
2823 static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb };
2824 static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb };
2825 static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb };
2826 static const SkPath::Verb resultVerbs12[] = {
2827 SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb
2828 };
2829 static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb };
2830 static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb };
2831 static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb };
2832 static const SkPath::Verb resultVerbs16[] = {
2833 SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb
2834 };
2835 static const struct zeroPathTestData gZeroLengthTests[] = {
2836 { "M 1 1", 1, {1, 1, 1, 1}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
2837 { "M 1 1 M 2 1", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
2838 { "M 1 1 z", 1, {1, 1, 1, 1}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
2839 { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
2840 { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) },
2841 { "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) },
2842 { "M 1 1 L 1 1 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) },
2843 { "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) },
2844 { "M 1 1 Q 1 1 1 1", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) },
2845 { "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) },
2846 { "M 1 1 Q 1 1 1 1 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) },
2847 { "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) },
2848 { "M 1 1 C 1 1 1 1 1 1", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) },
2849 { "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,
2850 SK_ARRAY_COUNT(resultVerbs14)
2851 },
2852 { "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) },
2853 { "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,
2854 SK_ARRAY_COUNT(resultVerbs16)
2855 }
2856 };
2857
2858 for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) {
2859 p.reset();
2860 bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p);
2861 REPORTER_ASSERT(reporter, valid);
2862 REPORTER_ASSERT(reporter, !p.isEmpty());
2863 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints());
2864 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds());
2865 REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
2866 for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) {
2867 REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]);
2868 }
2869 }
2870}
2871
2872struct SegmentInfo {
2873 SkPath fPath;
2874 int fPointCount;
2875};
2876
2877#define kCurveSegmentMask (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
2878
2879static void test_segment_masks(skiatest::Reporter* reporter) {
2880 SkPath p, p2;
2881
2882 p.moveTo(0, 0);
2883 p.quadTo(100, 100, 200, 200);
2884 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
2885 REPORTER_ASSERT(reporter, !p.isEmpty());
2886 p2 = p;
2887 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
2888 p.cubicTo(100, 100, 200, 200, 300, 300);
2889 REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
2890 REPORTER_ASSERT(reporter, !p.isEmpty());
2891 p2 = p;
2892 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
2893
2894 p.reset();
2895 p.moveTo(0, 0);
2896 p.cubicTo(100, 100, 200, 200, 300, 300);
2897 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
2898 p2 = p;
2899 REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
2900
2901 REPORTER_ASSERT(reporter, !p.isEmpty());
2902}
2903
2904static void test_iter(skiatest::Reporter* reporter) {
2905 SkPath p;
2906 SkPoint pts[4];
2907
2908 // Test an iterator with no path
2909 SkPath::Iter noPathIter;
2910 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
2911
2912 // Test that setting an empty path works
2913 noPathIter.setPath(p, false);
2914 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
2915
2916 // Test that close path makes no difference for an empty path
2917 noPathIter.setPath(p, true);
2918 REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
2919
2920 // Test an iterator with an initial empty path
2921 SkPath::Iter iter(p, false);
2922 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
2923
2924 // Test that close path makes no difference
2925 iter.setPath(p, true);
2926 REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
2927
2928
2929 struct iterTestData {
2930 const char* testPath;
2931 const bool forceClose;
Yuqian Li3154a532017-09-06 13:33:30 -04002932 const size_t* numResultPtsPerVerb;
2933 const SkPoint* resultPts;
2934 const SkPath::Verb* resultVerbs;
2935 const size_t numResultVerbs;
2936 };
2937
2938 static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb };
2939 static const SkPath::Verb resultVerbs2[] = {
Yuqian Li3154a532017-09-06 13:33:30 -04002940 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
2941 };
Mike Reedba7e9a62019-08-16 13:30:34 -04002942 static const SkPath::Verb resultVerbs3[] = {
Yuqian Li3154a532017-09-06 13:33:30 -04002943 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
2944 };
2945 static const size_t resultPtsSizes1[] = { 0 };
Mike Reedba7e9a62019-08-16 13:30:34 -04002946 static const size_t resultPtsSizes2[] = { 1, 2, 1, 1, 0 };
2947 static const size_t resultPtsSizes3[] = { 1, 2, 1, 1, 1, 0 };
Yuqian Li3154a532017-09-06 13:33:30 -04002948 static const SkPoint* resultPts1 = nullptr;
2949 static const SkPoint resultPts2[] = {
Yuqian Li3154a532017-09-06 13:33:30 -04002950 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
2951 };
Mike Reedba7e9a62019-08-16 13:30:34 -04002952 static const SkPoint resultPts3[] = {
Yuqian Li3154a532017-09-06 13:33:30 -04002953 { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
2954 };
2955 static const struct iterTestData gIterTests[] = {
Mike Reedba7e9a62019-08-16 13:30:34 -04002956 { "M 1 0", false, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
2957 { "z", false, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
2958 { "z", true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
2959 { "M 1 0 L 1 0 M 0 0 z", false, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
2960 { "M 1 0 L 1 0 M 0 0 z", true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) }
Yuqian Li3154a532017-09-06 13:33:30 -04002961 };
2962
2963 for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) {
2964 p.reset();
2965 bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p);
2966 REPORTER_ASSERT(reporter, valid);
2967 iter.setPath(p, gIterTests[i].forceClose);
2968 int j = 0, l = 0;
2969 do {
Mike Reedba7e9a62019-08-16 13:30:34 -04002970 REPORTER_ASSERT(reporter, iter.next(pts) == gIterTests[i].resultVerbs[j]);
Yuqian Li3154a532017-09-06 13:33:30 -04002971 for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) {
2972 REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]);
2973 }
2974 } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb);
2975 REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs);
2976 }
2977
2978 p.reset();
2979 iter.setPath(p, false);
2980 REPORTER_ASSERT(reporter, !iter.isClosedContour());
2981 p.lineTo(1, 1);
2982 p.close();
2983 iter.setPath(p, false);
2984 REPORTER_ASSERT(reporter, iter.isClosedContour());
2985 p.reset();
2986 iter.setPath(p, true);
2987 REPORTER_ASSERT(reporter, !iter.isClosedContour());
2988 p.lineTo(1, 1);
2989 iter.setPath(p, true);
2990 REPORTER_ASSERT(reporter, iter.isClosedContour());
2991 p.moveTo(0, 0);
2992 p.lineTo(2, 2);
2993 iter.setPath(p, false);
2994 REPORTER_ASSERT(reporter, !iter.isClosedContour());
2995
2996 // this checks to see if the NaN logic is executed in SkPath::autoClose(), but does not
2997 // check to see if the result is correct.
2998 for (int setNaN = 0; setNaN < 4; ++setNaN) {
2999 p.reset();
3000 p.moveTo(setNaN == 0 ? SK_ScalarNaN : 0, setNaN == 1 ? SK_ScalarNaN : 0);
3001 p.lineTo(setNaN == 2 ? SK_ScalarNaN : 1, setNaN == 3 ? SK_ScalarNaN : 1);
3002 iter.setPath(p, true);
Mike Reedba7e9a62019-08-16 13:30:34 -04003003 iter.next(pts);
3004 iter.next(pts);
3005 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == iter.next(pts));
Yuqian Li3154a532017-09-06 13:33:30 -04003006 }
3007
3008 p.reset();
3009 p.quadTo(0, 0, 0, 0);
3010 iter.setPath(p, false);
Mike Reedba7e9a62019-08-16 13:30:34 -04003011 iter.next(pts);
3012 REPORTER_ASSERT(reporter, SkPath::kQuad_Verb == iter.next(pts));
Yuqian Li3154a532017-09-06 13:33:30 -04003013
3014 p.reset();
3015 p.conicTo(0, 0, 0, 0, 0.5f);
3016 iter.setPath(p, false);
Mike Reedba7e9a62019-08-16 13:30:34 -04003017 iter.next(pts);
3018 REPORTER_ASSERT(reporter, SkPath::kConic_Verb == iter.next(pts));
Yuqian Li3154a532017-09-06 13:33:30 -04003019
3020 p.reset();
3021 p.cubicTo(0, 0, 0, 0, 0, 0);
3022 iter.setPath(p, false);
Mike Reedba7e9a62019-08-16 13:30:34 -04003023 iter.next(pts);
3024 REPORTER_ASSERT(reporter, SkPath::kCubic_Verb == iter.next(pts));
Yuqian Li3154a532017-09-06 13:33:30 -04003025
3026 p.moveTo(1, 1); // add a trailing moveto
3027 iter.setPath(p, false);
Mike Reedba7e9a62019-08-16 13:30:34 -04003028 iter.next(pts);
3029 REPORTER_ASSERT(reporter, SkPath::kCubic_Verb == iter.next(pts));
Yuqian Li3154a532017-09-06 13:33:30 -04003030
3031 // The GM degeneratesegments.cpp test is more extensive
3032
3033 // Test out mixed degenerate and non-degenerate geometry with Conics
3034 const SkVector radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 100, 100 } };
3035 SkRect r = SkRect::MakeWH(100, 100);
3036 SkRRect rr;
3037 rr.setRectRadii(r, radii);
3038 p.reset();
3039 p.addRRect(rr);
3040 iter.setPath(p, false);
3041 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == iter.next(pts));
3042 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == iter.next(pts));
Mike Reedba7e9a62019-08-16 13:30:34 -04003043 return;
Yuqian Li3154a532017-09-06 13:33:30 -04003044 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == iter.next(pts));
3045 REPORTER_ASSERT(reporter, SkPath::kConic_Verb == iter.next(pts));
3046 REPORTER_ASSERT(reporter, SK_ScalarRoot2Over2 == iter.conicWeight());
3047}
3048
Chris Daltonde500372020-05-05 15:06:30 -06003049static void test_range_iter(skiatest::Reporter* reporter) {
3050 SkPath path;
Yuqian Li3154a532017-09-06 13:33:30 -04003051
3052 // Test an iterator with an initial empty path
Chris Daltonde500372020-05-05 15:06:30 -06003053 SkPathPriv::Iterate iterate(path);
3054 REPORTER_ASSERT(reporter, iterate.begin() == iterate.end());
Yuqian Li3154a532017-09-06 13:33:30 -04003055
3056 // Test that a move-only path returns the move.
Chris Daltonde500372020-05-05 15:06:30 -06003057 path.moveTo(SK_Scalar1, 0);
3058 iterate = SkPathPriv::Iterate(path);
3059 SkPathPriv::RangeIter iter = iterate.begin();
3060 {
3061 auto [verb, pts, w] = *iter++;
3062 REPORTER_ASSERT(reporter, verb == SkPathVerb::kMove);
3063 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
3064 REPORTER_ASSERT(reporter, pts[0].fY == 0);
3065 }
3066 REPORTER_ASSERT(reporter, iter == iterate.end());
Yuqian Li3154a532017-09-06 13:33:30 -04003067
3068 // No matter how many moves we add, we should get them all back
Chris Daltonde500372020-05-05 15:06:30 -06003069 path.moveTo(SK_Scalar1*2, SK_Scalar1);
3070 path.moveTo(SK_Scalar1*3, SK_Scalar1*2);
3071 iterate = SkPathPriv::Iterate(path);
3072 iter = iterate.begin();
3073 {
3074 auto [verb, pts, w] = *iter++;
3075 REPORTER_ASSERT(reporter, verb == SkPathVerb::kMove);
3076 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
3077 REPORTER_ASSERT(reporter, pts[0].fY == 0);
3078 }
3079 {
3080 auto [verb, pts, w] = *iter++;
3081 REPORTER_ASSERT(reporter, verb == SkPathVerb::kMove);
3082 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
3083 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
3084 }
3085 {
3086 auto [verb, pts, w] = *iter++;
3087 REPORTER_ASSERT(reporter, verb == SkPathVerb::kMove);
3088 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
3089 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
3090 }
3091 REPORTER_ASSERT(reporter, iter == iterate.end());
Yuqian Li3154a532017-09-06 13:33:30 -04003092
3093 // Initial close is never ever stored
Chris Daltonde500372020-05-05 15:06:30 -06003094 path.reset();
3095 path.close();
3096 iterate = SkPathPriv::Iterate(path);
3097 REPORTER_ASSERT(reporter, iterate.begin() == iterate.end());
Yuqian Li3154a532017-09-06 13:33:30 -04003098
3099 // Move/close sequences
Chris Daltonde500372020-05-05 15:06:30 -06003100 path.reset();
3101 path.close(); // Not stored, no purpose
3102 path.moveTo(SK_Scalar1, 0);
3103 path.close();
3104 path.close(); // Not stored, no purpose
3105 path.moveTo(SK_Scalar1*2, SK_Scalar1);
3106 path.close();
3107 path.moveTo(SK_Scalar1*3, SK_Scalar1*2);
3108 path.moveTo(SK_Scalar1*4, SK_Scalar1*3);
3109 path.close();
3110 iterate = SkPathPriv::Iterate(path);
3111 iter = iterate.begin();
3112 {
3113 auto [verb, pts, w] = *iter++;
3114 REPORTER_ASSERT(reporter, verb == SkPathVerb::kMove);
3115 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1);
3116 REPORTER_ASSERT(reporter, pts[0].fY == 0);
3117 }
3118 {
3119 auto [verb, pts, w] = *iter++;
3120 REPORTER_ASSERT(reporter, verb == SkPathVerb::kClose);
3121 }
3122 {
3123 auto [verb, pts, w] = *iter++;
3124 REPORTER_ASSERT(reporter, verb == SkPathVerb::kMove);
3125 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*2);
3126 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1);
3127 }
3128 {
3129 auto [verb, pts, w] = *iter++;
3130 REPORTER_ASSERT(reporter, verb == SkPathVerb::kClose);
3131 }
3132 {
3133 auto [verb, pts, w] = *iter++;
3134 REPORTER_ASSERT(reporter, verb == SkPathVerb::kMove);
3135 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*3);
3136 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*2);
3137 }
3138 {
3139 auto [verb, pts, w] = *iter++;
3140 REPORTER_ASSERT(reporter, verb == SkPathVerb::kMove);
3141 REPORTER_ASSERT(reporter, pts[0].fX == SK_Scalar1*4);
3142 REPORTER_ASSERT(reporter, pts[0].fY == SK_Scalar1*3);
3143 }
3144 {
3145 auto [verb, pts, w] = *iter++;
3146 REPORTER_ASSERT(reporter, verb == SkPathVerb::kClose);
3147 }
3148 REPORTER_ASSERT(reporter, iter == iterate.end());
Yuqian Li3154a532017-09-06 13:33:30 -04003149
3150 // Generate random paths and verify
3151 SkPoint randomPts[25];
3152 for (int i = 0; i < 5; ++i) {
3153 for (int j = 0; j < 5; ++j) {
3154 randomPts[i*5+j].set(SK_Scalar1*i, SK_Scalar1*j);
3155 }
3156 }
3157
3158 // Max of 10 segments, max 3 points per segment
3159 SkRandom rand(9876543);
Chris Daltonde500372020-05-05 15:06:30 -06003160 SkPoint expectedPts[31]; // May have leading moveTo
3161 SkPathVerb expectedVerbs[22]; // May have leading moveTo
3162 SkPathVerb nextVerb;
Yuqian Li3154a532017-09-06 13:33:30 -04003163
3164 for (int i = 0; i < 500; ++i) {
Chris Daltonde500372020-05-05 15:06:30 -06003165 path.reset();
Yuqian Li3154a532017-09-06 13:33:30 -04003166 bool lastWasClose = true;
3167 bool haveMoveTo = false;
3168 SkPoint lastMoveToPt = { 0, 0 };
3169 int numPoints = 0;
3170 int numVerbs = (rand.nextU() >> 16) % 10;
3171 int numIterVerbs = 0;
3172 for (int j = 0; j < numVerbs; ++j) {
3173 do {
Chris Daltonde500372020-05-05 15:06:30 -06003174 nextVerb = static_cast<SkPathVerb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
3175 } while (lastWasClose && nextVerb == SkPathVerb::kClose);
Yuqian Li3154a532017-09-06 13:33:30 -04003176 switch (nextVerb) {
Chris Daltonde500372020-05-05 15:06:30 -06003177 case SkPathVerb::kMove:
Yuqian Li3154a532017-09-06 13:33:30 -04003178 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
Chris Daltonde500372020-05-05 15:06:30 -06003179 path.moveTo(expectedPts[numPoints]);
Yuqian Li3154a532017-09-06 13:33:30 -04003180 lastMoveToPt = expectedPts[numPoints];
3181 numPoints += 1;
3182 lastWasClose = false;
3183 haveMoveTo = true;
3184 break;
Chris Daltonde500372020-05-05 15:06:30 -06003185 case SkPathVerb::kLine:
Yuqian Li3154a532017-09-06 13:33:30 -04003186 if (!haveMoveTo) {
3187 expectedPts[numPoints++] = lastMoveToPt;
Chris Daltonde500372020-05-05 15:06:30 -06003188 expectedVerbs[numIterVerbs++] = SkPathVerb::kMove;
Yuqian Li3154a532017-09-06 13:33:30 -04003189 haveMoveTo = true;
3190 }
3191 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
Chris Daltonde500372020-05-05 15:06:30 -06003192 path.lineTo(expectedPts[numPoints]);
Yuqian Li3154a532017-09-06 13:33:30 -04003193 numPoints += 1;
3194 lastWasClose = false;
3195 break;
Chris Daltonde500372020-05-05 15:06:30 -06003196 case SkPathVerb::kQuad:
Yuqian Li3154a532017-09-06 13:33:30 -04003197 if (!haveMoveTo) {
3198 expectedPts[numPoints++] = lastMoveToPt;
Chris Daltonde500372020-05-05 15:06:30 -06003199 expectedVerbs[numIterVerbs++] = SkPathVerb::kMove;
Yuqian Li3154a532017-09-06 13:33:30 -04003200 haveMoveTo = true;
3201 }
3202 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
3203 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
Chris Daltonde500372020-05-05 15:06:30 -06003204 path.quadTo(expectedPts[numPoints], expectedPts[numPoints + 1]);
Yuqian Li3154a532017-09-06 13:33:30 -04003205 numPoints += 2;
3206 lastWasClose = false;
3207 break;
Chris Daltonde500372020-05-05 15:06:30 -06003208 case SkPathVerb::kConic:
Yuqian Li3154a532017-09-06 13:33:30 -04003209 if (!haveMoveTo) {
3210 expectedPts[numPoints++] = lastMoveToPt;
Chris Daltonde500372020-05-05 15:06:30 -06003211 expectedVerbs[numIterVerbs++] = SkPathVerb::kMove;
Yuqian Li3154a532017-09-06 13:33:30 -04003212 haveMoveTo = true;
3213 }
3214 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
3215 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
Chris Daltonde500372020-05-05 15:06:30 -06003216 path.conicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
3217 rand.nextUScalar1() * 4);
Yuqian Li3154a532017-09-06 13:33:30 -04003218 numPoints += 2;
3219 lastWasClose = false;
3220 break;
Chris Daltonde500372020-05-05 15:06:30 -06003221 case SkPathVerb::kCubic:
Yuqian Li3154a532017-09-06 13:33:30 -04003222 if (!haveMoveTo) {
3223 expectedPts[numPoints++] = lastMoveToPt;
Chris Daltonde500372020-05-05 15:06:30 -06003224 expectedVerbs[numIterVerbs++] = SkPathVerb::kMove;
Yuqian Li3154a532017-09-06 13:33:30 -04003225 haveMoveTo = true;
3226 }
3227 expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
3228 expectedPts[numPoints + 1] = randomPts[(rand.nextU() >> 16) % 25];
3229 expectedPts[numPoints + 2] = randomPts[(rand.nextU() >> 16) % 25];
Chris Daltonde500372020-05-05 15:06:30 -06003230 path.cubicTo(expectedPts[numPoints], expectedPts[numPoints + 1],
3231 expectedPts[numPoints + 2]);
Yuqian Li3154a532017-09-06 13:33:30 -04003232 numPoints += 3;
3233 lastWasClose = false;
3234 break;
Chris Daltonde500372020-05-05 15:06:30 -06003235 case SkPathVerb::kClose:
3236 path.close();
Yuqian Li3154a532017-09-06 13:33:30 -04003237 haveMoveTo = false;
3238 lastWasClose = true;
3239 break;
3240 default:
3241 SkDEBUGFAIL("unexpected verb");
3242 }
3243 expectedVerbs[numIterVerbs++] = nextVerb;
3244 }
3245
Yuqian Li3154a532017-09-06 13:33:30 -04003246 numVerbs = numIterVerbs;
3247 numIterVerbs = 0;
3248 int numIterPts = 0;
3249 SkPoint lastMoveTo;
3250 SkPoint lastPt;
3251 lastMoveTo.set(0, 0);
3252 lastPt.set(0, 0);
Chris Daltonde500372020-05-05 15:06:30 -06003253 for (auto [nextVerb, pts, w] : SkPathPriv::Iterate(path)) {
Yuqian Li3154a532017-09-06 13:33:30 -04003254 REPORTER_ASSERT(reporter, nextVerb == expectedVerbs[numIterVerbs]);
3255 numIterVerbs++;
3256 switch (nextVerb) {
Chris Daltonde500372020-05-05 15:06:30 -06003257 case SkPathVerb::kMove:
Yuqian Li3154a532017-09-06 13:33:30 -04003258 REPORTER_ASSERT(reporter, numIterPts < numPoints);
3259 REPORTER_ASSERT(reporter, pts[0] == expectedPts[numIterPts]);
3260 lastPt = lastMoveTo = pts[0];
3261 numIterPts += 1;
3262 break;
Chris Daltonde500372020-05-05 15:06:30 -06003263 case SkPathVerb::kLine:
Yuqian Li3154a532017-09-06 13:33:30 -04003264 REPORTER_ASSERT(reporter, numIterPts < numPoints + 1);
3265 REPORTER_ASSERT(reporter, pts[0] == lastPt);
3266 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
3267 lastPt = pts[1];
3268 numIterPts += 1;
3269 break;
Chris Daltonde500372020-05-05 15:06:30 -06003270 case SkPathVerb::kQuad:
3271 case SkPathVerb::kConic:
Yuqian Li3154a532017-09-06 13:33:30 -04003272 REPORTER_ASSERT(reporter, numIterPts < numPoints + 2);
3273 REPORTER_ASSERT(reporter, pts[0] == lastPt);
3274 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
3275 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
3276 lastPt = pts[2];
3277 numIterPts += 2;
3278 break;
Chris Daltonde500372020-05-05 15:06:30 -06003279 case SkPathVerb::kCubic:
Yuqian Li3154a532017-09-06 13:33:30 -04003280 REPORTER_ASSERT(reporter, numIterPts < numPoints + 3);
3281 REPORTER_ASSERT(reporter, pts[0] == lastPt);
3282 REPORTER_ASSERT(reporter, pts[1] == expectedPts[numIterPts]);
3283 REPORTER_ASSERT(reporter, pts[2] == expectedPts[numIterPts + 1]);
3284 REPORTER_ASSERT(reporter, pts[3] == expectedPts[numIterPts + 2]);
3285 lastPt = pts[3];
3286 numIterPts += 3;
3287 break;
Chris Daltonde500372020-05-05 15:06:30 -06003288 case SkPathVerb::kClose:
Yuqian Li3154a532017-09-06 13:33:30 -04003289 lastPt = lastMoveTo;
3290 break;
3291 default:
3292 SkDEBUGFAIL("unexpected verb");
3293 }
3294 }
3295 REPORTER_ASSERT(reporter, numIterPts == numPoints);
3296 REPORTER_ASSERT(reporter, numIterVerbs == numVerbs);
3297 }
3298}
3299
3300static void check_for_circle(skiatest::Reporter* reporter,
3301 const SkPath& path,
3302 bool expectedCircle,
Mike Reed3872c982020-08-29 17:46:51 -04003303 SkPathFirstDirection expectedDir) {
Yuqian Li3154a532017-09-06 13:33:30 -04003304 SkRect rect = SkRect::MakeEmpty();
3305 REPORTER_ASSERT(reporter, path.isOval(&rect) == expectedCircle);
Mike Reed30bc5272019-11-22 18:34:02 +00003306 SkPathDirection isOvalDir;
Yuqian Li3154a532017-09-06 13:33:30 -04003307 unsigned isOvalStart;
Mike Reed0c3137c2018-02-20 13:57:05 -05003308 if (SkPathPriv::IsOval(path, &rect, &isOvalDir, &isOvalStart)) {
Yuqian Li3154a532017-09-06 13:33:30 -04003309 REPORTER_ASSERT(reporter, rect.height() == rect.width());
3310 REPORTER_ASSERT(reporter, SkPathPriv::AsFirstDirection(isOvalDir) == expectedDir);
3311 SkPath tmpPath;
3312 tmpPath.addOval(rect, isOvalDir, isOvalStart);
3313 REPORTER_ASSERT(reporter, path == tmpPath);
3314 }
Mike Reed85f51b22020-08-30 10:32:06 -04003315 REPORTER_ASSERT(reporter, SkPathPriv::ComputeFirstDirection(path) == expectedDir);
Yuqian Li3154a532017-09-06 13:33:30 -04003316}
3317
3318static void test_circle_skew(skiatest::Reporter* reporter,
3319 const SkPath& path,
Mike Reed3872c982020-08-29 17:46:51 -04003320 SkPathFirstDirection dir) {
Yuqian Li3154a532017-09-06 13:33:30 -04003321 SkPath tmp;
3322
3323 SkMatrix m;
3324 m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
3325 path.transform(m, &tmp);
3326 // this matrix reverses the direction.
Mike Reed3872c982020-08-29 17:46:51 -04003327 if (SkPathFirstDirection::kCCW == dir) {
3328 dir = SkPathFirstDirection::kCW;
Yuqian Li3154a532017-09-06 13:33:30 -04003329 } else {
Mike Reed3872c982020-08-29 17:46:51 -04003330 REPORTER_ASSERT(reporter, SkPathFirstDirection::kCW == dir);
3331 dir = SkPathFirstDirection::kCCW;
Yuqian Li3154a532017-09-06 13:33:30 -04003332 }
3333 check_for_circle(reporter, tmp, false, dir);
3334}
3335
3336static void test_circle_translate(skiatest::Reporter* reporter,
3337 const SkPath& path,
Mike Reed3872c982020-08-29 17:46:51 -04003338 SkPathFirstDirection dir) {
Yuqian Li3154a532017-09-06 13:33:30 -04003339 SkPath tmp;
3340
3341 // translate at small offset
3342 SkMatrix m;
3343 m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
3344 path.transform(m, &tmp);
3345 check_for_circle(reporter, tmp, true, dir);
3346
3347 tmp.reset();
3348 m.reset();
3349
3350 // translate at a relatively big offset
3351 m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
3352 path.transform(m, &tmp);
3353 check_for_circle(reporter, tmp, true, dir);
3354}
3355
3356static void test_circle_rotate(skiatest::Reporter* reporter,
3357 const SkPath& path,
Mike Reed3872c982020-08-29 17:46:51 -04003358 SkPathFirstDirection dir) {
Yuqian Li3154a532017-09-06 13:33:30 -04003359 for (int angle = 0; angle < 360; ++angle) {
3360 SkPath tmp;
3361 SkMatrix m;
3362 m.setRotate(SkIntToScalar(angle));
3363 path.transform(m, &tmp);
3364
3365 // TODO: a rotated circle whose rotated angle is not a multiple of 90
3366 // degrees is not an oval anymore, this can be improved. we made this
3367 // for the simplicity of our implementation.
3368 if (angle % 90 == 0) {
3369 check_for_circle(reporter, tmp, true, dir);
3370 } else {
3371 check_for_circle(reporter, tmp, false, dir);
3372 }
3373 }
3374}
3375
3376static void test_circle_mirror_x(skiatest::Reporter* reporter,
3377 const SkPath& path,
Mike Reed3872c982020-08-29 17:46:51 -04003378 SkPathFirstDirection dir) {
Yuqian Li3154a532017-09-06 13:33:30 -04003379 SkPath tmp;
3380 SkMatrix m;
3381 m.reset();
3382 m.setScaleX(-SK_Scalar1);
3383 path.transform(m, &tmp);
Mike Reed3872c982020-08-29 17:46:51 -04003384 if (SkPathFirstDirection::kCW == dir) {
3385 dir = SkPathFirstDirection::kCCW;
Yuqian Li3154a532017-09-06 13:33:30 -04003386 } else {
Mike Reed3872c982020-08-29 17:46:51 -04003387 REPORTER_ASSERT(reporter, SkPathFirstDirection::kCCW == dir);
3388 dir = SkPathFirstDirection::kCW;
Yuqian Li3154a532017-09-06 13:33:30 -04003389 }
3390 check_for_circle(reporter, tmp, true, dir);
3391}
3392
3393static void test_circle_mirror_y(skiatest::Reporter* reporter,
3394 const SkPath& path,
Mike Reed3872c982020-08-29 17:46:51 -04003395 SkPathFirstDirection dir) {
Yuqian Li3154a532017-09-06 13:33:30 -04003396 SkPath tmp;
3397 SkMatrix m;
3398 m.reset();
3399 m.setScaleY(-SK_Scalar1);
3400 path.transform(m, &tmp);
3401
Mike Reed3872c982020-08-29 17:46:51 -04003402 if (SkPathFirstDirection::kCW == dir) {
3403 dir = SkPathFirstDirection::kCCW;
Yuqian Li3154a532017-09-06 13:33:30 -04003404 } else {
Mike Reed3872c982020-08-29 17:46:51 -04003405 REPORTER_ASSERT(reporter, SkPathFirstDirection::kCCW == dir);
3406 dir = SkPathFirstDirection::kCW;
Yuqian Li3154a532017-09-06 13:33:30 -04003407 }
3408
3409 check_for_circle(reporter, tmp, true, dir);
3410}
3411
3412static void test_circle_mirror_xy(skiatest::Reporter* reporter,
3413 const SkPath& path,
Mike Reed3872c982020-08-29 17:46:51 -04003414 SkPathFirstDirection dir) {
Yuqian Li3154a532017-09-06 13:33:30 -04003415 SkPath tmp;
3416 SkMatrix m;
3417 m.reset();
3418 m.setScaleX(-SK_Scalar1);
3419 m.setScaleY(-SK_Scalar1);
3420 path.transform(m, &tmp);
3421
3422 check_for_circle(reporter, tmp, true, dir);
3423}
3424
3425static void test_circle_with_direction(skiatest::Reporter* reporter,
Mike Reed30bc5272019-11-22 18:34:02 +00003426 SkPathDirection inDir) {
Mike Reed3872c982020-08-29 17:46:51 -04003427 const SkPathFirstDirection dir = SkPathPriv::AsFirstDirection(inDir);
Yuqian Li3154a532017-09-06 13:33:30 -04003428 SkPath path;
3429
3430 // circle at origin
3431 path.addCircle(0, 0, SkIntToScalar(20), inDir);
3432
3433 check_for_circle(reporter, path, true, dir);
3434 test_circle_rotate(reporter, path, dir);
3435 test_circle_translate(reporter, path, dir);
3436 test_circle_skew(reporter, path, dir);
3437 test_circle_mirror_x(reporter, path, dir);
3438 test_circle_mirror_y(reporter, path, dir);
3439 test_circle_mirror_xy(reporter, path, dir);
3440
3441 // circle at an offset at (10, 10)
3442 path.reset();
3443 path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
3444 SkIntToScalar(20), inDir);
3445
3446 check_for_circle(reporter, path, true, dir);
3447 test_circle_rotate(reporter, path, dir);
3448 test_circle_translate(reporter, path, dir);
3449 test_circle_skew(reporter, path, dir);
3450 test_circle_mirror_x(reporter, path, dir);
3451 test_circle_mirror_y(reporter, path, dir);
3452 test_circle_mirror_xy(reporter, path, dir);
3453
3454 // Try different starting points for the contour.
3455 for (unsigned start = 0; start < 4; ++start) {
3456 path.reset();
3457 path.addOval(SkRect::MakeXYWH(20, 10, 5, 5), inDir, start);
3458 test_circle_rotate(reporter, path, dir);
3459 test_circle_translate(reporter, path, dir);
3460 test_circle_skew(reporter, path, dir);
3461 test_circle_mirror_x(reporter, path, dir);
3462 test_circle_mirror_y(reporter, path, dir);
3463 test_circle_mirror_xy(reporter, path, dir);
3464 }
3465}
3466
3467static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
3468 SkPath path;
3469 SkPath circle;
3470 SkPath rect;
3471 SkPath empty;
3472
Mike Reed30bc5272019-11-22 18:34:02 +00003473 const SkPathDirection kCircleDir = SkPathDirection::kCW;
3474 const SkPathDirection kCircleDirOpposite = SkPathDirection::kCCW;
Yuqian Li3154a532017-09-06 13:33:30 -04003475
3476 circle.addCircle(0, 0, SkIntToScalar(10), kCircleDir);
3477 rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
Mike Reed30bc5272019-11-22 18:34:02 +00003478 SkIntToScalar(20), SkIntToScalar(20), SkPathDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04003479
3480 SkMatrix translate;
3481 translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
3482
3483 // Although all the path concatenation related operations leave
3484 // the path a circle, most mark it as a non-circle for simplicity
3485
3486 // empty + circle (translate)
3487 path = empty;
3488 path.addPath(circle, translate);
3489 check_for_circle(reporter, path, false, SkPathPriv::AsFirstDirection(kCircleDir));
3490
3491 // circle + empty (translate)
3492 path = circle;
3493 path.addPath(empty, translate);
3494
3495 check_for_circle(reporter, path, true, SkPathPriv::AsFirstDirection(kCircleDir));
3496
3497 // test reverseAddPath
3498 path = circle;
3499 path.reverseAddPath(rect);
3500 check_for_circle(reporter, path, false, SkPathPriv::AsFirstDirection(kCircleDirOpposite));
3501}
3502
3503static void test_circle(skiatest::Reporter* reporter) {
Mike Reed30bc5272019-11-22 18:34:02 +00003504 test_circle_with_direction(reporter, SkPathDirection::kCW);
3505 test_circle_with_direction(reporter, SkPathDirection::kCCW);
Yuqian Li3154a532017-09-06 13:33:30 -04003506
3507 // multiple addCircle()
3508 SkPath path;
Mike Reed30bc5272019-11-22 18:34:02 +00003509 path.addCircle(0, 0, SkIntToScalar(10), SkPathDirection::kCW);
3510 path.addCircle(0, 0, SkIntToScalar(20), SkPathDirection::kCW);
Mike Reed3872c982020-08-29 17:46:51 -04003511 check_for_circle(reporter, path, false, SkPathFirstDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04003512
3513 // some extra lineTo() would make isOval() fail
3514 path.reset();
Mike Reed30bc5272019-11-22 18:34:02 +00003515 path.addCircle(0, 0, SkIntToScalar(10), SkPathDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04003516 path.lineTo(0, 0);
Mike Reed3872c982020-08-29 17:46:51 -04003517 check_for_circle(reporter, path, false, SkPathFirstDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04003518
3519 // not back to the original point
3520 path.reset();
Mike Reed30bc5272019-11-22 18:34:02 +00003521 path.addCircle(0, 0, SkIntToScalar(10), SkPathDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04003522 path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
Mike Reed3872c982020-08-29 17:46:51 -04003523 check_for_circle(reporter, path, false, SkPathFirstDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04003524
3525 test_circle_with_add_paths(reporter);
3526
3527 // test negative radius
3528 path.reset();
Mike Reed30bc5272019-11-22 18:34:02 +00003529 path.addCircle(0, 0, -1, SkPathDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04003530 REPORTER_ASSERT(reporter, path.isEmpty());
3531}
3532
3533static void test_oval(skiatest::Reporter* reporter) {
3534 SkRect rect;
3535 SkMatrix m;
3536 SkPath path;
3537 unsigned start = 0;
Mike Reed30bc5272019-11-22 18:34:02 +00003538 SkPathDirection dir = SkPathDirection::kCCW;
Yuqian Li3154a532017-09-06 13:33:30 -04003539
3540 rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
3541 path.addOval(rect);
3542
3543 // Defaults to dir = CW and start = 1
3544 REPORTER_ASSERT(reporter, path.isOval(nullptr));
3545
3546 m.setRotate(SkIntToScalar(90));
3547 SkPath tmp;
3548 path.transform(m, &tmp);
3549 // an oval rotated 90 degrees is still an oval. The start index changes from 1 to 2. Direction
3550 // is unchanged.
Mike Reed0c3137c2018-02-20 13:57:05 -05003551 REPORTER_ASSERT(reporter, SkPathPriv::IsOval(tmp, nullptr, &dir, &start));
Yuqian Li3154a532017-09-06 13:33:30 -04003552 REPORTER_ASSERT(reporter, 2 == start);
Mike Reed30bc5272019-11-22 18:34:02 +00003553 REPORTER_ASSERT(reporter, SkPathDirection::kCW == dir);
Yuqian Li3154a532017-09-06 13:33:30 -04003554
3555 m.reset();
3556 m.setRotate(SkIntToScalar(30));
3557 tmp.reset();
3558 path.transform(m, &tmp);
3559 // an oval rotated 30 degrees is not an oval anymore.
3560 REPORTER_ASSERT(reporter, !tmp.isOval(nullptr));
3561
3562 // since empty path being transformed.
3563 path.reset();
3564 tmp.reset();
3565 m.reset();
3566 path.transform(m, &tmp);
3567 REPORTER_ASSERT(reporter, !tmp.isOval(nullptr));
3568
3569 // empty path is not an oval
3570 tmp.reset();
3571 REPORTER_ASSERT(reporter, !tmp.isOval(nullptr));
3572
3573 // only has moveTo()s
3574 tmp.reset();
3575 tmp.moveTo(0, 0);
3576 tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
3577 REPORTER_ASSERT(reporter, !tmp.isOval(nullptr));
3578
3579 // mimic WebKit's calling convention,
3580 // call moveTo() first and then call addOval()
3581 path.reset();
3582 path.moveTo(0, 0);
3583 path.addOval(rect);
3584 REPORTER_ASSERT(reporter, path.isOval(nullptr));
3585
3586 // copy path
3587 path.reset();
3588 tmp.reset();
3589 tmp.addOval(rect);
3590 path = tmp;
Mike Reed0c3137c2018-02-20 13:57:05 -05003591 REPORTER_ASSERT(reporter, SkPathPriv::IsOval(path, nullptr, &dir, &start));
Mike Reed30bc5272019-11-22 18:34:02 +00003592 REPORTER_ASSERT(reporter, SkPathDirection::kCW == dir);
Yuqian Li3154a532017-09-06 13:33:30 -04003593 REPORTER_ASSERT(reporter, 1 == start);
3594}
3595
3596static void test_empty(skiatest::Reporter* reporter, const SkPath& p) {
3597 SkPath empty;
3598
3599 REPORTER_ASSERT(reporter, p.isEmpty());
3600 REPORTER_ASSERT(reporter, 0 == p.countPoints());
3601 REPORTER_ASSERT(reporter, 0 == p.countVerbs());
3602 REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
3603 REPORTER_ASSERT(reporter, p.isConvex());
Mike Reedcf0e3c62019-12-03 16:26:15 -05003604 REPORTER_ASSERT(reporter, p.getFillType() == SkPathFillType::kWinding);
Yuqian Li3154a532017-09-06 13:33:30 -04003605 REPORTER_ASSERT(reporter, !p.isInverseFillType());
3606 REPORTER_ASSERT(reporter, p == empty);
3607 REPORTER_ASSERT(reporter, !(p != empty));
3608}
3609
3610static void test_rrect_is_convex(skiatest::Reporter* reporter, SkPath* path,
Mike Reed30bc5272019-11-22 18:34:02 +00003611 SkPathDirection dir) {
Yuqian Li3154a532017-09-06 13:33:30 -04003612 REPORTER_ASSERT(reporter, path->isConvex());
Mike Reed85f51b22020-08-30 10:32:06 -04003613 REPORTER_ASSERT(reporter,
3614 SkPathPriv::ComputeFirstDirection(*path) == SkPathPriv::AsFirstDirection(dir));
Mike Reed6052c0b2020-08-28 11:49:56 -04003615 SkPathPriv::ForceComputeConvexity(*path);
Yuqian Li3154a532017-09-06 13:33:30 -04003616 REPORTER_ASSERT(reporter, path->isConvex());
3617 path->reset();
3618}
3619
3620static void test_rrect_convexity_is_unknown(skiatest::Reporter* reporter, SkPath* path,
Mike Reed30bc5272019-11-22 18:34:02 +00003621 SkPathDirection dir) {
Yuqian Li3154a532017-09-06 13:33:30 -04003622 REPORTER_ASSERT(reporter, path->isConvex());
Mike Reed85f51b22020-08-30 10:32:06 -04003623 REPORTER_ASSERT(reporter,
3624 SkPathPriv::ComputeFirstDirection(*path) == SkPathPriv::AsFirstDirection(dir));
Mike Reed6052c0b2020-08-28 11:49:56 -04003625 SkPathPriv::ForceComputeConvexity(*path);
3626 REPORTER_ASSERT(reporter, !path->isConvex());
Yuqian Li3154a532017-09-06 13:33:30 -04003627 path->reset();
3628}
3629
3630static void test_rrect(skiatest::Reporter* reporter) {
3631 SkPath p;
3632 SkRRect rr;
3633 SkVector radii[] = {{1, 2}, {3, 4}, {5, 6}, {7, 8}};
3634 SkRect r = {10, 20, 30, 40};
3635 rr.setRectRadii(r, radii);
3636 p.addRRect(rr);
Mike Reed30bc5272019-11-22 18:34:02 +00003637 test_rrect_is_convex(reporter, &p, SkPathDirection::kCW);
3638 p.addRRect(rr, SkPathDirection::kCCW);
3639 test_rrect_is_convex(reporter, &p, SkPathDirection::kCCW);
Yuqian Li3154a532017-09-06 13:33:30 -04003640 p.addRoundRect(r, &radii[0].fX);
Mike Reed30bc5272019-11-22 18:34:02 +00003641 test_rrect_is_convex(reporter, &p, SkPathDirection::kCW);
3642 p.addRoundRect(r, &radii[0].fX, SkPathDirection::kCCW);
3643 test_rrect_is_convex(reporter, &p, SkPathDirection::kCCW);
Yuqian Li3154a532017-09-06 13:33:30 -04003644 p.addRoundRect(r, radii[1].fX, radii[1].fY);
Mike Reed30bc5272019-11-22 18:34:02 +00003645 test_rrect_is_convex(reporter, &p, SkPathDirection::kCW);
3646 p.addRoundRect(r, radii[1].fX, radii[1].fY, SkPathDirection::kCCW);
3647 test_rrect_is_convex(reporter, &p, SkPathDirection::kCCW);
Yuqian Li3154a532017-09-06 13:33:30 -04003648 for (size_t i = 0; i < SK_ARRAY_COUNT(radii); ++i) {
3649 SkVector save = radii[i];
3650 radii[i].set(0, 0);
3651 rr.setRectRadii(r, radii);
3652 p.addRRect(rr);
Mike Reed30bc5272019-11-22 18:34:02 +00003653 test_rrect_is_convex(reporter, &p, SkPathDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04003654 radii[i] = save;
3655 }
3656 p.addRoundRect(r, 0, 0);
3657 SkRect returnedRect;
3658 REPORTER_ASSERT(reporter, p.isRect(&returnedRect));
3659 REPORTER_ASSERT(reporter, returnedRect == r);
Mike Reed30bc5272019-11-22 18:34:02 +00003660 test_rrect_is_convex(reporter, &p, SkPathDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04003661 SkVector zeroRadii[] = {{0, 0}, {0, 0}, {0, 0}, {0, 0}};
3662 rr.setRectRadii(r, zeroRadii);
3663 p.addRRect(rr);
3664 bool closed;
Mike Reed30bc5272019-11-22 18:34:02 +00003665 SkPathDirection dir;
Yuqian Li3154a532017-09-06 13:33:30 -04003666 REPORTER_ASSERT(reporter, p.isRect(nullptr, &closed, &dir));
3667 REPORTER_ASSERT(reporter, closed);
Mike Reed30bc5272019-11-22 18:34:02 +00003668 REPORTER_ASSERT(reporter, SkPathDirection::kCW == dir);
3669 test_rrect_is_convex(reporter, &p, SkPathDirection::kCW);
3670 p.addRRect(rr, SkPathDirection::kCW);
3671 p.addRRect(rr, SkPathDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04003672 REPORTER_ASSERT(reporter, !p.isConvex());
3673 p.reset();
Mike Reed30bc5272019-11-22 18:34:02 +00003674 p.addRRect(rr, SkPathDirection::kCCW);
3675 p.addRRect(rr, SkPathDirection::kCCW);
Yuqian Li3154a532017-09-06 13:33:30 -04003676 REPORTER_ASSERT(reporter, !p.isConvex());
3677 p.reset();
3678 SkRect emptyR = {10, 20, 10, 30};
3679 rr.setRectRadii(emptyR, radii);
3680 p.addRRect(rr);
Brian Salomon0a241ce2017-12-15 11:31:05 -05003681 // The round rect is "empty" in that it has no fill area. However,
3682 // the path isn't "empty" in that it should have verbs and points.
3683 REPORTER_ASSERT(reporter, !p.isEmpty());
3684 p.reset();
Yuqian Li3154a532017-09-06 13:33:30 -04003685 SkRect largeR = {0, 0, SK_ScalarMax, SK_ScalarMax};
3686 rr.setRectRadii(largeR, radii);
3687 p.addRRect(rr);
Mike Reed30bc5272019-11-22 18:34:02 +00003688 test_rrect_convexity_is_unknown(reporter, &p, SkPathDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04003689
3690 // we check for non-finites
3691 SkRect infR = {0, 0, SK_ScalarMax, SK_ScalarInfinity};
3692 rr.setRectRadii(infR, radii);
3693 REPORTER_ASSERT(reporter, rr.isEmpty());
3694
Brian Osman4be31e22019-08-20 15:35:26 -04003695 // We consider any path with very small (numerically unstable) edges to be concave.
Yuqian Li3154a532017-09-06 13:33:30 -04003696 SkRect tinyR = {0, 0, 1e-9f, 1e-9f};
3697 p.addRoundRect(tinyR, 5e-11f, 5e-11f);
Mike Reed30bc5272019-11-22 18:34:02 +00003698 test_rrect_convexity_is_unknown(reporter, &p, SkPathDirection::kCW);
Yuqian Li3154a532017-09-06 13:33:30 -04003699}
3700
3701static void test_arc(skiatest::Reporter* reporter) {
3702 SkPath p;
3703 SkRect emptyOval = {10, 20, 30, 20};
3704 REPORTER_ASSERT(reporter, emptyOval.isEmpty());
3705 p.addArc(emptyOval, 1, 2);
3706 REPORTER_ASSERT(reporter, p.isEmpty());
3707 p.reset();
3708 SkRect oval = {10, 20, 30, 40};
3709 p.addArc(oval, 1, 0);
3710 REPORTER_ASSERT(reporter, p.isEmpty());
3711 p.reset();
3712 SkPath cwOval;
3713 cwOval.addOval(oval);
3714 p.addArc(oval, 0, 360);
3715 REPORTER_ASSERT(reporter, p == cwOval);
3716 p.reset();
3717 SkPath ccwOval;
Mike Reed30bc5272019-11-22 18:34:02 +00003718 ccwOval.addOval(oval, SkPathDirection::kCCW);
Yuqian Li3154a532017-09-06 13:33:30 -04003719 p.addArc(oval, 0, -360);
3720 REPORTER_ASSERT(reporter, p == ccwOval);
3721 p.reset();
3722 p.addArc(oval, 1, 180);
Cary Clarkc9b7c722018-12-12 14:50:23 -05003723 // diagonal colinear points make arc convex
3724 // TODO: one way to keep it concave would be to introduce interpolated on curve points
3725 // between control points and computing the on curve point at scan conversion time
Mike Reed6052c0b2020-08-28 11:49:56 -04003726 REPORTER_ASSERT(reporter, p.isConvex());
Mike Reed85f51b22020-08-30 10:32:06 -04003727 REPORTER_ASSERT(reporter, SkPathPriv::ComputeFirstDirection(p) == SkPathFirstDirection::kCW);
Mike Reed6052c0b2020-08-28 11:49:56 -04003728 SkPathPriv::ForceComputeConvexity(p);
3729 REPORTER_ASSERT(reporter, p.isConvex());
Yuqian Li3154a532017-09-06 13:33:30 -04003730}
3731
3732static inline SkScalar oval_start_index_to_angle(unsigned start) {
3733 switch (start) {
3734 case 0:
3735 return 270.f;
3736 case 1:
3737 return 0.f;
3738 case 2:
3739 return 90.f;
3740 case 3:
3741 return 180.f;
3742 default:
3743 return -1.f;
3744 }
3745}
3746
3747static inline SkScalar canonical_start_angle(float angle) {
3748 while (angle < 0.f) {
3749 angle += 360.f;
3750 }
3751 while (angle >= 360.f) {
3752 angle -= 360.f;
3753 }
3754 return angle;
3755}
3756
3757static void check_oval_arc(skiatest::Reporter* reporter, SkScalar start, SkScalar sweep,
3758 const SkPath& path) {
3759 SkRect r = SkRect::MakeEmpty();
Mike Reed30bc5272019-11-22 18:34:02 +00003760 SkPathDirection d = SkPathDirection::kCCW;
Yuqian Li3154a532017-09-06 13:33:30 -04003761 unsigned s = ~0U;
Mike Reed0c3137c2018-02-20 13:57:05 -05003762 bool isOval = SkPathPriv::IsOval(path, &r, &d, &s);
Yuqian Li3154a532017-09-06 13:33:30 -04003763 REPORTER_ASSERT(reporter, isOval);
3764 SkPath recreatedPath;
3765 recreatedPath.addOval(r, d, s);
3766 REPORTER_ASSERT(reporter, path == recreatedPath);
3767 REPORTER_ASSERT(reporter, oval_start_index_to_angle(s) == canonical_start_angle(start));
Mike Reed30bc5272019-11-22 18:34:02 +00003768 REPORTER_ASSERT(reporter, (SkPathDirection::kCW == d) == (sweep > 0.f));
Yuqian Li3154a532017-09-06 13:33:30 -04003769}
3770
3771static void test_arc_ovals(skiatest::Reporter* reporter) {
3772 SkRect oval = SkRect::MakeWH(10, 20);
3773 for (SkScalar sweep : {-720.f, -540.f, -360.f, 360.f, 432.f, 720.f}) {
3774 for (SkScalar start = -360.f; start <= 360.f; start += 1.f) {
3775 SkPath path;
3776 path.addArc(oval, start, sweep);
3777 // SkPath's interfaces for inserting and extracting ovals only allow contours
3778 // to start at multiples of 90 degrees.
3779 if (std::fmod(start, 90.f) == 0) {
3780 check_oval_arc(reporter, start, sweep, path);
3781 } else {
3782 REPORTER_ASSERT(reporter, !path.isOval(nullptr));
3783 }
3784 }
3785 // Test start angles that are nearly at valid oval start angles.
3786 for (float start : {-180.f, -90.f, 90.f, 180.f}) {
3787 for (float delta : {-SK_ScalarNearlyZero, SK_ScalarNearlyZero}) {
3788 SkPath path;
3789 path.addArc(oval, start + delta, sweep);
3790 check_oval_arc(reporter, start, sweep, path);
3791 }
3792 }
3793 }
3794}
3795
Chris Daltonde500372020-05-05 15:06:30 -06003796static void check_move(skiatest::Reporter* reporter, SkPathPriv::RangeIter* iter,
Yuqian Li3154a532017-09-06 13:33:30 -04003797 SkScalar x0, SkScalar y0) {
Chris Daltonde500372020-05-05 15:06:30 -06003798 auto [v, pts, w] = *(*iter)++;
3799 REPORTER_ASSERT(reporter, v == SkPathVerb::kMove);
Yuqian Li3154a532017-09-06 13:33:30 -04003800 REPORTER_ASSERT(reporter, pts[0].fX == x0);
3801 REPORTER_ASSERT(reporter, pts[0].fY == y0);
3802}
3803
Chris Daltonde500372020-05-05 15:06:30 -06003804static void check_line(skiatest::Reporter* reporter, SkPathPriv::RangeIter* iter,
Yuqian Li3154a532017-09-06 13:33:30 -04003805 SkScalar x1, SkScalar y1) {
Chris Daltonde500372020-05-05 15:06:30 -06003806 auto [v, pts, w] = *(*iter)++;
3807 REPORTER_ASSERT(reporter, v == SkPathVerb::kLine);
Yuqian Li3154a532017-09-06 13:33:30 -04003808 REPORTER_ASSERT(reporter, pts[1].fX == x1);
3809 REPORTER_ASSERT(reporter, pts[1].fY == y1);
3810}
3811
Chris Daltonde500372020-05-05 15:06:30 -06003812static void check_quad(skiatest::Reporter* reporter, SkPathPriv::RangeIter* iter,
Yuqian Li3154a532017-09-06 13:33:30 -04003813 SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
Chris Daltonde500372020-05-05 15:06:30 -06003814 auto [v, pts, w] = *(*iter)++;
3815 REPORTER_ASSERT(reporter, v == SkPathVerb::kQuad);
Yuqian Li3154a532017-09-06 13:33:30 -04003816 REPORTER_ASSERT(reporter, pts[1].fX == x1);
3817 REPORTER_ASSERT(reporter, pts[1].fY == y1);
3818 REPORTER_ASSERT(reporter, pts[2].fX == x2);
3819 REPORTER_ASSERT(reporter, pts[2].fY == y2);
3820}
3821
Chris Daltonde500372020-05-05 15:06:30 -06003822static void check_done(skiatest::Reporter* reporter, SkPath* p, SkPathPriv::RangeIter* iter) {
3823 REPORTER_ASSERT(reporter, *iter == SkPathPriv::Iterate(*p).end());
Yuqian Li3154a532017-09-06 13:33:30 -04003824}
3825
Chris Daltonde500372020-05-05 15:06:30 -06003826static void check_done_and_reset(skiatest::Reporter* reporter, SkPath* p,
3827 SkPathPriv::RangeIter* iter) {
Yuqian Li3154a532017-09-06 13:33:30 -04003828 check_done(reporter, p, iter);
3829 p->reset();
3830}
3831
3832static void check_path_is_move_and_reset(skiatest::Reporter* reporter, SkPath* p,
3833 SkScalar x0, SkScalar y0) {
Chris Daltonde500372020-05-05 15:06:30 -06003834 SkPathPriv::RangeIter iter = SkPathPriv::Iterate(*p).begin();
Yuqian Li3154a532017-09-06 13:33:30 -04003835 check_move(reporter, &iter, x0, y0);
3836 check_done_and_reset(reporter, p, &iter);
3837}
3838
3839static void check_path_is_line_and_reset(skiatest::Reporter* reporter, SkPath* p,
3840 SkScalar x1, SkScalar y1) {
Chris Daltonde500372020-05-05 15:06:30 -06003841 SkPathPriv::RangeIter iter = SkPathPriv::Iterate(*p).begin();
Yuqian Li3154a532017-09-06 13:33:30 -04003842 check_move(reporter, &iter, 0, 0);
3843 check_line(reporter, &iter, x1, y1);
3844 check_done_and_reset(reporter, p, &iter);
3845}
3846
3847static void check_path_is_line(skiatest::Reporter* reporter, SkPath* p,
3848 SkScalar x1, SkScalar y1) {
Chris Daltonde500372020-05-05 15:06:30 -06003849 SkPathPriv::RangeIter iter = SkPathPriv::Iterate(*p).begin();
Yuqian Li3154a532017-09-06 13:33:30 -04003850 check_move(reporter, &iter, 0, 0);
3851 check_line(reporter, &iter, x1, y1);
3852 check_done(reporter, p, &iter);
3853}
3854
3855static void check_path_is_line_pair_and_reset(skiatest::Reporter* reporter, SkPath* p,
3856 SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
Chris Daltonde500372020-05-05 15:06:30 -06003857 SkPathPriv::RangeIter iter = SkPathPriv::Iterate(*p).begin();
Yuqian Li3154a532017-09-06 13:33:30 -04003858 check_move(reporter, &iter, 0, 0);
3859 check_line(reporter, &iter, x1, y1);
3860 check_line(reporter, &iter, x2, y2);
3861 check_done_and_reset(reporter, p, &iter);
3862}
3863
3864static void check_path_is_quad_and_reset(skiatest::Reporter* reporter, SkPath* p,
3865 SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2) {
Chris Daltonde500372020-05-05 15:06:30 -06003866 SkPathPriv::RangeIter iter = SkPathPriv::Iterate(*p).begin();
Yuqian Li3154a532017-09-06 13:33:30 -04003867 check_move(reporter, &iter, 0, 0);
3868 check_quad(reporter, &iter, x1, y1, x2, y2);
3869 check_done_and_reset(reporter, p, &iter);
3870}
3871
3872static bool nearly_equal(const SkRect& a, const SkRect& b) {
3873 return SkScalarNearlyEqual(a.fLeft, b.fLeft) &&
3874 SkScalarNearlyEqual(a.fTop, b.fTop) &&
3875 SkScalarNearlyEqual(a.fRight, b.fRight) &&
3876 SkScalarNearlyEqual(a.fBottom, b.fBottom);
3877}
3878
3879static void test_arcTo(skiatest::Reporter* reporter) {
3880 SkPath p;
3881 p.arcTo(0, 0, 1, 2, 1);
3882 check_path_is_line_and_reset(reporter, &p, 0, 0);
3883 p.arcTo(1, 2, 1, 2, 1);
3884 check_path_is_line_and_reset(reporter, &p, 1, 2);
3885 p.arcTo(1, 2, 3, 4, 0);
3886 check_path_is_line_and_reset(reporter, &p, 1, 2);
3887 p.arcTo(1, 2, 0, 0, 1);
3888 check_path_is_line_and_reset(reporter, &p, 1, 2);
3889 p.arcTo(1, 0, 1, 1, 1);
3890 SkPoint pt;
3891 REPORTER_ASSERT(reporter, p.getLastPt(&pt) && pt.fX == 1 && pt.fY == 1);
3892 p.reset();
3893 p.arcTo(1, 0, 1, -1, 1);
3894 REPORTER_ASSERT(reporter, p.getLastPt(&pt) && pt.fX == 1 && pt.fY == -1);
3895 p.reset();
3896 SkRect oval = {1, 2, 3, 4};
3897 p.arcTo(oval, 0, 0, true);
3898 check_path_is_move_and_reset(reporter, &p, oval.fRight, oval.centerY());
3899 p.arcTo(oval, 0, 0, false);
3900 check_path_is_move_and_reset(reporter, &p, oval.fRight, oval.centerY());
3901 p.arcTo(oval, 360, 0, true);
3902 check_path_is_move_and_reset(reporter, &p, oval.fRight, oval.centerY());
3903 p.arcTo(oval, 360, 0, false);
3904 check_path_is_move_and_reset(reporter, &p, oval.fRight, oval.centerY());
3905
3906 for (float sweep = 359, delta = 0.5f; sweep != (float) (sweep + delta); ) {
3907 p.arcTo(oval, 0, sweep, false);
3908 REPORTER_ASSERT(reporter, nearly_equal(p.getBounds(), oval));
3909 sweep += delta;
3910 delta /= 2;
3911 }
3912 for (float sweep = 361, delta = 0.5f; sweep != (float) (sweep - delta);) {
3913 p.arcTo(oval, 0, sweep, false);
3914 REPORTER_ASSERT(reporter, nearly_equal(p.getBounds(), oval));
3915 sweep -= delta;
3916 delta /= 2;
3917 }
3918 SkRect noOvalWidth = {1, 2, 0, 3};
3919 p.reset();
3920 p.arcTo(noOvalWidth, 0, 360, false);
3921 REPORTER_ASSERT(reporter, p.isEmpty());
3922
3923 SkRect noOvalHeight = {1, 2, 3, 1};
3924 p.reset();
3925 p.arcTo(noOvalHeight, 0, 360, false);
3926 REPORTER_ASSERT(reporter, p.isEmpty());
Ian Prest05676f72020-02-13 10:46:56 -08003927
3928#ifndef SK_LEGACY_PATH_ARCTO_ENDPOINT
3929 // Inspired by http://code.google.com/p/chromium/issues/detail?id=1001768
3930 {
3931 p.reset();
3932 p.moveTo(216, 216);
3933 p.arcTo(216, 108, 0, SkPath::ArcSize::kLarge_ArcSize, SkPathDirection::kCW, 216, 0);
3934 p.arcTo(270, 135, 0, SkPath::ArcSize::kLarge_ArcSize, SkPathDirection::kCCW, 216, 216);
3935
3936 // The 'arcTo' call should end up exactly at the starting location.
3937 int n = p.countPoints();
3938 REPORTER_ASSERT(reporter, p.getPoint(0) == p.getPoint(n - 1));
3939 }
3940#endif
Yuqian Li3154a532017-09-06 13:33:30 -04003941}
3942
3943static void test_addPath(skiatest::Reporter* reporter) {
3944 SkPath p, q;
3945 p.lineTo(1, 2);
3946 q.moveTo(4, 4);
3947 q.lineTo(7, 8);
3948 q.conicTo(8, 7, 6, 5, 0.5f);
3949 q.quadTo(6, 7, 8, 6);
3950 q.cubicTo(5, 6, 7, 8, 7, 5);
3951 q.close();
3952 p.addPath(q, -4, -4);
3953 SkRect expected = {0, 0, 4, 4};
3954 REPORTER_ASSERT(reporter, p.getBounds() == expected);
3955 p.reset();
3956 p.reverseAddPath(q);
3957 SkRect reverseExpected = {4, 4, 8, 8};
3958 REPORTER_ASSERT(reporter, p.getBounds() == reverseExpected);
3959}
3960
3961static void test_addPathMode(skiatest::Reporter* reporter, bool explicitMoveTo, bool extend) {
3962 SkPath p, q;
3963 if (explicitMoveTo) {
3964 p.moveTo(1, 1);
3965 }
3966 p.lineTo(1, 2);
3967 if (explicitMoveTo) {
3968 q.moveTo(2, 1);
3969 }
3970 q.lineTo(2, 2);
3971 p.addPath(q, extend ? SkPath::kExtend_AddPathMode : SkPath::kAppend_AddPathMode);
3972 uint8_t verbs[4];
3973 int verbcount = p.getVerbs(verbs, 4);
3974 REPORTER_ASSERT(reporter, verbcount == 4);
3975 REPORTER_ASSERT(reporter, verbs[0] == SkPath::kMove_Verb);
3976 REPORTER_ASSERT(reporter, verbs[1] == SkPath::kLine_Verb);
3977 REPORTER_ASSERT(reporter, verbs[2] == (extend ? SkPath::kLine_Verb : SkPath::kMove_Verb));
3978 REPORTER_ASSERT(reporter, verbs[3] == SkPath::kLine_Verb);
3979}
3980
3981static void test_extendClosedPath(skiatest::Reporter* reporter) {
3982 SkPath p, q;
3983 p.moveTo(1, 1);
3984 p.lineTo(1, 2);
3985 p.lineTo(2, 2);
3986 p.close();
3987 q.moveTo(2, 1);
3988 q.lineTo(2, 3);
3989 p.addPath(q, SkPath::kExtend_AddPathMode);
3990 uint8_t verbs[7];
3991 int verbcount = p.getVerbs(verbs, 7);
3992 REPORTER_ASSERT(reporter, verbcount == 7);
3993 REPORTER_ASSERT(reporter, verbs[0] == SkPath::kMove_Verb);
3994 REPORTER_ASSERT(reporter, verbs[1] == SkPath::kLine_Verb);
3995 REPORTER_ASSERT(reporter, verbs[2] == SkPath::kLine_Verb);
3996 REPORTER_ASSERT(reporter, verbs[3] == SkPath::kClose_Verb);
3997 REPORTER_ASSERT(reporter, verbs[4] == SkPath::kMove_Verb);
3998 REPORTER_ASSERT(reporter, verbs[5] == SkPath::kLine_Verb);
3999 REPORTER_ASSERT(reporter, verbs[6] == SkPath::kLine_Verb);
4000
4001 SkPoint pt;
4002 REPORTER_ASSERT(reporter, p.getLastPt(&pt));
4003 REPORTER_ASSERT(reporter, pt == SkPoint::Make(2, 3));
4004 REPORTER_ASSERT(reporter, p.getPoint(3) == SkPoint::Make(1, 1));
4005}
4006
4007static void test_addEmptyPath(skiatest::Reporter* reporter, SkPath::AddPathMode mode) {
4008 SkPath p, q, r;
4009 // case 1: dst is empty
4010 p.moveTo(2, 1);
4011 p.lineTo(2, 3);
4012 q.addPath(p, mode);
4013 REPORTER_ASSERT(reporter, q == p);
4014 // case 2: src is empty
4015 p.addPath(r, mode);
4016 REPORTER_ASSERT(reporter, q == p);
4017 // case 3: src and dst are empty
4018 q.reset();
4019 q.addPath(r, mode);
4020 REPORTER_ASSERT(reporter, q.isEmpty());
4021}
4022
4023static void test_conicTo_special_case(skiatest::Reporter* reporter) {
4024 SkPath p;
4025 p.conicTo(1, 2, 3, 4, -1);
4026 check_path_is_line_and_reset(reporter, &p, 3, 4);
4027 p.conicTo(1, 2, 3, 4, SK_ScalarInfinity);
4028 check_path_is_line_pair_and_reset(reporter, &p, 1, 2, 3, 4);
4029 p.conicTo(1, 2, 3, 4, 1);
4030 check_path_is_quad_and_reset(reporter, &p, 1, 2, 3, 4);
4031}
4032
4033static void test_get_point(skiatest::Reporter* reporter) {
4034 SkPath p;
4035 SkPoint pt = p.getPoint(0);
4036 REPORTER_ASSERT(reporter, pt == SkPoint::Make(0, 0));
4037 REPORTER_ASSERT(reporter, !p.getLastPt(nullptr));
4038 REPORTER_ASSERT(reporter, !p.getLastPt(&pt) && pt == SkPoint::Make(0, 0));
4039 p.setLastPt(10, 10);
4040 pt = p.getPoint(0);
4041 REPORTER_ASSERT(reporter, pt == SkPoint::Make(10, 10));
4042 REPORTER_ASSERT(reporter, p.getLastPt(nullptr));
4043 p.rMoveTo(10, 10);
4044 REPORTER_ASSERT(reporter, p.getLastPt(&pt) && pt == SkPoint::Make(20, 20));
4045}
4046
4047static void test_contains(skiatest::Reporter* reporter) {
4048 SkPath p;
4049 p.moveTo(SkBits2Float(0xe085e7b1), SkBits2Float(0x5f512c00)); // -7.7191e+19f, 1.50724e+19f
4050 p.conicTo(SkBits2Float(0xdfdaa221), SkBits2Float(0x5eaac338), SkBits2Float(0x60342f13), SkBits2Float(0xdf0cbb58), SkBits2Float(0x3f3504f3)); // -3.15084e+19f, 6.15237e+18f, 5.19345e+19f, -1.01408e+19f, 0.707107f
4051 p.conicTo(SkBits2Float(0x60ead799), SkBits2Float(0xdfb76c24), SkBits2Float(0x609b9872), SkBits2Float(0xdf730de8), SkBits2Float(0x3f3504f4)); // 1.35377e+20f, -2.6434e+19f, 8.96947e+19f, -1.75139e+19f, 0.707107f
4052 p.lineTo(SkBits2Float(0x609b9872), SkBits2Float(0xdf730de8)); // 8.96947e+19f, -1.75139e+19f
4053 p.conicTo(SkBits2Float(0x6018b296), SkBits2Float(0xdeee870d), SkBits2Float(0xe008cd8e), SkBits2Float(0x5ed5b2db), SkBits2Float(0x3f3504f3)); // 4.40121e+19f, -8.59386e+18f, -3.94308e+19f, 7.69931e+18f, 0.707107f
4054 p.conicTo(SkBits2Float(0xe0d526d9), SkBits2Float(0x5fa67b31), SkBits2Float(0xe085e7b2), SkBits2Float(0x5f512c01), SkBits2Float(0x3f3504f3)); // -1.22874e+20f, 2.39925e+19f, -7.7191e+19f, 1.50724e+19f, 0.707107f
4055 // this may return true or false, depending on the platform's numerics, but it should not crash
4056 (void) p.contains(-77.2027664f, 15.3066053f);
4057
4058 p.reset();
Mike Reed7d34dc72019-11-26 12:17:17 -05004059 p.setFillType(SkPathFillType::kInverseWinding);
Yuqian Li3154a532017-09-06 13:33:30 -04004060 REPORTER_ASSERT(reporter, p.contains(0, 0));
Mike Reed7d34dc72019-11-26 12:17:17 -05004061 p.setFillType(SkPathFillType::kWinding);
Yuqian Li3154a532017-09-06 13:33:30 -04004062 REPORTER_ASSERT(reporter, !p.contains(0, 0));
4063 p.moveTo(4, 4);
4064 p.lineTo(6, 8);
4065 p.lineTo(8, 4);
4066 // test on edge
4067 REPORTER_ASSERT(reporter, p.contains(6, 4));
4068 REPORTER_ASSERT(reporter, p.contains(5, 6));
4069 REPORTER_ASSERT(reporter, p.contains(7, 6));
4070 // test quick reject
4071 REPORTER_ASSERT(reporter, !p.contains(4, 0));
4072 REPORTER_ASSERT(reporter, !p.contains(0, 4));
4073 REPORTER_ASSERT(reporter, !p.contains(4, 10));
4074 REPORTER_ASSERT(reporter, !p.contains(10, 4));
4075 // test various crossings in x
4076 REPORTER_ASSERT(reporter, !p.contains(5, 7));
4077 REPORTER_ASSERT(reporter, p.contains(6, 7));
4078 REPORTER_ASSERT(reporter, !p.contains(7, 7));
4079 p.reset();
4080 p.moveTo(4, 4);
4081 p.lineTo(8, 6);
4082 p.lineTo(4, 8);
4083 // test on edge
4084 REPORTER_ASSERT(reporter, p.contains(4, 6));
4085 REPORTER_ASSERT(reporter, p.contains(6, 5));
4086 REPORTER_ASSERT(reporter, p.contains(6, 7));
4087 // test various crossings in y
4088 REPORTER_ASSERT(reporter, !p.contains(7, 5));
4089 REPORTER_ASSERT(reporter, p.contains(7, 6));
4090 REPORTER_ASSERT(reporter, !p.contains(7, 7));
4091 p.reset();
4092 p.moveTo(4, 4);
4093 p.lineTo(8, 4);
4094 p.lineTo(8, 8);
4095 p.lineTo(4, 8);
4096 // test on vertices
4097 REPORTER_ASSERT(reporter, p.contains(4, 4));
4098 REPORTER_ASSERT(reporter, p.contains(8, 4));
4099 REPORTER_ASSERT(reporter, p.contains(8, 8));
4100 REPORTER_ASSERT(reporter, p.contains(4, 8));
4101 p.reset();
4102 p.moveTo(4, 4);
4103 p.lineTo(6, 8);
4104 p.lineTo(2, 8);
4105 // test on edge
4106 REPORTER_ASSERT(reporter, p.contains(5, 6));
4107 REPORTER_ASSERT(reporter, p.contains(4, 8));
4108 REPORTER_ASSERT(reporter, p.contains(3, 6));
4109 p.reset();
4110 p.moveTo(4, 4);
4111 p.lineTo(0, 6);
4112 p.lineTo(4, 8);
4113 // test on edge
4114 REPORTER_ASSERT(reporter, p.contains(2, 5));
4115 REPORTER_ASSERT(reporter, p.contains(2, 7));
4116 REPORTER_ASSERT(reporter, p.contains(4, 6));
4117 // test canceling coincident edge (a smaller triangle is coincident with a larger one)
4118 p.reset();
4119 p.moveTo(4, 0);
4120 p.lineTo(6, 4);
4121 p.lineTo(2, 4);
4122 p.moveTo(4, 0);
4123 p.lineTo(0, 8);
4124 p.lineTo(8, 8);
4125 REPORTER_ASSERT(reporter, !p.contains(1, 2));
4126 REPORTER_ASSERT(reporter, !p.contains(3, 2));
4127 REPORTER_ASSERT(reporter, !p.contains(4, 0));
4128 REPORTER_ASSERT(reporter, p.contains(4, 4));
4129
4130 // test quads
4131 p.reset();
4132 p.moveTo(4, 4);
4133 p.quadTo(6, 6, 8, 8);
4134 p.quadTo(6, 8, 4, 8);
4135 p.quadTo(4, 6, 4, 4);
4136 REPORTER_ASSERT(reporter, p.contains(5, 6));
4137 REPORTER_ASSERT(reporter, !p.contains(6, 5));
4138 // test quad edge
4139 REPORTER_ASSERT(reporter, p.contains(5, 5));
4140 REPORTER_ASSERT(reporter, p.contains(5, 8));
4141 REPORTER_ASSERT(reporter, p.contains(4, 5));
4142 // test quad endpoints
4143 REPORTER_ASSERT(reporter, p.contains(4, 4));
4144 REPORTER_ASSERT(reporter, p.contains(8, 8));
4145 REPORTER_ASSERT(reporter, p.contains(4, 8));
4146
4147 p.reset();
4148 const SkPoint qPts[] = {{6, 6}, {8, 8}, {6, 8}, {4, 8}, {4, 6}, {4, 4}, {6, 6}};
4149 p.moveTo(qPts[0]);
4150 for (int index = 1; index < (int) SK_ARRAY_COUNT(qPts); index += 2) {
4151 p.quadTo(qPts[index], qPts[index + 1]);
4152 }
4153 REPORTER_ASSERT(reporter, p.contains(5, 6));
4154 REPORTER_ASSERT(reporter, !p.contains(6, 5));
4155 // test quad edge
4156 SkPoint halfway;
4157 for (int index = 0; index < (int) SK_ARRAY_COUNT(qPts) - 2; index += 2) {
4158 SkEvalQuadAt(&qPts[index], 0.5f, &halfway, nullptr);
4159 REPORTER_ASSERT(reporter, p.contains(halfway.fX, halfway.fY));
4160 }
4161
4162 // test conics
4163 p.reset();
4164 const SkPoint kPts[] = {{4, 4}, {6, 6}, {8, 8}, {6, 8}, {4, 8}, {4, 6}, {4, 4}};
4165 p.moveTo(kPts[0]);
4166 for (int index = 1; index < (int) SK_ARRAY_COUNT(kPts); index += 2) {
4167 p.conicTo(kPts[index], kPts[index + 1], 0.5f);
4168 }
4169 REPORTER_ASSERT(reporter, p.contains(5, 6));
4170 REPORTER_ASSERT(reporter, !p.contains(6, 5));
4171 // test conic edge
4172 for (int index = 0; index < (int) SK_ARRAY_COUNT(kPts) - 2; index += 2) {
4173 SkConic conic(&kPts[index], 0.5f);
4174 halfway = conic.evalAt(0.5f);
4175 REPORTER_ASSERT(reporter, p.contains(halfway.fX, halfway.fY));
4176 }
4177 // test conic end points
4178 REPORTER_ASSERT(reporter, p.contains(4, 4));
4179 REPORTER_ASSERT(reporter, p.contains(8, 8));
4180 REPORTER_ASSERT(reporter, p.contains(4, 8));
4181
4182 // test cubics
4183 SkPoint pts[] = {{5, 4}, {6, 5}, {7, 6}, {6, 6}, {4, 6}, {5, 7}, {5, 5}, {5, 4}, {6, 5}, {7, 6}};
4184 for (int i = 0; i < 3; ++i) {
4185 p.reset();
Mike Reed7d34dc72019-11-26 12:17:17 -05004186 p.setFillType(SkPathFillType::kEvenOdd);
Yuqian Li3154a532017-09-06 13:33:30 -04004187 p.moveTo(pts[i].fX, pts[i].fY);
4188 p.cubicTo(pts[i + 1].fX, pts[i + 1].fY, pts[i + 2].fX, pts[i + 2].fY, pts[i + 3].fX, pts[i + 3].fY);
4189 p.cubicTo(pts[i + 4].fX, pts[i + 4].fY, pts[i + 5].fX, pts[i + 5].fY, pts[i + 6].fX, pts[i + 6].fY);
4190 p.close();
4191 REPORTER_ASSERT(reporter, p.contains(5.5f, 5.5f));
4192 REPORTER_ASSERT(reporter, !p.contains(4.5f, 5.5f));
4193 // test cubic edge
4194 SkEvalCubicAt(&pts[i], 0.5f, &halfway, nullptr, nullptr);
4195 REPORTER_ASSERT(reporter, p.contains(halfway.fX, halfway.fY));
4196 SkEvalCubicAt(&pts[i + 3], 0.5f, &halfway, nullptr, nullptr);
4197 REPORTER_ASSERT(reporter, p.contains(halfway.fX, halfway.fY));
4198 // test cubic end points
4199 REPORTER_ASSERT(reporter, p.contains(pts[i].fX, pts[i].fY));
4200 REPORTER_ASSERT(reporter, p.contains(pts[i + 3].fX, pts[i + 3].fY));
4201 REPORTER_ASSERT(reporter, p.contains(pts[i + 6].fX, pts[i + 6].fY));
4202 }
4203}
4204
4205class PathRefTest_Private {
4206public:
Florin Malita0e0f1a72018-09-11 16:16:19 -04004207 static size_t GetFreeSpace(const SkPathRef& ref) {
Mike Reed8fda88e2019-09-05 14:14:38 -04004208 return (ref.fPoints.reserved() - ref.fPoints.count()) * sizeof(SkPoint)
4209 + (ref.fVerbs.reserved() - ref.fVerbs.count()) * sizeof(uint8_t);
Florin Malita0e0f1a72018-09-11 16:16:19 -04004210 }
4211
Yuqian Li3154a532017-09-06 13:33:30 -04004212 static void TestPathRef(skiatest::Reporter* reporter) {
4213 static const int kRepeatCnt = 10;
4214
4215 sk_sp<SkPathRef> pathRef(new SkPathRef);
4216
4217 SkPathRef::Editor ed(&pathRef);
4218
4219 {
4220 ed.growForRepeatedVerb(SkPath::kMove_Verb, kRepeatCnt);
4221 REPORTER_ASSERT(reporter, kRepeatCnt == pathRef->countVerbs());
4222 REPORTER_ASSERT(reporter, kRepeatCnt == pathRef->countPoints());
4223 REPORTER_ASSERT(reporter, 0 == pathRef->getSegmentMasks());
4224 for (int i = 0; i < kRepeatCnt; ++i) {
4225 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == pathRef->atVerb(i));
4226 }
4227 ed.resetToSize(0, 0, 0);
4228 }
4229
4230 {
4231 ed.growForRepeatedVerb(SkPath::kLine_Verb, kRepeatCnt);
4232 REPORTER_ASSERT(reporter, kRepeatCnt == pathRef->countVerbs());
4233 REPORTER_ASSERT(reporter, kRepeatCnt == pathRef->countPoints());
4234 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == pathRef->getSegmentMasks());
4235 for (int i = 0; i < kRepeatCnt; ++i) {
4236 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == pathRef->atVerb(i));
4237 }
4238 ed.resetToSize(0, 0, 0);
4239 }
4240
4241 {
4242 ed.growForRepeatedVerb(SkPath::kQuad_Verb, kRepeatCnt);
4243 REPORTER_ASSERT(reporter, kRepeatCnt == pathRef->countVerbs());
4244 REPORTER_ASSERT(reporter, 2*kRepeatCnt == pathRef->countPoints());
4245 REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == pathRef->getSegmentMasks());
4246 for (int i = 0; i < kRepeatCnt; ++i) {
4247 REPORTER_ASSERT(reporter, SkPath::kQuad_Verb == pathRef->atVerb(i));
4248 }
4249 ed.resetToSize(0, 0, 0);
4250 }
4251
4252 {
4253 SkScalar* weights = nullptr;
4254 ed.growForRepeatedVerb(SkPath::kConic_Verb, kRepeatCnt, &weights);
4255 REPORTER_ASSERT(reporter, kRepeatCnt == pathRef->countVerbs());
4256 REPORTER_ASSERT(reporter, 2*kRepeatCnt == pathRef->countPoints());
4257 REPORTER_ASSERT(reporter, kRepeatCnt == pathRef->countWeights());
4258 REPORTER_ASSERT(reporter, SkPath::kConic_SegmentMask == pathRef->getSegmentMasks());
4259 REPORTER_ASSERT(reporter, weights);
4260 for (int i = 0; i < kRepeatCnt; ++i) {
4261 REPORTER_ASSERT(reporter, SkPath::kConic_Verb == pathRef->atVerb(i));
4262 }
4263 ed.resetToSize(0, 0, 0);
4264 }
4265
4266 {
4267 ed.growForRepeatedVerb(SkPath::kCubic_Verb, kRepeatCnt);
4268 REPORTER_ASSERT(reporter, kRepeatCnt == pathRef->countVerbs());
4269 REPORTER_ASSERT(reporter, 3*kRepeatCnt == pathRef->countPoints());
4270 REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == pathRef->getSegmentMasks());
4271 for (int i = 0; i < kRepeatCnt; ++i) {
4272 REPORTER_ASSERT(reporter, SkPath::kCubic_Verb == pathRef->atVerb(i));
4273 }
4274 ed.resetToSize(0, 0, 0);
4275 }
4276 }
4277};
4278
4279static void test_operatorEqual(skiatest::Reporter* reporter) {
4280 SkPath a;
4281 SkPath b;
4282 REPORTER_ASSERT(reporter, a == a);
4283 REPORTER_ASSERT(reporter, a == b);
Mike Reed7d34dc72019-11-26 12:17:17 -05004284 a.setFillType(SkPathFillType::kInverseWinding);
Yuqian Li3154a532017-09-06 13:33:30 -04004285 REPORTER_ASSERT(reporter, a != b);
4286 a.reset();
4287 REPORTER_ASSERT(reporter, a == b);
4288 a.lineTo(1, 1);
4289 REPORTER_ASSERT(reporter, a != b);
4290 a.reset();
4291 REPORTER_ASSERT(reporter, a == b);
4292 a.lineTo(1, 1);
4293 b.lineTo(1, 2);
4294 REPORTER_ASSERT(reporter, a != b);
4295 a.reset();
4296 a.lineTo(1, 2);
4297 REPORTER_ASSERT(reporter, a == b);
4298}
4299
4300static void compare_dump(skiatest::Reporter* reporter, const SkPath& path, bool force,
4301 bool dumpAsHex, const char* str) {
4302 SkDynamicMemoryWStream wStream;
4303 path.dump(&wStream, force, dumpAsHex);
4304 sk_sp<SkData> data = wStream.detachAsData();
4305 REPORTER_ASSERT(reporter, data->size() == strlen(str));
4306 if (strlen(str) > 0) {
4307 REPORTER_ASSERT(reporter, !memcmp(data->data(), str, strlen(str)));
4308 } else {
4309 REPORTER_ASSERT(reporter, data->data() == nullptr || !memcmp(data->data(), str, strlen(str)));
4310 }
4311}
4312
4313static void test_dump(skiatest::Reporter* reporter) {
4314 SkPath p;
Mike Reed7d34dc72019-11-26 12:17:17 -05004315 compare_dump(reporter, p, false, false, "path.setFillType(SkPathFillType::kWinding);\n");
4316 compare_dump(reporter, p, true, false, "path.setFillType(SkPathFillType::kWinding);\n");
Yuqian Li3154a532017-09-06 13:33:30 -04004317 p.moveTo(1, 2);
4318 p.lineTo(3, 4);
Mike Reed7d34dc72019-11-26 12:17:17 -05004319 compare_dump(reporter, p, false, false, "path.setFillType(SkPathFillType::kWinding);\n"
Yuqian Li3154a532017-09-06 13:33:30 -04004320 "path.moveTo(1, 2);\n"
4321 "path.lineTo(3, 4);\n");
Mike Reed7d34dc72019-11-26 12:17:17 -05004322 compare_dump(reporter, p, true, false, "path.setFillType(SkPathFillType::kWinding);\n"
Yuqian Li3154a532017-09-06 13:33:30 -04004323 "path.moveTo(1, 2);\n"
4324 "path.lineTo(3, 4);\n"
4325 "path.lineTo(1, 2);\n"
4326 "path.close();\n");
4327 p.reset();
Mike Reed7d34dc72019-11-26 12:17:17 -05004328 p.setFillType(SkPathFillType::kEvenOdd);
Yuqian Li3154a532017-09-06 13:33:30 -04004329 p.moveTo(1, 2);
4330 p.quadTo(3, 4, 5, 6);
Mike Reed7d34dc72019-11-26 12:17:17 -05004331 compare_dump(reporter, p, false, false, "path.setFillType(SkPathFillType::kEvenOdd);\n"
Yuqian Li3154a532017-09-06 13:33:30 -04004332 "path.moveTo(1, 2);\n"
4333 "path.quadTo(3, 4, 5, 6);\n");
4334 p.reset();
Mike Reed7d34dc72019-11-26 12:17:17 -05004335 p.setFillType(SkPathFillType::kInverseWinding);
Yuqian Li3154a532017-09-06 13:33:30 -04004336 p.moveTo(1, 2);
4337 p.conicTo(3, 4, 5, 6, 0.5f);
Mike Reed7d34dc72019-11-26 12:17:17 -05004338 compare_dump(reporter, p, false, false, "path.setFillType(SkPathFillType::kInverseWinding);\n"
Yuqian Li3154a532017-09-06 13:33:30 -04004339 "path.moveTo(1, 2);\n"
4340 "path.conicTo(3, 4, 5, 6, 0.5f);\n");
4341 p.reset();
Mike Reed7d34dc72019-11-26 12:17:17 -05004342 p.setFillType(SkPathFillType::kInverseEvenOdd);
Yuqian Li3154a532017-09-06 13:33:30 -04004343 p.moveTo(1, 2);
4344 p.cubicTo(3, 4, 5, 6, 7, 8);
Mike Reed7d34dc72019-11-26 12:17:17 -05004345 compare_dump(reporter, p, false, false, "path.setFillType(SkPathFillType::kInverseEvenOdd);\n"
Yuqian Li3154a532017-09-06 13:33:30 -04004346 "path.moveTo(1, 2);\n"
4347 "path.cubicTo(3, 4, 5, 6, 7, 8);\n");
4348 p.reset();
Mike Reed7d34dc72019-11-26 12:17:17 -05004349 p.setFillType(SkPathFillType::kWinding);
Yuqian Li3154a532017-09-06 13:33:30 -04004350 p.moveTo(1, 2);
4351 p.lineTo(3, 4);
4352 compare_dump(reporter, p, false, true,
Mike Reed7d34dc72019-11-26 12:17:17 -05004353 "path.setFillType(SkPathFillType::kWinding);\n"
Yuqian Li3154a532017-09-06 13:33:30 -04004354 "path.moveTo(SkBits2Float(0x3f800000), SkBits2Float(0x40000000)); // 1, 2\n"
4355 "path.lineTo(SkBits2Float(0x40400000), SkBits2Float(0x40800000)); // 3, 4\n");
4356 p.reset();
4357 p.moveTo(SkBits2Float(0x3f800000), SkBits2Float(0x40000000));
4358 p.lineTo(SkBits2Float(0x40400000), SkBits2Float(0x40800000));
Mike Reed7d34dc72019-11-26 12:17:17 -05004359 compare_dump(reporter, p, false, false, "path.setFillType(SkPathFillType::kWinding);\n"
Yuqian Li3154a532017-09-06 13:33:30 -04004360 "path.moveTo(1, 2);\n"
4361 "path.lineTo(3, 4);\n");
4362}
4363
4364namespace {
4365
Brian Salomon99a813c2020-03-02 12:50:47 -05004366class ChangeListener : public SkIDChangeListener {
Yuqian Li3154a532017-09-06 13:33:30 -04004367public:
4368 ChangeListener(bool *changed) : fChanged(changed) { *fChanged = false; }
4369 ~ChangeListener() override {}
Brian Salomon99a813c2020-03-02 12:50:47 -05004370 void changed() override { *fChanged = true; }
4371
Yuqian Li3154a532017-09-06 13:33:30 -04004372private:
4373 bool* fChanged;
4374};
4375
John Stilesa6841be2020-08-06 14:11:56 -04004376} // namespace
Yuqian Li3154a532017-09-06 13:33:30 -04004377
4378class PathTest_Private {
4379public:
Florin Malita0e0f1a72018-09-11 16:16:19 -04004380 static size_t GetFreeSpace(const SkPath& path) {
4381 return PathRefTest_Private::GetFreeSpace(*path.fPathRef);
4382 }
4383
Yuqian Li3154a532017-09-06 13:33:30 -04004384 static void TestPathTo(skiatest::Reporter* reporter) {
4385 SkPath p, q;
4386 p.lineTo(4, 4);
4387 p.reversePathTo(q);
4388 check_path_is_line(reporter, &p, 4, 4);
4389 q.moveTo(-4, -4);
4390 p.reversePathTo(q);
4391 check_path_is_line(reporter, &p, 4, 4);
4392 q.lineTo(7, 8);
4393 q.conicTo(8, 7, 6, 5, 0.5f);
4394 q.quadTo(6, 7, 8, 6);
4395 q.cubicTo(5, 6, 7, 8, 7, 5);
4396 q.close();
4397 p.reversePathTo(q);
4398 SkRect reverseExpected = {-4, -4, 8, 8};
4399 REPORTER_ASSERT(reporter, p.getBounds() == reverseExpected);
4400 }
4401
4402 static void TestPathrefListeners(skiatest::Reporter* reporter) {
4403 SkPath p;
4404
4405 bool changed = false;
4406 p.moveTo(0, 0);
4407
4408 // Check that listener is notified on moveTo().
4409
Chris Daltonafa11582018-06-08 12:00:44 -06004410 SkPathPriv::AddGenIDChangeListener(p, sk_make_sp<ChangeListener>(&changed));
Yuqian Li3154a532017-09-06 13:33:30 -04004411 REPORTER_ASSERT(reporter, !changed);
4412 p.moveTo(10, 0);
4413 REPORTER_ASSERT(reporter, changed);
4414
4415 // Check that listener is notified on lineTo().
Chris Daltonafa11582018-06-08 12:00:44 -06004416 SkPathPriv::AddGenIDChangeListener(p, sk_make_sp<ChangeListener>(&changed));
Yuqian Li3154a532017-09-06 13:33:30 -04004417 REPORTER_ASSERT(reporter, !changed);
4418 p.lineTo(20, 0);
4419 REPORTER_ASSERT(reporter, changed);
4420
4421 // Check that listener is notified on reset().
Chris Daltonafa11582018-06-08 12:00:44 -06004422 SkPathPriv::AddGenIDChangeListener(p, sk_make_sp<ChangeListener>(&changed));
Yuqian Li3154a532017-09-06 13:33:30 -04004423 REPORTER_ASSERT(reporter, !changed);
4424 p.reset();
4425 REPORTER_ASSERT(reporter, changed);
4426
4427 p.moveTo(0, 0);
4428
4429 // Check that listener is notified on rewind().
Chris Daltonafa11582018-06-08 12:00:44 -06004430 SkPathPriv::AddGenIDChangeListener(p, sk_make_sp<ChangeListener>(&changed));
Yuqian Li3154a532017-09-06 13:33:30 -04004431 REPORTER_ASSERT(reporter, !changed);
4432 p.rewind();
4433 REPORTER_ASSERT(reporter, changed);
4434
Brian Osmandbfcd922019-03-07 16:50:30 -05004435 // Check that listener is notified on transform().
4436 {
4437 SkPath q;
4438 q.moveTo(10, 10);
4439 SkPathPriv::AddGenIDChangeListener(q, sk_make_sp<ChangeListener>(&changed));
4440 REPORTER_ASSERT(reporter, !changed);
4441 SkMatrix matrix;
4442 matrix.setScale(2, 2);
4443 p.transform(matrix, &q);
4444 REPORTER_ASSERT(reporter, changed);
4445 }
4446
Yuqian Li3154a532017-09-06 13:33:30 -04004447 // Check that listener is notified when pathref is deleted.
4448 {
4449 SkPath q;
4450 q.moveTo(10, 10);
Chris Daltonafa11582018-06-08 12:00:44 -06004451 SkPathPriv::AddGenIDChangeListener(q, sk_make_sp<ChangeListener>(&changed));
Yuqian Li3154a532017-09-06 13:33:30 -04004452 REPORTER_ASSERT(reporter, !changed);
4453 }
4454 // q went out of scope.
4455 REPORTER_ASSERT(reporter, changed);
4456 }
4457};
4458
4459static void test_crbug_629455(skiatest::Reporter* reporter) {
4460 SkPath path;
4461 path.moveTo(0, 0);
4462 path.cubicTo(SkBits2Float(0xcdcdcd00), SkBits2Float(0xcdcdcdcd),
4463 SkBits2Float(0xcdcdcdcd), SkBits2Float(0xcdcdcdcd),
4464 SkBits2Float(0x423fcdcd), SkBits2Float(0x40ed9341));
4465// AKA: cubicTo(-4.31596e+08f, -4.31602e+08f, -4.31602e+08f, -4.31602e+08f, 47.951f, 7.42423f);
4466 path.lineTo(0, 0);
Yuqian Lif13beef2017-09-14 17:15:04 -04004467 test_draw_AA_path(100, 100, path);
Yuqian Li3154a532017-09-06 13:33:30 -04004468}
4469
4470static void test_fuzz_crbug_662952(skiatest::Reporter* reporter) {
4471 SkPath path;
4472 path.moveTo(SkBits2Float(0x4109999a), SkBits2Float(0x411c0000)); // 8.6f, 9.75f
4473 path.lineTo(SkBits2Float(0x410a6666), SkBits2Float(0x411c0000)); // 8.65f, 9.75f
4474 path.lineTo(SkBits2Float(0x410a6666), SkBits2Float(0x411e6666)); // 8.65f, 9.9f
4475 path.lineTo(SkBits2Float(0x4109999a), SkBits2Float(0x411e6666)); // 8.6f, 9.9f
4476 path.lineTo(SkBits2Float(0x4109999a), SkBits2Float(0x411c0000)); // 8.6f, 9.75f
4477 path.close();
4478
4479 auto surface = SkSurface::MakeRasterN32Premul(100, 100);
4480 SkPaint paint;
4481 paint.setAntiAlias(true);
4482 surface->getCanvas()->clipPath(path, true);
4483 surface->getCanvas()->drawRect(SkRect::MakeWH(100, 100), paint);
4484}
4485
4486static void test_path_crbugskia6003() {
4487 auto surface(SkSurface::MakeRasterN32Premul(500, 500));
4488 SkCanvas* canvas = surface->getCanvas();
4489 SkPaint paint;
4490 paint.setAntiAlias(true);
4491 SkPath path;
4492 path.moveTo(SkBits2Float(0x4325e666), SkBits2Float(0x42a1999a)); // 165.9f, 80.8f
4493 path.lineTo(SkBits2Float(0x4325e666), SkBits2Float(0x42a2999a)); // 165.9f, 81.3f
4494 path.lineTo(SkBits2Float(0x4325b333), SkBits2Float(0x42a2999a)); // 165.7f, 81.3f
4495 path.lineTo(SkBits2Float(0x4325b333), SkBits2Float(0x42a16666)); // 165.7f, 80.7f
4496 path.lineTo(SkBits2Float(0x4325b333), SkBits2Float(0x429f6666)); // 165.7f, 79.7f
4497 // 165.7f, 79.7f, 165.8f, 79.7f, 165.8f, 79.7f
4498 path.cubicTo(SkBits2Float(0x4325b333), SkBits2Float(0x429f6666), SkBits2Float(0x4325cccc),
4499 SkBits2Float(0x429f6666), SkBits2Float(0x4325cccc), SkBits2Float(0x429f6666));
4500 // 165.8f, 79.7f, 165.8f, 79.7f, 165.9f, 79.7f
4501 path.cubicTo(SkBits2Float(0x4325cccc), SkBits2Float(0x429f6666), SkBits2Float(0x4325cccc),
4502 SkBits2Float(0x429f6666), SkBits2Float(0x4325e666), SkBits2Float(0x429f6666));
4503 path.lineTo(SkBits2Float(0x4325e666), SkBits2Float(0x42a1999a)); // 165.9f, 80.8f
4504 path.close();
4505 canvas->clipPath(path, true);
4506 canvas->drawRect(SkRect::MakeWH(500, 500), paint);
4507}
4508
4509static void test_fuzz_crbug_662730(skiatest::Reporter* reporter) {
4510 SkPath path;
4511 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
4512 path.lineTo(SkBits2Float(0xd5394437), SkBits2Float(0x37373737)); // -1.2731e+13f, 1.09205e-05f
4513 path.lineTo(SkBits2Float(0x37373737), SkBits2Float(0x37373737)); // 1.09205e-05f, 1.09205e-05f
4514 path.lineTo(SkBits2Float(0x37373745), SkBits2Float(0x0001b800)); // 1.09205e-05f, 1.57842e-40f
4515 path.close();
Yuqian Lif13beef2017-09-14 17:15:04 -04004516 test_draw_AA_path(100, 100, path);
Yuqian Li3154a532017-09-06 13:33:30 -04004517}
4518
Yuqian Li3154a532017-09-06 13:33:30 -04004519static void test_skbug_6947() {
4520 SkPath path;
4521 SkPoint points[] =
4522 {{125.126022f, -0.499872506f}, {125.288895f, -0.499338806f},
4523 {125.299316f, -0.499290764f}, {126.294594f, 0.505449712f},
4524 {125.999992f, 62.5047531f}, {124.0f, 62.4980202f},
4525 {124.122749f, 0.498142242f}, {125.126022f, -0.499872506f},
4526 {125.119476f, 1.50011659f}, {125.122749f, 0.50012207f},
4527 {126.122749f, 0.502101898f}, {126.0f, 62.5019798f},
4528 {125.0f, 62.5f}, {124.000008f, 62.4952469f},
4529 {124.294609f, 0.495946467f}, {125.294601f, 0.50069809f},
4530 {125.289886f, 1.50068688f}, {125.282349f, 1.50065041f},
4531 {125.119476f, 1.50011659f}};
4532 constexpr SkPath::Verb kMove = SkPath::kMove_Verb;
4533 constexpr SkPath::Verb kLine = SkPath::kLine_Verb;
4534 constexpr SkPath::Verb kClose = SkPath::kClose_Verb;
4535 SkPath::Verb verbs[] = {kMove, kLine, kLine, kLine, kLine, kLine, kLine, kLine, kClose,
4536 kMove, kLine, kLine, kLine, kLine, kLine, kLine, kLine, kLine, kLine, kLine, kClose};
4537 int pointIndex = 0;
4538 for(auto verb : verbs) {
4539 switch (verb) {
4540 case kMove:
4541 path.moveTo(points[pointIndex++]);
4542 break;
4543 case kLine:
4544 path.lineTo(points[pointIndex++]);
4545 break;
4546 case kClose:
4547 default:
4548 path.close();
4549 break;
4550 }
4551 }
Yuqian Lif13beef2017-09-14 17:15:04 -04004552 test_draw_AA_path(250, 125, path);
Yuqian Li3154a532017-09-06 13:33:30 -04004553}
Yuqian Lia81b6262017-09-06 17:10:05 -04004554
4555static void test_skbug_7015() {
4556 SkPath path;
Mike Reed7d34dc72019-11-26 12:17:17 -05004557 path.setFillType(SkPathFillType::kWinding);
Yuqian Lia81b6262017-09-06 17:10:05 -04004558 path.moveTo(SkBits2Float(0x4388c000), SkBits2Float(0x43947c08)); // 273.5f, 296.969f
4559 path.lineTo(SkBits2Float(0x4386c000), SkBits2Float(0x43947c08)); // 269.5f, 296.969f
4560 // 269.297f, 292.172f, 273.695f, 292.172f, 273.5f, 296.969f
4561 path.cubicTo(SkBits2Float(0x4386a604), SkBits2Float(0x43921604),
4562 SkBits2Float(0x4388d8f6), SkBits2Float(0x43921604),
4563 SkBits2Float(0x4388c000), SkBits2Float(0x43947c08));
4564 path.close();
Yuqian Lif13beef2017-09-14 17:15:04 -04004565 test_draw_AA_path(500, 500, path);
Yuqian Lia81b6262017-09-06 17:10:05 -04004566}
4567
Yuqian Lic5e4e742017-09-18 14:38:43 -04004568static void test_skbug_7051() {
4569 SkPath path;
4570 path.moveTo(10, 10);
4571 path.cubicTo(10, 20, 10, 30, 30, 30);
4572 path.lineTo(50, 20);
4573 path.lineTo(50, 10);
4574 path.close();
4575 test_draw_AA_path(100, 100, path);
4576}
4577
Ben Wagner2e9f7722019-08-14 15:45:37 -04004578static void test_skbug_7435() {
4579 SkPaint paint;
4580 SkPath path;
Mike Reed7d34dc72019-11-26 12:17:17 -05004581 path.setFillType(SkPathFillType::kWinding);
Ben Wagner2e9f7722019-08-14 15:45:37 -04004582 path.moveTo(SkBits2Float(0x7f07a5af), SkBits2Float(0xff07ff1d)); // 1.80306e+38f, -1.8077e+38f
4583 path.lineTo(SkBits2Float(0x7edf4b2d), SkBits2Float(0xfedffe0a)); // 1.48404e+38f, -1.48868e+38f
4584 path.lineTo(SkBits2Float(0x7edf4585), SkBits2Float(0xfee003b2)); // 1.48389e+38f, -1.48883e+38f
4585 path.lineTo(SkBits2Float(0x7ef348e9), SkBits2Float(0xfef403c6)); // 1.6169e+38f, -1.62176e+38f
4586 path.lineTo(SkBits2Float(0x7ef74c4e), SkBits2Float(0xfef803cb)); // 1.64358e+38f, -1.64834e+38f
4587 path.conicTo(SkBits2Float(0x7ef74f23), SkBits2Float(0xfef8069e), SkBits2Float(0x7ef751f6), SkBits2Float(0xfef803c9), SkBits2Float(0x3f3504f3)); // 1.64365e+38f, -1.64841e+38f, 1.64372e+38f, -1.64834e+38f, 0.707107f
4588 path.conicTo(SkBits2Float(0x7ef754c8), SkBits2Float(0xfef800f5), SkBits2Float(0x7ef751f5), SkBits2Float(0xfef7fe22), SkBits2Float(0x3f353472)); // 1.6438e+38f, -1.64827e+38f, 1.64372e+38f, -1.64819e+38f, 0.707832f
4589 path.lineTo(SkBits2Float(0x7edb57a9), SkBits2Float(0xfedbfe06)); // 1.45778e+38f, -1.4621e+38f
4590 path.lineTo(SkBits2Float(0x7e875976), SkBits2Float(0xfe87fdb3)); // 8.99551e+37f, -9.03815e+37f
4591 path.lineTo(SkBits2Float(0x7ded5c2b), SkBits2Float(0xfdeff59e)); // 3.94382e+37f, -3.98701e+37f
4592 path.lineTo(SkBits2Float(0x7d7a78a7), SkBits2Float(0xfd7fda0f)); // 2.08083e+37f, -2.12553e+37f
4593 path.lineTo(SkBits2Float(0x7d7a6403), SkBits2Float(0xfd7fe461)); // 2.08016e+37f, -2.12587e+37f
4594 path.conicTo(SkBits2Float(0x7d7a4764), SkBits2Float(0xfd7ff2b0), SkBits2Float(0x7d7a55b4), SkBits2Float(0xfd8007a8), SkBits2Float(0x3f3504f3)); // 2.07924e+37f, -2.12633e+37f, 2.0797e+37f, -2.12726e+37f, 0.707107f
4595 path.conicTo(SkBits2Float(0x7d7a5803), SkBits2Float(0xfd8009f7), SkBits2Float(0x7d7a5ba9), SkBits2Float(0xfd800bcc), SkBits2Float(0x3f7cba66)); // 2.07977e+37f, -2.12741e+37f, 2.07989e+37f, -2.12753e+37f, 0.987219f
4596 path.lineTo(SkBits2Float(0x7d8d2067), SkBits2Float(0xfd900bdb)); // 2.34487e+37f, -2.39338e+37f
4597 path.lineTo(SkBits2Float(0x7ddd137a), SkBits2Float(0xfde00c2d)); // 3.67326e+37f, -3.72263e+37f
4598 path.lineTo(SkBits2Float(0x7ddd2a1b), SkBits2Float(0xfddff58e)); // 3.67473e+37f, -3.72116e+37f
4599 path.lineTo(SkBits2Float(0x7c694ae5), SkBits2Float(0xfc7fa67c)); // 4.8453e+36f, -5.30965e+36f
4600 path.lineTo(SkBits2Float(0xfc164a8b), SkBits2Float(0x7c005af5)); // -3.12143e+36f, 2.66584e+36f
4601 path.lineTo(SkBits2Float(0xfc8ae983), SkBits2Float(0x7c802da7)); // -5.77019e+36f, 5.32432e+36f
4602 path.lineTo(SkBits2Float(0xfc8b16d9), SkBits2Float(0x7c80007b)); // -5.77754e+36f, 5.31699e+36f
4603 path.lineTo(SkBits2Float(0xfc8b029c), SkBits2Float(0x7c7f8788)); // -5.77426e+36f, 5.30714e+36f
4604 path.lineTo(SkBits2Float(0xfc8b0290), SkBits2Float(0x7c7f8790)); // -5.77425e+36f, 5.30714e+36f
4605 path.lineTo(SkBits2Float(0xfc8b16cd), SkBits2Float(0x7c80007f)); // -5.77753e+36f, 5.31699e+36f
4606 path.lineTo(SkBits2Float(0xfc8b4409), SkBits2Float(0x7c7fa672)); // -5.78487e+36f, 5.30965e+36f
4607 path.lineTo(SkBits2Float(0x7d7aa2ba), SkBits2Float(0xfd800bd1)); // 2.0822e+37f, -2.12753e+37f
4608 path.lineTo(SkBits2Float(0x7e8757ee), SkBits2Float(0xfe88035b)); // 8.99512e+37f, -9.03962e+37f
4609 path.lineTo(SkBits2Float(0x7ef7552d), SkBits2Float(0xfef803ca)); // 1.64381e+38f, -1.64834e+38f
4610 path.lineTo(SkBits2Float(0x7f0fa653), SkBits2Float(0xff1001f9)); // 1.90943e+38f, -1.91419e+38f
4611 path.lineTo(SkBits2Float(0x7f0fa926), SkBits2Float(0xff0fff24)); // 1.90958e+38f, -1.91404e+38f
4612 path.lineTo(SkBits2Float(0x7f0da75c), SkBits2Float(0xff0dff22)); // 1.8829e+38f, -1.88746e+38f
4613 path.lineTo(SkBits2Float(0x7f07a5af), SkBits2Float(0xff07ff1d)); // 1.80306e+38f, -1.8077e+38f
4614 path.close();
4615 path.moveTo(SkBits2Float(0x7f07a2db), SkBits2Float(0xff0801f1)); // 1.80291e+38f, -1.80785e+38f
4616 path.lineTo(SkBits2Float(0x7f0da48a), SkBits2Float(0xff0e01f8)); // 1.88275e+38f, -1.88761e+38f
4617 path.lineTo(SkBits2Float(0x7f0fa654), SkBits2Float(0xff1001fa)); // 1.90943e+38f, -1.91419e+38f
4618 path.lineTo(SkBits2Float(0x7f0fa7bd), SkBits2Float(0xff10008f)); // 1.90951e+38f, -1.91412e+38f
4619 path.lineTo(SkBits2Float(0x7f0fa927), SkBits2Float(0xff0fff25)); // 1.90958e+38f, -1.91404e+38f
4620 path.lineTo(SkBits2Float(0x7ef75ad5), SkBits2Float(0xfef7fe22)); // 1.64395e+38f, -1.64819e+38f
4621 path.lineTo(SkBits2Float(0x7e875d96), SkBits2Float(0xfe87fdb3)); // 8.99659e+37f, -9.03815e+37f
4622 path.lineTo(SkBits2Float(0x7d7acff6), SkBits2Float(0xfd7fea5b)); // 2.08367e+37f, -2.12606e+37f
4623 path.lineTo(SkBits2Float(0xfc8b0588), SkBits2Float(0x7c8049b7)); // -5.77473e+36f, 5.32887e+36f
4624 path.lineTo(SkBits2Float(0xfc8b2b16), SkBits2Float(0x7c803d32)); // -5.78083e+36f, 5.32684e+36f
4625 path.conicTo(SkBits2Float(0xfc8b395c), SkBits2Float(0x7c803870), SkBits2Float(0xfc8b4405), SkBits2Float(0x7c802dd1), SkBits2Float(0x3f79349d)); // -5.78314e+36f, 5.32607e+36f, -5.78487e+36f, 5.32435e+36f, 0.973459f
4626 path.conicTo(SkBits2Float(0xfc8b715b), SkBits2Float(0x7c8000a5), SkBits2Float(0xfc8b442f), SkBits2Float(0x7c7fa69e), SkBits2Float(0x3f3504f3)); // -5.79223e+36f, 5.31702e+36f, -5.7849e+36f, 5.30966e+36f, 0.707107f
4627 path.lineTo(SkBits2Float(0xfc16ffaa), SkBits2Float(0x7bff4c12)); // -3.13612e+36f, 2.65116e+36f
4628 path.lineTo(SkBits2Float(0x7c6895e0), SkBits2Float(0xfc802dc0)); // 4.83061e+36f, -5.32434e+36f
4629 path.lineTo(SkBits2Float(0x7ddd137b), SkBits2Float(0xfde00c2e)); // 3.67326e+37f, -3.72263e+37f
4630 path.lineTo(SkBits2Float(0x7ddd1ecb), SkBits2Float(0xfde000de)); // 3.67399e+37f, -3.72189e+37f
4631 path.lineTo(SkBits2Float(0x7ddd2a1c), SkBits2Float(0xfddff58f)); // 3.67473e+37f, -3.72116e+37f
4632 path.lineTo(SkBits2Float(0x7d8d3711), SkBits2Float(0xfd8ff543)); // 2.34634e+37f, -2.39191e+37f
4633 path.lineTo(SkBits2Float(0x7d7a88fe), SkBits2Float(0xfd7fea69)); // 2.08136e+37f, -2.12606e+37f
4634 path.lineTo(SkBits2Float(0x7d7a7254), SkBits2Float(0xfd800080)); // 2.08063e+37f, -2.1268e+37f
4635 path.lineTo(SkBits2Float(0x7d7a80a4), SkBits2Float(0xfd800ed0)); // 2.08109e+37f, -2.12773e+37f
4636 path.lineTo(SkBits2Float(0x7d7a80a8), SkBits2Float(0xfd800ecf)); // 2.08109e+37f, -2.12773e+37f
4637 path.lineTo(SkBits2Float(0x7d7a7258), SkBits2Float(0xfd80007f)); // 2.08063e+37f, -2.1268e+37f
4638 path.lineTo(SkBits2Float(0x7d7a5bb9), SkBits2Float(0xfd800bd0)); // 2.0799e+37f, -2.12753e+37f
4639 path.lineTo(SkBits2Float(0x7ded458b), SkBits2Float(0xfdf00c3e)); // 3.94235e+37f, -3.98848e+37f
4640 path.lineTo(SkBits2Float(0x7e8753ce), SkBits2Float(0xfe88035b)); // 8.99405e+37f, -9.03962e+37f
4641 path.lineTo(SkBits2Float(0x7edb5201), SkBits2Float(0xfedc03ae)); // 1.45763e+38f, -1.46225e+38f
4642 path.lineTo(SkBits2Float(0x7ef74c4d), SkBits2Float(0xfef803ca)); // 1.64358e+38f, -1.64834e+38f
4643 path.lineTo(SkBits2Float(0x7ef74f21), SkBits2Float(0xfef800f6)); // 1.64365e+38f, -1.64827e+38f
4644 path.lineTo(SkBits2Float(0x7ef751f4), SkBits2Float(0xfef7fe21)); // 1.64372e+38f, -1.64819e+38f
4645 path.lineTo(SkBits2Float(0x7ef34e91), SkBits2Float(0xfef3fe1e)); // 1.61705e+38f, -1.62161e+38f
4646 path.lineTo(SkBits2Float(0x7edf4b2d), SkBits2Float(0xfedffe0a)); // 1.48404e+38f, -1.48868e+38f
4647 path.lineTo(SkBits2Float(0x7edf4859), SkBits2Float(0xfee000de)); // 1.48397e+38f, -1.48876e+38f
4648 path.lineTo(SkBits2Float(0x7edf4585), SkBits2Float(0xfee003b2)); // 1.48389e+38f, -1.48883e+38f
4649 path.lineTo(SkBits2Float(0x7f07a2db), SkBits2Float(0xff0801f1)); // 1.80291e+38f, -1.80785e+38f
4650 path.close();
4651 path.moveTo(SkBits2Float(0xfab120db), SkBits2Float(0x77b50b4f)); // -4.59851e+35f, 7.34402e+33f
4652 path.lineTo(SkBits2Float(0xfd6597e5), SkBits2Float(0x7d60177f)); // -1.90739e+37f, 1.86168e+37f
4653 path.lineTo(SkBits2Float(0xfde2cea1), SkBits2Float(0x7de00c2e)); // -3.76848e+37f, 3.72263e+37f
4654 path.lineTo(SkBits2Float(0xfe316511), SkBits2Float(0x7e300657)); // -5.89495e+37f, 5.84943e+37f
4655 path.lineTo(SkBits2Float(0xfe415da1), SkBits2Float(0x7e400666)); // -6.42568e+37f, 6.38112e+37f
4656 path.lineTo(SkBits2Float(0xfe41634a), SkBits2Float(0x7e4000be)); // -6.42641e+37f, 6.38039e+37f
4657 path.lineTo(SkBits2Float(0xfe41634a), SkBits2Float(0x7e3ff8be)); // -6.42641e+37f, 6.37935e+37f
4658 path.lineTo(SkBits2Float(0xfe416349), SkBits2Float(0x7e3ff8be)); // -6.42641e+37f, 6.37935e+37f
4659 path.lineTo(SkBits2Float(0xfe415f69), SkBits2Float(0x7e3ff8be)); // -6.42591e+37f, 6.37935e+37f
4660 path.lineTo(SkBits2Float(0xfe415bc9), SkBits2Float(0x7e3ff8be)); // -6.42544e+37f, 6.37935e+37f
4661 path.lineTo(SkBits2Float(0xfe415bc9), SkBits2Float(0x7e4000be)); // -6.42544e+37f, 6.38039e+37f
4662 path.lineTo(SkBits2Float(0xfe416171), SkBits2Float(0x7e3ffb16)); // -6.42617e+37f, 6.37966e+37f
4663 path.lineTo(SkBits2Float(0xfe016131), SkBits2Float(0x7dfff5ae)); // -4.29938e+37f, 4.25286e+37f
4664 path.lineTo(SkBits2Float(0xfe0155e2), SkBits2Float(0x7e000628)); // -4.29791e+37f, 4.25433e+37f
4665 path.lineTo(SkBits2Float(0xfe0958ea), SkBits2Float(0x7e080630)); // -4.56415e+37f, 4.52018e+37f
4666 path.lineTo(SkBits2Float(0xfe115c92), SkBits2Float(0x7e100638)); // -4.83047e+37f, 4.78603e+37f
4667 path.conicTo(SkBits2Float(0xfe11623c), SkBits2Float(0x7e100bdf), SkBits2Float(0xfe1167e2), SkBits2Float(0x7e100636), SkBits2Float(0x3f3504f3)); // -4.8312e+37f, 4.78676e+37f, -4.83194e+37f, 4.78603e+37f, 0.707107f
4668 path.conicTo(SkBits2Float(0xfe116d87), SkBits2Float(0x7e10008e), SkBits2Float(0xfe1167e2), SkBits2Float(0x7e0ffae8), SkBits2Float(0x3f35240a)); // -4.83267e+37f, 4.78529e+37f, -4.83194e+37f, 4.78456e+37f, 0.707581f
4669 path.lineTo(SkBits2Float(0xfe016b92), SkBits2Float(0x7dfff5af)); // -4.30072e+37f, 4.25286e+37f
4670 path.lineTo(SkBits2Float(0xfdc2d963), SkBits2Float(0x7dbff56e)); // -3.23749e+37f, 3.18946e+37f
4671 path.lineTo(SkBits2Float(0xfd65ae25), SkBits2Float(0x7d5fea3d)); // -1.90811e+37f, 1.86021e+37f
4672 path.lineTo(SkBits2Float(0xfab448de), SkBits2Float(0xf7b50a19)); // -4.68046e+35f, -7.34383e+33f
4673 path.lineTo(SkBits2Float(0xfab174d9), SkBits2Float(0x43480000)); // -4.60703e+35f, 200
4674 path.lineTo(SkBits2Float(0xfab174d9), SkBits2Float(0x7800007f)); // -4.60703e+35f, 1.03848e+34f
4675 path.lineTo(SkBits2Float(0xfab3f4db), SkBits2Float(0x7800007f)); // -4.67194e+35f, 1.03848e+34f
4676 path.lineTo(SkBits2Float(0xfab3f4db), SkBits2Float(0x43480000)); // -4.67194e+35f, 200
4677 path.lineTo(SkBits2Float(0xfab120db), SkBits2Float(0x77b50b4f)); // -4.59851e+35f, 7.34402e+33f
4678 path.close();
4679 path.moveTo(SkBits2Float(0xfab59cf2), SkBits2Float(0xf800007e)); // -4.71494e+35f, -1.03847e+34f
4680 path.lineTo(SkBits2Float(0xfaa7cc52), SkBits2Float(0xf800007f)); // -4.35629e+35f, -1.03848e+34f
4681 path.lineTo(SkBits2Float(0xfd6580e5), SkBits2Float(0x7d60177f)); // -1.90664e+37f, 1.86168e+37f
4682 path.lineTo(SkBits2Float(0xfdc2c2c1), SkBits2Float(0x7dc00c0f)); // -3.23602e+37f, 3.19093e+37f
4683 path.lineTo(SkBits2Float(0xfe016040), SkBits2Float(0x7e000626)); // -4.29925e+37f, 4.25433e+37f
4684 path.lineTo(SkBits2Float(0xfe115c90), SkBits2Float(0x7e100636)); // -4.83047e+37f, 4.78603e+37f
4685 path.lineTo(SkBits2Float(0xfe116239), SkBits2Float(0x7e10008f)); // -4.8312e+37f, 4.78529e+37f
4686 path.lineTo(SkBits2Float(0xfe1167e0), SkBits2Float(0x7e0ffae6)); // -4.83194e+37f, 4.78456e+37f
4687 path.lineTo(SkBits2Float(0xfe096438), SkBits2Float(0x7e07fade)); // -4.56562e+37f, 4.51871e+37f
4688 path.lineTo(SkBits2Float(0xfe016130), SkBits2Float(0x7dfff5ac)); // -4.29938e+37f, 4.25286e+37f
4689 path.lineTo(SkBits2Float(0xfe015b89), SkBits2Float(0x7e00007f)); // -4.29864e+37f, 4.25359e+37f
4690 path.lineTo(SkBits2Float(0xfe0155e1), SkBits2Float(0x7e000627)); // -4.29791e+37f, 4.25433e+37f
4691 path.lineTo(SkBits2Float(0xfe415879), SkBits2Float(0x7e4008bf)); // -6.42501e+37f, 6.38143e+37f
4692 path.lineTo(SkBits2Float(0xfe415f69), SkBits2Float(0x7e4008bf)); // -6.42591e+37f, 6.38143e+37f
4693 path.lineTo(SkBits2Float(0xfe416349), SkBits2Float(0x7e4008bf)); // -6.42641e+37f, 6.38143e+37f
4694 path.lineTo(SkBits2Float(0xfe41634a), SkBits2Float(0x7e4008bf)); // -6.42641e+37f, 6.38143e+37f
4695 path.conicTo(SkBits2Float(0xfe416699), SkBits2Float(0x7e4008bf), SkBits2Float(0xfe4168f1), SkBits2Float(0x7e400668), SkBits2Float(0x3f6c8ed9)); // -6.42684e+37f, 6.38143e+37f, -6.42715e+37f, 6.38113e+37f, 0.924055f
4696 path.conicTo(SkBits2Float(0xfe416e9a), SkBits2Float(0x7e4000c2), SkBits2Float(0xfe4168f3), SkBits2Float(0x7e3ffb17), SkBits2Float(0x3f3504f3)); // -6.42788e+37f, 6.38039e+37f, -6.42715e+37f, 6.37966e+37f, 0.707107f
4697 path.lineTo(SkBits2Float(0xfe317061), SkBits2Float(0x7e2ffb07)); // -5.89642e+37f, 5.84796e+37f
4698 path.lineTo(SkBits2Float(0xfde2e542), SkBits2Float(0x7ddff58e)); // -3.76995e+37f, 3.72116e+37f
4699 path.lineTo(SkBits2Float(0xfd65c525), SkBits2Float(0x7d5fea3d)); // -1.90886e+37f, 1.86021e+37f
4700 path.lineTo(SkBits2Float(0xfab6c8db), SkBits2Float(0xf7b50b4f)); // -4.74536e+35f, -7.34402e+33f
4701 path.lineTo(SkBits2Float(0xfab59cf2), SkBits2Float(0xf800007e)); // -4.71494e+35f, -1.03847e+34f
4702 path.close();
4703 path.moveTo(SkBits2Float(0xfab3f4db), SkBits2Float(0x43480000)); // -4.67194e+35f, 200
4704 path.lineTo(SkBits2Float(0xfab174d9), SkBits2Float(0x43480000)); // -4.60703e+35f, 200
4705 path.quadTo(SkBits2Float(0xfd0593a5), SkBits2Float(0x7d00007f), SkBits2Float(0xfd659785), SkBits2Float(0x7d6000de)); // -1.10971e+37f, 1.0634e+37f, -1.90737e+37f, 1.86095e+37f
4706 path.quadTo(SkBits2Float(0xfda2cdf2), SkBits2Float(0x7da0009f), SkBits2Float(0xfdc2ce12), SkBits2Float(0x7dc000be)); // -2.70505e+37f, 2.6585e+37f, -3.23675e+37f, 3.1902e+37f
4707 path.quadTo(SkBits2Float(0xfde2ce31), SkBits2Float(0x7de000de), SkBits2Float(0xfe0165e9), SkBits2Float(0x7e00007f)); // -3.76845e+37f, 3.72189e+37f, -4.29999e+37f, 4.25359e+37f
4708 path.quadTo(SkBits2Float(0xfe1164b9), SkBits2Float(0x7e10008f), SkBits2Float(0xfe116239), SkBits2Float(0x7e10008f)); // -4.83153e+37f, 4.78529e+37f, -4.8312e+37f, 4.78529e+37f
4709 path.quadTo(SkBits2Float(0xfe116039), SkBits2Float(0x7e10008f), SkBits2Float(0xfe095e91), SkBits2Float(0x7e080087)); // -4.83094e+37f, 4.78529e+37f, -4.56488e+37f, 4.51944e+37f
4710 path.quadTo(SkBits2Float(0xfe015d09), SkBits2Float(0x7e00007f), SkBits2Float(0xfe015b89), SkBits2Float(0x7e00007f)); // -4.29884e+37f, 4.25359e+37f, -4.29864e+37f, 4.25359e+37f
4711 path.lineTo(SkBits2Float(0xfe415bc9), SkBits2Float(0x7e4000be)); // -6.42544e+37f, 6.38039e+37f
4712 path.quadTo(SkBits2Float(0xfe415da9), SkBits2Float(0x7e4000be), SkBits2Float(0xfe415f69), SkBits2Float(0x7e4000be)); // -6.42568e+37f, 6.38039e+37f, -6.42591e+37f, 6.38039e+37f
4713 path.quadTo(SkBits2Float(0xfe416149), SkBits2Float(0x7e4000be), SkBits2Float(0xfe416349), SkBits2Float(0x7e4000be)); // -6.42615e+37f, 6.38039e+37f, -6.42641e+37f, 6.38039e+37f
4714 path.quadTo(SkBits2Float(0xfe416849), SkBits2Float(0x7e4000be), SkBits2Float(0xfe316ab9), SkBits2Float(0x7e3000af)); // -6.42706e+37f, 6.38039e+37f, -5.89569e+37f, 5.84869e+37f
4715 path.quadTo(SkBits2Float(0xfe216d29), SkBits2Float(0x7e20009f), SkBits2Float(0xfde2d9f2), SkBits2Float(0x7de000de)); // -5.36431e+37f, 5.31699e+37f, -3.76921e+37f, 3.72189e+37f
4716 path.quadTo(SkBits2Float(0xfda2d9b2), SkBits2Float(0x7da0009f), SkBits2Float(0xfd65ae85), SkBits2Float(0x7d6000de)); // -2.70582e+37f, 2.6585e+37f, -1.90812e+37f, 1.86095e+37f
4717 path.quadTo(SkBits2Float(0xfd05a9a6), SkBits2Float(0x7d00007f), SkBits2Float(0xfab3f4db), SkBits2Float(0x43480000)); // -1.11043e+37f, 1.0634e+37f, -4.67194e+35f, 200
4718 path.close();
4719 path.moveTo(SkBits2Float(0x7f07a445), SkBits2Float(0xff080087)); // 1.80299e+38f, -1.80778e+38f
4720 path.quadTo(SkBits2Float(0x7f0ba519), SkBits2Float(0xff0c008b), SkBits2Float(0x7f0da5f3), SkBits2Float(0xff0e008d)); // 1.8562e+38f, -1.86095e+38f, 1.88283e+38f, -1.88753e+38f
4721 path.quadTo(SkBits2Float(0x7f0fa6d5), SkBits2Float(0xff10008f), SkBits2Float(0x7f0fa7bd), SkBits2Float(0xff10008f)); // 1.90946e+38f, -1.91412e+38f, 1.90951e+38f, -1.91412e+38f
4722 path.quadTo(SkBits2Float(0x7f0faa7d), SkBits2Float(0xff10008f), SkBits2Float(0x7ef75801), SkBits2Float(0xfef800f6)); // 1.90965e+38f, -1.91412e+38f, 1.64388e+38f, -1.64827e+38f
4723 path.quadTo(SkBits2Float(0x7ecf5b09), SkBits2Float(0xfed000ce), SkBits2Float(0x7e875ac2), SkBits2Float(0xfe880087)); // 1.37811e+38f, -1.38242e+38f, 8.99585e+37f, -9.03889e+37f
4724 path.quadTo(SkBits2Float(0x7e0eb505), SkBits2Float(0xfe10008f), SkBits2Float(0x7d7ab958), SkBits2Float(0xfd80007f)); // 4.74226e+37f, -4.78529e+37f, 2.08293e+37f, -2.1268e+37f
4725 path.quadTo(SkBits2Float(0xfc8ac1cd), SkBits2Float(0x7c80007f), SkBits2Float(0xfc8b16cd), SkBits2Float(0x7c80007f)); // -5.76374e+36f, 5.31699e+36f, -5.77753e+36f, 5.31699e+36f
4726 path.quadTo(SkBits2Float(0xfc8b36cd), SkBits2Float(0x7c80007f), SkBits2Float(0xfc16a51a), SkBits2Float(0x7c00007f)); // -5.78273e+36f, 5.31699e+36f, -3.12877e+36f, 2.6585e+36f
4727 path.quadTo(SkBits2Float(0xfab6e4de), SkBits2Float(0x43480000), SkBits2Float(0x7c68f062), SkBits2Float(0xfc80007f)); // -4.7482e+35f, 200, 4.83795e+36f, -5.31699e+36f
4728 path.lineTo(SkBits2Float(0x7ddd1ecb), SkBits2Float(0xfde000de)); // 3.67399e+37f, -3.72189e+37f
4729 path.quadTo(SkBits2Float(0x7d9d254b), SkBits2Float(0xfda0009f), SkBits2Float(0x7d8d2bbc), SkBits2Float(0xfd90008f)); // 2.61103e+37f, -2.6585e+37f, 2.3456e+37f, -2.39265e+37f
4730 path.quadTo(SkBits2Float(0x7d7a64d8), SkBits2Float(0xfd80007f), SkBits2Float(0x7d7a7258), SkBits2Float(0xfd80007f)); // 2.08019e+37f, -2.1268e+37f, 2.08063e+37f, -2.1268e+37f
4731 path.quadTo(SkBits2Float(0x7d7a9058), SkBits2Float(0xfd80007f), SkBits2Float(0x7ded50db), SkBits2Float(0xfdf000ee)); // 2.0816e+37f, -2.1268e+37f, 3.94309e+37f, -3.98774e+37f
4732 path.quadTo(SkBits2Float(0x7e2eace5), SkBits2Float(0xfe3000af), SkBits2Float(0x7e8756a2), SkBits2Float(0xfe880087)); // 5.80458e+37f, -5.84869e+37f, 8.99478e+37f, -9.03889e+37f
4733 path.quadTo(SkBits2Float(0x7ebf56d9), SkBits2Float(0xfec000be), SkBits2Float(0x7edb54d5), SkBits2Float(0xfedc00da)); // 1.27167e+38f, -1.27608e+38f, 1.45771e+38f, -1.46217e+38f
4734 path.quadTo(SkBits2Float(0x7ef752e1), SkBits2Float(0xfef800f6), SkBits2Float(0x7ef74f21), SkBits2Float(0xfef800f6)); // 1.64375e+38f, -1.64827e+38f, 1.64365e+38f, -1.64827e+38f
4735 path.quadTo(SkBits2Float(0x7ef74d71), SkBits2Float(0xfef800f6), SkBits2Float(0x7ef34bbd), SkBits2Float(0xfef400f2)); // 1.64361e+38f, -1.64827e+38f, 1.61698e+38f, -1.62168e+38f
4736 path.quadTo(SkBits2Float(0x7eef4a19), SkBits2Float(0xfef000ee), SkBits2Float(0x7edf4859), SkBits2Float(0xfee000de)); // 1.59035e+38f, -1.5951e+38f, 1.48397e+38f, -1.48876e+38f
4737 path.lineTo(SkBits2Float(0x7f07a445), SkBits2Float(0xff080087)); // 1.80299e+38f, -1.80778e+38f
4738 path.close();
4739 SkSurface::MakeRasterN32Premul(250, 250, nullptr)->getCanvas()->drawPath(path, paint);
4740}
4741
Yuqian Li3154a532017-09-06 13:33:30 -04004742static void test_interp(skiatest::Reporter* reporter) {
4743 SkPath p1, p2, out;
4744 REPORTER_ASSERT(reporter, p1.isInterpolatable(p2));
4745 REPORTER_ASSERT(reporter, p1.interpolate(p2, 0, &out));
4746 REPORTER_ASSERT(reporter, p1 == out);
4747 REPORTER_ASSERT(reporter, p1.interpolate(p2, 1, &out));
4748 REPORTER_ASSERT(reporter, p1 == out);
4749 p1.moveTo(0, 2);
4750 p1.lineTo(0, 4);
4751 REPORTER_ASSERT(reporter, !p1.isInterpolatable(p2));
4752 REPORTER_ASSERT(reporter, !p1.interpolate(p2, 1, &out));
4753 p2.moveTo(6, 0);
4754 p2.lineTo(8, 0);
4755 REPORTER_ASSERT(reporter, p1.isInterpolatable(p2));
4756 REPORTER_ASSERT(reporter, p1.interpolate(p2, 0, &out));
4757 REPORTER_ASSERT(reporter, p2 == out);
4758 REPORTER_ASSERT(reporter, p1.interpolate(p2, 1, &out));
4759 REPORTER_ASSERT(reporter, p1 == out);
4760 REPORTER_ASSERT(reporter, p1.interpolate(p2, 0.5f, &out));
4761 REPORTER_ASSERT(reporter, out.getBounds() == SkRect::MakeLTRB(3, 1, 4, 2));
4762 p1.reset();
4763 p1.moveTo(4, 4);
4764 p1.conicTo(5, 4, 5, 5, 1 / SkScalarSqrt(2));
4765 p2.reset();
4766 p2.moveTo(4, 2);
4767 p2.conicTo(7, 2, 7, 5, 1 / SkScalarSqrt(2));
4768 REPORTER_ASSERT(reporter, p1.isInterpolatable(p2));
4769 REPORTER_ASSERT(reporter, p1.interpolate(p2, 0.5f, &out));
4770 REPORTER_ASSERT(reporter, out.getBounds() == SkRect::MakeLTRB(4, 3, 6, 5));
4771 p2.reset();
4772 p2.moveTo(4, 2);
4773 p2.conicTo(6, 3, 6, 5, 1);
4774 REPORTER_ASSERT(reporter, !p1.isInterpolatable(p2));
4775 p2.reset();
4776 p2.moveTo(4, 4);
4777 p2.conicTo(5, 4, 5, 5, 0.5f);
4778 REPORTER_ASSERT(reporter, !p1.isInterpolatable(p2));
4779}
4780
4781DEF_TEST(PathInterp, reporter) {
4782 test_interp(reporter);
4783}
4784
Mike Kleinc0bd9f92019-04-23 12:05:21 -05004785#include "include/core/SkSurface.h"
Yuqian Li3154a532017-09-06 13:33:30 -04004786DEF_TEST(PathBigCubic, reporter) {
4787 SkPath path;
4788 path.moveTo(SkBits2Float(0x00000000), SkBits2Float(0x00000000)); // 0, 0
4789 path.moveTo(SkBits2Float(0x44000000), SkBits2Float(0x373938b8)); // 512, 1.10401e-05f
4790 path.cubicTo(SkBits2Float(0x00000001), SkBits2Float(0xdf000052), SkBits2Float(0x00000100), SkBits2Float(0x00000000), SkBits2Float(0x00000100), SkBits2Float(0x00000000)); // 1.4013e-45f, -9.22346e+18f, 3.58732e-43f, 0, 3.58732e-43f, 0
4791 path.moveTo(0, 512);
4792
4793 // this call should not assert
4794 SkSurface::MakeRasterN32Premul(255, 255, nullptr)->getCanvas()->drawPath(path, SkPaint());
4795}
4796
4797DEF_TEST(PathContains, reporter) {
4798 test_contains(reporter);
4799}
4800
4801DEF_TEST(Paths, reporter) {
4802 test_fuzz_crbug_647922();
4803 test_fuzz_crbug_643933();
4804 test_sect_with_horizontal_needs_pinning();
4805 test_crbug_629455(reporter);
4806 test_fuzz_crbug_627414(reporter);
4807 test_path_crbug364224();
4808 test_fuzz_crbug_662952(reporter);
4809 test_fuzz_crbug_662730(reporter);
4810 test_fuzz_crbug_662780();
4811 test_mask_overflow();
4812 test_path_crbugskia6003();
4813 test_fuzz_crbug_668907();
Yuqian Li3154a532017-09-06 13:33:30 -04004814 test_skbug_6947();
Yuqian Lia81b6262017-09-06 17:10:05 -04004815 test_skbug_7015();
Yuqian Lic5e4e742017-09-18 14:38:43 -04004816 test_skbug_7051();
Ben Wagner2e9f7722019-08-14 15:45:37 -04004817 test_skbug_7435();
Yuqian Li3154a532017-09-06 13:33:30 -04004818
4819 SkSize::Make(3, 4);
4820
4821 SkPath p, empty;
4822 SkRect bounds, bounds2;
4823 test_empty(reporter, p);
4824
4825 REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
4826
4827 // this triggers a code path in SkPath::operator= which is otherwise unexercised
4828 SkPath& self = p;
4829 p = self;
4830
4831 // this triggers a code path in SkPath::swap which is otherwise unexercised
4832 p.swap(self);
4833
Mike Reed92b33352019-08-24 19:39:13 -04004834 bounds.setLTRB(0, 0, SK_Scalar1, SK_Scalar1);
Yuqian Li3154a532017-09-06 13:33:30 -04004835
4836 p.addRoundRect(bounds, SK_Scalar1, SK_Scalar1);
4837 check_convex_bounds(reporter, p, bounds);
4838 // we have quads or cubics
4839 REPORTER_ASSERT(reporter,
4840 p.getSegmentMasks() & (kCurveSegmentMask | SkPath::kConic_SegmentMask));
4841 REPORTER_ASSERT(reporter, !p.isEmpty());
4842
4843 p.reset();
4844 test_empty(reporter, p);
4845
4846 p.addOval(bounds);
4847 check_convex_bounds(reporter, p, bounds);
4848 REPORTER_ASSERT(reporter, !p.isEmpty());
4849
4850 p.rewind();
4851 test_empty(reporter, p);
4852
4853 p.addRect(bounds);
4854 check_convex_bounds(reporter, p, bounds);
4855 // we have only lines
4856 REPORTER_ASSERT(reporter, SkPath::kLine_SegmentMask == p.getSegmentMasks());
4857 REPORTER_ASSERT(reporter, !p.isEmpty());
4858
4859 REPORTER_ASSERT(reporter, p != empty);
4860 REPORTER_ASSERT(reporter, !(p == empty));
4861
4862 // do getPoints and getVerbs return the right result
4863 REPORTER_ASSERT(reporter, p.getPoints(nullptr, 0) == 4);
4864 REPORTER_ASSERT(reporter, p.getVerbs(nullptr, 0) == 5);
4865 SkPoint pts[4];
4866 int count = p.getPoints(pts, 4);
4867 REPORTER_ASSERT(reporter, count == 4);
4868 uint8_t verbs[6];
4869 verbs[5] = 0xff;
4870 p.getVerbs(verbs, 5);
4871 REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
4872 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
4873 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
4874 REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
4875 REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
4876 REPORTER_ASSERT(reporter, 0xff == verbs[5]);
Mike Reed92b33352019-08-24 19:39:13 -04004877 bounds2.setBounds(pts, 4);
Yuqian Li3154a532017-09-06 13:33:30 -04004878 REPORTER_ASSERT(reporter, bounds == bounds2);
4879
4880 bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
4881 p.offset(SK_Scalar1*3, SK_Scalar1*4);
4882 REPORTER_ASSERT(reporter, bounds == p.getBounds());
4883
4884 REPORTER_ASSERT(reporter, p.isRect(nullptr));
4885 bounds2.setEmpty();
4886 REPORTER_ASSERT(reporter, p.isRect(&bounds2));
4887 REPORTER_ASSERT(reporter, bounds == bounds2);
4888
4889 // now force p to not be a rect
Mike Reed92b33352019-08-24 19:39:13 -04004890 bounds.setWH(SK_Scalar1/2, SK_Scalar1/2);
Yuqian Li3154a532017-09-06 13:33:30 -04004891 p.addRect(bounds);
4892 REPORTER_ASSERT(reporter, !p.isRect(nullptr));
4893
4894 // Test an edge case w.r.t. the bound returned by isRect (i.e., the
4895 // path has a trailing moveTo. Please see crbug.com\445368)
4896 {
4897 SkRect r;
4898 p.reset();
4899 p.addRect(bounds);
4900 REPORTER_ASSERT(reporter, p.isRect(&r));
4901 REPORTER_ASSERT(reporter, r == bounds);
4902 // add a moveTo outside of our bounds
4903 p.moveTo(bounds.fLeft + 10, bounds.fBottom + 10);
4904 REPORTER_ASSERT(reporter, p.isRect(&r));
4905 REPORTER_ASSERT(reporter, r == bounds);
4906 }
4907
4908 test_operatorEqual(reporter);
4909 test_isLine(reporter);
4910 test_isRect(reporter);
4911 test_is_simple_closed_rect(reporter);
4912 test_isNestedFillRects(reporter);
4913 test_zero_length_paths(reporter);
4914 test_direction(reporter);
4915 test_convexity(reporter);
4916 test_convexity2(reporter);
Cary Clarkc9b7c722018-12-12 14:50:23 -05004917 test_convexity_doubleback(reporter);
Yuqian Li3154a532017-09-06 13:33:30 -04004918 test_conservativelyContains(reporter);
4919 test_close(reporter);
4920 test_segment_masks(reporter);
4921 test_flattening(reporter);
4922 test_transform(reporter);
4923 test_bounds(reporter);
4924 test_iter(reporter);
Chris Daltonde500372020-05-05 15:06:30 -06004925 test_range_iter(reporter);
Yuqian Li3154a532017-09-06 13:33:30 -04004926 test_circle(reporter);
4927 test_oval(reporter);
4928 test_strokerec(reporter);
4929 test_addPoly(reporter);
4930 test_isfinite(reporter);
4931 test_isfinite_after_transform(reporter);
4932 test_islastcontourclosed(reporter);
4933 test_arb_round_rect_is_convex(reporter);
4934 test_arb_zero_rad_round_rect_is_rect(reporter);
4935 test_addrect(reporter);
4936 test_addrect_isfinite(reporter);
4937 test_tricky_cubic();
4938 test_clipped_cubic();
4939 test_crbug_170666();
4940 test_crbug_493450(reporter);
4941 test_crbug_495894(reporter);
4942 test_crbug_613918();
4943 test_bad_cubic_crbug229478();
4944 test_bad_cubic_crbug234190();
4945 test_gen_id(reporter);
4946 test_path_close_issue1474(reporter);
4947 test_path_to_region(reporter);
4948 test_rrect(reporter);
4949 test_arc(reporter);
4950 test_arc_ovals(reporter);
4951 test_arcTo(reporter);
4952 test_addPath(reporter);
4953 test_addPathMode(reporter, false, false);
4954 test_addPathMode(reporter, true, false);
4955 test_addPathMode(reporter, false, true);
4956 test_addPathMode(reporter, true, true);
4957 test_extendClosedPath(reporter);
4958 test_addEmptyPath(reporter, SkPath::kExtend_AddPathMode);
4959 test_addEmptyPath(reporter, SkPath::kAppend_AddPathMode);
4960 test_conicTo_special_case(reporter);
4961 test_get_point(reporter);
4962 test_contains(reporter);
4963 PathTest_Private::TestPathTo(reporter);
4964 PathRefTest_Private::TestPathRef(reporter);
4965 PathTest_Private::TestPathrefListeners(reporter);
4966 test_dump(reporter);
4967 test_path_crbug389050(reporter);
4968 test_path_crbugskia2820(reporter);
4969 test_path_crbugskia5995();
4970 test_skbug_3469(reporter);
4971 test_skbug_3239(reporter);
4972 test_bounds_crbug_513799(reporter);
4973 test_fuzz_crbug_638223();
4974}
4975
4976DEF_TEST(conservatively_contains_rect, reporter) {
4977 SkPath path;
4978
4979 path.moveTo(SkBits2Float(0x44000000), SkBits2Float(0x373938b8)); // 512, 1.10401e-05f
4980 // 1.4013e-45f, -9.22346e+18f, 3.58732e-43f, 0, 3.58732e-43f, 0
4981 path.cubicTo(SkBits2Float(0x00000001), SkBits2Float(0xdf000052),
4982 SkBits2Float(0x00000100), SkBits2Float(0x00000000),
4983 SkBits2Float(0x00000100), SkBits2Float(0x00000000));
4984 path.moveTo(0, 0);
4985
Leon Scroggins IIIa4c80982020-07-28 10:20:58 -04004986 // this should not assert
Yuqian Li3154a532017-09-06 13:33:30 -04004987 path.conservativelyContainsRect({ -211747, 12.1115f, -197893, 25.0321f });
4988}
4989
4990///////////////////////////////////////////////////////////////////////////////////////////////////
4991
4992static void rand_path(SkPath* path, SkRandom& rand, SkPath::Verb verb, int n) {
4993 for (int i = 0; i < n; ++i) {
4994 switch (verb) {
4995 case SkPath::kLine_Verb:
4996 path->lineTo(rand.nextF()*100, rand.nextF()*100);
4997 break;
4998 case SkPath::kQuad_Verb:
4999 path->quadTo(rand.nextF()*100, rand.nextF()*100,
5000 rand.nextF()*100, rand.nextF()*100);
5001 break;
5002 case SkPath::kConic_Verb:
5003 path->conicTo(rand.nextF()*100, rand.nextF()*100,
5004 rand.nextF()*100, rand.nextF()*100, rand.nextF()*10);
5005 break;
5006 case SkPath::kCubic_Verb:
5007 path->cubicTo(rand.nextF()*100, rand.nextF()*100,
5008 rand.nextF()*100, rand.nextF()*100,
5009 rand.nextF()*100, rand.nextF()*100);
5010 break;
5011 default:
5012 SkASSERT(false);
5013 }
5014 }
5015}
5016
Mike Kleinc0bd9f92019-04-23 12:05:21 -05005017#include "include/pathops/SkPathOps.h"
Yuqian Li3154a532017-09-06 13:33:30 -04005018DEF_TEST(path_tight_bounds, reporter) {
5019 SkRandom rand;
5020
5021 const SkPath::Verb verbs[] = {
5022 SkPath::kLine_Verb, SkPath::kQuad_Verb, SkPath::kConic_Verb, SkPath::kCubic_Verb,
5023 };
5024 for (int i = 0; i < 1000; ++i) {
5025 for (int n = 1; n <= 10; n += 9) {
5026 for (SkPath::Verb verb : verbs) {
5027 SkPath path;
5028 rand_path(&path, rand, verb, n);
5029 SkRect bounds = path.getBounds();
5030 SkRect tight = path.computeTightBounds();
5031 REPORTER_ASSERT(reporter, bounds.contains(tight));
5032
5033 SkRect tight2;
5034 TightBounds(path, &tight2);
5035 REPORTER_ASSERT(reporter, nearly_equal(tight, tight2));
5036 }
5037 }
5038 }
5039}
5040
5041DEF_TEST(skbug_6450, r) {
5042 SkRect ri = { 0.18554693f, 195.26283f, 0.185784385f, 752.644409f };
5043 SkVector rdi[4] = {
5044 { 1.81159976e-09f, 7.58768801e-05f },
5045 { 0.000118725002f, 0.000118725002f },
5046 { 0.000118725002f, 0.000118725002f },
5047 { 0.000118725002f, 0.486297607f }
5048 };
5049 SkRRect irr;
5050 irr.setRectRadii(ri, rdi);
5051 SkRect ro = { 9.18354821e-39f, 2.1710848e+9f, 2.16945843e+9f, 3.47808128e+9f };
5052 SkVector rdo[4] = {
5053 { 0, 0 },
5054 { 0.0103298295f, 0.185887396f },
5055 { 2.52999727e-29f, 169.001938f },
5056 { 195.262741f, 195.161255f }
5057 };
5058 SkRRect orr;
5059 orr.setRectRadii(ro, rdo);
5060 SkMakeNullCanvas()->drawDRRect(orr, irr, SkPaint());
5061}
5062
5063DEF_TEST(PathRefSerialization, reporter) {
5064 SkPath path;
5065 const size_t numMoves = 5;
5066 const size_t numConics = 7;
5067 const size_t numPoints = numMoves + 2 * numConics;
5068 const size_t numVerbs = numMoves + numConics;
5069 for (size_t i = 0; i < numMoves; ++i) path.moveTo(1, 2);
5070 for (size_t i = 0; i < numConics; ++i) path.conicTo(1, 2, 3, 4, 5);
5071 REPORTER_ASSERT(reporter, path.countPoints() == numPoints);
5072 REPORTER_ASSERT(reporter, path.countVerbs() == numVerbs);
5073
5074 // Verify that path serializes/deserializes properly.
5075 sk_sp<SkData> data = path.serialize();
5076 size_t bytesWritten = data->size();
5077
5078 {
5079 SkPath readBack;
5080 REPORTER_ASSERT(reporter, readBack != path);
5081 size_t bytesRead = readBack.readFromMemory(data->data(), bytesWritten);
5082 REPORTER_ASSERT(reporter, bytesRead == bytesWritten);
5083 REPORTER_ASSERT(reporter, readBack == path);
5084 }
5085
Yuqian Li3154a532017-09-06 13:33:30 -04005086 // One less byte (rounded down to alignment) than was written will also
5087 // fail to be deserialized.
5088 {
5089 SkPath readBack;
5090 size_t bytesRead = readBack.readFromMemory(data->data(), bytesWritten - 4);
5091 REPORTER_ASSERT(reporter, !bytesRead);
5092 }
5093}
Mike Klein1170a552017-09-08 15:00:25 -04005094
5095DEF_TEST(NonFinitePathIteration, reporter) {
5096 SkPath path;
5097 path.moveTo(SK_ScalarInfinity, SK_ScalarInfinity);
Chris Daltonde500372020-05-05 15:06:30 -06005098 SkPathPriv::Iterate iterate(path);
5099 REPORTER_ASSERT(reporter, iterate.begin() == iterate.end());
Mike Klein1170a552017-09-08 15:00:25 -04005100}
Cary Clark1acd3c72017-12-08 11:37:01 -05005101
Cary Clark1acd3c72017-12-08 11:37:01 -05005102DEF_TEST(AndroidArc, reporter) {
5103 const char* tests[] = {
Brian Salomon7a492f72020-08-21 12:39:33 -04005104 "M50,0A50,50,0,0 1 100,50 L100,85 A15,15,0,0 1 85,100 L50,100 A50,50,0,0 1 50,0z",
5105 ("M50,0L92,0 A8,8,0,0 1 100,8 L100,92 A8,8,0,0 1 92,100 L8,100"
5106 " A8,8,0,0 1 0,92 L 0,8 A8,8,0,0 1 8,0z"),
5107 "M50 0A50 50,0,1,1,50 100A50 50,0,1,1,50 0"
Cary Clark1acd3c72017-12-08 11:37:01 -05005108 };
5109 for (auto test : tests) {
5110 SkPath aPath;
5111 SkAssertResult(SkParsePath::FromSVGString(test, &aPath));
5112 SkASSERT(aPath.isConvex());
5113 for (SkScalar scale = 1; scale < 1000; scale *= 1.1f) {
5114 SkPath scalePath = aPath;
5115 SkMatrix matrix;
5116 matrix.setScale(scale, scale);
5117 scalePath.transform(matrix);
5118 SkASSERT(scalePath.isConvex());
5119 }
5120 for (SkScalar scale = 1; scale < .001; scale /= 1.1f) {
5121 SkPath scalePath = aPath;
5122 SkMatrix matrix;
5123 matrix.setScale(scale, scale);
5124 scalePath.transform(matrix);
5125 SkASSERT(scalePath.isConvex());
5126 }
5127 }
5128}
Mike Reed1fe0bcc2018-01-22 16:49:49 -05005129
5130/*
5131 * Try a range of crazy values, just to ensure that we don't assert/crash.
5132 */
5133DEF_TEST(HugeGeometry, reporter) {
5134 auto surf = SkSurface::MakeRasterN32Premul(100, 100);
5135 auto canvas = surf->getCanvas();
5136
5137 const bool aas[] = { false, true };
5138 const SkPaint::Style styles[] = {
5139 SkPaint::kFill_Style, SkPaint::kStroke_Style, SkPaint::kStrokeAndFill_Style
5140 };
5141 const SkScalar values[] = {
5142 0, 1, 1000, 1000 * 1000, 1000.f * 1000 * 10000, SK_ScalarMax / 2, SK_ScalarMax,
5143 SK_ScalarInfinity
5144 };
5145
5146 SkPaint paint;
5147 for (auto x : values) {
5148 SkRect r = { -x, -x, x, x };
5149 for (auto width : values) {
5150 paint.setStrokeWidth(width);
5151 for (auto aa : aas) {
5152 paint.setAntiAlias(aa);
5153 for (auto style : styles) {
5154 paint.setStyle(style);
5155 canvas->drawRect(r, paint);
5156 canvas->drawOval(r, paint);
5157 }
5158 }
5159 }
5160 }
5161
5162}
5163
Mike Reed1c6e7832018-01-30 11:29:36 -05005164// Treat nonfinite paths as "empty" or "full", depending on inverse-filltype
5165DEF_TEST(ClipPath_nonfinite, reporter) {
5166 auto surf = SkSurface::MakeRasterN32Premul(10, 10);
5167 SkCanvas* canvas = surf->getCanvas();
5168
5169 REPORTER_ASSERT(reporter, !canvas->isClipEmpty());
5170 for (bool aa : {false, true}) {
Mike Reed7d34dc72019-11-26 12:17:17 -05005171 for (auto ft : {SkPathFillType::kWinding, SkPathFillType::kInverseWinding}) {
Mike Reed1c6e7832018-01-30 11:29:36 -05005172 for (SkScalar bad : {SK_ScalarInfinity, SK_ScalarNaN}) {
5173 for (int bits = 1; bits <= 15; ++bits) {
5174 SkPoint p0 = { 0, 0 };
5175 SkPoint p1 = { 0, 0 };
5176 if (bits & 1) p0.fX = -bad;
5177 if (bits & 2) p0.fY = -bad;
5178 if (bits & 4) p1.fX = bad;
5179 if (bits & 8) p1.fY = bad;
5180
5181 SkPath path;
5182 path.moveTo(p0);
5183 path.lineTo(p1);
5184 path.setFillType(ft);
5185 canvas->save();
5186 canvas->clipPath(path, aa);
5187 REPORTER_ASSERT(reporter, canvas->isClipEmpty() == !path.isInverseFillType());
5188 canvas->restore();
5189 }
5190 }
5191 }
5192 }
5193 REPORTER_ASSERT(reporter, !canvas->isClipEmpty());
5194}
5195
Cary Clark5c715182018-04-09 16:07:11 -04005196// skbug.com/7792
5197DEF_TEST(Path_isRect, reporter) {
Cary Clark8540e112018-04-11 14:30:27 -04005198 auto makePath = [](const SkPoint* points, size_t count, bool close) -> SkPath {
5199 SkPath path;
5200 for (size_t index = 0; index < count; ++index) {
5201 index < 2 ? path.moveTo(points[index]) : path.lineTo(points[index]);
5202 }
Cary Clark31608c02018-04-12 10:29:46 -04005203 if (close) {
5204 path.close();
5205 }
Cary Clark8540e112018-04-11 14:30:27 -04005206 return path;
5207 };
5208 auto makePath2 = [](const SkPoint* points, const SkPath::Verb* verbs, size_t count) -> SkPath {
5209 SkPath path;
5210 for (size_t index = 0; index < count; ++index) {
5211 switch (verbs[index]) {
5212 case SkPath::kMove_Verb:
5213 path.moveTo(*points++);
5214 break;
5215 case SkPath::kLine_Verb:
5216 path.lineTo(*points++);
5217 break;
5218 case SkPath::kClose_Verb:
5219 path.close();
5220 break;
5221 default:
5222 SkASSERT(0);
5223 }
5224 }
5225 return path;
5226 };
Cary Clarkd4228472018-04-13 07:07:04 -04005227 // isolated from skbug.com/7792 (bug description)
Cary Clark5c715182018-04-09 16:07:11 -04005228 SkRect rect;
Cary Clark8540e112018-04-11 14:30:27 -04005229 SkPoint points[] = { {10, 10}, {75, 75}, {150, 75}, {150, 150}, {75, 150} };
5230 SkPath path = makePath(points, SK_ARRAY_COUNT(points), false);
Cary Clarkb120e922018-04-18 12:25:08 -04005231 REPORTER_ASSERT(reporter, path.isRect(&rect));
Cary Clark5c715182018-04-09 16:07:11 -04005232 SkRect compare;
Mike Reed92b33352019-08-24 19:39:13 -04005233 compare.setBounds(&points[1], SK_ARRAY_COUNT(points) - 1);
Cary Clark5c715182018-04-09 16:07:11 -04005234 REPORTER_ASSERT(reporter, rect == compare);
Cary Clarkd4228472018-04-13 07:07:04 -04005235 // isolated from skbug.com/7792#c3
Cary Clark8540e112018-04-11 14:30:27 -04005236 SkPoint points3[] = { {75, 50}, {100, 75}, {150, 75}, {150, 150}, {75, 150}, {75, 50} };
5237 path = makePath(points3, SK_ARRAY_COUNT(points3), true);
Cary Clarkb120e922018-04-18 12:25:08 -04005238 REPORTER_ASSERT(reporter, !path.isRect(&rect));
Cary Clarkd4228472018-04-13 07:07:04 -04005239 // isolated from skbug.com/7792#c9
Cary Clark8540e112018-04-11 14:30:27 -04005240 SkPoint points9[] = { {10, 10}, {75, 75}, {150, 75}, {150, 150}, {75, 150} };
5241 path = makePath(points9, SK_ARRAY_COUNT(points9), true);
Cary Clarkb120e922018-04-18 12:25:08 -04005242 REPORTER_ASSERT(reporter, path.isRect(&rect));
Mike Reed92b33352019-08-24 19:39:13 -04005243 compare.setBounds(&points9[1], SK_ARRAY_COUNT(points9) - 1);
Cary Clark8540e112018-04-11 14:30:27 -04005244 REPORTER_ASSERT(reporter, rect == compare);
Cary Clarkd4228472018-04-13 07:07:04 -04005245 // isolated from skbug.com/7792#c11
Cary Clark8540e112018-04-11 14:30:27 -04005246 SkPath::Verb verbs11[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb,
5247 SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb };
5248 SkPoint points11[] = { {75, 150}, {75, 75}, {150, 75}, {150, 150}, {75, 150}, {75, 150} };
5249 path = makePath2(points11, verbs11, SK_ARRAY_COUNT(verbs11));
Cary Clarkb120e922018-04-18 12:25:08 -04005250 REPORTER_ASSERT(reporter, path.isRect(&rect));
Mike Reed92b33352019-08-24 19:39:13 -04005251 compare.setBounds(&points11[0], SK_ARRAY_COUNT(points11));
Cary Clark8540e112018-04-11 14:30:27 -04005252 REPORTER_ASSERT(reporter, rect == compare);
Cary Clarkd4228472018-04-13 07:07:04 -04005253 // isolated from skbug.com/7792#c14
Cary Clark8540e112018-04-11 14:30:27 -04005254 SkPath::Verb verbs14[] = { SkPath::kMove_Verb, SkPath::kMove_Verb, SkPath::kMove_Verb,
5255 SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb,
5256 SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb,
5257 SkPath::kLine_Verb, SkPath::kClose_Verb };
5258 SkPoint points14[] = { {250, 75}, {250, 75}, {250, 75}, {100, 75},
5259 {150, 75}, {150, 150}, {75, 150}, {75, 75}, {0, 0} };
5260 path = makePath2(points14, verbs14, SK_ARRAY_COUNT(verbs14));
Cary Clarkb120e922018-04-18 12:25:08 -04005261 REPORTER_ASSERT(reporter, !path.isRect(&rect));
Cary Clarkd4228472018-04-13 07:07:04 -04005262 // isolated from skbug.com/7792#c15
Cary Clark8540e112018-04-11 14:30:27 -04005263 SkPath::Verb verbs15[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb,
5264 SkPath::kLine_Verb, SkPath::kMove_Verb };
5265 SkPoint points15[] = { {75, 75}, {150, 75}, {150, 150}, {75, 150}, {250, 75} };
5266 path = makePath2(points15, verbs15, SK_ARRAY_COUNT(verbs15));
Cary Clarkb120e922018-04-18 12:25:08 -04005267 REPORTER_ASSERT(reporter, path.isRect(&rect));
Mike Reed92b33352019-08-24 19:39:13 -04005268 compare.setBounds(&points15[0], SK_ARRAY_COUNT(points15) - 1);
Cary Clark8540e112018-04-11 14:30:27 -04005269 REPORTER_ASSERT(reporter, rect == compare);
Cary Clarkd4228472018-04-13 07:07:04 -04005270 // isolated from skbug.com/7792#c17
Cary Clark31608c02018-04-12 10:29:46 -04005271 SkPoint points17[] = { {75, 10}, {75, 75}, {150, 75}, {150, 150}, {75, 150}, {75, 10} };
5272 path = makePath(points17, SK_ARRAY_COUNT(points17), true);
Cary Clarkb120e922018-04-18 12:25:08 -04005273 REPORTER_ASSERT(reporter, !path.isRect(&rect));
Cary Clarkd4228472018-04-13 07:07:04 -04005274 // isolated from skbug.com/7792#c19
Cary Clark88ba9712018-04-12 14:00:24 -04005275 SkPath::Verb verbs19[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb,
5276 SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb,
5277 SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb,
5278 SkPath::kLine_Verb, SkPath::kLine_Verb };
5279 SkPoint points19[] = { {75, 75}, {75, 75}, {75, 75}, {75, 75}, {150, 75}, {150, 150},
5280 {75, 150}, {10, 10}, {30, 10}, {10, 30} };
5281 path = makePath2(points19, verbs19, SK_ARRAY_COUNT(verbs19));
Cary Clarkb120e922018-04-18 12:25:08 -04005282 REPORTER_ASSERT(reporter, !path.isRect(&rect));
Cary Clarkd4228472018-04-13 07:07:04 -04005283 // isolated from skbug.com/7792#c23
5284 SkPath::Verb verbs23[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb,
5285 SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb,
5286 SkPath::kLine_Verb, SkPath::kClose_Verb };
5287 SkPoint points23[] = { {75, 75}, {75, 75}, {75, 75}, {75, 75}, {150, 75}, {150, 150},
5288 {75, 150} };
5289 path = makePath2(points23, verbs23, SK_ARRAY_COUNT(verbs23));
Cary Clarkb120e922018-04-18 12:25:08 -04005290 REPORTER_ASSERT(reporter, path.isRect(&rect));
Mike Reed92b33352019-08-24 19:39:13 -04005291 compare.setBounds(&points23[0], SK_ARRAY_COUNT(points23));
Cary Clarkd4228472018-04-13 07:07:04 -04005292 REPORTER_ASSERT(reporter, rect == compare);
Cary Clark48c464a2018-04-16 12:06:07 -04005293 // isolated from skbug.com/7792#c29
5294 SkPath::Verb verbs29[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb,
5295 SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb,
5296 SkPath::kClose_Verb };
5297 SkPoint points29[] = { {75, 75}, {150, 75}, {150, 150}, {75, 150}, {75, 250}, {75, 75} };
5298 path = makePath2(points29, verbs29, SK_ARRAY_COUNT(verbs29));
Cary Clarkb120e922018-04-18 12:25:08 -04005299 REPORTER_ASSERT(reporter, !path.isRect(&rect));
Cary Clarka7651562018-04-17 09:30:14 -04005300 // isolated from skbug.com/7792#c31
5301 SkPath::Verb verbs31[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb,
5302 SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb,
5303 SkPath::kClose_Verb };
5304 SkPoint points31[] = { {75, 75}, {150, 75}, {150, 150}, {75, 150}, {75, 10}, {75, 75} };
5305 path = makePath2(points31, verbs31, SK_ARRAY_COUNT(verbs31));
Cary Clarkb120e922018-04-18 12:25:08 -04005306 REPORTER_ASSERT(reporter, path.isRect(&rect));
Mike Reed92b33352019-08-24 19:39:13 -04005307 compare.setBounds(&points31[0], 4);
Cary Clarka7651562018-04-17 09:30:14 -04005308 REPORTER_ASSERT(reporter, rect == compare);
Cary Clark1cd60982018-04-17 11:53:34 -04005309 // isolated from skbug.com/7792#c36
5310 SkPath::Verb verbs36[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb,
5311 SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb };
5312 SkPoint points36[] = { {75, 75}, {150, 75}, {150, 150}, {10, 150}, {75, 75}, {75, 75} };
5313 path = makePath2(points36, verbs36, SK_ARRAY_COUNT(verbs36));
Cary Clarkb120e922018-04-18 12:25:08 -04005314 REPORTER_ASSERT(reporter, !path.isRect(&rect));
Cary Clark4eb6f7a2018-04-17 13:34:37 -04005315 // isolated from skbug.com/7792#c39
5316 SkPath::Verb verbs39[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb,
Cary Clarkdbc59ba2018-04-19 07:37:29 -04005317 SkPath::kLine_Verb };
Cary Clark4eb6f7a2018-04-17 13:34:37 -04005318 SkPoint points39[] = { {150, 75}, {150, 150}, {75, 150}, {75, 100} };
5319 path = makePath2(points39, verbs39, SK_ARRAY_COUNT(verbs39));
Cary Clarkb120e922018-04-18 12:25:08 -04005320 REPORTER_ASSERT(reporter, !path.isRect(&rect));
Cary Clarkdbc59ba2018-04-19 07:37:29 -04005321 // isolated from zero_length_paths_aa
Cary Clarkb120e922018-04-18 12:25:08 -04005322 SkPath::Verb verbsAA[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb,
5323 SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb,
5324 SkPath::kLine_Verb, SkPath::kClose_Verb };
5325 SkPoint pointsAA[] = { {32, 9.5f}, {32, 9.5f}, {32, 17}, {17, 17}, {17, 9.5f}, {17, 2},
5326 {32, 2} };
5327 path = makePath2(pointsAA, verbsAA, SK_ARRAY_COUNT(verbsAA));
5328 REPORTER_ASSERT(reporter, path.isRect(&rect));
Mike Reed92b33352019-08-24 19:39:13 -04005329 compare.setBounds(&pointsAA[0], SK_ARRAY_COUNT(pointsAA));
Cary Clarkb120e922018-04-18 12:25:08 -04005330 REPORTER_ASSERT(reporter, rect == compare);
Cary Clarkdbc59ba2018-04-19 07:37:29 -04005331 // isolated from skbug.com/7792#c41
5332 SkPath::Verb verbs41[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb,
5333 SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb,
5334 SkPath::kClose_Verb };
5335 SkPoint points41[] = { {75, 75}, {150, 75}, {150, 150}, {140, 150}, {140, 75}, {75, 75} };
5336 path = makePath2(points41, verbs41, SK_ARRAY_COUNT(verbs41));
5337 REPORTER_ASSERT(reporter, path.isRect(&rect));
Mike Reed92b33352019-08-24 19:39:13 -04005338 compare.setBounds(&points41[1], 4);
Cary Clarkdbc59ba2018-04-19 07:37:29 -04005339 REPORTER_ASSERT(reporter, rect == compare);
5340 // isolated from skbug.com/7792#c53
5341 SkPath::Verb verbs53[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb,
5342 SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb,
5343 SkPath::kClose_Verb };
5344 SkPoint points53[] = { {75, 75}, {150, 75}, {150, 150}, {140, 150}, {140, 75}, {75, 75} };
5345 path = makePath2(points53, verbs53, SK_ARRAY_COUNT(verbs53));
5346 REPORTER_ASSERT(reporter, path.isRect(&rect));
Mike Reed92b33352019-08-24 19:39:13 -04005347 compare.setBounds(&points53[1], 4);
Cary Clarkdbc59ba2018-04-19 07:37:29 -04005348 REPORTER_ASSERT(reporter, rect == compare);
Cary Clark5c715182018-04-09 16:07:11 -04005349}
Mike Reedee430912018-05-23 12:12:21 -04005350
Mike Reedc3d8a482018-09-12 10:08:40 -04005351// Be sure we can safely add ourselves
5352DEF_TEST(Path_self_add, reporter) {
5353 // The possible problem is that during path.add() we may have to grow the dst buffers as
5354 // we append the src pts/verbs, but all the while we are iterating over the src. If src == dst
5355 // we could realloc the buffer's (on behalf of dst) leaving the src iterator pointing at
5356 // garbage.
5357 //
5358 // The test runs though verious sized src paths, since its not defined publicly what the
5359 // reserve allocation strategy is for SkPath, therefore we can't know when an append operation
5360 // will trigger a realloc. At the time of this writing, these loops were sufficient to trigger
5361 // an ASAN error w/o the fix to SkPath::addPath().
5362 //
5363 for (int count = 0; count < 10; ++count) {
5364 SkPath path;
5365 for (int add = 0; add < count; ++add) {
5366 // just add some stuff, so we have something to copy/append in addPath()
5367 path.moveTo(1, 2).lineTo(3, 4).cubicTo(1,2,3,4,5,6).conicTo(1,2,3,4,5);
5368 }
5369 path.addPath(path, 1, 2);
5370 path.addPath(path, 3, 4);
5371 }
5372}
5373
Mike Kleinc0bd9f92019-04-23 12:05:21 -05005374#include "include/core/SkVertices.h"
Mike Reed50595412018-05-30 16:32:33 -04005375static void draw_triangle(SkCanvas* canvas, const SkPoint pts[]) {
5376 // draw in different ways, looking for an assert
5377
5378 {
5379 SkPath path;
5380 path.addPoly(pts, 3, false);
5381 canvas->drawPath(path, SkPaint());
5382 }
5383
5384 const SkColor colors[] = { SK_ColorBLACK, SK_ColorBLACK, SK_ColorBLACK };
5385 auto v = SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode, 3, pts, nullptr, colors);
5386 canvas->drawVertices(v, SkBlendMode::kSrcOver, SkPaint());
5387}
5388
Mike Reedee430912018-05-23 12:12:21 -04005389DEF_TEST(triangle_onehalf, reporter) {
5390 auto surface(SkSurface::MakeRasterN32Premul(100, 100));
5391
5392 const SkPoint pts[] = {
5393 { 0.499069244f, 9.63295173f },
5394 { 0.499402374f, 7.88207579f },
5395 { 10.2363272f, 0.49999997f }
5396 };
Mike Reed50595412018-05-30 16:32:33 -04005397 draw_triangle(surface->getCanvas(), pts);
Mike Reedee430912018-05-23 12:12:21 -04005398}
Mike Reed50595412018-05-30 16:32:33 -04005399
5400DEF_TEST(triangle_big, reporter) {
5401 auto surface(SkSurface::MakeRasterN32Premul(4, 4304));
5402
5403 // The first two points, when sent through our fixed-point SkEdge, can walk negative beyond
5404 // -0.5 due to accumulated += error of the slope. We have since make the bounds calculation
5405 // be conservative, so we invoke clipping if we get in this situation.
5406 // This test was added to demonstrate the need for this conservative bounds calc.
5407 // (found by a fuzzer)
5408 const SkPoint pts[] = {
5409 { 0.327190518f, -114.945152f },
5410 { -0.5f, 1.00003874f },
5411 { 0.666425824f, 4304.26172f },
5412 };
5413 draw_triangle(surface->getCanvas(), pts);
5414}
5415
Florin Malita3d413c52018-09-11 14:01:42 -04005416static void add_verbs(SkPath* path, int count) {
5417 path->moveTo(0, 0);
5418 for (int i = 0; i < count; ++i) {
5419 switch (i & 3) {
5420 case 0: path->lineTo(10, 20); break;
5421 case 1: path->quadTo(5, 6, 7, 8); break;
5422 case 2: path->conicTo(1, 2, 3, 4, 0.5f); break;
5423 case 3: path->cubicTo(2, 4, 6, 8, 10, 12); break;
5424 }
5425 }
5426}
5427
5428// Make sure when we call shrinkToFit() that we always shrink (or stay the same)
5429// and that if we call twice, we stay the same.
5430DEF_TEST(Path_shrinkToFit, reporter) {
5431 size_t max_free = 0;
5432 for (int verbs = 0; verbs < 100; ++verbs) {
5433 SkPath unique_path, shared_path;
5434 add_verbs(&unique_path, verbs);
5435 add_verbs(&shared_path, verbs);
5436
5437 const SkPath copy = shared_path;
5438 REPORTER_ASSERT(reporter, shared_path == unique_path);
5439 REPORTER_ASSERT(reporter, shared_path == copy);
5440
5441#ifdef SK_DEBUG
Florin Malita0e0f1a72018-09-11 16:16:19 -04005442 size_t before = PathTest_Private::GetFreeSpace(unique_path);
Florin Malita3d413c52018-09-11 14:01:42 -04005443#endif
5444 unique_path.shrinkToFit();
5445 shared_path.shrinkToFit();
5446 REPORTER_ASSERT(reporter, shared_path == unique_path);
5447 REPORTER_ASSERT(reporter, shared_path == copy);
5448
5449#ifdef SK_DEBUG
Florin Malita0e0f1a72018-09-11 16:16:19 -04005450 size_t after = PathTest_Private::GetFreeSpace(unique_path);
Florin Malita3d413c52018-09-11 14:01:42 -04005451 REPORTER_ASSERT(reporter, before >= after);
5452 max_free = std::max(max_free, before - after);
5453
Florin Malita0e0f1a72018-09-11 16:16:19 -04005454 size_t after2 = PathTest_Private::GetFreeSpace(unique_path);
Florin Malita3d413c52018-09-11 14:01:42 -04005455 REPORTER_ASSERT(reporter, after == after2);
5456#endif
5457 }
5458 if (false) {
5459 SkDebugf("max_free %zu\n", max_free);
5460 }
5461}
Mike Klein9de72162018-09-24 20:30:28 -04005462
5463DEF_TEST(Path_setLastPt, r) {
5464 // There was a time where SkPath::setLastPoint() didn't invalidate cached path bounds.
5465 SkPath p;
5466 p.moveTo(0,0);
5467 p.moveTo(20,01);
5468 p.moveTo(20,10);
5469 p.moveTo(20,61);
5470 REPORTER_ASSERT(r, p.getBounds() == SkRect::MakeLTRB(0,0, 20,61));
5471
5472 p.setLastPt(30,01);
5473 REPORTER_ASSERT(r, p.getBounds() == SkRect::MakeLTRB(0,0, 30,10)); // was {0,0, 20,61}
5474
5475 REPORTER_ASSERT(r, p.isValid());
Mike Klein9de72162018-09-24 20:30:28 -04005476}
Mike Reed6a388002018-10-16 13:13:09 -04005477
5478DEF_TEST(Path_increserve_handle_neg_crbug_883666, r) {
5479 SkPath path;
5480
5481 path.conicTo({0, 0}, {1, 1}, SK_FloatNegativeInfinity);
5482
5483 // <== use a copy path object to force SkPathRef::copy() and SkPathRef::resetToSize()
5484 SkPath shallowPath = path;
5485
5486 // make sure we don't assert/crash on this.
5487 shallowPath.incReserve(0xffffffff);
5488}
5489
Mike Reed07105bb2018-12-01 14:07:49 -05005490////////////////////////////////////////////////////////////////////////////////////////////////
5491
5492/*
5493 * For speed, we tried to preserve useful/expensive attributes about paths,
5494 * - convexity, isrect, isoval, ...
5495 * Axis-aligned shapes (rect, oval, rrect) should survive, including convexity if the matrix
5496 * is axis-aligned (e.g. scale+translate)
5497 */
5498
5499struct Xforms {
5500 SkMatrix fIM, fTM, fSM, fRM;
5501
5502 Xforms() {
5503 fIM.reset();
5504 fTM.setTranslate(10, 20);
5505 fSM.setScale(2, 3);
5506 fRM.setRotate(30);
5507 }
5508};
5509
Mike Reed07105bb2018-12-01 14:07:49 -05005510static bool conditional_convex(const SkPath& path, bool is_convex) {
Mike Reed3872c982020-08-29 17:46:51 -04005511 SkPathConvexity c = SkPathPriv::GetConvexityOrUnknown(path);
5512 return is_convex ? (c == SkPathConvexity::kConvex) : (c != SkPathConvexity::kConvex);
Mike Reed07105bb2018-12-01 14:07:49 -05005513}
Mike Reed07105bb2018-12-01 14:07:49 -05005514
5515// expect axis-aligned shape to survive assignment, identity and scale/translate matrices
5516template <typename ISA>
5517void survive(SkPath* path, const Xforms& x, bool isAxisAligned, skiatest::Reporter* reporter,
5518 ISA isa_proc) {
5519 REPORTER_ASSERT(reporter, isa_proc(*path));
5520 // force the issue (computing convexity) the first time.
Mike Reed6052c0b2020-08-28 11:49:56 -04005521 REPORTER_ASSERT(reporter, path->isConvex());
Mike Reed07105bb2018-12-01 14:07:49 -05005522
5523 SkPath path2;
5524
5525 // a path's isa and convexity should survive assignment
5526 path2 = *path;
5527 REPORTER_ASSERT(reporter, isa_proc(path2));
Mike Reed3872c982020-08-29 17:46:51 -04005528 REPORTER_ASSERT(reporter, SkPathPriv::GetConvexityOrUnknown(path2) == SkPathConvexity::kConvex);
Mike Reed07105bb2018-12-01 14:07:49 -05005529
5530 // a path's isa and convexity should identity transform
5531 path->transform(x.fIM, &path2);
5532 path->transform(x.fIM);
5533 REPORTER_ASSERT(reporter, isa_proc(path2));
Mike Reed3872c982020-08-29 17:46:51 -04005534 REPORTER_ASSERT(reporter, SkPathPriv::GetConvexityOrUnknown(path2) == SkPathConvexity::kConvex);
Mike Reed07105bb2018-12-01 14:07:49 -05005535 REPORTER_ASSERT(reporter, isa_proc(*path));
Mike Reed3872c982020-08-29 17:46:51 -04005536 REPORTER_ASSERT(reporter, SkPathPriv::GetConvexityOrUnknown(*path) == SkPathConvexity::kConvex);
Mike Reed07105bb2018-12-01 14:07:49 -05005537
5538 // a path's isa should survive translation, convexity depends on axis alignment
5539 path->transform(x.fTM, &path2);
5540 path->transform(x.fTM);
5541 REPORTER_ASSERT(reporter, isa_proc(path2));
5542 REPORTER_ASSERT(reporter, isa_proc(*path));
Mike Reed07105bb2018-12-01 14:07:49 -05005543 REPORTER_ASSERT(reporter, conditional_convex(path2, isAxisAligned));
5544 REPORTER_ASSERT(reporter, conditional_convex(*path, isAxisAligned));
Mike Reed07105bb2018-12-01 14:07:49 -05005545
5546 // a path's isa should survive scaling, convexity depends on axis alignment
5547 path->transform(x.fSM, &path2);
5548 path->transform(x.fSM);
5549 REPORTER_ASSERT(reporter, isa_proc(path2));
5550 REPORTER_ASSERT(reporter, isa_proc(*path));
Mike Reed07105bb2018-12-01 14:07:49 -05005551 REPORTER_ASSERT(reporter, conditional_convex(path2, isAxisAligned));
5552 REPORTER_ASSERT(reporter, conditional_convex(*path, isAxisAligned));
Mike Reed07105bb2018-12-01 14:07:49 -05005553
5554 // For security, post-rotation, we can't assume we're still convex. It might prove to be,
5555 // in fact, still be convex, be we can't have cached that setting, hence the call to
Mike Reed3872c982020-08-29 17:46:51 -04005556 // getConvexityOrUnknown() instead of getConvexity().
Mike Reed07105bb2018-12-01 14:07:49 -05005557 path->transform(x.fRM, &path2);
5558 path->transform(x.fRM);
Mike Reed3872c982020-08-29 17:46:51 -04005559 REPORTER_ASSERT(reporter, SkPathPriv::GetConvexityOrUnknown(path2) != SkPathConvexity::kConvex);
5560 REPORTER_ASSERT(reporter, SkPathPriv::GetConvexityOrUnknown(*path) != SkPathConvexity::kConvex);
Mike Reed375721d2020-08-07 16:00:06 -04005561
Mike Reed07105bb2018-12-01 14:07:49 -05005562 if (isAxisAligned) {
5563 REPORTER_ASSERT(reporter, !isa_proc(path2));
5564 REPORTER_ASSERT(reporter, !isa_proc(*path));
5565 }
Mike Reed07105bb2018-12-01 14:07:49 -05005566}
5567
5568DEF_TEST(Path_survive_transform, r) {
5569 const Xforms x;
5570
5571 SkPath path;
5572 path.addRect({10, 10, 40, 50});
5573 survive(&path, x, true, r, [](const SkPath& p) { return p.isRect(nullptr); });
5574
5575 path.reset();
5576 path.addOval({10, 10, 40, 50});
5577 survive(&path, x, true, r, [](const SkPath& p) { return p.isOval(nullptr); });
5578
5579 path.reset();
5580 path.addRRect(SkRRect::MakeRectXY({10, 10, 40, 50}, 5, 5));
5581 survive(&path, x, true, r, [](const SkPath& p) { return p.isRRect(nullptr); });
5582
5583 // make a trapazoid; definitely convex, but not marked as axis-aligned (e.g. oval, rrect)
5584 path.reset();
5585 path.moveTo(0, 0).lineTo(100, 0).lineTo(70, 100).lineTo(30, 100);
Mike Reed6052c0b2020-08-28 11:49:56 -04005586 REPORTER_ASSERT(r, path.isConvex());
Mike Reed07105bb2018-12-01 14:07:49 -05005587 survive(&path, x, false, r, [](const SkPath& p) { return true; });
5588}
Mike Reedfb5f43b2018-12-18 12:17:57 -05005589
5590DEF_TEST(path_last_move_to_index, r) {
5591 // Make sure that copyPath is safe after the call to path.offset().
5592 // Previously, we would leave its fLastMoveToIndex alone after the copy, but now we should
5593 // set it to path's value inside SkPath::transform()
5594
Mike Reeddf3d2252018-12-20 17:10:27 -05005595 const char text[] = "hello";
5596 constexpr size_t len = sizeof(text) - 1;
5597 SkGlyphID glyphs[len];
Mike Reedfb5f43b2018-12-18 12:17:57 -05005598
Mike Reeddf3d2252018-12-20 17:10:27 -05005599 SkFont font;
Ben Wagner51e15a62019-05-07 15:38:46 -04005600 font.textToGlyphs(text, len, SkTextEncoding::kUTF8, glyphs, len);
Mike Reeddf3d2252018-12-20 17:10:27 -05005601
5602 SkPath copyPath;
5603 SkFont().getPaths(glyphs, len, [](const SkPath* src, const SkMatrix& mx, void* ctx) {
5604 if (src) {
5605 ((SkPath*)ctx)->addPath(*src, mx);
5606 }
5607 }, &copyPath);
Mike Reedfb5f43b2018-12-18 12:17:57 -05005608
Kevin Lubick13486cb2019-01-04 09:49:47 -05005609 SkScalar radii[] = { 80, 100, 0, 0, 40, 60, 0, 0 };
Mike Reeddf3d2252018-12-20 17:10:27 -05005610 SkPath path;
Mike Reedfb5f43b2018-12-18 12:17:57 -05005611 path.addRoundRect({10, 10, 110, 110}, radii);
5612 path.offset(0, 5, &(copyPath)); // <== change buffer copyPath.fPathRef->fPoints but not reset copyPath.fLastMoveToIndex lead to out of bound
5613
5614 copyPath.rConicTo(1, 1, 3, 3, 0.707107f);
5615}
Mike Reed5f152f02019-08-19 14:40:16 -04005616
5617static void test_edger(skiatest::Reporter* r,
5618 const std::initializer_list<SkPath::Verb>& in,
5619 const std::initializer_list<SkPath::Verb>& expected) {
5620 SkPath path;
5621 SkScalar x = 0, y = 0;
5622 for (auto v : in) {
5623 switch (v) {
5624 case SkPath::kMove_Verb: path.moveTo(x++, y++); break;
5625 case SkPath::kLine_Verb: path.lineTo(x++, y++); break;
5626 case SkPath::kClose_Verb: path.close(); break;
5627 default: SkASSERT(false);
5628 }
5629 }
5630
5631 SkPathEdgeIter iter(path);
Mike Reed375721d2020-08-07 16:00:06 -04005632 SkPathEdgeIter iter2(path.view());
Mike Reed5f152f02019-08-19 14:40:16 -04005633 for (auto v : expected) {
5634 auto e = iter.next();
5635 REPORTER_ASSERT(r, e);
5636 REPORTER_ASSERT(r, SkPathEdgeIter::EdgeToVerb(e.fEdge) == v);
Mike Reed375721d2020-08-07 16:00:06 -04005637
5638 e = iter2.next();
5639 REPORTER_ASSERT(r, e);
5640 REPORTER_ASSERT(r, SkPathEdgeIter::EdgeToVerb(e.fEdge) == v);
Mike Reed5f152f02019-08-19 14:40:16 -04005641 }
Mike Reed375721d2020-08-07 16:00:06 -04005642 REPORTER_ASSERT(r, !iter.next());
5643 REPORTER_ASSERT(r, !iter2.next());
Mike Reed5f152f02019-08-19 14:40:16 -04005644}
5645
5646DEF_TEST(pathedger, r) {
5647 auto M = SkPath::kMove_Verb;
5648 auto L = SkPath::kLine_Verb;
5649 auto C = SkPath::kClose_Verb;
5650
5651 test_edger(r, { M }, {});
5652 test_edger(r, { M, M }, {});
5653 test_edger(r, { M, C }, {});
5654 test_edger(r, { M, M, C }, {});
5655 test_edger(r, { M, L }, { L, L });
5656 test_edger(r, { M, L, C }, { L, L });
5657 test_edger(r, { M, L, L }, { L, L, L });
5658 test_edger(r, { M, L, L, C }, { L, L, L });
5659
5660 test_edger(r, { M, L, L, M, L, L }, { L, L, L, L, L, L });
5661}