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 "include/core/SkClipOp.h"
9 #include "include/core/SkMatrix.h"
10 #include "include/core/SkPath.h"
11 #include "include/core/SkPathTypes.h"
12 #include "include/core/SkRRect.h"
13 #include "include/core/SkRect.h"
14 #include "include/core/SkRegion.h"
15 #include "include/core/SkScalar.h"
16 #include "include/core/SkTypes.h"
17 #include "include/private/base/SkTo.h"
18 #include "src/core/SkClipStack.h"
19 #include "tests/Test.h"
20
21 #include <array>
22 #include <cstring>
23 #include <initializer_list>
24
test_assign_and_comparison(skiatest::Reporter * reporter)25 static void test_assign_and_comparison(skiatest::Reporter* reporter) {
26 SkClipStack s;
27 bool doAA = false;
28
29 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
30
31 // Build up a clip stack with a path, an empty clip, and a rect.
32 s.save();
33 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
34
35 SkPath p;
36 p.moveTo(5, 6);
37 p.lineTo(7, 8);
38 p.lineTo(5, 9);
39 p.close();
40 s.clipPath(p, SkMatrix::I(), SkClipOp::kIntersect, doAA);
41
42 s.save();
43 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
44
45 SkRect r = SkRect::MakeLTRB(1, 2, 103, 104);
46 s.clipRect(r, SkMatrix::I(), SkClipOp::kIntersect, doAA);
47 r = SkRect::MakeLTRB(4, 5, 56, 57);
48 s.clipRect(r, SkMatrix::I(), SkClipOp::kIntersect, doAA);
49
50 s.save();
51 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
52
53 r = SkRect::MakeLTRB(14, 15, 16, 17);
54 s.clipRect(r, SkMatrix::I(), SkClipOp::kDifference, doAA);
55
56 // Test that assignment works.
57 SkClipStack copy = s;
58 REPORTER_ASSERT(reporter, s == copy);
59
60 // Test that different save levels triggers not equal.
61 s.restore();
62 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
63 REPORTER_ASSERT(reporter, s != copy);
64
65 // Test that an equal, but not copied version is equal.
66 s.save();
67 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
68 r = SkRect::MakeLTRB(14, 15, 16, 17);
69 s.clipRect(r, SkMatrix::I(), SkClipOp::kDifference, doAA);
70 REPORTER_ASSERT(reporter, s == copy);
71
72 // Test that a different op on one level triggers not equal.
73 s.restore();
74 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
75 s.save();
76 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
77 r = SkRect::MakeLTRB(14, 15, 16, 17);
78 s.clipRect(r, SkMatrix::I(), SkClipOp::kIntersect, doAA);
79 REPORTER_ASSERT(reporter, s != copy);
80
81 // Test that version constructed with rect-path rather than a rect is still considered equal.
82 s.restore();
83 s.save();
84 SkPath rp;
85 rp.addRect(r);
86 s.clipPath(rp, SkMatrix::I(), SkClipOp::kDifference, doAA);
87 REPORTER_ASSERT(reporter, s == copy);
88
89 // Test that different rects triggers not equal.
90 s.restore();
91 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
92 s.save();
93 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
94
95 r = SkRect::MakeLTRB(24, 25, 26, 27);
96 s.clipRect(r, SkMatrix::I(), SkClipOp::kDifference, doAA);
97 REPORTER_ASSERT(reporter, s != copy);
98
99 s.restore();
100 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
101
102 copy.restore();
103 REPORTER_ASSERT(reporter, 2 == copy.getSaveCount());
104 REPORTER_ASSERT(reporter, s == copy);
105 s.restore();
106 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
107 copy.restore();
108 REPORTER_ASSERT(reporter, 1 == copy.getSaveCount());
109 REPORTER_ASSERT(reporter, s == copy);
110
111 // Test that different paths triggers not equal.
112 s.restore();
113 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
114 s.save();
115 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
116
117 p.addRect(r);
118 s.clipPath(p, SkMatrix::I(), SkClipOp::kIntersect, doAA);
119 REPORTER_ASSERT(reporter, s != copy);
120 }
121
assert_count(skiatest::Reporter * reporter,const SkClipStack & stack,int count)122 static void assert_count(skiatest::Reporter* reporter, const SkClipStack& stack,
123 int count) {
124 SkClipStack::B2TIter iter(stack);
125 int counter = 0;
126 while (iter.next()) {
127 counter += 1;
128 }
129 REPORTER_ASSERT(reporter, count == counter);
130 }
131
132 // Exercise the SkClipStack's bottom to top and bidirectional iterators
133 // (including the skipToTopmost functionality)
test_iterators(skiatest::Reporter * reporter)134 static void test_iterators(skiatest::Reporter* reporter) {
135 SkClipStack stack;
136
137 static const SkRect gRects[] = {
138 { 0, 0, 40, 40 },
139 { 60, 0, 100, 40 },
140 { 0, 60, 40, 100 },
141 { 60, 60, 100, 100 }
142 };
143
144 for (size_t i = 0; i < std::size(gRects); i++) {
145 // the difference op will prevent these from being fused together
146 stack.clipRect(gRects[i], SkMatrix::I(), SkClipOp::kDifference, false);
147 }
148
149 assert_count(reporter, stack, 4);
150
151 // bottom to top iteration
152 {
153 const SkClipStack::Element* element = nullptr;
154
155 SkClipStack::B2TIter iter(stack);
156 int i;
157
158 for (i = 0, element = iter.next(); element; ++i, element = iter.next()) {
159 REPORTER_ASSERT(reporter, SkClipStack::Element::DeviceSpaceType::kRect ==
160 element->getDeviceSpaceType());
161 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == gRects[i]);
162 }
163
164 SkASSERT(i == 4);
165 }
166
167 // top to bottom iteration
168 {
169 const SkClipStack::Element* element = nullptr;
170
171 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
172 int i;
173
174 for (i = 3, element = iter.prev(); element; --i, element = iter.prev()) {
175 REPORTER_ASSERT(reporter, SkClipStack::Element::DeviceSpaceType::kRect ==
176 element->getDeviceSpaceType());
177 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == gRects[i]);
178 }
179
180 SkASSERT(i == -1);
181 }
182
183 // skipToTopmost
184 {
185 const SkClipStack::Element* element = nullptr;
186
187 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
188
189 element = iter.skipToTopmost(SkClipOp::kDifference);
190 REPORTER_ASSERT(reporter, SkClipStack::Element::DeviceSpaceType::kRect ==
191 element->getDeviceSpaceType());
192 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == gRects[3]);
193 }
194 }
195
196 // Exercise the SkClipStack's getConservativeBounds computation
test_bounds(skiatest::Reporter * reporter,SkClipStack::Element::DeviceSpaceType primType)197 static void test_bounds(skiatest::Reporter* reporter,
198 SkClipStack::Element::DeviceSpaceType primType) {
199 static const int gNumCases = 8;
200 static const SkRect gAnswerRectsBW[gNumCases] = {
201 // A op B
202 { 40, 40, 50, 50 },
203 { 10, 10, 50, 50 },
204
205 // invA op B
206 { 40, 40, 80, 80 },
207 { 0, 0, 100, 100 },
208
209 // A op invB
210 { 10, 10, 50, 50 },
211 { 40, 40, 50, 50 },
212
213 // invA op invB
214 { 0, 0, 100, 100 },
215 { 40, 40, 80, 80 },
216 };
217
218 static const SkClipOp gOps[] = {
219 SkClipOp::kIntersect,
220 SkClipOp::kDifference
221 };
222
223 SkRect rectA, rectB;
224
225 rectA.setLTRB(10, 10, 50, 50);
226 rectB.setLTRB(40, 40, 80, 80);
227
228 SkRRect rrectA, rrectB;
229 rrectA.setOval(rectA);
230 rrectB.setRectXY(rectB, SkIntToScalar(1), SkIntToScalar(2));
231
232 SkPath pathA, pathB;
233
234 pathA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
235 pathB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
236
237 SkClipStack stack;
238 SkRect devClipBound;
239 bool isIntersectionOfRects = false;
240
241 int testCase = 0;
242 int numBitTests = SkClipStack::Element::DeviceSpaceType::kPath == primType ? 4 : 1;
243 for (int invBits = 0; invBits < numBitTests; ++invBits) {
244 for (size_t op = 0; op < std::size(gOps); ++op) {
245
246 stack.save();
247 bool doInvA = SkToBool(invBits & 1);
248 bool doInvB = SkToBool(invBits & 2);
249
250 pathA.setFillType(doInvA ? SkPathFillType::kInverseEvenOdd :
251 SkPathFillType::kEvenOdd);
252 pathB.setFillType(doInvB ? SkPathFillType::kInverseEvenOdd :
253 SkPathFillType::kEvenOdd);
254
255 switch (primType) {
256 case SkClipStack::Element::DeviceSpaceType::kShader:
257 case SkClipStack::Element::DeviceSpaceType::kEmpty:
258 SkDEBUGFAIL("Don't call this with kEmpty or kShader.");
259 break;
260 case SkClipStack::Element::DeviceSpaceType::kRect:
261 stack.clipRect(rectA, SkMatrix::I(), SkClipOp::kIntersect, false);
262 stack.clipRect(rectB, SkMatrix::I(), gOps[op], false);
263 break;
264 case SkClipStack::Element::DeviceSpaceType::kRRect:
265 stack.clipRRect(rrectA, SkMatrix::I(), SkClipOp::kIntersect, false);
266 stack.clipRRect(rrectB, SkMatrix::I(), gOps[op], false);
267 break;
268 case SkClipStack::Element::DeviceSpaceType::kPath:
269 stack.clipPath(pathA, SkMatrix::I(), SkClipOp::kIntersect, false);
270 stack.clipPath(pathB, SkMatrix::I(), gOps[op], false);
271 break;
272 }
273
274 REPORTER_ASSERT(reporter, !stack.isWideOpen());
275 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
276
277 stack.getConservativeBounds(0, 0, 100, 100, &devClipBound,
278 &isIntersectionOfRects);
279
280 if (SkClipStack::Element::DeviceSpaceType::kRect == primType) {
281 REPORTER_ASSERT(reporter, isIntersectionOfRects ==
282 (gOps[op] == SkClipOp::kIntersect));
283 } else {
284 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
285 }
286
287 SkASSERT(testCase < gNumCases);
288 REPORTER_ASSERT(reporter, devClipBound == gAnswerRectsBW[testCase]);
289 ++testCase;
290
291 stack.restore();
292 }
293 }
294 }
295
296 // Test out 'isWideOpen' entry point
test_isWideOpen(skiatest::Reporter * reporter)297 static void test_isWideOpen(skiatest::Reporter* reporter) {
298 {
299 // Empty stack is wide open. Wide open stack means that gen id is wide open.
300 SkClipStack stack;
301 REPORTER_ASSERT(reporter, stack.isWideOpen());
302 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
303 }
304
305 SkRect rectA, rectB;
306
307 rectA.setLTRB(10, 10, 40, 40);
308 rectB.setLTRB(50, 50, 80, 80);
309
310 // Stack should initially be wide open
311 {
312 SkClipStack stack;
313
314 REPORTER_ASSERT(reporter, stack.isWideOpen());
315 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
316 }
317
318 // Test out empty difference from a wide open clip
319 {
320 SkClipStack stack;
321
322 SkRect emptyRect;
323 emptyRect.setEmpty();
324
325 stack.clipRect(emptyRect, SkMatrix::I(), SkClipOp::kDifference, false);
326
327 REPORTER_ASSERT(reporter, stack.isWideOpen());
328 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
329 }
330
331 // Test out return to wide open
332 {
333 SkClipStack stack;
334
335 stack.save();
336
337 stack.clipRect(rectA, SkMatrix::I(), SkClipOp::kIntersect, false);
338
339 REPORTER_ASSERT(reporter, !stack.isWideOpen());
340 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
341
342 stack.restore();
343
344 REPORTER_ASSERT(reporter, stack.isWideOpen());
345 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
346 }
347 }
348
count(const SkClipStack & stack)349 static int count(const SkClipStack& stack) {
350
351 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
352
353 const SkClipStack::Element* element = nullptr;
354 int count = 0;
355
356 for (element = iter.prev(); element; element = iter.prev(), ++count) {
357 }
358
359 return count;
360 }
361
test_rect_inverse_fill(skiatest::Reporter * reporter)362 static void test_rect_inverse_fill(skiatest::Reporter* reporter) {
363 // non-intersecting rectangles
364 SkRect rect = SkRect::MakeLTRB(0, 0, 10, 10);
365
366 SkPath path;
367 path.addRect(rect);
368 path.toggleInverseFillType();
369 SkClipStack stack;
370 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
371
372 SkRect bounds;
373 SkClipStack::BoundsType boundsType;
374 stack.getBounds(&bounds, &boundsType);
375 REPORTER_ASSERT(reporter, SkClipStack::kInsideOut_BoundsType == boundsType);
376 REPORTER_ASSERT(reporter, bounds == rect);
377 }
378
test_rect_replace(skiatest::Reporter * reporter)379 static void test_rect_replace(skiatest::Reporter* reporter) {
380 SkRect rect = SkRect::MakeWH(100, 100);
381 SkRect rect2 = SkRect::MakeXYWH(50, 50, 100, 100);
382
383 SkRect bound;
384 SkClipStack::BoundsType type;
385 bool isIntersectionOfRects;
386
387 // Adding a new rect with the replace operator should not increase
388 // the stack depth. BW replacing BW.
389 {
390 SkClipStack stack;
391 REPORTER_ASSERT(reporter, 0 == count(stack));
392 stack.replaceClip(rect, false);
393 REPORTER_ASSERT(reporter, 1 == count(stack));
394 stack.replaceClip(rect, false);
395 REPORTER_ASSERT(reporter, 1 == count(stack));
396 }
397
398 // Adding a new rect with the replace operator should not increase
399 // the stack depth. AA replacing AA.
400 {
401 SkClipStack stack;
402 REPORTER_ASSERT(reporter, 0 == count(stack));
403 stack.replaceClip(rect, true);
404 REPORTER_ASSERT(reporter, 1 == count(stack));
405 stack.replaceClip(rect, true);
406 REPORTER_ASSERT(reporter, 1 == count(stack));
407 }
408
409 // Adding a new rect with the replace operator should not increase
410 // the stack depth. BW replacing AA replacing BW.
411 {
412 SkClipStack stack;
413 REPORTER_ASSERT(reporter, 0 == count(stack));
414 stack.replaceClip(rect, false);
415 REPORTER_ASSERT(reporter, 1 == count(stack));
416 stack.replaceClip(rect, true);
417 REPORTER_ASSERT(reporter, 1 == count(stack));
418 stack.replaceClip(rect, false);
419 REPORTER_ASSERT(reporter, 1 == count(stack));
420 }
421
422 // Make sure replace clip rects don't collapse too much.
423 {
424 SkClipStack stack;
425 stack.replaceClip(rect, false);
426 stack.clipRect(rect2, SkMatrix::I(), SkClipOp::kIntersect, false);
427 REPORTER_ASSERT(reporter, 1 == count(stack));
428
429 stack.save();
430 stack.replaceClip(rect, false);
431 REPORTER_ASSERT(reporter, 2 == count(stack));
432 stack.getBounds(&bound, &type, &isIntersectionOfRects);
433 REPORTER_ASSERT(reporter, bound == rect);
434 stack.restore();
435 REPORTER_ASSERT(reporter, 1 == count(stack));
436
437 stack.save();
438 stack.replaceClip(rect, false);
439 stack.replaceClip(rect, false);
440 REPORTER_ASSERT(reporter, 2 == count(stack));
441 stack.restore();
442 REPORTER_ASSERT(reporter, 1 == count(stack));
443
444 stack.save();
445 stack.replaceClip(rect, false);
446 stack.clipRect(rect2, SkMatrix::I(), SkClipOp::kIntersect, false);
447 stack.replaceClip(rect, false);
448 REPORTER_ASSERT(reporter, 2 == count(stack));
449 stack.restore();
450 REPORTER_ASSERT(reporter, 1 == count(stack));
451 }
452 }
453
454 // Simplified path-based version of test_rect_replace.
test_path_replace(skiatest::Reporter * reporter)455 static void test_path_replace(skiatest::Reporter* reporter) {
456 auto replacePath = [](SkClipStack* stack, const SkPath& path, bool doAA) {
457 const SkRect wideOpen = SkRect::MakeLTRB(-1000, -1000, 1000, 1000);
458 stack->replaceClip(wideOpen, false);
459 stack->clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, doAA);
460 };
461 SkRect rect = SkRect::MakeWH(100, 100);
462 SkPath path;
463 path.addCircle(50, 50, 50);
464
465 // Emulating replace operations with more complex geometry is not atomic, it's a replace
466 // with a wide-open rect and then an intersection with the complex geometry. The replace can
467 // combine with prior elements, but the subsequent intersect cannot be combined so the stack
468 // continues to grow.
469 {
470 SkClipStack stack;
471 REPORTER_ASSERT(reporter, 0 == count(stack));
472 replacePath(&stack, path, false);
473 REPORTER_ASSERT(reporter, 2 == count(stack));
474 replacePath(&stack, path, false);
475 REPORTER_ASSERT(reporter, 2 == count(stack));
476 }
477
478 // Replacing rect with path.
479 {
480 SkClipStack stack;
481 stack.replaceClip(rect, true);
482 REPORTER_ASSERT(reporter, 1 == count(stack));
483 replacePath(&stack, path, true);
484 REPORTER_ASSERT(reporter, 2 == count(stack));
485 }
486 }
487
488 // Test out SkClipStack's merging of rect clips. In particular exercise
489 // merging of aa vs. bw rects.
test_rect_merging(skiatest::Reporter * reporter)490 static void test_rect_merging(skiatest::Reporter* reporter) {
491
492 SkRect overlapLeft = SkRect::MakeLTRB(10, 10, 50, 50);
493 SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80);
494
495 SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90);
496 SkRect nestedChild = SkRect::MakeLTRB(40, 40, 60, 60);
497
498 SkRect bound;
499 SkClipStack::BoundsType type;
500 bool isIntersectionOfRects;
501
502 // all bw overlapping - should merge
503 {
504 SkClipStack stack;
505 stack.clipRect(overlapLeft, SkMatrix::I(), SkClipOp::kIntersect, false);
506 stack.clipRect(overlapRight, SkMatrix::I(), SkClipOp::kIntersect, false);
507
508 REPORTER_ASSERT(reporter, 1 == count(stack));
509
510 stack.getBounds(&bound, &type, &isIntersectionOfRects);
511
512 REPORTER_ASSERT(reporter, isIntersectionOfRects);
513 }
514
515 // all aa overlapping - should merge
516 {
517 SkClipStack stack;
518 stack.clipRect(overlapLeft, SkMatrix::I(), SkClipOp::kIntersect, true);
519 stack.clipRect(overlapRight, SkMatrix::I(), SkClipOp::kIntersect, true);
520
521 REPORTER_ASSERT(reporter, 1 == count(stack));
522
523 stack.getBounds(&bound, &type, &isIntersectionOfRects);
524
525 REPORTER_ASSERT(reporter, isIntersectionOfRects);
526 }
527
528 // mixed overlapping - should _not_ merge
529 {
530 SkClipStack stack;
531 stack.clipRect(overlapLeft, SkMatrix::I(), SkClipOp::kIntersect, true);
532 stack.clipRect(overlapRight, SkMatrix::I(), SkClipOp::kIntersect, false);
533
534 REPORTER_ASSERT(reporter, 2 == count(stack));
535
536 stack.getBounds(&bound, &type, &isIntersectionOfRects);
537
538 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
539 }
540
541 // mixed nested (bw inside aa) - should merge
542 {
543 SkClipStack stack;
544 stack.clipRect(nestedParent, SkMatrix::I(), SkClipOp::kIntersect, true);
545 stack.clipRect(nestedChild, SkMatrix::I(), SkClipOp::kIntersect, false);
546
547 REPORTER_ASSERT(reporter, 1 == count(stack));
548
549 stack.getBounds(&bound, &type, &isIntersectionOfRects);
550
551 REPORTER_ASSERT(reporter, isIntersectionOfRects);
552 }
553
554 // mixed nested (aa inside bw) - should merge
555 {
556 SkClipStack stack;
557 stack.clipRect(nestedParent, SkMatrix::I(), SkClipOp::kIntersect, false);
558 stack.clipRect(nestedChild, SkMatrix::I(), SkClipOp::kIntersect, true);
559
560 REPORTER_ASSERT(reporter, 1 == count(stack));
561
562 stack.getBounds(&bound, &type, &isIntersectionOfRects);
563
564 REPORTER_ASSERT(reporter, isIntersectionOfRects);
565 }
566
567 // reverse nested (aa inside bw) - should _not_ merge
568 {
569 SkClipStack stack;
570 stack.clipRect(nestedChild, SkMatrix::I(), SkClipOp::kIntersect, false);
571 stack.clipRect(nestedParent, SkMatrix::I(), SkClipOp::kIntersect, true);
572
573 REPORTER_ASSERT(reporter, 2 == count(stack));
574
575 stack.getBounds(&bound, &type, &isIntersectionOfRects);
576
577 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
578 }
579 }
580
test_quickContains(skiatest::Reporter * reporter)581 static void test_quickContains(skiatest::Reporter* reporter) {
582 SkRect testRect = SkRect::MakeLTRB(10, 10, 40, 40);
583 SkRect insideRect = SkRect::MakeLTRB(20, 20, 30, 30);
584 SkRect intersectingRect = SkRect::MakeLTRB(25, 25, 50, 50);
585 SkRect outsideRect = SkRect::MakeLTRB(0, 0, 50, 50);
586 SkRect nonIntersectingRect = SkRect::MakeLTRB(100, 100, 110, 110);
587
588 SkPath insideCircle;
589 insideCircle.addCircle(25, 25, 5);
590 SkPath intersectingCircle;
591 intersectingCircle.addCircle(25, 40, 10);
592 SkPath outsideCircle;
593 outsideCircle.addCircle(25, 25, 50);
594 SkPath nonIntersectingCircle;
595 nonIntersectingCircle.addCircle(100, 100, 5);
596
597 {
598 SkClipStack stack;
599 stack.clipRect(outsideRect, SkMatrix::I(), SkClipOp::kDifference, false);
600 // return false because quickContains currently does not care for kDifference
601 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
602 }
603
604 // Replace Op tests
605 {
606 SkClipStack stack;
607 stack.replaceClip(outsideRect, false);
608 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
609 }
610
611 {
612 SkClipStack stack;
613 stack.clipRect(insideRect, SkMatrix::I(), SkClipOp::kIntersect, false);
614 stack.save(); // To prevent in-place substitution by replace OP
615 stack.replaceClip(outsideRect, false);
616 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
617 stack.restore();
618 }
619
620 {
621 SkClipStack stack;
622 stack.clipRect(outsideRect, SkMatrix::I(), SkClipOp::kIntersect, false);
623 stack.save(); // To prevent in-place substitution by replace OP
624 stack.replaceClip(insideRect, false);
625 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
626 stack.restore();
627 }
628
629 // Verify proper traversal of multi-element clip
630 {
631 SkClipStack stack;
632 stack.clipRect(insideRect, SkMatrix::I(), SkClipOp::kIntersect, false);
633 // Use a path for second clip to prevent in-place intersection
634 stack.clipPath(outsideCircle, SkMatrix::I(), SkClipOp::kIntersect, false);
635 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
636 }
637
638 // Intersect Op tests with rectangles
639 {
640 SkClipStack stack;
641 stack.clipRect(outsideRect, SkMatrix::I(), SkClipOp::kIntersect, false);
642 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
643 }
644
645 {
646 SkClipStack stack;
647 stack.clipRect(insideRect, SkMatrix::I(), SkClipOp::kIntersect, false);
648 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
649 }
650
651 {
652 SkClipStack stack;
653 stack.clipRect(intersectingRect, SkMatrix::I(), SkClipOp::kIntersect, false);
654 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
655 }
656
657 {
658 SkClipStack stack;
659 stack.clipRect(nonIntersectingRect, SkMatrix::I(), SkClipOp::kIntersect, false);
660 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
661 }
662
663 // Intersect Op tests with circle paths
664 {
665 SkClipStack stack;
666 stack.clipPath(outsideCircle, SkMatrix::I(), SkClipOp::kIntersect, false);
667 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
668 }
669
670 {
671 SkClipStack stack;
672 stack.clipPath(insideCircle, SkMatrix::I(), SkClipOp::kIntersect, false);
673 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
674 }
675
676 {
677 SkClipStack stack;
678 stack.clipPath(intersectingCircle, SkMatrix::I(), SkClipOp::kIntersect, false);
679 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
680 }
681
682 {
683 SkClipStack stack;
684 stack.clipPath(nonIntersectingCircle, SkMatrix::I(), SkClipOp::kIntersect, false);
685 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
686 }
687
688 // Intersect Op tests with inverse filled rectangles
689 {
690 SkClipStack stack;
691 SkPath path;
692 path.addRect(outsideRect);
693 path.toggleInverseFillType();
694 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
695 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
696 }
697
698 {
699 SkClipStack stack;
700 SkPath path;
701 path.addRect(insideRect);
702 path.toggleInverseFillType();
703 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
704 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
705 }
706
707 {
708 SkClipStack stack;
709 SkPath path;
710 path.addRect(intersectingRect);
711 path.toggleInverseFillType();
712 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
713 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
714 }
715
716 {
717 SkClipStack stack;
718 SkPath path;
719 path.addRect(nonIntersectingRect);
720 path.toggleInverseFillType();
721 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
722 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
723 }
724
725 // Intersect Op tests with inverse filled circles
726 {
727 SkClipStack stack;
728 SkPath path = outsideCircle;
729 path.toggleInverseFillType();
730 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
731 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
732 }
733
734 {
735 SkClipStack stack;
736 SkPath path = insideCircle;
737 path.toggleInverseFillType();
738 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
739 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
740 }
741
742 {
743 SkClipStack stack;
744 SkPath path = intersectingCircle;
745 path.toggleInverseFillType();
746 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
747 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
748 }
749
750 {
751 SkClipStack stack;
752 SkPath path = nonIntersectingCircle;
753 path.toggleInverseFillType();
754 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
755 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
756 }
757 }
758
set_region_to_stack(const SkClipStack & stack,const SkIRect & bounds,SkRegion * region)759 static void set_region_to_stack(const SkClipStack& stack, const SkIRect& bounds, SkRegion* region) {
760 region->setRect(bounds);
761 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
762 while (const SkClipStack::Element *element = iter.next()) {
763 SkRegion elemRegion;
764 SkRegion boundsRgn(bounds);
765 SkPath path;
766
767 switch (element->getDeviceSpaceType()) {
768 case SkClipStack::Element::DeviceSpaceType::kEmpty:
769 elemRegion.setEmpty();
770 break;
771 default:
772 element->asDeviceSpacePath(&path);
773 elemRegion.setPath(path, boundsRgn);
774 break;
775 }
776
777 region->op(elemRegion, element->isReplaceOp() ? SkRegion::kReplace_Op
778 : (SkRegion::Op) element->getOp());
779 }
780 }
781
test_invfill_diff_bug(skiatest::Reporter * reporter)782 static void test_invfill_diff_bug(skiatest::Reporter* reporter) {
783 SkClipStack stack;
784 stack.clipRect({10, 10, 20, 20}, SkMatrix::I(), SkClipOp::kIntersect, false);
785
786 SkPath path;
787 path.addRect({30, 10, 40, 20});
788 path.setFillType(SkPathFillType::kInverseWinding);
789 stack.clipPath(path, SkMatrix::I(), SkClipOp::kDifference, false);
790
791 REPORTER_ASSERT(reporter, SkClipStack::kEmptyGenID == stack.getTopmostGenID());
792
793 SkRect stackBounds;
794 SkClipStack::BoundsType stackBoundsType;
795 stack.getBounds(&stackBounds, &stackBoundsType);
796
797 REPORTER_ASSERT(reporter, stackBounds.isEmpty());
798 REPORTER_ASSERT(reporter, SkClipStack::kNormal_BoundsType == stackBoundsType);
799
800 SkRegion region;
801 set_region_to_stack(stack, {0, 0, 50, 30}, ®ion);
802
803 REPORTER_ASSERT(reporter, region.isEmpty());
804 }
805
806 ///////////////////////////////////////////////////////////////////////////////////////////////////
807
test_is_rrect_deep_rect_stack(skiatest::Reporter * reporter)808 static void test_is_rrect_deep_rect_stack(skiatest::Reporter* reporter) {
809 static constexpr SkRect kTargetBounds = SkRect::MakeWH(1000, 500);
810 // All antialiased or all not antialiased.
811 for (bool aa : {false, true}) {
812 SkClipStack stack;
813 for (int i = 0; i <= 100; ++i) {
814 stack.save();
815 stack.clipRect(SkRect::MakeLTRB(i, 0.5, kTargetBounds.width(), kTargetBounds.height()),
816 SkMatrix::I(), SkClipOp::kIntersect, aa);
817 }
818 SkRRect rrect;
819 bool isAA;
820 SkRRect expected = SkRRect::MakeRect(
821 SkRect::MakeLTRB(100, 0.5, kTargetBounds.width(), kTargetBounds.height()));
822 if (stack.isRRect(kTargetBounds, &rrect, &isAA)) {
823 REPORTER_ASSERT(reporter, rrect == expected);
824 REPORTER_ASSERT(reporter, aa == isAA);
825 } else {
826 ERRORF(reporter, "Expected to be an rrect.");
827 }
828 }
829 // Mixed AA and non-AA without simple containment.
830 SkClipStack stack;
831 for (int i = 0; i <= 100; ++i) {
832 bool aa = i & 0b1;
833 int j = 100 - i;
834 stack.save();
835 stack.clipRect(SkRect::MakeLTRB(i, j + 0.5, kTargetBounds.width(), kTargetBounds.height()),
836 SkMatrix::I(), SkClipOp::kIntersect, aa);
837 }
838 SkRRect rrect;
839 bool isAA;
840 REPORTER_ASSERT(reporter, !stack.isRRect(kTargetBounds, &rrect, &isAA));
841 }
842
DEF_TEST(ClipStack,reporter)843 DEF_TEST(ClipStack, reporter) {
844 SkClipStack stack;
845
846 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
847 assert_count(reporter, stack, 0);
848
849 static const SkIRect gRects[] = {
850 { 0, 0, 100, 100 },
851 { 25, 25, 125, 125 },
852 { 0, 0, 1000, 1000 },
853 { 0, 0, 75, 75 }
854 };
855 for (size_t i = 0; i < std::size(gRects); i++) {
856 stack.clipDevRect(gRects[i], SkClipOp::kIntersect);
857 }
858
859 // all of the above rects should have been intersected, leaving only 1 rect
860 SkClipStack::B2TIter iter(stack);
861 const SkClipStack::Element* element = iter.next();
862 SkRect answer;
863 answer.setLTRB(25, 25, 75, 75);
864
865 REPORTER_ASSERT(reporter, element);
866 REPORTER_ASSERT(reporter,
867 SkClipStack::Element::DeviceSpaceType::kRect == element->getDeviceSpaceType());
868 REPORTER_ASSERT(reporter, SkClipOp::kIntersect == element->getOp());
869 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == answer);
870 // now check that we only had one in our iterator
871 REPORTER_ASSERT(reporter, !iter.next());
872
873 stack.reset();
874 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
875 assert_count(reporter, stack, 0);
876
877 test_assign_and_comparison(reporter);
878 test_iterators(reporter);
879 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kRect);
880 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kRRect);
881 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kPath);
882 test_isWideOpen(reporter);
883 test_rect_merging(reporter);
884 test_rect_replace(reporter);
885 test_rect_inverse_fill(reporter);
886 test_path_replace(reporter);
887 test_quickContains(reporter);
888 test_invfill_diff_bug(reporter);
889 test_is_rrect_deep_rect_stack(reporter);
890 }
891