blob: 9c2c8872978c8ed9e550d68eed9ea23740a2e7e4 [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();
Michael Ludwigd59d3e12021-08-05 16:39:18 -040061 s.clipPath(p, SkMatrix::I(), SkClipOp::kIntersect, 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
Michael Ludwiga3a67ae2021-08-06 10:42:01 -040066 SkRect r = SkRect::MakeLTRB(1, 2, 103, 104);
Michael Ludwigd59d3e12021-08-05 16:39:18 -040067 s.clipRect(r, SkMatrix::I(), SkClipOp::kIntersect, doAA);
Michael Ludwiga3a67ae2021-08-06 10:42:01 -040068 r = SkRect::MakeLTRB(4, 5, 56, 57);
Michael Ludwigd59d3e12021-08-05 16:39:18 -040069 s.clipRect(r, SkMatrix::I(), SkClipOp::kIntersect, 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);
Michael Ludwiga3a67ae2021-08-06 10:42:01 -040075 s.clipRect(r, SkMatrix::I(), SkClipOp::kDifference, 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);
Michael Ludwiga3a67ae2021-08-06 10:42:01 -040090 s.clipRect(r, SkMatrix::I(), SkClipOp::kDifference, 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);
Michael Ludwigd59d3e12021-08-05 16:39:18 -040099 s.clipRect(r, SkMatrix::I(), SkClipOp::kIntersect, 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);
Michael Ludwiga3a67ae2021-08-06 10:42:01 -0400107 s.clipPath(rp, SkMatrix::I(), SkClipOp::kDifference, 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);
Michael Ludwiga3a67ae2021-08-06 10:42:01 -0400117 s.clipRect(r, SkMatrix::I(), SkClipOp::kDifference, 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);
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400139 s.clipPath(p, SkMatrix::I(), SkClipOp::kIntersect, 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++) {
Michael Ludwiga3a67ae2021-08-06 10:42:01 -0400166 // the difference op will prevent these from being fused together
167 stack.clipRect(gRects[i], SkMatrix::I(), SkClipOp::kDifference, 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
Michael Ludwiga3a67ae2021-08-06 10:42:01 -0400210 element = iter.skipToTopmost(SkClipOp::kDifference);
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[] = {
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400252 SkClipOp::kIntersect,
253 SkClipOp::kDifference,
Mike Reedc1f77742016-12-09 09:00:50 -0500254 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:
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400297 stack.clipRect(rectA, SkMatrix::I(), SkClipOp::kIntersect, 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:
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400301 stack.clipRRect(rrectA, SkMatrix::I(), SkClipOp::kIntersect, 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:
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400305 stack.clipPath(pathA, SkMatrix::I(), SkClipOp::kIntersect, 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 ==
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400318 (gOps[op] == SkClipOp::kIntersect));
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
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400366 stack.clipPath(clipA, SkMatrix::I(), SkClipOp::kIntersect, false);
Mike Reedc1f77742016-12-09 09:00:50 -0500367 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
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400390 stack.clipRect(emptyRect, SkMatrix::I(), SkClipOp::kDifference, 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
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400402 stack.clipRect(rectA, SkMatrix::I(), SkClipOp::kIntersect, 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;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400435 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, 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));
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400457 stack.replaceClip(rect, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000458 REPORTER_ASSERT(reporter, 1 == count(stack));
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400459 stack.replaceClip(rect, 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));
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400468 stack.replaceClip(rect, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000469 REPORTER_ASSERT(reporter, 1 == count(stack));
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400470 stack.replaceClip(rect, 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));
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400479 stack.replaceClip(rect, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000480 REPORTER_ASSERT(reporter, 1 == count(stack));
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400481 stack.replaceClip(rect, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000482 REPORTER_ASSERT(reporter, 1 == count(stack));
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400483 stack.replaceClip(rect, 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;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400490 stack.replaceClip(rect, false);
491 stack.clipRect(rect2, SkMatrix::I(), SkClipOp::kIntersect, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000492 REPORTER_ASSERT(reporter, 1 == count(stack));
493
494 stack.save();
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400495 stack.replaceClip(rect, 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();
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400503 stack.replaceClip(rect, false);
504 stack.replaceClip(rect, 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();
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400510 stack.replaceClip(rect, false);
511 stack.clipRect(rect2, SkMatrix::I(), SkClipOp::kIntersect, false);
512 stack.replaceClip(rect, 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) {
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400521 auto replacePath = [](SkClipStack* stack, const SkPath& path, bool doAA) {
522 const SkRect wideOpen = SkRect::MakeLTRB(-1000, -1000, 1000, 1000);
523 stack->replaceClip(wideOpen, false);
524 stack->clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, doAA);
525 };
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000526 SkRect rect = SkRect::MakeWH(100, 100);
527 SkPath path;
528 path.addCircle(50, 50, 50);
529
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400530 // Emulating replace operations with more complex geometry is not atomic, it's a replace
531 // with a wide-open rect and then an intersection with the complex geometry. The replace can
532 // combine with prior elements, but the subsequent intersect cannot be combined so the stack
533 // continues to grow.
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000534 {
535 SkClipStack stack;
536 REPORTER_ASSERT(reporter, 0 == count(stack));
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400537 replacePath(&stack, path, false);
538 REPORTER_ASSERT(reporter, 2 == count(stack));
539 replacePath(&stack, path, false);
540 REPORTER_ASSERT(reporter, 2 == count(stack));
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000541 }
542
543 // Replacing rect with path.
544 {
545 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400546 stack.replaceClip(rect, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000547 REPORTER_ASSERT(reporter, 1 == count(stack));
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400548 replacePath(&stack, path, true);
549 REPORTER_ASSERT(reporter, 2 == count(stack));
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000550 }
551}
552
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000553// Test out SkClipStack's merging of rect clips. In particular exercise
554// merging of aa vs. bw rects.
555static void test_rect_merging(skiatest::Reporter* reporter) {
556
557 SkRect overlapLeft = SkRect::MakeLTRB(10, 10, 50, 50);
558 SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80);
559
560 SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90);
561 SkRect nestedChild = SkRect::MakeLTRB(40, 40, 60, 60);
562
563 SkRect bound;
564 SkClipStack::BoundsType type;
565 bool isIntersectionOfRects;
566
567 // all bw overlapping - should merge
568 {
569 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400570 stack.clipRect(overlapLeft, SkMatrix::I(), SkClipOp::kIntersect, false);
571 stack.clipRect(overlapRight, SkMatrix::I(), SkClipOp::kIntersect, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000572
573 REPORTER_ASSERT(reporter, 1 == count(stack));
574
575 stack.getBounds(&bound, &type, &isIntersectionOfRects);
576
577 REPORTER_ASSERT(reporter, isIntersectionOfRects);
578 }
579
580 // all aa overlapping - should merge
581 {
582 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400583 stack.clipRect(overlapLeft, SkMatrix::I(), SkClipOp::kIntersect, true);
584 stack.clipRect(overlapRight, SkMatrix::I(), SkClipOp::kIntersect, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000585
586 REPORTER_ASSERT(reporter, 1 == count(stack));
587
588 stack.getBounds(&bound, &type, &isIntersectionOfRects);
589
590 REPORTER_ASSERT(reporter, isIntersectionOfRects);
591 }
592
593 // mixed overlapping - should _not_ merge
594 {
595 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400596 stack.clipRect(overlapLeft, SkMatrix::I(), SkClipOp::kIntersect, true);
597 stack.clipRect(overlapRight, SkMatrix::I(), SkClipOp::kIntersect, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000598
599 REPORTER_ASSERT(reporter, 2 == count(stack));
600
601 stack.getBounds(&bound, &type, &isIntersectionOfRects);
602
603 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
604 }
605
606 // mixed nested (bw inside aa) - should merge
607 {
608 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400609 stack.clipRect(nestedParent, SkMatrix::I(), SkClipOp::kIntersect, true);
610 stack.clipRect(nestedChild, SkMatrix::I(), SkClipOp::kIntersect, 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;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400622 stack.clipRect(nestedParent, SkMatrix::I(), SkClipOp::kIntersect, false);
623 stack.clipRect(nestedChild, SkMatrix::I(), SkClipOp::kIntersect, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000624
625 REPORTER_ASSERT(reporter, 1 == count(stack));
626
627 stack.getBounds(&bound, &type, &isIntersectionOfRects);
628
629 REPORTER_ASSERT(reporter, isIntersectionOfRects);
630 }
631
632 // reverse nested (aa inside bw) - should _not_ merge
633 {
634 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400635 stack.clipRect(nestedChild, SkMatrix::I(), SkClipOp::kIntersect, false);
636 stack.clipRect(nestedParent, SkMatrix::I(), SkClipOp::kIntersect, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000637
638 REPORTER_ASSERT(reporter, 2 == count(stack));
639
640 stack.getBounds(&bound, &type, &isIntersectionOfRects);
641
642 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
643 }
644}
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000645
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000646static void test_quickContains(skiatest::Reporter* reporter) {
647 SkRect testRect = SkRect::MakeLTRB(10, 10, 40, 40);
648 SkRect insideRect = SkRect::MakeLTRB(20, 20, 30, 30);
649 SkRect intersectingRect = SkRect::MakeLTRB(25, 25, 50, 50);
650 SkRect outsideRect = SkRect::MakeLTRB(0, 0, 50, 50);
651 SkRect nonIntersectingRect = SkRect::MakeLTRB(100, 100, 110, 110);
652
653 SkPath insideCircle;
654 insideCircle.addCircle(25, 25, 5);
655 SkPath intersectingCircle;
656 intersectingCircle.addCircle(25, 40, 10);
657 SkPath outsideCircle;
658 outsideCircle.addCircle(25, 25, 50);
659 SkPath nonIntersectingCircle;
660 nonIntersectingCircle.addCircle(100, 100, 5);
661
662 {
663 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400664 stack.clipRect(outsideRect, SkMatrix::I(), SkClipOp::kDifference, false);
665 // return false because quickContains currently does not care for kDifference
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000666 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
667 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000668
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000669 // Replace Op tests
670 {
671 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400672 stack.replaceClip(outsideRect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000673 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
674 }
675
676 {
677 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400678 stack.clipRect(insideRect, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000679 stack.save(); // To prevent in-place substitution by replace OP
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400680 stack.replaceClip(outsideRect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000681 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
682 stack.restore();
683 }
684
685 {
686 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400687 stack.clipRect(outsideRect, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000688 stack.save(); // To prevent in-place substitution by replace OP
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400689 stack.replaceClip(insideRect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000690 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
691 stack.restore();
692 }
693
694 // Verify proper traversal of multi-element clip
695 {
696 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400697 stack.clipRect(insideRect, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000698 // Use a path for second clip to prevent in-place intersection
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400699 stack.clipPath(outsideCircle, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000700 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
701 }
702
703 // Intersect Op tests with rectangles
704 {
705 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400706 stack.clipRect(outsideRect, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000707 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
708 }
709
710 {
711 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400712 stack.clipRect(insideRect, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000713 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
714 }
715
716 {
717 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400718 stack.clipRect(intersectingRect, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000719 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
720 }
721
722 {
723 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400724 stack.clipRect(nonIntersectingRect, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000725 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
726 }
727
728 // Intersect Op tests with circle paths
729 {
730 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400731 stack.clipPath(outsideCircle, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000732 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
733 }
734
735 {
736 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400737 stack.clipPath(insideCircle, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000738 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
739 }
740
741 {
742 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400743 stack.clipPath(intersectingCircle, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000744 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
745 }
746
747 {
748 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400749 stack.clipPath(nonIntersectingCircle, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000750 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
751 }
752
753 // Intersect Op tests with inverse filled rectangles
754 {
755 SkClipStack stack;
756 SkPath path;
757 path.addRect(outsideRect);
758 path.toggleInverseFillType();
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400759 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000760 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
761 }
762
763 {
764 SkClipStack stack;
765 SkPath path;
766 path.addRect(insideRect);
767 path.toggleInverseFillType();
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400768 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000769 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
770 }
771
772 {
773 SkClipStack stack;
774 SkPath path;
775 path.addRect(intersectingRect);
776 path.toggleInverseFillType();
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400777 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000778 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
779 }
780
781 {
782 SkClipStack stack;
783 SkPath path;
784 path.addRect(nonIntersectingRect);
785 path.toggleInverseFillType();
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400786 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000787 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
788 }
789
790 // Intersect Op tests with inverse filled circles
791 {
792 SkClipStack stack;
793 SkPath path = outsideCircle;
794 path.toggleInverseFillType();
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400795 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000796 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
797 }
798
799 {
800 SkClipStack stack;
801 SkPath path = insideCircle;
802 path.toggleInverseFillType();
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400803 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000804 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
805 }
806
807 {
808 SkClipStack stack;
809 SkPath path = intersectingCircle;
810 path.toggleInverseFillType();
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400811 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000812 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
813 }
814
815 {
816 SkClipStack stack;
817 SkPath path = nonIntersectingCircle;
818 path.toggleInverseFillType();
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400819 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000820 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
821 }
822}
823
csmartdaltond50e2402016-07-22 08:39:06 -0700824static void set_region_to_stack(const SkClipStack& stack, const SkIRect& bounds, SkRegion* region) {
825 region->setRect(bounds);
826 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
827 while (const SkClipStack::Element *element = iter.next()) {
828 SkRegion elemRegion;
829 SkRegion boundsRgn(bounds);
830 SkPath path;
831
Brian Salomonf3b46e52017-08-30 11:37:57 -0400832 switch (element->getDeviceSpaceType()) {
833 case SkClipStack::Element::DeviceSpaceType::kEmpty:
csmartdaltond50e2402016-07-22 08:39:06 -0700834 elemRegion.setEmpty();
835 break;
836 default:
Brian Salomonf3b46e52017-08-30 11:37:57 -0400837 element->asDeviceSpacePath(&path);
csmartdaltond50e2402016-07-22 08:39:06 -0700838 elemRegion.setPath(path, boundsRgn);
839 break;
840 }
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400841
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400842 region->op(elemRegion, element->getRegionOp());
csmartdaltond50e2402016-07-22 08:39:06 -0700843 }
844}
845
846static void test_invfill_diff_bug(skiatest::Reporter* reporter) {
847 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400848 stack.clipRect({10, 10, 20, 20}, SkMatrix::I(), SkClipOp::kIntersect, false);
csmartdaltond50e2402016-07-22 08:39:06 -0700849
850 SkPath path;
851 path.addRect({30, 10, 40, 20});
Mike Reed7d34dc72019-11-26 12:17:17 -0500852 path.setFillType(SkPathFillType::kInverseWinding);
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400853 stack.clipPath(path, SkMatrix::I(), SkClipOp::kDifference, false);
csmartdaltond50e2402016-07-22 08:39:06 -0700854
855 REPORTER_ASSERT(reporter, SkClipStack::kEmptyGenID == stack.getTopmostGenID());
856
857 SkRect stackBounds;
858 SkClipStack::BoundsType stackBoundsType;
859 stack.getBounds(&stackBounds, &stackBoundsType);
860
861 REPORTER_ASSERT(reporter, stackBounds.isEmpty());
862 REPORTER_ASSERT(reporter, SkClipStack::kNormal_BoundsType == stackBoundsType);
863
864 SkRegion region;
865 set_region_to_stack(stack, {0, 0, 50, 30}, &region);
866
867 REPORTER_ASSERT(reporter, region.isEmpty());
868}
869
bsalomon@google.com51a62862012-11-26 21:19:43 +0000870///////////////////////////////////////////////////////////////////////////////////////////////////
871
Robert Phillips7cef6782021-07-01 13:21:37 -0400872#if SK_GPU_V1
Robert Phillips1ca0da42021-06-14 15:27:19 -0400873#include "src/gpu/GrClipStackClip.h"
874#include "src/gpu/GrReducedClip.h"
Robert Phillips1a2e7de2021-07-29 11:23:48 -0400875#include "src/gpu/v1/SurfaceDrawContext_v1.h"
Robert Phillips1ca0da42021-06-14 15:27:19 -0400876
877typedef GrReducedClip::ElementList ElementList;
878typedef GrReducedClip::InitialState InitialState;
879
bsalomon@google.com705e8402012-11-27 15:43:57 +0000880// Functions that add a shape to the clip stack. The shape is computed from a rectangle.
881// AA is always disabled since the clip stack reducer can cause changes in aa rasterization of the
882// stack. A fractional edge repeated in different elements may be rasterized fewer times using the
883// reduced stack.
884typedef void (*AddElementFunc) (const SkRect& rect,
885 bool invert,
Mike Reedc1f77742016-12-09 09:00:50 -0500886 SkClipOp op,
csmartdaltoncbecb082016-07-22 08:59:08 -0700887 SkClipStack* stack,
888 bool doAA);
bsalomon@google.coma4e13c82012-11-26 21:38:37 +0000889
Mike Reedc1f77742016-12-09 09:00:50 -0500890static void add_round_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700891 bool doAA) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000892 SkScalar rx = rect.width() / 10;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000893 SkScalar ry = rect.height() / 20;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000894 if (invert) {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000895 SkPath path;
896 path.addRoundRect(rect, rx, ry);
Mike Reed7d34dc72019-11-26 12:17:17 -0500897 path.setFillType(SkPathFillType::kInverseWinding);
Brian Salomona3b45d42016-10-03 11:36:16 -0400898 stack->clipPath(path, SkMatrix::I(), op, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000899 } else {
900 SkRRect rrect;
901 rrect.setRectXY(rect, rx, ry);
Brian Salomona3b45d42016-10-03 11:36:16 -0400902 stack->clipRRect(rrect, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000903 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000904};
905
Mike Reedc1f77742016-12-09 09:00:50 -0500906static void add_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700907 bool doAA) {
bsalomon@google.com705e8402012-11-27 15:43:57 +0000908 if (invert) {
909 SkPath path;
910 path.addRect(rect);
Mike Reed7d34dc72019-11-26 12:17:17 -0500911 path.setFillType(SkPathFillType::kInverseWinding);
Brian Salomona3b45d42016-10-03 11:36:16 -0400912 stack->clipPath(path, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000913 } else {
Brian Salomona3b45d42016-10-03 11:36:16 -0400914 stack->clipRect(rect, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000915 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000916};
917
Mike Reedc1f77742016-12-09 09:00:50 -0500918static void add_oval(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700919 bool doAA) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000920 SkPath path;
921 path.addOval(rect);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000922 if (invert) {
Mike Reed7d34dc72019-11-26 12:17:17 -0500923 path.setFillType(SkPathFillType::kInverseWinding);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000924 }
Brian Salomona3b45d42016-10-03 11:36:16 -0400925 stack->clipPath(path, SkMatrix::I(), op, doAA);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000926};
927
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400928static void add_shader(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
929 bool doAA) {
930 // invert, op, and doAA don't apply to shaders at the SkClipStack level; this is handled earlier
931 // in the SkCanvas->SkDevice stack. Use rect to produce unique gradients, however.
932 SkPoint corners[2] = { {rect.fLeft, rect.fTop}, {rect.fRight, rect.fBottom} };
933 SkColor colors[2] = { SK_ColorBLACK, SK_ColorTRANSPARENT };
934 auto gradient = SkGradientShader::MakeLinear(corners, colors, nullptr, 2, SkTileMode::kDecal);
935 stack->clipShader(std::move(gradient));
936}
Michael Ludwigde00dc92020-06-05 10:56:32 -0400937
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000938static void add_elem_to_stack(const SkClipStack::Element& element, SkClipStack* stack) {
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400939 if (element.isReplaceOp()) {
940 const SkRect resetBounds = SkRect::MakeLTRB(-10000.f, -10000.f, 10000.f, 10000.f);
941 stack->replaceClip(resetBounds, element.isAA());
942 }
Brian Salomonf3b46e52017-08-30 11:37:57 -0400943 switch (element.getDeviceSpaceType()) {
944 case SkClipStack::Element::DeviceSpaceType::kRect:
945 stack->clipRect(element.getDeviceSpaceRect(), SkMatrix::I(), element.getOp(),
946 element.isAA());
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000947 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400948 case SkClipStack::Element::DeviceSpaceType::kRRect:
949 stack->clipRRect(element.getDeviceSpaceRRect(), SkMatrix::I(), element.getOp(),
950 element.isAA());
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000951 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400952 case SkClipStack::Element::DeviceSpaceType::kPath:
953 stack->clipPath(element.getDeviceSpacePath(), SkMatrix::I(), element.getOp(),
954 element.isAA());
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000955 break;
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400956 case SkClipStack::Element::DeviceSpaceType::kShader:
957 SkDEBUGFAIL("Why did the reducer put this in the mask elements.");
958 stack->clipShader(element.refShader());
959 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400960 case SkClipStack::Element::DeviceSpaceType::kEmpty:
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000961 SkDEBUGFAIL("Why did the reducer produce an explicit empty.");
962 stack->clipEmpty();
963 break;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000964 }
965}
966
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400967static void test_reduced_clip_stack(skiatest::Reporter* reporter, bool enableClipShader) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000968 // We construct random clip stacks, reduce them, and then rasterize both versions to verify that
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000969 // they are equal.
bsalomon@google.com51a62862012-11-26 21:19:43 +0000970
971 // All the clip elements will be contained within these bounds.
csmartdaltond211e782016-08-15 11:17:19 -0700972 static const SkIRect kIBounds = SkIRect::MakeWH(100, 100);
973 static const SkRect kBounds = SkRect::Make(kIBounds);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000974
975 enum {
csmartdaltoncbecb082016-07-22 08:59:08 -0700976 kNumTests = 250,
bsalomon@google.com51a62862012-11-26 21:19:43 +0000977 kMinElemsPerTest = 1,
978 kMaxElemsPerTest = 50,
979 };
980
981 // min/max size of a clip element as a fraction of kBounds.
982 static const SkScalar kMinElemSizeFrac = SK_Scalar1 / 5;
983 static const SkScalar kMaxElemSizeFrac = SK_Scalar1;
984
Mike Reedc1f77742016-12-09 09:00:50 -0500985 static const SkClipOp kOps[] = {
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400986 SkClipOp::kDifference,
987 SkClipOp::kIntersect,
Mike Reedc1f77742016-12-09 09:00:50 -0500988 kUnion_SkClipOp,
989 kXOR_SkClipOp,
990 kReverseDifference_SkClipOp,
bsalomon@google.com51a62862012-11-26 21:19:43 +0000991 };
992
bsalomon@google.com705e8402012-11-27 15:43:57 +0000993 // We want to test inverse fills. However, they are quite rare in practice so don't over do it.
994 static const SkScalar kFractionInverted = SK_Scalar1 / kMaxElemsPerTest;
995
csmartdaltoncbecb082016-07-22 08:59:08 -0700996 static const SkScalar kFractionAntialiased = 0.25;
997
bsalomon@google.com51a62862012-11-26 21:19:43 +0000998 static const AddElementFunc kElementFuncs[] = {
999 add_rect,
1000 add_round_rect,
1001 add_oval,
Michael Ludwig4e221bd2020-06-05 11:29:36 -04001002 add_shader
bsalomon@google.com51a62862012-11-26 21:19:43 +00001003 };
1004
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +00001005 SkRandom r;
bsalomon@google.com51a62862012-11-26 21:19:43 +00001006
1007 for (int i = 0; i < kNumTests; ++i) {
csmartdaltoncbecb082016-07-22 08:59:08 -07001008 SkString testCase;
1009 testCase.printf("Iteration %d", i);
1010
bsalomon@google.com51a62862012-11-26 21:19:43 +00001011 // Randomly generate a clip stack.
1012 SkClipStack stack;
1013 int numElems = r.nextRangeU(kMinElemsPerTest, kMaxElemsPerTest);
csmartdaltoncbecb082016-07-22 08:59:08 -07001014 bool doAA = r.nextBiasedBool(kFractionAntialiased);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001015 for (int e = 0; e < numElems; ++e) {
Michael Ludwigd59d3e12021-08-05 16:39:18 -04001016 size_t opLimit = enableClipShader ? ((size_t) SkClipOp::kIntersect + 1)
Michael Ludwig4e221bd2020-06-05 11:29:36 -04001017 : SK_ARRAY_COUNT(kOps);
1018 SkClipOp op = kOps[r.nextULessThan(opLimit)];
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +00001019
bsalomon@google.com51a62862012-11-26 21:19:43 +00001020 // saves can change the clip stack behavior when an element is added.
1021 bool doSave = r.nextBool();
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +00001022
bsalomon@google.com51a62862012-11-26 21:19:43 +00001023 SkSize size = SkSize::Make(
Mike Reeddf85c382017-02-14 10:59:19 -05001024 kBounds.width() * r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac),
1025 kBounds.height() * r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac));
bsalomon@google.com51a62862012-11-26 21:19:43 +00001026
csmartdaltoncbecb082016-07-22 08:59:08 -07001027 SkPoint xy = {r.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth),
1028 r.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight)};
bsalomon@google.com51a62862012-11-26 21:19:43 +00001029
csmartdaltoncbecb082016-07-22 08:59:08 -07001030 SkRect rect;
1031 if (doAA) {
1032 rect.setXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight);
1033 if (GrClip::IsPixelAligned(rect)) {
1034 // Don't create an element that may accidentally become not antialiased.
1035 rect.outset(0.5f, 0.5f);
1036 }
1037 SkASSERT(!GrClip::IsPixelAligned(rect));
1038 } else {
1039 rect.setXYWH(SkScalarFloorToScalar(xy.fX),
1040 SkScalarFloorToScalar(xy.fY),
1041 SkScalarCeilToScalar(size.fWidth),
1042 SkScalarCeilToScalar(size.fHeight));
1043 }
bsalomon@google.com51a62862012-11-26 21:19:43 +00001044
bsalomon@google.com705e8402012-11-27 15:43:57 +00001045 bool invert = r.nextBiasedBool(kFractionInverted);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001046
Michael Ludwig4e221bd2020-06-05 11:29:36 -04001047 size_t functionLimit = SK_ARRAY_COUNT(kElementFuncs);
1048 if (!enableClipShader) {
1049 functionLimit--;
1050 }
1051 kElementFuncs[r.nextULessThan(functionLimit)](rect, invert, op, &stack, doAA);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001052 if (doSave) {
1053 stack.save();
1054 }
1055 }
1056
Robert Phillipsf4f80112020-07-13 16:13:31 -04001057 sk_sp<GrDirectContext> context = GrDirectContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001058 const GrCaps* caps = context->priv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001059
Brian Salomonc3833b42018-07-09 18:23:58 +00001060 // Zero the memory we will new the GrReducedClip into. This ensures the elements gen ID
1061 // will be kInvalidGenID if left uninitialized.
1062 SkAlignedSTStorage<1, GrReducedClip> storage;
1063 memset(storage.get(), 0, sizeof(GrReducedClip));
Brian Salomon4dea72a2019-12-18 10:43:10 -05001064 static_assert(0 == SkClipStack::kInvalidGenID);
Brian Salomonc3833b42018-07-09 18:23:58 +00001065
csmartdalton77f2fae2016-08-08 09:55:06 -07001066 // Get the reduced version of the stack.
csmartdaltoncbecb082016-07-22 08:59:08 -07001067 SkRect queryBounds = kBounds;
1068 queryBounds.outset(kBounds.width() / 2, kBounds.height() / 2);
Ethan Nicholaseace9352018-10-15 20:09:54 +00001069 const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, queryBounds, caps);
bsalomon@google.coma4444302012-12-04 15:22:12 +00001070
Brian Salomonc3833b42018-07-09 18:23:58 +00001071 REPORTER_ASSERT(reporter,
1072 reduced->maskElements().isEmpty() ||
1073 SkClipStack::kInvalidGenID != reduced->maskGenID(),
1074 testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001075
Brian Salomonc3833b42018-07-09 18:23:58 +00001076 if (!reduced->maskElements().isEmpty()) {
1077 REPORTER_ASSERT(reporter, reduced->hasScissor(), testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001078 SkRect stackBounds;
1079 SkClipStack::BoundsType stackBoundsType;
1080 stack.getBounds(&stackBounds, &stackBoundsType);
Brian Salomonc3833b42018-07-09 18:23:58 +00001081 REPORTER_ASSERT(reporter, reduced->maskRequiresAA() == doAA, testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001082 }
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001083
bsalomon@google.com51a62862012-11-26 21:19:43 +00001084 // Build a new clip stack based on the reduced clip elements
1085 SkClipStack reducedStack;
Brian Salomonc3833b42018-07-09 18:23:58 +00001086 if (GrReducedClip::InitialState::kAllOut == reduced->initialState()) {
bsalomon@google.com51a62862012-11-26 21:19:43 +00001087 // whether the result is bounded or not, the whole plane should start outside the clip.
1088 reducedStack.clipEmpty();
1089 }
Brian Salomonc3833b42018-07-09 18:23:58 +00001090 for (ElementList::Iter iter(reduced->maskElements()); iter.get(); iter.next()) {
1091 add_elem_to_stack(*iter.get(), &reducedStack);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001092 }
Michael Ludwig4e221bd2020-06-05 11:29:36 -04001093 if (reduced->hasShader()) {
1094 REPORTER_ASSERT(reporter, enableClipShader);
1095 reducedStack.clipShader(reduced->shader());
1096 }
bsalomon@google.com51a62862012-11-26 21:19:43 +00001097
Brian Salomonc3833b42018-07-09 18:23:58 +00001098 SkIRect scissor = reduced->hasScissor() ? reduced->scissor() : kIBounds;
csmartdaltond211e782016-08-15 11:17:19 -07001099
bsalomon@google.com34cd70a2012-12-06 14:23:20 +00001100 // GrReducedClipStack assumes that the final result is clipped to the returned bounds
Michael Ludwigd59d3e12021-08-05 16:39:18 -04001101 reducedStack.clipDevRect(scissor, SkClipOp::kIntersect);
1102 stack.clipDevRect(scissor, SkClipOp::kIntersect);
bsalomon@google.com34cd70a2012-12-06 14:23:20 +00001103
bsalomon@google.com51a62862012-11-26 21:19:43 +00001104 // convert both the original stack and reduced stack to SkRegions and see if they're equal
bsalomon@google.com51a62862012-11-26 21:19:43 +00001105 SkRegion region;
Chris Dalton79471932017-10-27 01:50:57 -06001106 set_region_to_stack(stack, scissor, &region);
csmartdaltond50e2402016-07-22 08:39:06 -07001107
bsalomon@google.com51a62862012-11-26 21:19:43 +00001108 SkRegion reducedRegion;
Chris Dalton79471932017-10-27 01:50:57 -06001109 set_region_to_stack(reducedStack, scissor, &reducedRegion);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001110
Brian Salomon1c80e992018-01-29 09:50:47 -05001111 REPORTER_ASSERT(reporter, region == reducedRegion, testCase.c_str());
Brian Salomonc3833b42018-07-09 18:23:58 +00001112
1113 reduced->~GrReducedClip();
bsalomon@google.com51a62862012-11-26 21:19:43 +00001114 }
1115}
1116
halcanary4dbbd042016-06-07 17:21:10 -07001117#ifdef SK_BUILD_FOR_WIN
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001118 #define SUPPRESS_VISIBILITY_WARNING
1119#else
1120 #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden")))
1121#endif
1122
Brian Salomonc3833b42018-07-09 18:23:58 +00001123static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) {
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001124 {
1125 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -04001126 stack.replaceClip(SkRect::MakeXYWH(0, 0, 100, 100), true);
1127 stack.replaceClip(SkRect::MakeXYWH(0, 0, 50.3f, 50.3f), true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001128 SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001129
Robert Phillipsf4f80112020-07-13 16:13:31 -04001130 sk_sp<GrDirectContext> context = GrDirectContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001131 const GrCaps* caps = context->priv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001132
Brian Salomonc3833b42018-07-09 18:23:58 +00001133 SkAlignedSTStorage<1, GrReducedClip> storage;
1134 memset(storage.get(), 0, sizeof(GrReducedClip));
Brian Salomon4dea72a2019-12-18 10:43:10 -05001135 static_assert(0 == SkClipStack::kInvalidGenID);
Ethan Nicholaseace9352018-10-15 20:09:54 +00001136 const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, bounds, caps);
Brian Salomonc3833b42018-07-09 18:23:58 +00001137
1138 REPORTER_ASSERT(reporter, reduced->maskElements().count() == 1);
1139 // Clips will be cached based on the generation id. Make sure the gen id is valid.
1140 REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reduced->maskGenID());
1141
1142 reduced->~GrReducedClip();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001143 }
1144 {
1145 SkClipStack stack;
1146
1147 // Create a clip with following 25.3, 25.3 boxes which are 25 apart:
1148 // A B
1149 // C D
1150
Michael Ludwigd59d3e12021-08-05 16:39:18 -04001151 stack.replaceClip(SkRect::MakeXYWH(0, 0, 25.3f, 25.3f), true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001152 uint32_t genIDA = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001153 stack.clipRect(SkRect::MakeXYWH(50, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001154 kUnion_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001155 uint32_t genIDB = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001156 stack.clipRect(SkRect::MakeXYWH(0, 50, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001157 kUnion_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001158 uint32_t genIDC = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001159 stack.clipRect(SkRect::MakeXYWH(50, 50, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001160 kUnion_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001161 uint32_t genIDD = stack.getTopmostGenID();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001162
1163
csmartdaltoncbecb082016-07-22 08:59:08 -07001164#define IXYWH SkIRect::MakeXYWH
1165#define XYWH SkRect::MakeXYWH
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001166
csmartdaltoncbecb082016-07-22 08:59:08 -07001167 SkIRect stackBounds = IXYWH(0, 0, 76, 76);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001168
1169 // The base test is to test each rect in two ways:
1170 // 1) The box dimensions. (Should reduce to "all in", no elements).
1171 // 2) A bit over the box dimensions.
1172 // In the case 2, test that the generation id is what is expected.
1173 // The rects are of fractional size so that case 2 never gets optimized to an empty element
1174 // list.
1175
1176 // Not passing in tighter bounds is tested for consistency.
1177 static const struct SUPPRESS_VISIBILITY_WARNING {
csmartdaltoncbecb082016-07-22 08:59:08 -07001178 SkRect testBounds;
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001179 int reducedClipCount;
Brian Salomonc3833b42018-07-09 18:23:58 +00001180 uint32_t reducedGenID;
csmartdaltoncbecb082016-07-22 08:59:08 -07001181 InitialState initialState;
1182 SkIRect clipIRect;
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001183 // parameter.
1184 } testCases[] = {
csmartdalton77f2fae2016-08-08 09:55:06 -07001185
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001186 // Rect A.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001187 { XYWH(0, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 0, 25, 25) },
1188 { 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 -06001189 { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::InitialState::kAllOut, IXYWH(0, 0, 26, 26)},
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001190
1191 // Rect B.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001192 { XYWH(50, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 25, 25) },
1193 { XYWH(50, 0, 25.3f, 25.3f), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 26, 26) },
csmartdalton77f2fae2016-08-08 09:55:06 -07001194 { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::InitialState::kAllOut, IXYWH(50, 0, 26, 27) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001195
1196 // Rect C.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001197 { XYWH(0, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 50, 25, 25) },
1198 { 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 -07001199 { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::InitialState::kAllOut, IXYWH(0, 50, 27, 26) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001200
1201 // Rect D.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001202 { XYWH(50, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 25, 25)},
1203 { XYWH(50.3f, 50.3f, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 26, 26)},
csmartdalton77f2fae2016-08-08 09:55:06 -07001204 { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(50, 50, 26, 26)},
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001205
1206 // Other tests:
csmartdalton77f2fae2016-08-08 09:55:06 -07001207 { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::InitialState::kAllOut, stackBounds },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001208
Michael Ludwigde00dc92020-06-05 10:56:32 -04001209 // Rect in the middle, touches none (so should not be drawn)
1210 { XYWH(26, 26, 24, 24), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllOut, SkIRect::MakeEmpty() },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001211
1212 // Rect in the middle, touches all the rects. GenID is the last rect.
csmartdalton77f2fae2016-08-08 09:55:06 -07001213 { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(24, 24, 27, 27) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001214 };
1215
1216#undef XYWH
csmartdaltoncbecb082016-07-22 08:59:08 -07001217#undef IXYWH
Robert Phillipsf4f80112020-07-13 16:13:31 -04001218 sk_sp<GrDirectContext> context = GrDirectContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001219 const GrCaps* caps = context->priv().caps();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001220
1221 for (size_t i = 0; i < SK_ARRAY_COUNT(testCases); ++i) {
Ethan Nicholaseace9352018-10-15 20:09:54 +00001222 const GrReducedClip reduced(stack, testCases[i].testBounds, caps);
Chris Dalton79471932017-10-27 01:50:57 -06001223 REPORTER_ASSERT(reporter, reduced.maskElements().count() ==
1224 testCases[i].reducedClipCount);
Brian Salomonc3833b42018-07-09 18:23:58 +00001225 if (reduced.maskElements().count()) {
1226 REPORTER_ASSERT(reporter, reduced.maskGenID() == testCases[i].reducedGenID);
csmartdalton8d3f92a2016-08-17 09:39:38 -07001227 }
csmartdalton77f2fae2016-08-08 09:55:06 -07001228 REPORTER_ASSERT(reporter, reduced.initialState() == testCases[i].initialState);
Michael Ludwigde00dc92020-06-05 10:56:32 -04001229
1230 bool expectsScissor = !testCases[i].clipIRect.isEmpty();
1231 REPORTER_ASSERT(reporter, expectsScissor == reduced.hasScissor());
1232 if (expectsScissor) {
1233 REPORTER_ASSERT(reporter, reduced.scissor() == testCases[i].clipIRect);
1234 }
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001235 }
1236 }
1237}
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001238static void test_reduced_clip_stack_no_aa_crash(skiatest::Reporter* reporter) {
1239 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -04001240 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 100, 100), SkClipOp::kIntersect);
1241 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 50, 50), SkClipOp::kIntersect);
csmartdaltoncbecb082016-07-22 08:59:08 -07001242 SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001243
Robert Phillipsf4f80112020-07-13 16:13:31 -04001244 sk_sp<GrDirectContext> context = GrDirectContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001245 const GrCaps* caps = context->priv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001246
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001247 // At the time, this would crash.
Ethan Nicholaseace9352018-10-15 20:09:54 +00001248 const GrReducedClip reduced(stack, bounds, caps);
Chris Dalton79471932017-10-27 01:50:57 -06001249 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001250}
1251
csmartdaltoncbecb082016-07-22 08:59:08 -07001252enum class ClipMethod {
1253 kSkipDraw,
1254 kIgnoreClip,
1255 kScissor,
1256 kAAElements
1257};
1258
1259static void test_aa_query(skiatest::Reporter* reporter, const SkString& testName,
1260 const SkClipStack& stack, const SkMatrix& queryXform,
1261 const SkRect& preXformQuery, ClipMethod expectedMethod,
1262 int numExpectedElems = 0) {
Robert Phillipsf4f80112020-07-13 16:13:31 -04001263 sk_sp<GrDirectContext> context = GrDirectContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001264 const GrCaps* caps = context->priv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001265
csmartdaltoncbecb082016-07-22 08:59:08 -07001266 SkRect queryBounds;
1267 queryXform.mapRect(&queryBounds, preXformQuery);
Ethan Nicholaseace9352018-10-15 20:09:54 +00001268 const GrReducedClip reduced(stack, queryBounds, caps);
csmartdaltoncbecb082016-07-22 08:59:08 -07001269
1270 SkClipStack::BoundsType stackBoundsType;
1271 SkRect stackBounds;
1272 stack.getBounds(&stackBounds, &stackBoundsType);
1273
1274 switch (expectedMethod) {
1275 case ClipMethod::kSkipDraw:
1276 SkASSERT(0 == numExpectedElems);
Brian Salomon1c80e992018-01-29 09:50:47 -05001277 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty(), testName.c_str());
1278 REPORTER_ASSERT(reporter,
1279 GrReducedClip::InitialState::kAllOut == reduced.initialState(),
1280 testName.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001281 return;
1282 case ClipMethod::kIgnoreClip:
1283 SkASSERT(0 == numExpectedElems);
Brian Salomon1c80e992018-01-29 09:50:47 -05001284 REPORTER_ASSERT(
1285 reporter,
1286 !reduced.hasScissor() || GrClip::IsInsideClip(reduced.scissor(), queryBounds),
1287 testName.c_str());
1288 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty(), testName.c_str());
1289 REPORTER_ASSERT(reporter,
1290 GrReducedClip::InitialState::kAllIn == reduced.initialState(),
1291 testName.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001292 return;
1293 case ClipMethod::kScissor: {
1294 SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType);
1295 SkASSERT(0 == numExpectedElems);
1296 SkIRect expectedScissor;
1297 stackBounds.round(&expectedScissor);
Brian Salomon1c80e992018-01-29 09:50:47 -05001298 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty(), testName.c_str());
1299 REPORTER_ASSERT(reporter, reduced.hasScissor(), testName.c_str());
1300 REPORTER_ASSERT(reporter, expectedScissor == reduced.scissor(), testName.c_str());
1301 REPORTER_ASSERT(reporter,
1302 GrReducedClip::InitialState::kAllIn == reduced.initialState(),
1303 testName.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001304 return;
1305 }
1306 case ClipMethod::kAAElements: {
1307 SkIRect expectedClipIBounds = GrClip::GetPixelIBounds(queryBounds);
1308 if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
1309 SkAssertResult(expectedClipIBounds.intersect(GrClip::GetPixelIBounds(stackBounds)));
1310 }
Brian Salomon1c80e992018-01-29 09:50:47 -05001311 REPORTER_ASSERT(reporter, numExpectedElems == reduced.maskElements().count(),
1312 testName.c_str());
1313 REPORTER_ASSERT(reporter, reduced.hasScissor(), testName.c_str());
1314 REPORTER_ASSERT(reporter, expectedClipIBounds == reduced.scissor(), testName.c_str());
1315 REPORTER_ASSERT(reporter,
1316 reduced.maskElements().isEmpty() || reduced.maskRequiresAA(),
1317 testName.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001318 break;
1319 }
1320 }
1321}
1322
1323static void test_reduced_clip_stack_aa(skiatest::Reporter* reporter) {
1324 constexpr SkScalar IL = 2, IT = 1, IR = 6, IB = 7; // Pixel aligned rect.
1325 constexpr SkScalar L = 2.2f, T = 1.7f, R = 5.8f, B = 7.3f; // Generic rect.
1326 constexpr SkScalar l = 3.3f, t = 2.8f, r = 4.7f, b = 6.2f; // Small rect contained in R.
1327
1328 SkRect alignedRect = {IL, IT, IR, IB};
1329 SkRect rect = {L, T, R, B};
1330 SkRect innerRect = {l, t, r, b};
1331
1332 SkMatrix m;
1333 m.setIdentity();
1334
1335 constexpr SkScalar kMinScale = 2.0001f;
1336 constexpr SkScalar kMaxScale = 3;
1337 constexpr int kNumIters = 8;
1338
1339 SkString name;
1340 SkRandom rand;
1341
1342 for (int i = 0; i < kNumIters; ++i) {
1343 // Pixel-aligned rect (iior=true).
1344 name.printf("Pixel-aligned rect test, iter %i", i);
1345 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -04001346 stack.clipRect(alignedRect, SkMatrix::I(), SkClipOp::kIntersect, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001347 test_aa_query(reporter, name, stack, m, {IL, IT, IR, IB}, ClipMethod::kIgnoreClip);
1348 test_aa_query(reporter, name, stack, m, {IL, IT-1, IR, IT}, ClipMethod::kSkipDraw);
csmartdalton77f2fae2016-08-08 09:55:06 -07001349 test_aa_query(reporter, name, stack, m, {IL, IT-2, IR, IB}, ClipMethod::kScissor);
csmartdaltoncbecb082016-07-22 08:59:08 -07001350
1351 // Rect (iior=true).
1352 name.printf("Rect test, iter %i", i);
1353 stack.reset();
Michael Ludwigd59d3e12021-08-05 16:39:18 -04001354 stack.clipRect(rect, SkMatrix::I(), SkClipOp::kIntersect, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001355 test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kIgnoreClip);
1356 test_aa_query(reporter, name, stack, m, {L-.1f, T, L, B}, ClipMethod::kSkipDraw);
1357 test_aa_query(reporter, name, stack, m, {L-.1f, T, L+.1f, B}, ClipMethod::kAAElements, 1);
1358
1359 // Difference rect (iior=false, inside-out bounds).
1360 name.printf("Difference rect test, iter %i", i);
1361 stack.reset();
Michael Ludwigd59d3e12021-08-05 16:39:18 -04001362 stack.clipRect(rect, SkMatrix::I(), SkClipOp::kDifference, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001363 test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kSkipDraw);
1364 test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T}, ClipMethod::kIgnoreClip);
1365 test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T+.1f}, ClipMethod::kAAElements, 1);
1366
1367 // Complex clip (iior=false, normal bounds).
1368 name.printf("Complex clip test, iter %i", i);
1369 stack.reset();
Michael Ludwigd59d3e12021-08-05 16:39:18 -04001370 stack.clipRect(rect, SkMatrix::I(), SkClipOp::kIntersect, true);
Michael Ludwiga3a67ae2021-08-06 10:42:01 -04001371 stack.clipRect(innerRect, SkMatrix::I(), SkClipOp::kDifference, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001372 test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
1373 test_aa_query(reporter, name, stack, m, {r-.1f, t, R, b}, ClipMethod::kAAElements, 1);
1374 test_aa_query(reporter, name, stack, m, {r-.1f, t, R+.1f, b}, ClipMethod::kAAElements, 2);
1375 test_aa_query(reporter, name, stack, m, {r, t, R+.1f, b}, ClipMethod::kAAElements, 1);
1376 test_aa_query(reporter, name, stack, m, {r, t, R, b}, ClipMethod::kIgnoreClip);
1377 test_aa_query(reporter, name, stack, m, {R, T, R+.1f, B}, ClipMethod::kSkipDraw);
1378
1379 // Complex clip where outer rect is pixel aligned (iior=false, normal bounds).
1380 name.printf("Aligned Complex clip test, iter %i", i);
1381 stack.reset();
Michael Ludwigd59d3e12021-08-05 16:39:18 -04001382 stack.clipRect(alignedRect, SkMatrix::I(), SkClipOp::kIntersect, true);
Michael Ludwiga3a67ae2021-08-06 10:42:01 -04001383 stack.clipRect(innerRect, SkMatrix::I(), SkClipOp::kDifference, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001384 test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
1385 test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB}, ClipMethod::kAAElements, 1);
1386 test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB+.1f}, ClipMethod::kAAElements, 1);
1387 test_aa_query(reporter, name, stack, m, {l, b, r, IB+.1f}, ClipMethod::kAAElements, 0);
1388 test_aa_query(reporter, name, stack, m, {l, b, r, IB}, ClipMethod::kIgnoreClip);
1389 test_aa_query(reporter, name, stack, m, {IL, IB, IR, IB+.1f}, ClipMethod::kSkipDraw);
1390
1391 // Apply random transforms and try again. This ensures the clip stack reduction is hardened
1392 // against FP rounding error.
1393 SkScalar sx = rand.nextRangeScalar(kMinScale, kMaxScale);
1394 sx = SkScalarFloorToScalar(sx * alignedRect.width()) / alignedRect.width();
1395 SkScalar sy = rand.nextRangeScalar(kMinScale, kMaxScale);
1396 sy = SkScalarFloorToScalar(sy * alignedRect.height()) / alignedRect.height();
1397 SkScalar tx = SkScalarRoundToScalar(sx * alignedRect.x()) - sx * alignedRect.x();
1398 SkScalar ty = SkScalarRoundToScalar(sy * alignedRect.y()) - sy * alignedRect.y();
1399
Mike Reed1f607332020-05-21 12:11:27 -04001400 SkMatrix xform = SkMatrix::Scale(sx, sy);
csmartdaltoncbecb082016-07-22 08:59:08 -07001401 xform.postTranslate(tx, ty);
1402 xform.mapRect(&alignedRect);
1403 xform.mapRect(&rect);
1404 xform.mapRect(&innerRect);
1405 m.postConcat(xform);
1406 }
1407}
1408
Chris Dalton348060f2017-06-05 13:15:37 -06001409static void test_tiny_query_bounds_assertion_bug(skiatest::Reporter* reporter) {
1410 // https://bugs.chromium.org/p/skia/issues/detail?id=5990
1411 const SkRect clipBounds = SkRect::MakeXYWH(1.5f, 100, 1000, 1000);
1412
1413 SkClipStack rectStack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -04001414 rectStack.clipRect(clipBounds, SkMatrix::I(), SkClipOp::kIntersect, true);
Chris Dalton348060f2017-06-05 13:15:37 -06001415
1416 SkPath clipPath;
1417 clipPath.moveTo(clipBounds.left(), clipBounds.top());
1418 clipPath.quadTo(clipBounds.right(), clipBounds.top(),
1419 clipBounds.right(), clipBounds.bottom());
1420 clipPath.quadTo(clipBounds.left(), clipBounds.bottom(),
1421 clipBounds.left(), clipBounds.top());
1422 SkClipStack pathStack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -04001423 pathStack.clipPath(clipPath, SkMatrix::I(), SkClipOp::kIntersect, true);
Chris Dalton348060f2017-06-05 13:15:37 -06001424
Robert Phillipsf4f80112020-07-13 16:13:31 -04001425 sk_sp<GrDirectContext> context = GrDirectContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001426 const GrCaps* caps = context->priv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001427
Chris Dalton348060f2017-06-05 13:15:37 -06001428 for (const SkClipStack& stack : {rectStack, pathStack}) {
1429 for (SkRect queryBounds : {SkRect::MakeXYWH(53, 60, GrClip::kBoundsTolerance, 1000),
1430 SkRect::MakeXYWH(53, 60, GrClip::kBoundsTolerance/2, 1000),
1431 SkRect::MakeXYWH(53, 160, 1000, GrClip::kBoundsTolerance),
1432 SkRect::MakeXYWH(53, 160, 1000, GrClip::kBoundsTolerance/2)}) {
Ethan Nicholaseace9352018-10-15 20:09:54 +00001433 const GrReducedClip reduced(stack, queryBounds, caps);
Chris Dalton79471932017-10-27 01:50:57 -06001434 REPORTER_ASSERT(reporter, !reduced.hasScissor());
1435 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty());
Chris Dalton348060f2017-06-05 13:15:37 -06001436 REPORTER_ASSERT(reporter,
1437 GrReducedClip::InitialState::kAllOut == reduced.initialState());
1438 }
1439 }
1440}
1441
Robert Phillips7cef6782021-07-01 13:21:37 -04001442#endif // SK_GPU_V1
Robert Phillips1ca0da42021-06-14 15:27:19 -04001443
Brian Salomon1c0b05a2019-04-19 15:37:28 -04001444static void test_is_rrect_deep_rect_stack(skiatest::Reporter* reporter) {
1445 static constexpr SkRect kTargetBounds = SkRect::MakeWH(1000, 500);
1446 // All antialiased or all not antialiased.
1447 for (bool aa : {false, true}) {
1448 SkClipStack stack;
1449 for (int i = 0; i <= 100; ++i) {
1450 stack.save();
1451 stack.clipRect(SkRect::MakeLTRB(i, 0.5, kTargetBounds.width(), kTargetBounds.height()),
1452 SkMatrix::I(), SkClipOp::kIntersect, aa);
1453 }
1454 SkRRect rrect;
1455 bool isAA;
1456 SkRRect expected = SkRRect::MakeRect(
1457 SkRect::MakeLTRB(100, 0.5, kTargetBounds.width(), kTargetBounds.height()));
1458 if (stack.isRRect(kTargetBounds, &rrect, &isAA)) {
1459 REPORTER_ASSERT(reporter, rrect == expected);
1460 REPORTER_ASSERT(reporter, aa == isAA);
1461 } else {
1462 ERRORF(reporter, "Expected to be an rrect.");
1463 }
1464 }
1465 // Mixed AA and non-AA without simple containment.
1466 SkClipStack stack;
1467 for (int i = 0; i <= 100; ++i) {
1468 bool aa = i & 0b1;
1469 int j = 100 - i;
1470 stack.save();
1471 stack.clipRect(SkRect::MakeLTRB(i, j + 0.5, kTargetBounds.width(), kTargetBounds.height()),
1472 SkMatrix::I(), SkClipOp::kIntersect, aa);
1473 }
1474 SkRRect rrect;
1475 bool isAA;
1476 REPORTER_ASSERT(reporter, !stack.isRRect(kTargetBounds, &rrect, &isAA));
1477}
1478
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00001479DEF_TEST(ClipStack, reporter) {
reed@google.combdee9fc2011-02-22 20:17:43 +00001480 SkClipStack stack;
1481
robertphillips@google.com80214e22012-07-20 15:33:18 +00001482 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001483 assert_count(reporter, stack, 0);
1484
1485 static const SkIRect gRects[] = {
1486 { 0, 0, 100, 100 },
1487 { 25, 25, 125, 125 },
1488 { 0, 0, 1000, 1000 },
1489 { 0, 0, 75, 75 }
1490 };
1491 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
Michael Ludwigd59d3e12021-08-05 16:39:18 -04001492 stack.clipDevRect(gRects[i], SkClipOp::kIntersect);
reed@google.combdee9fc2011-02-22 20:17:43 +00001493 }
1494
1495 // all of the above rects should have been intersected, leaving only 1 rect
robertphillips@google.com80214e22012-07-20 15:33:18 +00001496 SkClipStack::B2TIter iter(stack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001497 const SkClipStack::Element* element = iter.next();
epoger@google.com2047f002011-05-17 17:36:59 +00001498 SkRect answer;
Mike Reed92b33352019-08-24 19:39:13 -04001499 answer.setLTRB(25, 25, 75, 75);
reed@google.combdee9fc2011-02-22 20:17:43 +00001500
bsalomon49f085d2014-09-05 13:34:00 -07001501 REPORTER_ASSERT(reporter, element);
Brian Salomonf3b46e52017-08-30 11:37:57 -04001502 REPORTER_ASSERT(reporter,
1503 SkClipStack::Element::DeviceSpaceType::kRect == element->getDeviceSpaceType());
Michael Ludwigd59d3e12021-08-05 16:39:18 -04001504 REPORTER_ASSERT(reporter, SkClipOp::kIntersect == element->getOp());
Brian Salomonf3b46e52017-08-30 11:37:57 -04001505 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == answer);
reed@google.combdee9fc2011-02-22 20:17:43 +00001506 // now check that we only had one in our iterator
1507 REPORTER_ASSERT(reporter, !iter.next());
1508
1509 stack.reset();
robertphillips@google.com80214e22012-07-20 15:33:18 +00001510 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001511 assert_count(reporter, stack, 0);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +00001512
1513 test_assign_and_comparison(reporter);
robertphillips@google.com80214e22012-07-20 15:33:18 +00001514 test_iterators(reporter);
Brian Salomonf3b46e52017-08-30 11:37:57 -04001515 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kRect);
1516 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kRRect);
1517 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kPath);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +00001518 test_isWideOpen(reporter);
robertphillips@google.com08eacc12012-08-02 12:49:00 +00001519 test_rect_merging(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001520 test_rect_replace(reporter);
junov@chromium.orgedf32d52012-12-10 14:57:54 +00001521 test_rect_inverse_fill(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001522 test_path_replace(reporter);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +00001523 test_quickContains(reporter);
csmartdaltond50e2402016-07-22 08:39:06 -07001524 test_invfill_diff_bug(reporter);
Brian Osmanc7ad40f2018-05-31 14:27:17 -04001525
Robert Phillips7cef6782021-07-01 13:21:37 -04001526#if SK_GPU_V1
Michael Ludwig4e221bd2020-06-05 11:29:36 -04001527 test_reduced_clip_stack(reporter, /* clipShader */ false);
1528 test_reduced_clip_stack(reporter, /* clipShader */ true);
Brian Salomonc3833b42018-07-09 18:23:58 +00001529 test_reduced_clip_stack_genid(reporter);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001530 test_reduced_clip_stack_no_aa_crash(reporter);
csmartdaltoncbecb082016-07-22 08:59:08 -07001531 test_reduced_clip_stack_aa(reporter);
Chris Dalton348060f2017-06-05 13:15:37 -06001532 test_tiny_query_bounds_assertion_bug(reporter);
Robert Phillips1ca0da42021-06-14 15:27:19 -04001533#endif
Brian Salomon1c0b05a2019-04-19 15:37:28 -04001534 test_is_rrect_deep_rect_stack(reporter);
reed@google.combdee9fc2011-02-22 20:17:43 +00001535}
Brian Salomon19f0ed52017-01-06 13:54:58 -05001536
1537//////////////////////////////////////////////////////////////////////////////
1538
Robert Phillips8faa3402021-04-05 14:01:36 -04001539// For the GrClipStack case, this is covered in GrClipStack_SWMask
1540#if defined(SK_DISABLE_NEW_GR_CLIP_STACK)
1541
Robert Phillips4e105e22020-07-16 09:18:50 -04001542sk_sp<GrTextureProxy> GrClipStackClip::testingOnly_createClipMask(
1543 GrRecordingContext* context) const {
John Stilesfe0de302020-08-14 10:52:06 -04001544 const GrReducedClip reducedClip(*fStack, SkRect::MakeWH(512, 512), nullptr);
Greg Daniele32506b2020-02-10 16:00:54 -05001545 return this->createSoftwareClipMask(context, reducedClip, nullptr).asTextureProxyRef();
Brian Salomon19f0ed52017-01-06 13:54:58 -05001546}
1547
1548// Verify that clip masks are freed up when the clip state that generated them goes away.
1549DEF_GPUTEST_FOR_ALL_CONTEXTS(ClipMaskCache, reporter, ctxInfo) {
1550 // This test uses resource key tags which only function in debug builds.
1551#ifdef SK_DEBUG
Robert Phillips6d344c32020-07-06 10:56:46 -04001552 auto context = ctxInfo.directContext();
Brian Salomon19f0ed52017-01-06 13:54:58 -05001553 SkClipStack stack;
1554
1555 SkPath path;
1556 path.addCircle(10, 10, 8);
1557 path.addCircle(15, 15, 8);
Mike Reed7d34dc72019-11-26 12:17:17 -05001558 path.setFillType(SkPathFillType::kEvenOdd);
Brian Salomon19f0ed52017-01-06 13:54:58 -05001559
Michael Ludwige06a8972020-06-11 10:29:00 -04001560 SkIRect stackBounds = path.getBounds().roundOut();
1561
Brian Salomonc3833b42018-07-09 18:23:58 +00001562 static const char* kTag = GrClipStackClip::kMaskTestTag;
Robert Phillips9da87e02019-02-04 13:26:26 -05001563 GrResourceCache* cache = context->priv().getResourceCache();
Brian Salomon19f0ed52017-01-06 13:54:58 -05001564
1565 static constexpr int kN = 5;
1566
1567 for (int i = 0; i < kN; ++i) {
1568 SkMatrix m;
1569 m.setTranslate(0.5, 0.5);
1570 stack.save();
1571 stack.clipPath(path, m, SkClipOp::kIntersect, true);
Michael Ludwige06a8972020-06-11 10:29:00 -04001572 sk_sp<GrTextureProxy> mask =
1573 GrClipStackClip(stackBounds.size(), &stack).testingOnly_createClipMask(context);
Adlai Hollercc25d532021-02-10 13:58:34 +00001574 mask->instantiate(context->priv().resourceProvider());
Brian Salomonfd98c2c2018-07-31 17:25:29 -04001575 GrTexture* tex = mask->peekTexture();
Robert Phillips875218e2017-02-24 08:37:13 -05001576 REPORTER_ASSERT(reporter, 0 == strcmp(tex->getUniqueKey().tag(), kTag));
Brian Salomon19f0ed52017-01-06 13:54:58 -05001577 // Make sure mask isn't pinned in cache.
1578 mask.reset(nullptr);
Greg Daniel0a2464f2020-05-14 15:45:44 -04001579 context->flushAndSubmit();
Brian Salomon19f0ed52017-01-06 13:54:58 -05001580 REPORTER_ASSERT(reporter, i + 1 == cache->countUniqueKeysWithTag(kTag));
1581 }
1582
1583 for (int i = 0; i < kN; ++i) {
1584 stack.restore();
1585 cache->purgeAsNeeded();
1586 REPORTER_ASSERT(reporter, kN - (i + 1) == cache->countUniqueKeysWithTag(kTag));
1587 }
1588#endif
1589}
Robert Phillips8faa3402021-04-05 14:01:36 -04001590#endif // SK_DISABLE_NEW_GR_CLIP_STACK