ClipStackTest.cpp revision 80bacfeb4bda06541e8695bd502229727bccfeab
1 2/* 3 * Copyright 2011 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8#include "Test.h" 9#include "SkClipStack.h" 10#include "SkPath.h" 11#include "SkRect.h" 12 13 14 15static void test_assign_and_comparison(skiatest::Reporter* reporter) { 16 SkClipStack s; 17 bool doAA = false; 18 19 REPORTER_ASSERT(reporter, 0 == s.getSaveCount()); 20 21 // Build up a clip stack with a path, an empty clip, and a rect. 22 s.save(); 23 REPORTER_ASSERT(reporter, 1 == s.getSaveCount()); 24 25 SkPath p; 26 p.moveTo(5, 6); 27 p.lineTo(7, 8); 28 p.lineTo(5, 9); 29 p.close(); 30 s.clipDevPath(p, SkRegion::kIntersect_Op, doAA); 31 32 s.save(); 33 REPORTER_ASSERT(reporter, 2 == s.getSaveCount()); 34 35 SkRect r = SkRect::MakeLTRB(1, 2, 3, 4); 36 s.clipDevRect(r, SkRegion::kIntersect_Op, doAA); 37 r = SkRect::MakeLTRB(10, 11, 12, 13); 38 s.clipDevRect(r, SkRegion::kIntersect_Op, doAA); 39 40 s.save(); 41 REPORTER_ASSERT(reporter, 3 == s.getSaveCount()); 42 43 r = SkRect::MakeLTRB(14, 15, 16, 17); 44 s.clipDevRect(r, SkRegion::kUnion_Op, doAA); 45 46 // Test that assignment works. 47 SkClipStack copy = s; 48 REPORTER_ASSERT(reporter, s == copy); 49 50 // Test that different save levels triggers not equal. 51 s.restore(); 52 REPORTER_ASSERT(reporter, 2 == s.getSaveCount()); 53 REPORTER_ASSERT(reporter, s != copy); 54 55 // Test that an equal, but not copied version is equal. 56 s.save(); 57 REPORTER_ASSERT(reporter, 3 == s.getSaveCount()); 58 59 r = SkRect::MakeLTRB(14, 15, 16, 17); 60 s.clipDevRect(r, SkRegion::kUnion_Op, doAA); 61 REPORTER_ASSERT(reporter, s == copy); 62 63 // Test that a different op on one level triggers not equal. 64 s.restore(); 65 REPORTER_ASSERT(reporter, 2 == s.getSaveCount()); 66 s.save(); 67 REPORTER_ASSERT(reporter, 3 == s.getSaveCount()); 68 69 r = SkRect::MakeLTRB(14, 15, 16, 17); 70 s.clipDevRect(r, SkRegion::kIntersect_Op, doAA); 71 REPORTER_ASSERT(reporter, s != copy); 72 73 // Test that different state (clip type) triggers not equal. 74 // NO LONGER VALID: if a path contains only a rect, we turn 75 // it into a bare rect for performance reasons (working 76 // around Chromium/JavaScript bad pattern). 77/* 78 s.restore(); 79 s.save(); 80 SkPath rp; 81 rp.addRect(r); 82 s.clipDevPath(rp, SkRegion::kUnion_Op, doAA); 83 REPORTER_ASSERT(reporter, s != copy); 84*/ 85 86 // Test that different rects triggers not equal. 87 s.restore(); 88 REPORTER_ASSERT(reporter, 2 == s.getSaveCount()); 89 s.save(); 90 REPORTER_ASSERT(reporter, 3 == s.getSaveCount()); 91 92 r = SkRect::MakeLTRB(24, 25, 26, 27); 93 s.clipDevRect(r, SkRegion::kUnion_Op, doAA); 94 REPORTER_ASSERT(reporter, s != copy); 95 96 // Sanity check 97 s.restore(); 98 REPORTER_ASSERT(reporter, 2 == s.getSaveCount()); 99 100 copy.restore(); 101 REPORTER_ASSERT(reporter, 2 == copy.getSaveCount()); 102 REPORTER_ASSERT(reporter, s == copy); 103 s.restore(); 104 REPORTER_ASSERT(reporter, 1 == s.getSaveCount()); 105 copy.restore(); 106 REPORTER_ASSERT(reporter, 1 == copy.getSaveCount()); 107 REPORTER_ASSERT(reporter, s == copy); 108 109 // Test that different paths triggers not equal. 110 s.restore(); 111 REPORTER_ASSERT(reporter, 0 == s.getSaveCount()); 112 s.save(); 113 REPORTER_ASSERT(reporter, 1 == s.getSaveCount()); 114 115 p.addRect(r); 116 s.clipDevPath(p, SkRegion::kIntersect_Op, doAA); 117 REPORTER_ASSERT(reporter, s != copy); 118} 119 120static void assert_count(skiatest::Reporter* reporter, const SkClipStack& stack, 121 int count) { 122 SkClipStack::B2TIter iter(stack); 123 int counter = 0; 124 while (iter.next()) { 125 counter += 1; 126 } 127 REPORTER_ASSERT(reporter, count == counter); 128} 129 130// Exercise the SkClipStack's bottom to top and bidirectional iterators 131// (including the skipToTopmost functionality) 132static void test_iterators(skiatest::Reporter* reporter) { 133 SkClipStack stack; 134 135 static const SkRect gRects[] = { 136 { 0, 0, 40, 40 }, 137 { 60, 0, 100, 40 }, 138 { 0, 60, 40, 100 }, 139 { 60, 60, 100, 100 } 140 }; 141 142 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) { 143 // the union op will prevent these from being fused together 144 stack.clipDevRect(gRects[i], SkRegion::kUnion_Op, false); 145 } 146 147 assert_count(reporter, stack, 4); 148 149 // bottom to top iteration 150 { 151 const SkClipStack::B2TIter::Clip* clip = NULL; 152 153 SkClipStack::B2TIter iter(stack); 154 int i; 155 156 for (i = 0, clip = iter.next(); clip; ++i, clip = iter.next()) { 157 REPORTER_ASSERT(reporter, *clip->fRect == gRects[i]); 158 } 159 160 SkASSERT(i == 4); 161 } 162 163 // top to bottom iteration 164 { 165 const SkClipStack::Iter::Clip* clip = NULL; 166 167 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); 168 int i; 169 170 for (i = 3, clip = iter.prev(); clip; --i, clip = iter.prev()) { 171 REPORTER_ASSERT(reporter, *clip->fRect == gRects[i]); 172 } 173 174 SkASSERT(i == -1); 175 } 176 177 // skipToTopmost 178 { 179 const SkClipStack::Iter::Clip*clip = NULL; 180 181 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart); 182 183 clip = iter.skipToTopmost(SkRegion::kUnion_Op); 184 REPORTER_ASSERT(reporter, *clip->fRect == gRects[3]); 185 } 186} 187 188// Exercise the SkClipStack's getConservativeBounds computation 189static void test_bounds(skiatest::Reporter* reporter, bool useRects) { 190 191 static const int gNumCases = 20; 192 static const SkRect gAnswerRectsBW[gNumCases] = { 193 // A op B 194 { 40, 40, 50, 50 }, 195 { 10, 10, 50, 50 }, 196 { 10, 10, 80, 80 }, 197 { 10, 10, 80, 80 }, 198 { 40, 40, 80, 80 }, 199 200 // invA op B 201 { 40, 40, 80, 80 }, 202 { 0, 0, 100, 100 }, 203 { 0, 0, 100, 100 }, 204 { 0, 0, 100, 100 }, 205 { 40, 40, 50, 50 }, 206 207 // A op invB 208 { 10, 10, 50, 50 }, 209 { 40, 40, 50, 50 }, 210 { 0, 0, 100, 100 }, 211 { 0, 0, 100, 100 }, 212 { 0, 0, 100, 100 }, 213 214 // invA op invB 215 { 0, 0, 100, 100 }, 216 { 40, 40, 80, 80 }, 217 { 0, 0, 100, 100 }, 218 { 10, 10, 80, 80 }, 219 { 10, 10, 50, 50 }, 220 }; 221 222 static const SkRegion::Op gOps[] = { 223 SkRegion::kIntersect_Op, 224 SkRegion::kDifference_Op, 225 SkRegion::kUnion_Op, 226 SkRegion::kXOR_Op, 227 SkRegion::kReverseDifference_Op 228 }; 229 230 SkRect rectA, rectB; 231 232 rectA.iset(10, 10, 50, 50); 233 rectB.iset(40, 40, 80, 80); 234 235 SkPath clipA, clipB; 236 237 clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5)); 238 clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5)); 239 240 SkClipStack stack; 241 SkRect devClipBound; 242 bool isIntersectionOfRects = false; 243 244 int testCase = 0; 245 int numBitTests = useRects ? 1 : 4; 246 for (int invBits = 0; invBits < numBitTests; ++invBits) { 247 for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) { 248 249 stack.save(); 250 bool doInvA = SkToBool(invBits & 1); 251 bool doInvB = SkToBool(invBits & 2); 252 253 clipA.setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType : 254 SkPath::kEvenOdd_FillType); 255 clipB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType : 256 SkPath::kEvenOdd_FillType); 257 258 if (useRects) { 259 stack.clipDevRect(rectA, SkRegion::kIntersect_Op, false); 260 stack.clipDevRect(rectB, gOps[op], false); 261 } else { 262 stack.clipDevPath(clipA, SkRegion::kIntersect_Op, false); 263 stack.clipDevPath(clipB, gOps[op], false); 264 } 265 266 REPORTER_ASSERT(reporter, !stack.isWideOpen()); 267 268 stack.getConservativeBounds(0, 0, 100, 100, &devClipBound, 269 &isIntersectionOfRects); 270 271 if (useRects) { 272 REPORTER_ASSERT(reporter, isIntersectionOfRects == 273 (gOps[op] == SkRegion::kIntersect_Op)); 274 } else { 275 REPORTER_ASSERT(reporter, !isIntersectionOfRects); 276 } 277 278 SkASSERT(testCase < gNumCases); 279 REPORTER_ASSERT(reporter, devClipBound == gAnswerRectsBW[testCase]); 280 ++testCase; 281 282 stack.restore(); 283 } 284 } 285} 286 287// Test out 'isWideOpen' entry point 288static void test_isWideOpen(skiatest::Reporter* reporter) { 289 290 SkRect rectA, rectB; 291 292 rectA.iset(10, 10, 40, 40); 293 rectB.iset(50, 50, 80, 80); 294 295 // Stack should initially be wide open 296 { 297 SkClipStack stack; 298 299 REPORTER_ASSERT(reporter, stack.isWideOpen()); 300 } 301 302 // Test out case where the user specifies a union that includes everything 303 { 304 SkClipStack stack; 305 306 SkPath clipA, clipB; 307 308 clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5)); 309 clipA.setFillType(SkPath::kInverseEvenOdd_FillType); 310 311 clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5)); 312 clipB.setFillType(SkPath::kInverseEvenOdd_FillType); 313 314 stack.clipDevPath(clipA, SkRegion::kReplace_Op, false); 315 stack.clipDevPath(clipB, SkRegion::kUnion_Op, false); 316 317 REPORTER_ASSERT(reporter, stack.isWideOpen()); 318 } 319 320 // Test out union w/ a wide open clip 321 { 322 SkClipStack stack; 323 324 stack.clipDevRect(rectA, SkRegion::kUnion_Op, false); 325 326 REPORTER_ASSERT(reporter, stack.isWideOpen()); 327 } 328 329 // Test out empty difference from a wide open clip 330 { 331 SkClipStack stack; 332 333 SkRect emptyRect; 334 emptyRect.setEmpty(); 335 336 stack.clipDevRect(emptyRect, SkRegion::kDifference_Op, false); 337 338 REPORTER_ASSERT(reporter, stack.isWideOpen()); 339 } 340 341 // Test out return to wide open 342 { 343 SkClipStack stack; 344 345 stack.save(); 346 347 stack.clipDevRect(rectA, SkRegion::kReplace_Op, false); 348 349 REPORTER_ASSERT(reporter, !stack.isWideOpen()); 350 351 stack.restore(); 352 353 REPORTER_ASSERT(reporter, stack.isWideOpen()); 354 } 355} 356 357static int count(const SkClipStack& stack) { 358 359 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); 360 361 const SkClipStack::Iter::Clip* clip = NULL; 362 int count = 0; 363 364 for (clip = iter.prev(); clip; clip = iter.prev(), ++count) { 365 ; 366 } 367 368 return count; 369} 370 371// Test out SkClipStack's merging of rect clips. In particular exercise 372// merging of aa vs. bw rects. 373static void test_rect_merging(skiatest::Reporter* reporter) { 374 375 SkRect overlapLeft = SkRect::MakeLTRB(10, 10, 50, 50); 376 SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80); 377 378 SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90); 379 SkRect nestedChild = SkRect::MakeLTRB(40, 40, 60, 60); 380 381 SkRect bound; 382 SkClipStack::BoundsType type; 383 bool isIntersectionOfRects; 384 385 // all bw overlapping - should merge 386 { 387 SkClipStack stack; 388 389 stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, false); 390 391 stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false); 392 393 REPORTER_ASSERT(reporter, 1 == count(stack)); 394 395 stack.getBounds(&bound, &type, &isIntersectionOfRects); 396 397 REPORTER_ASSERT(reporter, isIntersectionOfRects); 398 } 399 400 // all aa overlapping - should merge 401 { 402 SkClipStack stack; 403 404 stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true); 405 406 stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, true); 407 408 REPORTER_ASSERT(reporter, 1 == count(stack)); 409 410 stack.getBounds(&bound, &type, &isIntersectionOfRects); 411 412 REPORTER_ASSERT(reporter, isIntersectionOfRects); 413 } 414 415 // mixed overlapping - should _not_ merge 416 { 417 SkClipStack stack; 418 419 stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true); 420 421 stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false); 422 423 REPORTER_ASSERT(reporter, 2 == count(stack)); 424 425 stack.getBounds(&bound, &type, &isIntersectionOfRects); 426 427 REPORTER_ASSERT(reporter, !isIntersectionOfRects); 428 } 429 430 // mixed nested (bw inside aa) - should merge 431 { 432 SkClipStack stack; 433 434 stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, true); 435 436 stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, false); 437 438 REPORTER_ASSERT(reporter, 1 == count(stack)); 439 440 stack.getBounds(&bound, &type, &isIntersectionOfRects); 441 442 REPORTER_ASSERT(reporter, isIntersectionOfRects); 443 } 444 445 // mixed nested (aa inside bw) - should merge 446 { 447 SkClipStack stack; 448 449 stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, false); 450 451 stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, true); 452 453 REPORTER_ASSERT(reporter, 1 == count(stack)); 454 455 stack.getBounds(&bound, &type, &isIntersectionOfRects); 456 457 REPORTER_ASSERT(reporter, isIntersectionOfRects); 458 } 459 460 // reverse nested (aa inside bw) - should _not_ merge 461 { 462 SkClipStack stack; 463 464 stack.clipDevRect(nestedChild, SkRegion::kReplace_Op, false); 465 466 stack.clipDevRect(nestedParent, SkRegion::kIntersect_Op, true); 467 468 REPORTER_ASSERT(reporter, 2 == count(stack)); 469 470 stack.getBounds(&bound, &type, &isIntersectionOfRects); 471 472 REPORTER_ASSERT(reporter, !isIntersectionOfRects); 473 } 474} 475 476static void TestClipStack(skiatest::Reporter* reporter) { 477 SkClipStack stack; 478 479 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount()); 480 assert_count(reporter, stack, 0); 481 482 static const SkIRect gRects[] = { 483 { 0, 0, 100, 100 }, 484 { 25, 25, 125, 125 }, 485 { 0, 0, 1000, 1000 }, 486 { 0, 0, 75, 75 } 487 }; 488 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) { 489 stack.clipDevRect(gRects[i], SkRegion::kIntersect_Op); 490 } 491 492 // all of the above rects should have been intersected, leaving only 1 rect 493 SkClipStack::B2TIter iter(stack); 494 const SkClipStack::B2TIter::Clip* clip = iter.next(); 495 SkRect answer; 496 answer.iset(25, 25, 75, 75); 497 498 REPORTER_ASSERT(reporter, clip); 499 REPORTER_ASSERT(reporter, clip->fRect); 500 REPORTER_ASSERT(reporter, !clip->fPath); 501 REPORTER_ASSERT(reporter, SkRegion::kIntersect_Op == clip->fOp); 502 REPORTER_ASSERT(reporter, *clip->fRect == answer); 503 // now check that we only had one in our iterator 504 REPORTER_ASSERT(reporter, !iter.next()); 505 506 stack.reset(); 507 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount()); 508 assert_count(reporter, stack, 0); 509 510 test_assign_and_comparison(reporter); 511 test_iterators(reporter); 512 test_bounds(reporter, true); // once with rects 513 test_bounds(reporter, false); // once with paths 514 test_isWideOpen(reporter); 515 test_rect_merging(reporter); 516} 517 518#include "TestClassDef.h" 519DEFINE_TESTCLASS("ClipStack", TestClipStackClass, TestClipStack) 520