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