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