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