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