blob: a5cd69dc4923b2685ee7c9c3382d405c875e1fff [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
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 */
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00007
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#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"
Michael Ludwig4e221bd2020-06-05 11:29:36 -040023#include "include/effects/SkGradientShader.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050024#include "include/gpu/GrConfig.h"
Robert Phillips6d344c32020-07-06 10:56:46 -040025#include "include/gpu/GrDirectContext.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050026#include "include/private/GrResourceKey.h"
Ben Wagner9707a7e2019-05-06 17:17:19 -040027#include "include/private/SkTemplates.h"
28#include "include/utils/SkRandom.h"
29#include "src/core/SkClipOpPriv.h"
30#include "src/core/SkClipStack.h"
31#include "src/core/SkTLList.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050032#include "src/gpu/GrClip.h"
Adlai Hollera0693042020-10-14 11:23:11 -040033#include "src/gpu/GrDirectContextPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050034#include "src/gpu/GrResourceCache.h"
Greg Daniel456f9b52020-03-05 19:14:18 +000035#include "src/gpu/GrTexture.h"
Greg Danielf91aeb22019-06-18 09:58:02 -040036#include "src/gpu/GrTextureProxy.h"
Ben Wagner9707a7e2019-05-06 17:17:19 -040037#include "tests/Test.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050038#include "tools/gpu/GrContextFactory.h"
csmartdaltoncbecb082016-07-22 08:59:08 -070039
Ben Wagnerb607a8f2018-03-12 13:46:21 -040040#include <cstring>
Ben Wagner9707a7e2019-05-06 17:17:19 -040041#include <initializer_list>
Ben Wagnerb607a8f2018-03-12 13:46:21 -040042#include <new>
43
Ben Wagner9707a7e2019-05-06 17:17:19 -040044class GrCaps;
45
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000046static void test_assign_and_comparison(skiatest::Reporter* reporter) {
47 SkClipStack s;
reed@google.comd9f2dea2011-10-12 14:43:27 +000048 bool doAA = false;
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000049
robertphillips@google.com80214e22012-07-20 15:33:18 +000050 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
51
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000052 // Build up a clip stack with a path, an empty clip, and a rect.
53 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000054 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
55
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000056 SkPath p;
57 p.moveTo(5, 6);
58 p.lineTo(7, 8);
59 p.lineTo(5, 9);
60 p.close();
Mike Reedc1f77742016-12-09 09:00:50 -050061 s.clipPath(p, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000062
63 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000064 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
65
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000066 SkRect r = SkRect::MakeLTRB(1, 2, 3, 4);
Mike Reedc1f77742016-12-09 09:00:50 -050067 s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000068 r = SkRect::MakeLTRB(10, 11, 12, 13);
Mike Reedc1f77742016-12-09 09:00:50 -050069 s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000070
71 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000072 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
73
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000074 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -050075 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000076
77 // Test that assignment works.
78 SkClipStack copy = s;
79 REPORTER_ASSERT(reporter, s == copy);
80
81 // Test that different save levels triggers not equal.
82 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000083 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000084 REPORTER_ASSERT(reporter, s != copy);
85
86 // Test that an equal, but not copied version is equal.
87 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000088 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000089 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -050090 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000091 REPORTER_ASSERT(reporter, s == copy);
92
93 // Test that a different op on one level triggers not equal.
94 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000095 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000096 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000097 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000098 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -050099 s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000100 REPORTER_ASSERT(reporter, s != copy);
101
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000102 // Test that version constructed with rect-path rather than a rect is still considered equal.
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000103 s.restore();
104 s.save();
105 SkPath rp;
106 rp.addRect(r);
Mike Reedc1f77742016-12-09 09:00:50 -0500107 s.clipPath(rp, SkMatrix::I(), kUnion_SkClipOp, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000108 REPORTER_ASSERT(reporter, s == copy);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000109
110 // Test that different rects triggers not equal.
111 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000112 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000113 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000114 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
115
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000116 r = SkRect::MakeLTRB(24, 25, 26, 27);
Mike Reedc1f77742016-12-09 09:00:50 -0500117 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000118 REPORTER_ASSERT(reporter, s != copy);
119
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000120 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000121 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
122
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000123 copy.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000124 REPORTER_ASSERT(reporter, 2 == copy.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000125 REPORTER_ASSERT(reporter, s == copy);
126 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000127 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000128 copy.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000129 REPORTER_ASSERT(reporter, 1 == copy.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000130 REPORTER_ASSERT(reporter, s == copy);
131
132 // Test that different paths triggers not equal.
133 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000134 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000135 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000136 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
137
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000138 p.addRect(r);
Mike Reedc1f77742016-12-09 09:00:50 -0500139 s.clipPath(p, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000140 REPORTER_ASSERT(reporter, s != copy);
141}
reed@google.combdee9fc2011-02-22 20:17:43 +0000142
143static void assert_count(skiatest::Reporter* reporter, const SkClipStack& stack,
144 int count) {
robertphillips@google.com80214e22012-07-20 15:33:18 +0000145 SkClipStack::B2TIter iter(stack);
reed@google.combdee9fc2011-02-22 20:17:43 +0000146 int counter = 0;
147 while (iter.next()) {
148 counter += 1;
149 }
150 REPORTER_ASSERT(reporter, count == counter);
151}
152
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000153// Exercise the SkClipStack's bottom to top and bidirectional iterators
154// (including the skipToTopmost functionality)
robertphillips@google.com80214e22012-07-20 15:33:18 +0000155static void test_iterators(skiatest::Reporter* reporter) {
156 SkClipStack stack;
157
158 static const SkRect gRects[] = {
159 { 0, 0, 40, 40 },
160 { 60, 0, 100, 40 },
161 { 0, 60, 40, 100 },
162 { 60, 60, 100, 100 }
163 };
164
165 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
166 // the union op will prevent these from being fused together
Mike Reedc1f77742016-12-09 09:00:50 -0500167 stack.clipRect(gRects[i], SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000168 }
169
170 assert_count(reporter, stack, 4);
171
172 // bottom to top iteration
173 {
halcanary96fcdcc2015-08-27 07:41:13 -0700174 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000175
176 SkClipStack::B2TIter iter(stack);
177 int i;
178
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000179 for (i = 0, element = iter.next(); element; ++i, element = iter.next()) {
Brian Salomonf3b46e52017-08-30 11:37:57 -0400180 REPORTER_ASSERT(reporter, SkClipStack::Element::DeviceSpaceType::kRect ==
181 element->getDeviceSpaceType());
182 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == gRects[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000183 }
184
185 SkASSERT(i == 4);
186 }
187
188 // top to bottom iteration
189 {
halcanary96fcdcc2015-08-27 07:41:13 -0700190 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000191
192 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
193 int i;
194
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000195 for (i = 3, element = iter.prev(); element; --i, element = iter.prev()) {
Brian Salomonf3b46e52017-08-30 11:37:57 -0400196 REPORTER_ASSERT(reporter, SkClipStack::Element::DeviceSpaceType::kRect ==
197 element->getDeviceSpaceType());
198 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == gRects[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000199 }
200
201 SkASSERT(i == -1);
202 }
203
204 // skipToTopmost
205 {
halcanary96fcdcc2015-08-27 07:41:13 -0700206 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000207
208 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
209
Mike Reedc1f77742016-12-09 09:00:50 -0500210 element = iter.skipToTopmost(kUnion_SkClipOp);
Brian Salomonf3b46e52017-08-30 11:37:57 -0400211 REPORTER_ASSERT(reporter, SkClipStack::Element::DeviceSpaceType::kRect ==
212 element->getDeviceSpaceType());
213 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == gRects[3]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000214 }
215}
216
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000217// Exercise the SkClipStack's getConservativeBounds computation
Brian Salomonf3b46e52017-08-30 11:37:57 -0400218static void test_bounds(skiatest::Reporter* reporter,
219 SkClipStack::Element::DeviceSpaceType primType) {
robertphillips@google.com607fe072012-07-24 13:54:00 +0000220 static const int gNumCases = 20;
221 static const SkRect gAnswerRectsBW[gNumCases] = {
222 // A op B
223 { 40, 40, 50, 50 },
224 { 10, 10, 50, 50 },
225 { 10, 10, 80, 80 },
226 { 10, 10, 80, 80 },
227 { 40, 40, 80, 80 },
228
229 // invA op B
230 { 40, 40, 80, 80 },
231 { 0, 0, 100, 100 },
232 { 0, 0, 100, 100 },
233 { 0, 0, 100, 100 },
234 { 40, 40, 50, 50 },
235
236 // A op invB
237 { 10, 10, 50, 50 },
238 { 40, 40, 50, 50 },
239 { 0, 0, 100, 100 },
240 { 0, 0, 100, 100 },
241 { 0, 0, 100, 100 },
242
243 // invA op invB
244 { 0, 0, 100, 100 },
245 { 40, 40, 80, 80 },
246 { 0, 0, 100, 100 },
247 { 10, 10, 80, 80 },
248 { 10, 10, 50, 50 },
249 };
250
Mike Reedc1f77742016-12-09 09:00:50 -0500251 static const SkClipOp gOps[] = {
252 kIntersect_SkClipOp,
253 kDifference_SkClipOp,
254 kUnion_SkClipOp,
255 kXOR_SkClipOp,
256 kReverseDifference_SkClipOp
robertphillips@google.com607fe072012-07-24 13:54:00 +0000257 };
258
259 SkRect rectA, rectB;
260
Mike Reed92b33352019-08-24 19:39:13 -0400261 rectA.setLTRB(10, 10, 50, 50);
262 rectB.setLTRB(40, 40, 80, 80);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000263
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000264 SkRRect rrectA, rrectB;
265 rrectA.setOval(rectA);
266 rrectB.setRectXY(rectB, SkIntToScalar(1), SkIntToScalar(2));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000267
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000268 SkPath pathA, pathB;
269
270 pathA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
271 pathB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000272
273 SkClipStack stack;
robertphillips@google.com7b112892012-07-31 15:18:21 +0000274 SkRect devClipBound;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000275 bool isIntersectionOfRects = false;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000276
277 int testCase = 0;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400278 int numBitTests = SkClipStack::Element::DeviceSpaceType::kPath == primType ? 4 : 1;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000279 for (int invBits = 0; invBits < numBitTests; ++invBits) {
robertphillips@google.com607fe072012-07-24 13:54:00 +0000280 for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) {
281
282 stack.save();
283 bool doInvA = SkToBool(invBits & 1);
284 bool doInvB = SkToBool(invBits & 2);
285
Mike Reed7d34dc72019-11-26 12:17:17 -0500286 pathA.setFillType(doInvA ? SkPathFillType::kInverseEvenOdd :
287 SkPathFillType::kEvenOdd);
288 pathB.setFillType(doInvB ? SkPathFillType::kInverseEvenOdd :
289 SkPathFillType::kEvenOdd);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000290
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000291 switch (primType) {
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400292 case SkClipStack::Element::DeviceSpaceType::kShader:
Brian Salomonf3b46e52017-08-30 11:37:57 -0400293 case SkClipStack::Element::DeviceSpaceType::kEmpty:
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400294 SkDEBUGFAIL("Don't call this with kEmpty or kShader.");
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000295 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400296 case SkClipStack::Element::DeviceSpaceType::kRect:
Mike Reedc1f77742016-12-09 09:00:50 -0500297 stack.clipRect(rectA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400298 stack.clipRect(rectB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000299 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400300 case SkClipStack::Element::DeviceSpaceType::kRRect:
Mike Reedc1f77742016-12-09 09:00:50 -0500301 stack.clipRRect(rrectA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400302 stack.clipRRect(rrectB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000303 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400304 case SkClipStack::Element::DeviceSpaceType::kPath:
Mike Reedc1f77742016-12-09 09:00:50 -0500305 stack.clipPath(pathA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400306 stack.clipPath(pathB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000307 break;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000308 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000309
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000310 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000311 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000312
robertphillips@google.com7b112892012-07-31 15:18:21 +0000313 stack.getConservativeBounds(0, 0, 100, 100, &devClipBound,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000314 &isIntersectionOfRects);
315
Brian Salomonf3b46e52017-08-30 11:37:57 -0400316 if (SkClipStack::Element::DeviceSpaceType::kRect == primType) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000317 REPORTER_ASSERT(reporter, isIntersectionOfRects ==
Mike Reedc1f77742016-12-09 09:00:50 -0500318 (gOps[op] == kIntersect_SkClipOp));
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000319 } else {
320 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
321 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000322
323 SkASSERT(testCase < gNumCases);
robertphillips@google.com7b112892012-07-31 15:18:21 +0000324 REPORTER_ASSERT(reporter, devClipBound == gAnswerRectsBW[testCase]);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000325 ++testCase;
326
327 stack.restore();
328 }
329 }
330}
331
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000332// Test out 'isWideOpen' entry point
333static void test_isWideOpen(skiatest::Reporter* reporter) {
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000334 {
335 // Empty stack is wide open. Wide open stack means that gen id is wide open.
336 SkClipStack stack;
337 REPORTER_ASSERT(reporter, stack.isWideOpen());
338 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
339 }
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000340
341 SkRect rectA, rectB;
342
Mike Reed92b33352019-08-24 19:39:13 -0400343 rectA.setLTRB(10, 10, 40, 40);
344 rectB.setLTRB(50, 50, 80, 80);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000345
346 // Stack should initially be wide open
347 {
348 SkClipStack stack;
349
350 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000351 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000352 }
353
354 // Test out case where the user specifies a union that includes everything
355 {
356 SkClipStack stack;
357
358 SkPath clipA, clipB;
359
360 clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
Mike Reed7d34dc72019-11-26 12:17:17 -0500361 clipA.setFillType(SkPathFillType::kInverseEvenOdd);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000362
363 clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
Mike Reed7d34dc72019-11-26 12:17:17 -0500364 clipB.setFillType(SkPathFillType::kInverseEvenOdd);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000365
Mike Reedc1f77742016-12-09 09:00:50 -0500366 stack.clipPath(clipA, SkMatrix::I(), kReplace_SkClipOp, false);
367 stack.clipPath(clipB, SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000368
369 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000370 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000371 }
372
373 // Test out union w/ a wide open clip
374 {
375 SkClipStack stack;
376
Mike Reedc1f77742016-12-09 09:00:50 -0500377 stack.clipRect(rectA, SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000378
379 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000380 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000381 }
382
383 // Test out empty difference from a wide open clip
384 {
385 SkClipStack stack;
386
387 SkRect emptyRect;
388 emptyRect.setEmpty();
389
Mike Reedc1f77742016-12-09 09:00:50 -0500390 stack.clipRect(emptyRect, SkMatrix::I(), kDifference_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000391
392 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000393 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000394 }
395
396 // Test out return to wide open
397 {
398 SkClipStack stack;
399
400 stack.save();
401
Mike Reedc1f77742016-12-09 09:00:50 -0500402 stack.clipRect(rectA, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000403
404 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000405 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000406
407 stack.restore();
408
409 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000410 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000411 }
412}
413
bsalomon@google.com100abf42012-09-05 17:40:04 +0000414static int count(const SkClipStack& stack) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000415
416 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
417
halcanary96fcdcc2015-08-27 07:41:13 -0700418 const SkClipStack::Element* element = nullptr;
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000419 int count = 0;
420
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000421 for (element = iter.prev(); element; element = iter.prev(), ++count) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000422 }
423
424 return count;
425}
426
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000427static void test_rect_inverse_fill(skiatest::Reporter* reporter) {
428 // non-intersecting rectangles
429 SkRect rect = SkRect::MakeLTRB(0, 0, 10, 10);
430
431 SkPath path;
432 path.addRect(rect);
433 path.toggleInverseFillType();
434 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500435 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000436
437 SkRect bounds;
438 SkClipStack::BoundsType boundsType;
439 stack.getBounds(&bounds, &boundsType);
440 REPORTER_ASSERT(reporter, SkClipStack::kInsideOut_BoundsType == boundsType);
441 REPORTER_ASSERT(reporter, bounds == rect);
442}
443
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000444static void test_rect_replace(skiatest::Reporter* reporter) {
445 SkRect rect = SkRect::MakeWH(100, 100);
446 SkRect rect2 = SkRect::MakeXYWH(50, 50, 100, 100);
447
448 SkRect bound;
449 SkClipStack::BoundsType type;
450 bool isIntersectionOfRects;
451
452 // Adding a new rect with the replace operator should not increase
453 // the stack depth. BW replacing BW.
454 {
455 SkClipStack stack;
456 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500457 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000458 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500459 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000460 REPORTER_ASSERT(reporter, 1 == count(stack));
461 }
462
463 // Adding a new rect with the replace operator should not increase
464 // the stack depth. AA replacing AA.
465 {
466 SkClipStack stack;
467 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500468 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000469 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500470 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000471 REPORTER_ASSERT(reporter, 1 == count(stack));
472 }
473
474 // Adding a new rect with the replace operator should not increase
475 // the stack depth. BW replacing AA replacing BW.
476 {
477 SkClipStack stack;
478 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500479 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000480 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500481 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000482 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500483 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000484 REPORTER_ASSERT(reporter, 1 == count(stack));
485 }
486
487 // Make sure replace clip rects don't collapse too much.
488 {
489 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500490 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
491 stack.clipRect(rect2, SkMatrix::I(), kIntersect_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000492 REPORTER_ASSERT(reporter, 1 == count(stack));
493
494 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500495 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000496 REPORTER_ASSERT(reporter, 2 == count(stack));
497 stack.getBounds(&bound, &type, &isIntersectionOfRects);
498 REPORTER_ASSERT(reporter, bound == rect);
499 stack.restore();
500 REPORTER_ASSERT(reporter, 1 == count(stack));
501
502 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500503 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
504 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000505 REPORTER_ASSERT(reporter, 2 == count(stack));
506 stack.restore();
507 REPORTER_ASSERT(reporter, 1 == count(stack));
508
509 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500510 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
511 stack.clipRect(rect2, SkMatrix::I(), kIntersect_SkClipOp, false);
512 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000513 REPORTER_ASSERT(reporter, 2 == count(stack));
514 stack.restore();
515 REPORTER_ASSERT(reporter, 1 == count(stack));
516 }
517}
518
519// Simplified path-based version of test_rect_replace.
520static void test_path_replace(skiatest::Reporter* reporter) {
521 SkRect rect = SkRect::MakeWH(100, 100);
522 SkPath path;
523 path.addCircle(50, 50, 50);
524
525 // Replace operation doesn't grow the stack.
526 {
527 SkClipStack stack;
528 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500529 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000530 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500531 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000532 REPORTER_ASSERT(reporter, 1 == count(stack));
533 }
534
535 // Replacing rect with path.
536 {
537 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500538 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000539 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500540 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000541 REPORTER_ASSERT(reporter, 1 == count(stack));
542 }
543}
544
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000545// Test out SkClipStack's merging of rect clips. In particular exercise
546// merging of aa vs. bw rects.
547static void test_rect_merging(skiatest::Reporter* reporter) {
548
549 SkRect overlapLeft = SkRect::MakeLTRB(10, 10, 50, 50);
550 SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80);
551
552 SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90);
553 SkRect nestedChild = SkRect::MakeLTRB(40, 40, 60, 60);
554
555 SkRect bound;
556 SkClipStack::BoundsType type;
557 bool isIntersectionOfRects;
558
559 // all bw overlapping - should merge
560 {
561 SkClipStack stack;
562
Mike Reedc1f77742016-12-09 09:00:50 -0500563 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000564
Mike Reedc1f77742016-12-09 09:00:50 -0500565 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000566
567 REPORTER_ASSERT(reporter, 1 == count(stack));
568
569 stack.getBounds(&bound, &type, &isIntersectionOfRects);
570
571 REPORTER_ASSERT(reporter, isIntersectionOfRects);
572 }
573
574 // all aa overlapping - should merge
575 {
576 SkClipStack stack;
577
Mike Reedc1f77742016-12-09 09:00:50 -0500578 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000579
Mike Reedc1f77742016-12-09 09:00:50 -0500580 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000581
582 REPORTER_ASSERT(reporter, 1 == count(stack));
583
584 stack.getBounds(&bound, &type, &isIntersectionOfRects);
585
586 REPORTER_ASSERT(reporter, isIntersectionOfRects);
587 }
588
589 // mixed overlapping - should _not_ merge
590 {
591 SkClipStack stack;
592
Mike Reedc1f77742016-12-09 09:00:50 -0500593 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000594
Mike Reedc1f77742016-12-09 09:00:50 -0500595 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000596
597 REPORTER_ASSERT(reporter, 2 == count(stack));
598
599 stack.getBounds(&bound, &type, &isIntersectionOfRects);
600
601 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
602 }
603
604 // mixed nested (bw inside aa) - should merge
605 {
606 SkClipStack stack;
607
Mike Reedc1f77742016-12-09 09:00:50 -0500608 stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000609
Mike Reedc1f77742016-12-09 09:00:50 -0500610 stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000611
612 REPORTER_ASSERT(reporter, 1 == count(stack));
613
614 stack.getBounds(&bound, &type, &isIntersectionOfRects);
615
616 REPORTER_ASSERT(reporter, isIntersectionOfRects);
617 }
618
619 // mixed nested (aa inside bw) - should merge
620 {
621 SkClipStack stack;
622
Mike Reedc1f77742016-12-09 09:00:50 -0500623 stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000624
Mike Reedc1f77742016-12-09 09:00:50 -0500625 stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000626
627 REPORTER_ASSERT(reporter, 1 == count(stack));
628
629 stack.getBounds(&bound, &type, &isIntersectionOfRects);
630
631 REPORTER_ASSERT(reporter, isIntersectionOfRects);
632 }
633
634 // reverse nested (aa inside bw) - should _not_ merge
635 {
636 SkClipStack stack;
637
Mike Reedc1f77742016-12-09 09:00:50 -0500638 stack.clipRect(nestedChild, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000639
Mike Reedc1f77742016-12-09 09:00:50 -0500640 stack.clipRect(nestedParent, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000641
642 REPORTER_ASSERT(reporter, 2 == count(stack));
643
644 stack.getBounds(&bound, &type, &isIntersectionOfRects);
645
646 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
647 }
648}
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000649
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000650static void test_quickContains(skiatest::Reporter* reporter) {
651 SkRect testRect = SkRect::MakeLTRB(10, 10, 40, 40);
652 SkRect insideRect = SkRect::MakeLTRB(20, 20, 30, 30);
653 SkRect intersectingRect = SkRect::MakeLTRB(25, 25, 50, 50);
654 SkRect outsideRect = SkRect::MakeLTRB(0, 0, 50, 50);
655 SkRect nonIntersectingRect = SkRect::MakeLTRB(100, 100, 110, 110);
656
657 SkPath insideCircle;
658 insideCircle.addCircle(25, 25, 5);
659 SkPath intersectingCircle;
660 intersectingCircle.addCircle(25, 40, 10);
661 SkPath outsideCircle;
662 outsideCircle.addCircle(25, 25, 50);
663 SkPath nonIntersectingCircle;
664 nonIntersectingCircle.addCircle(100, 100, 5);
665
666 {
667 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500668 stack.clipRect(outsideRect, SkMatrix::I(), kDifference_SkClipOp, false);
669 // return false because quickContains currently does not care for kDifference_SkClipOp
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000670 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
671 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000672
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000673 // Replace Op tests
674 {
675 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500676 stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000677 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
678 }
679
680 {
681 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500682 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000683 stack.save(); // To prevent in-place substitution by replace OP
Mike Reedc1f77742016-12-09 09:00:50 -0500684 stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000685 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
686 stack.restore();
687 }
688
689 {
690 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500691 stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000692 stack.save(); // To prevent in-place substitution by replace OP
Mike Reedc1f77742016-12-09 09:00:50 -0500693 stack.clipRect(insideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000694 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
695 stack.restore();
696 }
697
698 // Verify proper traversal of multi-element clip
699 {
700 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500701 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000702 // Use a path for second clip to prevent in-place intersection
Mike Reedc1f77742016-12-09 09:00:50 -0500703 stack.clipPath(outsideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000704 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
705 }
706
707 // Intersect Op tests with rectangles
708 {
709 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500710 stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000711 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
712 }
713
714 {
715 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500716 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000717 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
718 }
719
720 {
721 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500722 stack.clipRect(intersectingRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000723 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
724 }
725
726 {
727 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500728 stack.clipRect(nonIntersectingRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000729 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
730 }
731
732 // Intersect Op tests with circle paths
733 {
734 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500735 stack.clipPath(outsideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000736 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
737 }
738
739 {
740 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500741 stack.clipPath(insideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000742 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
743 }
744
745 {
746 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500747 stack.clipPath(intersectingCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000748 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
749 }
750
751 {
752 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500753 stack.clipPath(nonIntersectingCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000754 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
755 }
756
757 // Intersect Op tests with inverse filled rectangles
758 {
759 SkClipStack stack;
760 SkPath path;
761 path.addRect(outsideRect);
762 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500763 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000764 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
765 }
766
767 {
768 SkClipStack stack;
769 SkPath path;
770 path.addRect(insideRect);
771 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500772 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000773 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
774 }
775
776 {
777 SkClipStack stack;
778 SkPath path;
779 path.addRect(intersectingRect);
780 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500781 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000782 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
783 }
784
785 {
786 SkClipStack stack;
787 SkPath path;
788 path.addRect(nonIntersectingRect);
789 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500790 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000791 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
792 }
793
794 // Intersect Op tests with inverse filled circles
795 {
796 SkClipStack stack;
797 SkPath path = outsideCircle;
798 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500799 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000800 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
801 }
802
803 {
804 SkClipStack stack;
805 SkPath path = insideCircle;
806 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500807 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000808 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
809 }
810
811 {
812 SkClipStack stack;
813 SkPath path = intersectingCircle;
814 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500815 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000816 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
817 }
818
819 {
820 SkClipStack stack;
821 SkPath path = nonIntersectingCircle;
822 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500823 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000824 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
825 }
826}
827
csmartdaltond50e2402016-07-22 08:39:06 -0700828static void set_region_to_stack(const SkClipStack& stack, const SkIRect& bounds, SkRegion* region) {
829 region->setRect(bounds);
830 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
831 while (const SkClipStack::Element *element = iter.next()) {
832 SkRegion elemRegion;
833 SkRegion boundsRgn(bounds);
834 SkPath path;
835
Brian Salomonf3b46e52017-08-30 11:37:57 -0400836 switch (element->getDeviceSpaceType()) {
837 case SkClipStack::Element::DeviceSpaceType::kEmpty:
csmartdaltond50e2402016-07-22 08:39:06 -0700838 elemRegion.setEmpty();
839 break;
840 default:
Brian Salomonf3b46e52017-08-30 11:37:57 -0400841 element->asDeviceSpacePath(&path);
csmartdaltond50e2402016-07-22 08:39:06 -0700842 elemRegion.setPath(path, boundsRgn);
843 break;
844 }
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400845
reed73603f32016-09-20 08:42:38 -0700846 region->op(elemRegion, (SkRegion::Op)element->getOp());
csmartdaltond50e2402016-07-22 08:39:06 -0700847 }
848}
849
850static void test_invfill_diff_bug(skiatest::Reporter* reporter) {
851 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500852 stack.clipRect({10, 10, 20, 20}, SkMatrix::I(), kIntersect_SkClipOp, false);
csmartdaltond50e2402016-07-22 08:39:06 -0700853
854 SkPath path;
855 path.addRect({30, 10, 40, 20});
Mike Reed7d34dc72019-11-26 12:17:17 -0500856 path.setFillType(SkPathFillType::kInverseWinding);
Mike Reedc1f77742016-12-09 09:00:50 -0500857 stack.clipPath(path, SkMatrix::I(), kDifference_SkClipOp, false);
csmartdaltond50e2402016-07-22 08:39:06 -0700858
859 REPORTER_ASSERT(reporter, SkClipStack::kEmptyGenID == stack.getTopmostGenID());
860
861 SkRect stackBounds;
862 SkClipStack::BoundsType stackBoundsType;
863 stack.getBounds(&stackBounds, &stackBoundsType);
864
865 REPORTER_ASSERT(reporter, stackBounds.isEmpty());
866 REPORTER_ASSERT(reporter, SkClipStack::kNormal_BoundsType == stackBoundsType);
867
868 SkRegion region;
869 set_region_to_stack(stack, {0, 0, 50, 30}, &region);
870
871 REPORTER_ASSERT(reporter, region.isEmpty());
872}
873
bsalomon@google.com51a62862012-11-26 21:19:43 +0000874///////////////////////////////////////////////////////////////////////////////////////////////////
875
Robert Phillips1ca0da42021-06-14 15:27:19 -0400876#if GR_OGA
877#include "src/gpu/GrClipStackClip.h"
878#include "src/gpu/GrReducedClip.h"
879
880typedef GrReducedClip::ElementList ElementList;
881typedef GrReducedClip::InitialState InitialState;
882
bsalomon@google.com705e8402012-11-27 15:43:57 +0000883// Functions that add a shape to the clip stack. The shape is computed from a rectangle.
884// AA is always disabled since the clip stack reducer can cause changes in aa rasterization of the
885// stack. A fractional edge repeated in different elements may be rasterized fewer times using the
886// reduced stack.
887typedef void (*AddElementFunc) (const SkRect& rect,
888 bool invert,
Mike Reedc1f77742016-12-09 09:00:50 -0500889 SkClipOp op,
csmartdaltoncbecb082016-07-22 08:59:08 -0700890 SkClipStack* stack,
891 bool doAA);
bsalomon@google.coma4e13c82012-11-26 21:38:37 +0000892
Mike Reedc1f77742016-12-09 09:00:50 -0500893static void add_round_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700894 bool doAA) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000895 SkScalar rx = rect.width() / 10;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000896 SkScalar ry = rect.height() / 20;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000897 if (invert) {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000898 SkPath path;
899 path.addRoundRect(rect, rx, ry);
Mike Reed7d34dc72019-11-26 12:17:17 -0500900 path.setFillType(SkPathFillType::kInverseWinding);
Brian Salomona3b45d42016-10-03 11:36:16 -0400901 stack->clipPath(path, SkMatrix::I(), op, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000902 } else {
903 SkRRect rrect;
904 rrect.setRectXY(rect, rx, ry);
Brian Salomona3b45d42016-10-03 11:36:16 -0400905 stack->clipRRect(rrect, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000906 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000907};
908
Mike Reedc1f77742016-12-09 09:00:50 -0500909static void add_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700910 bool doAA) {
bsalomon@google.com705e8402012-11-27 15:43:57 +0000911 if (invert) {
912 SkPath path;
913 path.addRect(rect);
Mike Reed7d34dc72019-11-26 12:17:17 -0500914 path.setFillType(SkPathFillType::kInverseWinding);
Brian Salomona3b45d42016-10-03 11:36:16 -0400915 stack->clipPath(path, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000916 } else {
Brian Salomona3b45d42016-10-03 11:36:16 -0400917 stack->clipRect(rect, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000918 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000919};
920
Mike Reedc1f77742016-12-09 09:00:50 -0500921static void add_oval(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700922 bool doAA) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000923 SkPath path;
924 path.addOval(rect);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000925 if (invert) {
Mike Reed7d34dc72019-11-26 12:17:17 -0500926 path.setFillType(SkPathFillType::kInverseWinding);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000927 }
Brian Salomona3b45d42016-10-03 11:36:16 -0400928 stack->clipPath(path, SkMatrix::I(), op, doAA);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000929};
930
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400931static void add_shader(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
932 bool doAA) {
933 // invert, op, and doAA don't apply to shaders at the SkClipStack level; this is handled earlier
934 // in the SkCanvas->SkDevice stack. Use rect to produce unique gradients, however.
935 SkPoint corners[2] = { {rect.fLeft, rect.fTop}, {rect.fRight, rect.fBottom} };
936 SkColor colors[2] = { SK_ColorBLACK, SK_ColorTRANSPARENT };
937 auto gradient = SkGradientShader::MakeLinear(corners, colors, nullptr, 2, SkTileMode::kDecal);
938 stack->clipShader(std::move(gradient));
939}
Michael Ludwigde00dc92020-06-05 10:56:32 -0400940
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000941static void add_elem_to_stack(const SkClipStack::Element& element, SkClipStack* stack) {
Brian Salomonf3b46e52017-08-30 11:37:57 -0400942 switch (element.getDeviceSpaceType()) {
943 case SkClipStack::Element::DeviceSpaceType::kRect:
944 stack->clipRect(element.getDeviceSpaceRect(), SkMatrix::I(), element.getOp(),
945 element.isAA());
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000946 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400947 case SkClipStack::Element::DeviceSpaceType::kRRect:
948 stack->clipRRect(element.getDeviceSpaceRRect(), SkMatrix::I(), element.getOp(),
949 element.isAA());
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000950 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400951 case SkClipStack::Element::DeviceSpaceType::kPath:
952 stack->clipPath(element.getDeviceSpacePath(), SkMatrix::I(), element.getOp(),
953 element.isAA());
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000954 break;
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400955 case SkClipStack::Element::DeviceSpaceType::kShader:
956 SkDEBUGFAIL("Why did the reducer put this in the mask elements.");
957 stack->clipShader(element.refShader());
958 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400959 case SkClipStack::Element::DeviceSpaceType::kEmpty:
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000960 SkDEBUGFAIL("Why did the reducer produce an explicit empty.");
961 stack->clipEmpty();
962 break;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000963 }
964}
965
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400966static void test_reduced_clip_stack(skiatest::Reporter* reporter, bool enableClipShader) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000967 // We construct random clip stacks, reduce them, and then rasterize both versions to verify that
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000968 // they are equal.
bsalomon@google.com51a62862012-11-26 21:19:43 +0000969
970 // All the clip elements will be contained within these bounds.
csmartdaltond211e782016-08-15 11:17:19 -0700971 static const SkIRect kIBounds = SkIRect::MakeWH(100, 100);
972 static const SkRect kBounds = SkRect::Make(kIBounds);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000973
974 enum {
csmartdaltoncbecb082016-07-22 08:59:08 -0700975 kNumTests = 250,
bsalomon@google.com51a62862012-11-26 21:19:43 +0000976 kMinElemsPerTest = 1,
977 kMaxElemsPerTest = 50,
978 };
979
980 // min/max size of a clip element as a fraction of kBounds.
981 static const SkScalar kMinElemSizeFrac = SK_Scalar1 / 5;
982 static const SkScalar kMaxElemSizeFrac = SK_Scalar1;
983
Mike Reedc1f77742016-12-09 09:00:50 -0500984 static const SkClipOp kOps[] = {
985 kDifference_SkClipOp,
986 kIntersect_SkClipOp,
987 kUnion_SkClipOp,
988 kXOR_SkClipOp,
989 kReverseDifference_SkClipOp,
990 kReplace_SkClipOp,
bsalomon@google.com51a62862012-11-26 21:19:43 +0000991 };
992
993 // Replace operations short-circuit the optimizer. We want to make sure that we test this code
994 // path a little bit but we don't want it to prevent us from testing many longer traversals in
995 // the optimizer.
996 static const int kReplaceDiv = 4 * kMaxElemsPerTest;
997
bsalomon@google.com705e8402012-11-27 15:43:57 +0000998 // We want to test inverse fills. However, they are quite rare in practice so don't over do it.
999 static const SkScalar kFractionInverted = SK_Scalar1 / kMaxElemsPerTest;
1000
csmartdaltoncbecb082016-07-22 08:59:08 -07001001 static const SkScalar kFractionAntialiased = 0.25;
1002
bsalomon@google.com51a62862012-11-26 21:19:43 +00001003 static const AddElementFunc kElementFuncs[] = {
1004 add_rect,
1005 add_round_rect,
1006 add_oval,
Michael Ludwig4e221bd2020-06-05 11:29:36 -04001007 add_shader
bsalomon@google.com51a62862012-11-26 21:19:43 +00001008 };
1009
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +00001010 SkRandom r;
bsalomon@google.com51a62862012-11-26 21:19:43 +00001011
1012 for (int i = 0; i < kNumTests; ++i) {
csmartdaltoncbecb082016-07-22 08:59:08 -07001013 SkString testCase;
1014 testCase.printf("Iteration %d", i);
1015
bsalomon@google.com51a62862012-11-26 21:19:43 +00001016 // Randomly generate a clip stack.
1017 SkClipStack stack;
1018 int numElems = r.nextRangeU(kMinElemsPerTest, kMaxElemsPerTest);
csmartdaltoncbecb082016-07-22 08:59:08 -07001019 bool doAA = r.nextBiasedBool(kFractionAntialiased);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001020 for (int e = 0; e < numElems; ++e) {
Michael Ludwig4e221bd2020-06-05 11:29:36 -04001021 size_t opLimit = enableClipShader ? ((size_t) kIntersect_SkClipOp + 1)
1022 : SK_ARRAY_COUNT(kOps);
1023 SkClipOp op = kOps[r.nextULessThan(opLimit)];
Mike Reedc1f77742016-12-09 09:00:50 -05001024 if (op == kReplace_SkClipOp) {
bsalomon@google.com51a62862012-11-26 21:19:43 +00001025 if (r.nextU() % kReplaceDiv) {
1026 --e;
1027 continue;
1028 }
1029 }
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +00001030
bsalomon@google.com51a62862012-11-26 21:19:43 +00001031 // saves can change the clip stack behavior when an element is added.
1032 bool doSave = r.nextBool();
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +00001033
bsalomon@google.com51a62862012-11-26 21:19:43 +00001034 SkSize size = SkSize::Make(
Mike Reeddf85c382017-02-14 10:59:19 -05001035 kBounds.width() * r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac),
1036 kBounds.height() * r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac));
bsalomon@google.com51a62862012-11-26 21:19:43 +00001037
csmartdaltoncbecb082016-07-22 08:59:08 -07001038 SkPoint xy = {r.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth),
1039 r.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight)};
bsalomon@google.com51a62862012-11-26 21:19:43 +00001040
csmartdaltoncbecb082016-07-22 08:59:08 -07001041 SkRect rect;
1042 if (doAA) {
1043 rect.setXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight);
1044 if (GrClip::IsPixelAligned(rect)) {
1045 // Don't create an element that may accidentally become not antialiased.
1046 rect.outset(0.5f, 0.5f);
1047 }
1048 SkASSERT(!GrClip::IsPixelAligned(rect));
1049 } else {
1050 rect.setXYWH(SkScalarFloorToScalar(xy.fX),
1051 SkScalarFloorToScalar(xy.fY),
1052 SkScalarCeilToScalar(size.fWidth),
1053 SkScalarCeilToScalar(size.fHeight));
1054 }
bsalomon@google.com51a62862012-11-26 21:19:43 +00001055
bsalomon@google.com705e8402012-11-27 15:43:57 +00001056 bool invert = r.nextBiasedBool(kFractionInverted);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001057
Michael Ludwig4e221bd2020-06-05 11:29:36 -04001058 size_t functionLimit = SK_ARRAY_COUNT(kElementFuncs);
1059 if (!enableClipShader) {
1060 functionLimit--;
1061 }
1062 kElementFuncs[r.nextULessThan(functionLimit)](rect, invert, op, &stack, doAA);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001063 if (doSave) {
1064 stack.save();
1065 }
1066 }
1067
Robert Phillipsf4f80112020-07-13 16:13:31 -04001068 sk_sp<GrDirectContext> context = GrDirectContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001069 const GrCaps* caps = context->priv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001070
Brian Salomonc3833b42018-07-09 18:23:58 +00001071 // Zero the memory we will new the GrReducedClip into. This ensures the elements gen ID
1072 // will be kInvalidGenID if left uninitialized.
1073 SkAlignedSTStorage<1, GrReducedClip> storage;
1074 memset(storage.get(), 0, sizeof(GrReducedClip));
Brian Salomon4dea72a2019-12-18 10:43:10 -05001075 static_assert(0 == SkClipStack::kInvalidGenID);
Brian Salomonc3833b42018-07-09 18:23:58 +00001076
csmartdalton77f2fae2016-08-08 09:55:06 -07001077 // Get the reduced version of the stack.
csmartdaltoncbecb082016-07-22 08:59:08 -07001078 SkRect queryBounds = kBounds;
1079 queryBounds.outset(kBounds.width() / 2, kBounds.height() / 2);
Ethan Nicholaseace9352018-10-15 20:09:54 +00001080 const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, queryBounds, caps);
bsalomon@google.coma4444302012-12-04 15:22:12 +00001081
Brian Salomonc3833b42018-07-09 18:23:58 +00001082 REPORTER_ASSERT(reporter,
1083 reduced->maskElements().isEmpty() ||
1084 SkClipStack::kInvalidGenID != reduced->maskGenID(),
1085 testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001086
Brian Salomonc3833b42018-07-09 18:23:58 +00001087 if (!reduced->maskElements().isEmpty()) {
1088 REPORTER_ASSERT(reporter, reduced->hasScissor(), testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001089 SkRect stackBounds;
1090 SkClipStack::BoundsType stackBoundsType;
1091 stack.getBounds(&stackBounds, &stackBoundsType);
Brian Salomonc3833b42018-07-09 18:23:58 +00001092 REPORTER_ASSERT(reporter, reduced->maskRequiresAA() == doAA, testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001093 }
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001094
bsalomon@google.com51a62862012-11-26 21:19:43 +00001095 // Build a new clip stack based on the reduced clip elements
1096 SkClipStack reducedStack;
Brian Salomonc3833b42018-07-09 18:23:58 +00001097 if (GrReducedClip::InitialState::kAllOut == reduced->initialState()) {
bsalomon@google.com51a62862012-11-26 21:19:43 +00001098 // whether the result is bounded or not, the whole plane should start outside the clip.
1099 reducedStack.clipEmpty();
1100 }
Brian Salomonc3833b42018-07-09 18:23:58 +00001101 for (ElementList::Iter iter(reduced->maskElements()); iter.get(); iter.next()) {
1102 add_elem_to_stack(*iter.get(), &reducedStack);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001103 }
Michael Ludwig4e221bd2020-06-05 11:29:36 -04001104 if (reduced->hasShader()) {
1105 REPORTER_ASSERT(reporter, enableClipShader);
1106 reducedStack.clipShader(reduced->shader());
1107 }
bsalomon@google.com51a62862012-11-26 21:19:43 +00001108
Brian Salomonc3833b42018-07-09 18:23:58 +00001109 SkIRect scissor = reduced->hasScissor() ? reduced->scissor() : kIBounds;
csmartdaltond211e782016-08-15 11:17:19 -07001110
bsalomon@google.com34cd70a2012-12-06 14:23:20 +00001111 // GrReducedClipStack assumes that the final result is clipped to the returned bounds
Chris Dalton79471932017-10-27 01:50:57 -06001112 reducedStack.clipDevRect(scissor, kIntersect_SkClipOp);
1113 stack.clipDevRect(scissor, kIntersect_SkClipOp);
bsalomon@google.com34cd70a2012-12-06 14:23:20 +00001114
bsalomon@google.com51a62862012-11-26 21:19:43 +00001115 // convert both the original stack and reduced stack to SkRegions and see if they're equal
bsalomon@google.com51a62862012-11-26 21:19:43 +00001116 SkRegion region;
Chris Dalton79471932017-10-27 01:50:57 -06001117 set_region_to_stack(stack, scissor, &region);
csmartdaltond50e2402016-07-22 08:39:06 -07001118
bsalomon@google.com51a62862012-11-26 21:19:43 +00001119 SkRegion reducedRegion;
Chris Dalton79471932017-10-27 01:50:57 -06001120 set_region_to_stack(reducedStack, scissor, &reducedRegion);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001121
Brian Salomon1c80e992018-01-29 09:50:47 -05001122 REPORTER_ASSERT(reporter, region == reducedRegion, testCase.c_str());
Brian Salomonc3833b42018-07-09 18:23:58 +00001123
1124 reduced->~GrReducedClip();
bsalomon@google.com51a62862012-11-26 21:19:43 +00001125 }
1126}
1127
halcanary4dbbd042016-06-07 17:21:10 -07001128#ifdef SK_BUILD_FOR_WIN
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001129 #define SUPPRESS_VISIBILITY_WARNING
1130#else
1131 #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden")))
1132#endif
1133
Brian Salomonc3833b42018-07-09 18:23:58 +00001134static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) {
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001135 {
1136 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001137 stack.clipRect(SkRect::MakeXYWH(0, 0, 100, 100), SkMatrix::I(), kReplace_SkClipOp,
Brian Salomona3b45d42016-10-03 11:36:16 -04001138 true);
1139 stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(50.3), SkScalar(50.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001140 kReplace_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001141 SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001142
Robert Phillipsf4f80112020-07-13 16:13:31 -04001143 sk_sp<GrDirectContext> context = GrDirectContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001144 const GrCaps* caps = context->priv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001145
Brian Salomonc3833b42018-07-09 18:23:58 +00001146 SkAlignedSTStorage<1, GrReducedClip> storage;
1147 memset(storage.get(), 0, sizeof(GrReducedClip));
Brian Salomon4dea72a2019-12-18 10:43:10 -05001148 static_assert(0 == SkClipStack::kInvalidGenID);
Ethan Nicholaseace9352018-10-15 20:09:54 +00001149 const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, bounds, caps);
Brian Salomonc3833b42018-07-09 18:23:58 +00001150
1151 REPORTER_ASSERT(reporter, reduced->maskElements().count() == 1);
1152 // Clips will be cached based on the generation id. Make sure the gen id is valid.
1153 REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reduced->maskGenID());
1154
1155 reduced->~GrReducedClip();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001156 }
1157 {
1158 SkClipStack stack;
1159
1160 // Create a clip with following 25.3, 25.3 boxes which are 25 apart:
1161 // A B
1162 // C D
1163
Brian Salomona3b45d42016-10-03 11:36:16 -04001164 stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001165 kReplace_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001166 uint32_t genIDA = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001167 stack.clipRect(SkRect::MakeXYWH(50, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001168 kUnion_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001169 uint32_t genIDB = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001170 stack.clipRect(SkRect::MakeXYWH(0, 50, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001171 kUnion_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001172 uint32_t genIDC = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001173 stack.clipRect(SkRect::MakeXYWH(50, 50, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001174 kUnion_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001175 uint32_t genIDD = stack.getTopmostGenID();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001176
1177
csmartdaltoncbecb082016-07-22 08:59:08 -07001178#define IXYWH SkIRect::MakeXYWH
1179#define XYWH SkRect::MakeXYWH
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001180
csmartdaltoncbecb082016-07-22 08:59:08 -07001181 SkIRect stackBounds = IXYWH(0, 0, 76, 76);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001182
1183 // The base test is to test each rect in two ways:
1184 // 1) The box dimensions. (Should reduce to "all in", no elements).
1185 // 2) A bit over the box dimensions.
1186 // In the case 2, test that the generation id is what is expected.
1187 // The rects are of fractional size so that case 2 never gets optimized to an empty element
1188 // list.
1189
1190 // Not passing in tighter bounds is tested for consistency.
1191 static const struct SUPPRESS_VISIBILITY_WARNING {
csmartdaltoncbecb082016-07-22 08:59:08 -07001192 SkRect testBounds;
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001193 int reducedClipCount;
Brian Salomonc3833b42018-07-09 18:23:58 +00001194 uint32_t reducedGenID;
csmartdaltoncbecb082016-07-22 08:59:08 -07001195 InitialState initialState;
1196 SkIRect clipIRect;
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001197 // parameter.
1198 } testCases[] = {
csmartdalton77f2fae2016-08-08 09:55:06 -07001199
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001200 // Rect A.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001201 { XYWH(0, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 0, 25, 25) },
1202 { XYWH(0.1f, 0.1f, 25.1f, 25.1f), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 0, 26, 26) },
Chris Dalton79471932017-10-27 01:50:57 -06001203 { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::InitialState::kAllOut, IXYWH(0, 0, 26, 26)},
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001204
1205 // Rect B.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001206 { XYWH(50, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 25, 25) },
1207 { XYWH(50, 0, 25.3f, 25.3f), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 26, 26) },
csmartdalton77f2fae2016-08-08 09:55:06 -07001208 { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::InitialState::kAllOut, IXYWH(50, 0, 26, 27) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001209
1210 // Rect C.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001211 { XYWH(0, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 50, 25, 25) },
1212 { XYWH(0.2f, 50.1f, 25.1f, 25.2f), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 50, 26, 26) },
csmartdalton77f2fae2016-08-08 09:55:06 -07001213 { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::InitialState::kAllOut, IXYWH(0, 50, 27, 26) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001214
1215 // Rect D.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001216 { XYWH(50, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 25, 25)},
1217 { XYWH(50.3f, 50.3f, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 26, 26)},
csmartdalton77f2fae2016-08-08 09:55:06 -07001218 { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(50, 50, 26, 26)},
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001219
1220 // Other tests:
csmartdalton77f2fae2016-08-08 09:55:06 -07001221 { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::InitialState::kAllOut, stackBounds },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001222
Michael Ludwigde00dc92020-06-05 10:56:32 -04001223 // Rect in the middle, touches none (so should not be drawn)
1224 { XYWH(26, 26, 24, 24), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllOut, SkIRect::MakeEmpty() },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001225
1226 // Rect in the middle, touches all the rects. GenID is the last rect.
csmartdalton77f2fae2016-08-08 09:55:06 -07001227 { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(24, 24, 27, 27) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001228 };
1229
1230#undef XYWH
csmartdaltoncbecb082016-07-22 08:59:08 -07001231#undef IXYWH
Robert Phillipsf4f80112020-07-13 16:13:31 -04001232 sk_sp<GrDirectContext> context = GrDirectContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001233 const GrCaps* caps = context->priv().caps();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001234
1235 for (size_t i = 0; i < SK_ARRAY_COUNT(testCases); ++i) {
Ethan Nicholaseace9352018-10-15 20:09:54 +00001236 const GrReducedClip reduced(stack, testCases[i].testBounds, caps);
Chris Dalton79471932017-10-27 01:50:57 -06001237 REPORTER_ASSERT(reporter, reduced.maskElements().count() ==
1238 testCases[i].reducedClipCount);
Brian Salomonc3833b42018-07-09 18:23:58 +00001239 if (reduced.maskElements().count()) {
1240 REPORTER_ASSERT(reporter, reduced.maskGenID() == testCases[i].reducedGenID);
csmartdalton8d3f92a2016-08-17 09:39:38 -07001241 }
csmartdalton77f2fae2016-08-08 09:55:06 -07001242 REPORTER_ASSERT(reporter, reduced.initialState() == testCases[i].initialState);
Michael Ludwigde00dc92020-06-05 10:56:32 -04001243
1244 bool expectsScissor = !testCases[i].clipIRect.isEmpty();
1245 REPORTER_ASSERT(reporter, expectsScissor == reduced.hasScissor());
1246 if (expectsScissor) {
1247 REPORTER_ASSERT(reporter, reduced.scissor() == testCases[i].clipIRect);
1248 }
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001249 }
1250 }
1251}
1252
1253static void test_reduced_clip_stack_no_aa_crash(skiatest::Reporter* reporter) {
1254 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001255 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 100, 100), kReplace_SkClipOp);
1256 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 50, 50), kReplace_SkClipOp);
csmartdaltoncbecb082016-07-22 08:59:08 -07001257 SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001258
Robert Phillipsf4f80112020-07-13 16:13:31 -04001259 sk_sp<GrDirectContext> context = GrDirectContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001260 const GrCaps* caps = context->priv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001261
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001262 // At the time, this would crash.
Ethan Nicholaseace9352018-10-15 20:09:54 +00001263 const GrReducedClip reduced(stack, bounds, caps);
Chris Dalton79471932017-10-27 01:50:57 -06001264 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001265}
1266
csmartdaltoncbecb082016-07-22 08:59:08 -07001267enum class ClipMethod {
1268 kSkipDraw,
1269 kIgnoreClip,
1270 kScissor,
1271 kAAElements
1272};
1273
1274static void test_aa_query(skiatest::Reporter* reporter, const SkString& testName,
1275 const SkClipStack& stack, const SkMatrix& queryXform,
1276 const SkRect& preXformQuery, ClipMethod expectedMethod,
1277 int numExpectedElems = 0) {
Robert Phillipsf4f80112020-07-13 16:13:31 -04001278 sk_sp<GrDirectContext> context = GrDirectContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001279 const GrCaps* caps = context->priv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001280
csmartdaltoncbecb082016-07-22 08:59:08 -07001281 SkRect queryBounds;
1282 queryXform.mapRect(&queryBounds, preXformQuery);
Ethan Nicholaseace9352018-10-15 20:09:54 +00001283 const GrReducedClip reduced(stack, queryBounds, caps);
csmartdaltoncbecb082016-07-22 08:59:08 -07001284
1285 SkClipStack::BoundsType stackBoundsType;
1286 SkRect stackBounds;
1287 stack.getBounds(&stackBounds, &stackBoundsType);
1288
1289 switch (expectedMethod) {
1290 case ClipMethod::kSkipDraw:
1291 SkASSERT(0 == numExpectedElems);
Brian Salomon1c80e992018-01-29 09:50:47 -05001292 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty(), testName.c_str());
1293 REPORTER_ASSERT(reporter,
1294 GrReducedClip::InitialState::kAllOut == reduced.initialState(),
1295 testName.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001296 return;
1297 case ClipMethod::kIgnoreClip:
1298 SkASSERT(0 == numExpectedElems);
Brian Salomon1c80e992018-01-29 09:50:47 -05001299 REPORTER_ASSERT(
1300 reporter,
1301 !reduced.hasScissor() || GrClip::IsInsideClip(reduced.scissor(), queryBounds),
1302 testName.c_str());
1303 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty(), testName.c_str());
1304 REPORTER_ASSERT(reporter,
1305 GrReducedClip::InitialState::kAllIn == reduced.initialState(),
1306 testName.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001307 return;
1308 case ClipMethod::kScissor: {
1309 SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType);
1310 SkASSERT(0 == numExpectedElems);
1311 SkIRect expectedScissor;
1312 stackBounds.round(&expectedScissor);
Brian Salomon1c80e992018-01-29 09:50:47 -05001313 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty(), testName.c_str());
1314 REPORTER_ASSERT(reporter, reduced.hasScissor(), testName.c_str());
1315 REPORTER_ASSERT(reporter, expectedScissor == reduced.scissor(), testName.c_str());
1316 REPORTER_ASSERT(reporter,
1317 GrReducedClip::InitialState::kAllIn == reduced.initialState(),
1318 testName.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001319 return;
1320 }
1321 case ClipMethod::kAAElements: {
1322 SkIRect expectedClipIBounds = GrClip::GetPixelIBounds(queryBounds);
1323 if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
1324 SkAssertResult(expectedClipIBounds.intersect(GrClip::GetPixelIBounds(stackBounds)));
1325 }
Brian Salomon1c80e992018-01-29 09:50:47 -05001326 REPORTER_ASSERT(reporter, numExpectedElems == reduced.maskElements().count(),
1327 testName.c_str());
1328 REPORTER_ASSERT(reporter, reduced.hasScissor(), testName.c_str());
1329 REPORTER_ASSERT(reporter, expectedClipIBounds == reduced.scissor(), testName.c_str());
1330 REPORTER_ASSERT(reporter,
1331 reduced.maskElements().isEmpty() || reduced.maskRequiresAA(),
1332 testName.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001333 break;
1334 }
1335 }
1336}
1337
1338static void test_reduced_clip_stack_aa(skiatest::Reporter* reporter) {
1339 constexpr SkScalar IL = 2, IT = 1, IR = 6, IB = 7; // Pixel aligned rect.
1340 constexpr SkScalar L = 2.2f, T = 1.7f, R = 5.8f, B = 7.3f; // Generic rect.
1341 constexpr SkScalar l = 3.3f, t = 2.8f, r = 4.7f, b = 6.2f; // Small rect contained in R.
1342
1343 SkRect alignedRect = {IL, IT, IR, IB};
1344 SkRect rect = {L, T, R, B};
1345 SkRect innerRect = {l, t, r, b};
1346
1347 SkMatrix m;
1348 m.setIdentity();
1349
1350 constexpr SkScalar kMinScale = 2.0001f;
1351 constexpr SkScalar kMaxScale = 3;
1352 constexpr int kNumIters = 8;
1353
1354 SkString name;
1355 SkRandom rand;
1356
1357 for (int i = 0; i < kNumIters; ++i) {
1358 // Pixel-aligned rect (iior=true).
1359 name.printf("Pixel-aligned rect test, iter %i", i);
1360 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001361 stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001362 test_aa_query(reporter, name, stack, m, {IL, IT, IR, IB}, ClipMethod::kIgnoreClip);
1363 test_aa_query(reporter, name, stack, m, {IL, IT-1, IR, IT}, ClipMethod::kSkipDraw);
csmartdalton77f2fae2016-08-08 09:55:06 -07001364 test_aa_query(reporter, name, stack, m, {IL, IT-2, IR, IB}, ClipMethod::kScissor);
csmartdaltoncbecb082016-07-22 08:59:08 -07001365
1366 // Rect (iior=true).
1367 name.printf("Rect test, iter %i", i);
1368 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001369 stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001370 test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kIgnoreClip);
1371 test_aa_query(reporter, name, stack, m, {L-.1f, T, L, B}, ClipMethod::kSkipDraw);
1372 test_aa_query(reporter, name, stack, m, {L-.1f, T, L+.1f, B}, ClipMethod::kAAElements, 1);
1373
1374 // Difference rect (iior=false, inside-out bounds).
1375 name.printf("Difference rect test, iter %i", i);
1376 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001377 stack.clipRect(rect, SkMatrix::I(), kDifference_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001378 test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kSkipDraw);
1379 test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T}, ClipMethod::kIgnoreClip);
1380 test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T+.1f}, ClipMethod::kAAElements, 1);
1381
1382 // Complex clip (iior=false, normal bounds).
1383 name.printf("Complex clip test, iter %i", i);
1384 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001385 stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true);
1386 stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001387 test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
1388 test_aa_query(reporter, name, stack, m, {r-.1f, t, R, b}, ClipMethod::kAAElements, 1);
1389 test_aa_query(reporter, name, stack, m, {r-.1f, t, R+.1f, b}, ClipMethod::kAAElements, 2);
1390 test_aa_query(reporter, name, stack, m, {r, t, R+.1f, b}, ClipMethod::kAAElements, 1);
1391 test_aa_query(reporter, name, stack, m, {r, t, R, b}, ClipMethod::kIgnoreClip);
1392 test_aa_query(reporter, name, stack, m, {R, T, R+.1f, B}, ClipMethod::kSkipDraw);
1393
1394 // Complex clip where outer rect is pixel aligned (iior=false, normal bounds).
1395 name.printf("Aligned Complex clip test, iter %i", i);
1396 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001397 stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true);
1398 stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001399 test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
1400 test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB}, ClipMethod::kAAElements, 1);
1401 test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB+.1f}, ClipMethod::kAAElements, 1);
1402 test_aa_query(reporter, name, stack, m, {l, b, r, IB+.1f}, ClipMethod::kAAElements, 0);
1403 test_aa_query(reporter, name, stack, m, {l, b, r, IB}, ClipMethod::kIgnoreClip);
1404 test_aa_query(reporter, name, stack, m, {IL, IB, IR, IB+.1f}, ClipMethod::kSkipDraw);
1405
1406 // Apply random transforms and try again. This ensures the clip stack reduction is hardened
1407 // against FP rounding error.
1408 SkScalar sx = rand.nextRangeScalar(kMinScale, kMaxScale);
1409 sx = SkScalarFloorToScalar(sx * alignedRect.width()) / alignedRect.width();
1410 SkScalar sy = rand.nextRangeScalar(kMinScale, kMaxScale);
1411 sy = SkScalarFloorToScalar(sy * alignedRect.height()) / alignedRect.height();
1412 SkScalar tx = SkScalarRoundToScalar(sx * alignedRect.x()) - sx * alignedRect.x();
1413 SkScalar ty = SkScalarRoundToScalar(sy * alignedRect.y()) - sy * alignedRect.y();
1414
Mike Reed1f607332020-05-21 12:11:27 -04001415 SkMatrix xform = SkMatrix::Scale(sx, sy);
csmartdaltoncbecb082016-07-22 08:59:08 -07001416 xform.postTranslate(tx, ty);
1417 xform.mapRect(&alignedRect);
1418 xform.mapRect(&rect);
1419 xform.mapRect(&innerRect);
1420 m.postConcat(xform);
1421 }
1422}
1423
Chris Dalton348060f2017-06-05 13:15:37 -06001424static void test_tiny_query_bounds_assertion_bug(skiatest::Reporter* reporter) {
1425 // https://bugs.chromium.org/p/skia/issues/detail?id=5990
1426 const SkRect clipBounds = SkRect::MakeXYWH(1.5f, 100, 1000, 1000);
1427
1428 SkClipStack rectStack;
1429 rectStack.clipRect(clipBounds, SkMatrix::I(), kIntersect_SkClipOp, true);
1430
1431 SkPath clipPath;
1432 clipPath.moveTo(clipBounds.left(), clipBounds.top());
1433 clipPath.quadTo(clipBounds.right(), clipBounds.top(),
1434 clipBounds.right(), clipBounds.bottom());
1435 clipPath.quadTo(clipBounds.left(), clipBounds.bottom(),
1436 clipBounds.left(), clipBounds.top());
1437 SkClipStack pathStack;
1438 pathStack.clipPath(clipPath, SkMatrix::I(), kIntersect_SkClipOp, true);
1439
Robert Phillipsf4f80112020-07-13 16:13:31 -04001440 sk_sp<GrDirectContext> context = GrDirectContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001441 const GrCaps* caps = context->priv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001442
Chris Dalton348060f2017-06-05 13:15:37 -06001443 for (const SkClipStack& stack : {rectStack, pathStack}) {
1444 for (SkRect queryBounds : {SkRect::MakeXYWH(53, 60, GrClip::kBoundsTolerance, 1000),
1445 SkRect::MakeXYWH(53, 60, GrClip::kBoundsTolerance/2, 1000),
1446 SkRect::MakeXYWH(53, 160, 1000, GrClip::kBoundsTolerance),
1447 SkRect::MakeXYWH(53, 160, 1000, GrClip::kBoundsTolerance/2)}) {
Ethan Nicholaseace9352018-10-15 20:09:54 +00001448 const GrReducedClip reduced(stack, queryBounds, caps);
Chris Dalton79471932017-10-27 01:50:57 -06001449 REPORTER_ASSERT(reporter, !reduced.hasScissor());
1450 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty());
Chris Dalton348060f2017-06-05 13:15:37 -06001451 REPORTER_ASSERT(reporter,
1452 GrReducedClip::InitialState::kAllOut == reduced.initialState());
1453 }
1454 }
1455}
1456
Robert Phillips1ca0da42021-06-14 15:27:19 -04001457#endif // GR_OGA
1458
Brian Salomon1c0b05a2019-04-19 15:37:28 -04001459static void test_is_rrect_deep_rect_stack(skiatest::Reporter* reporter) {
1460 static constexpr SkRect kTargetBounds = SkRect::MakeWH(1000, 500);
1461 // All antialiased or all not antialiased.
1462 for (bool aa : {false, true}) {
1463 SkClipStack stack;
1464 for (int i = 0; i <= 100; ++i) {
1465 stack.save();
1466 stack.clipRect(SkRect::MakeLTRB(i, 0.5, kTargetBounds.width(), kTargetBounds.height()),
1467 SkMatrix::I(), SkClipOp::kIntersect, aa);
1468 }
1469 SkRRect rrect;
1470 bool isAA;
1471 SkRRect expected = SkRRect::MakeRect(
1472 SkRect::MakeLTRB(100, 0.5, kTargetBounds.width(), kTargetBounds.height()));
1473 if (stack.isRRect(kTargetBounds, &rrect, &isAA)) {
1474 REPORTER_ASSERT(reporter, rrect == expected);
1475 REPORTER_ASSERT(reporter, aa == isAA);
1476 } else {
1477 ERRORF(reporter, "Expected to be an rrect.");
1478 }
1479 }
1480 // Mixed AA and non-AA without simple containment.
1481 SkClipStack stack;
1482 for (int i = 0; i <= 100; ++i) {
1483 bool aa = i & 0b1;
1484 int j = 100 - i;
1485 stack.save();
1486 stack.clipRect(SkRect::MakeLTRB(i, j + 0.5, kTargetBounds.width(), kTargetBounds.height()),
1487 SkMatrix::I(), SkClipOp::kIntersect, aa);
1488 }
1489 SkRRect rrect;
1490 bool isAA;
1491 REPORTER_ASSERT(reporter, !stack.isRRect(kTargetBounds, &rrect, &isAA));
1492}
1493
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00001494DEF_TEST(ClipStack, reporter) {
reed@google.combdee9fc2011-02-22 20:17:43 +00001495 SkClipStack stack;
1496
robertphillips@google.com80214e22012-07-20 15:33:18 +00001497 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001498 assert_count(reporter, stack, 0);
1499
1500 static const SkIRect gRects[] = {
1501 { 0, 0, 100, 100 },
1502 { 25, 25, 125, 125 },
1503 { 0, 0, 1000, 1000 },
1504 { 0, 0, 75, 75 }
1505 };
1506 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
Mike Reedc1f77742016-12-09 09:00:50 -05001507 stack.clipDevRect(gRects[i], kIntersect_SkClipOp);
reed@google.combdee9fc2011-02-22 20:17:43 +00001508 }
1509
1510 // all of the above rects should have been intersected, leaving only 1 rect
robertphillips@google.com80214e22012-07-20 15:33:18 +00001511 SkClipStack::B2TIter iter(stack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001512 const SkClipStack::Element* element = iter.next();
epoger@google.com2047f002011-05-17 17:36:59 +00001513 SkRect answer;
Mike Reed92b33352019-08-24 19:39:13 -04001514 answer.setLTRB(25, 25, 75, 75);
reed@google.combdee9fc2011-02-22 20:17:43 +00001515
bsalomon49f085d2014-09-05 13:34:00 -07001516 REPORTER_ASSERT(reporter, element);
Brian Salomonf3b46e52017-08-30 11:37:57 -04001517 REPORTER_ASSERT(reporter,
1518 SkClipStack::Element::DeviceSpaceType::kRect == element->getDeviceSpaceType());
Mike Reedc1f77742016-12-09 09:00:50 -05001519 REPORTER_ASSERT(reporter, kIntersect_SkClipOp == element->getOp());
Brian Salomonf3b46e52017-08-30 11:37:57 -04001520 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == answer);
reed@google.combdee9fc2011-02-22 20:17:43 +00001521 // now check that we only had one in our iterator
1522 REPORTER_ASSERT(reporter, !iter.next());
1523
1524 stack.reset();
robertphillips@google.com80214e22012-07-20 15:33:18 +00001525 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001526 assert_count(reporter, stack, 0);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +00001527
1528 test_assign_and_comparison(reporter);
robertphillips@google.com80214e22012-07-20 15:33:18 +00001529 test_iterators(reporter);
Brian Salomonf3b46e52017-08-30 11:37:57 -04001530 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kRect);
1531 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kRRect);
1532 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kPath);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +00001533 test_isWideOpen(reporter);
robertphillips@google.com08eacc12012-08-02 12:49:00 +00001534 test_rect_merging(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001535 test_rect_replace(reporter);
junov@chromium.orgedf32d52012-12-10 14:57:54 +00001536 test_rect_inverse_fill(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001537 test_path_replace(reporter);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +00001538 test_quickContains(reporter);
csmartdaltond50e2402016-07-22 08:39:06 -07001539 test_invfill_diff_bug(reporter);
Brian Osmanc7ad40f2018-05-31 14:27:17 -04001540
Robert Phillips1ca0da42021-06-14 15:27:19 -04001541#if GR_OGA
Michael Ludwig4e221bd2020-06-05 11:29:36 -04001542 test_reduced_clip_stack(reporter, /* clipShader */ false);
1543 test_reduced_clip_stack(reporter, /* clipShader */ true);
Brian Salomonc3833b42018-07-09 18:23:58 +00001544 test_reduced_clip_stack_genid(reporter);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001545 test_reduced_clip_stack_no_aa_crash(reporter);
csmartdaltoncbecb082016-07-22 08:59:08 -07001546 test_reduced_clip_stack_aa(reporter);
Chris Dalton348060f2017-06-05 13:15:37 -06001547 test_tiny_query_bounds_assertion_bug(reporter);
Robert Phillips1ca0da42021-06-14 15:27:19 -04001548#endif
Brian Salomon1c0b05a2019-04-19 15:37:28 -04001549 test_is_rrect_deep_rect_stack(reporter);
reed@google.combdee9fc2011-02-22 20:17:43 +00001550}
Brian Salomon19f0ed52017-01-06 13:54:58 -05001551
1552//////////////////////////////////////////////////////////////////////////////
1553
Robert Phillips8faa3402021-04-05 14:01:36 -04001554// For the GrClipStack case, this is covered in GrClipStack_SWMask
1555#if defined(SK_DISABLE_NEW_GR_CLIP_STACK)
1556
Robert Phillips4e105e22020-07-16 09:18:50 -04001557sk_sp<GrTextureProxy> GrClipStackClip::testingOnly_createClipMask(
1558 GrRecordingContext* context) const {
John Stilesfe0de302020-08-14 10:52:06 -04001559 const GrReducedClip reducedClip(*fStack, SkRect::MakeWH(512, 512), nullptr);
Greg Daniele32506b2020-02-10 16:00:54 -05001560 return this->createSoftwareClipMask(context, reducedClip, nullptr).asTextureProxyRef();
Brian Salomon19f0ed52017-01-06 13:54:58 -05001561}
1562
1563// Verify that clip masks are freed up when the clip state that generated them goes away.
1564DEF_GPUTEST_FOR_ALL_CONTEXTS(ClipMaskCache, reporter, ctxInfo) {
1565 // This test uses resource key tags which only function in debug builds.
1566#ifdef SK_DEBUG
Robert Phillips6d344c32020-07-06 10:56:46 -04001567 auto context = ctxInfo.directContext();
Brian Salomon19f0ed52017-01-06 13:54:58 -05001568 SkClipStack stack;
1569
1570 SkPath path;
1571 path.addCircle(10, 10, 8);
1572 path.addCircle(15, 15, 8);
Mike Reed7d34dc72019-11-26 12:17:17 -05001573 path.setFillType(SkPathFillType::kEvenOdd);
Brian Salomon19f0ed52017-01-06 13:54:58 -05001574
Michael Ludwige06a8972020-06-11 10:29:00 -04001575 SkIRect stackBounds = path.getBounds().roundOut();
1576
Brian Salomonc3833b42018-07-09 18:23:58 +00001577 static const char* kTag = GrClipStackClip::kMaskTestTag;
Robert Phillips9da87e02019-02-04 13:26:26 -05001578 GrResourceCache* cache = context->priv().getResourceCache();
Brian Salomon19f0ed52017-01-06 13:54:58 -05001579
1580 static constexpr int kN = 5;
1581
1582 for (int i = 0; i < kN; ++i) {
1583 SkMatrix m;
1584 m.setTranslate(0.5, 0.5);
1585 stack.save();
1586 stack.clipPath(path, m, SkClipOp::kIntersect, true);
Michael Ludwige06a8972020-06-11 10:29:00 -04001587 sk_sp<GrTextureProxy> mask =
1588 GrClipStackClip(stackBounds.size(), &stack).testingOnly_createClipMask(context);
Adlai Hollercc25d532021-02-10 13:58:34 +00001589 mask->instantiate(context->priv().resourceProvider());
Brian Salomonfd98c2c2018-07-31 17:25:29 -04001590 GrTexture* tex = mask->peekTexture();
Robert Phillips875218e2017-02-24 08:37:13 -05001591 REPORTER_ASSERT(reporter, 0 == strcmp(tex->getUniqueKey().tag(), kTag));
Brian Salomon19f0ed52017-01-06 13:54:58 -05001592 // Make sure mask isn't pinned in cache.
1593 mask.reset(nullptr);
Greg Daniel0a2464f2020-05-14 15:45:44 -04001594 context->flushAndSubmit();
Brian Salomon19f0ed52017-01-06 13:54:58 -05001595 REPORTER_ASSERT(reporter, i + 1 == cache->countUniqueKeysWithTag(kTag));
1596 }
1597
1598 for (int i = 0; i < kN; ++i) {
1599 stack.restore();
1600 cache->purgeAsNeeded();
1601 REPORTER_ASSERT(reporter, kN - (i + 1) == cache->countUniqueKeysWithTag(kTag));
1602 }
1603#endif
1604}
Robert Phillips8faa3402021-04-05 14:01:36 -04001605#endif // SK_DISABLE_NEW_GR_CLIP_STACK