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