blob: 465e33206dcfad102db77f409b4a2add96ddbc8c [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"
33#include "src/gpu/GrClipStackClip.h"
Adlai Hollera0693042020-10-14 11:23:11 -040034#include "src/gpu/GrDirectContextPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050035#include "src/gpu/GrReducedClip.h"
36#include "src/gpu/GrResourceCache.h"
Greg Daniel456f9b52020-03-05 19:14:18 +000037#include "src/gpu/GrTexture.h"
Greg Danielf91aeb22019-06-18 09:58:02 -040038#include "src/gpu/GrTextureProxy.h"
Ben Wagner9707a7e2019-05-06 17:17:19 -040039#include "tests/Test.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050040#include "tools/gpu/GrContextFactory.h"
csmartdaltoncbecb082016-07-22 08:59:08 -070041
Ben Wagnerb607a8f2018-03-12 13:46:21 -040042#include <cstring>
Ben Wagner9707a7e2019-05-06 17:17:19 -040043#include <initializer_list>
Ben Wagnerb607a8f2018-03-12 13:46:21 -040044#include <new>
45
Ben Wagner9707a7e2019-05-06 17:17:19 -040046class GrCaps;
47
48typedef GrReducedClip::ElementList ElementList;
49typedef GrReducedClip::InitialState InitialState;
50
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000051static void test_assign_and_comparison(skiatest::Reporter* reporter) {
52 SkClipStack s;
reed@google.comd9f2dea2011-10-12 14:43:27 +000053 bool doAA = false;
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000054
robertphillips@google.com80214e22012-07-20 15:33:18 +000055 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
56
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000057 // Build up a clip stack with a path, an empty clip, and a rect.
58 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000059 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
60
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000061 SkPath p;
62 p.moveTo(5, 6);
63 p.lineTo(7, 8);
64 p.lineTo(5, 9);
65 p.close();
Mike Reedc1f77742016-12-09 09:00:50 -050066 s.clipPath(p, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000067
68 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000069 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
70
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000071 SkRect r = SkRect::MakeLTRB(1, 2, 3, 4);
Mike Reedc1f77742016-12-09 09:00:50 -050072 s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000073 r = SkRect::MakeLTRB(10, 11, 12, 13);
Mike Reedc1f77742016-12-09 09:00:50 -050074 s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000075
76 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000077 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
78
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000079 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -050080 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000081
82 // Test that assignment works.
83 SkClipStack copy = s;
84 REPORTER_ASSERT(reporter, s == copy);
85
86 // Test that different save levels triggers not equal.
87 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000088 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000089 REPORTER_ASSERT(reporter, s != copy);
90
91 // Test that an equal, but not copied version is equal.
92 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000093 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000094 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -050095 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000096 REPORTER_ASSERT(reporter, s == copy);
97
98 // Test that a different op on one level triggers not equal.
99 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000100 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000101 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000102 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000103 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -0500104 s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000105 REPORTER_ASSERT(reporter, s != copy);
106
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000107 // Test that version constructed with rect-path rather than a rect is still considered equal.
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000108 s.restore();
109 s.save();
110 SkPath rp;
111 rp.addRect(r);
Mike Reedc1f77742016-12-09 09:00:50 -0500112 s.clipPath(rp, SkMatrix::I(), kUnion_SkClipOp, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000113 REPORTER_ASSERT(reporter, s == copy);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000114
115 // Test that different rects triggers not equal.
116 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000117 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000118 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000119 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
120
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000121 r = SkRect::MakeLTRB(24, 25, 26, 27);
Mike Reedc1f77742016-12-09 09:00:50 -0500122 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000123 REPORTER_ASSERT(reporter, s != copy);
124
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000125 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000126 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
127
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000128 copy.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000129 REPORTER_ASSERT(reporter, 2 == copy.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000130 REPORTER_ASSERT(reporter, s == copy);
131 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000132 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000133 copy.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000134 REPORTER_ASSERT(reporter, 1 == copy.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000135 REPORTER_ASSERT(reporter, s == copy);
136
137 // Test that different paths triggers not equal.
138 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000139 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000140 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000141 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
142
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000143 p.addRect(r);
Mike Reedc1f77742016-12-09 09:00:50 -0500144 s.clipPath(p, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000145 REPORTER_ASSERT(reporter, s != copy);
146}
reed@google.combdee9fc2011-02-22 20:17:43 +0000147
148static void assert_count(skiatest::Reporter* reporter, const SkClipStack& stack,
149 int count) {
robertphillips@google.com80214e22012-07-20 15:33:18 +0000150 SkClipStack::B2TIter iter(stack);
reed@google.combdee9fc2011-02-22 20:17:43 +0000151 int counter = 0;
152 while (iter.next()) {
153 counter += 1;
154 }
155 REPORTER_ASSERT(reporter, count == counter);
156}
157
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000158// Exercise the SkClipStack's bottom to top and bidirectional iterators
159// (including the skipToTopmost functionality)
robertphillips@google.com80214e22012-07-20 15:33:18 +0000160static void test_iterators(skiatest::Reporter* reporter) {
161 SkClipStack stack;
162
163 static const SkRect gRects[] = {
164 { 0, 0, 40, 40 },
165 { 60, 0, 100, 40 },
166 { 0, 60, 40, 100 },
167 { 60, 60, 100, 100 }
168 };
169
170 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
171 // the union op will prevent these from being fused together
Mike Reedc1f77742016-12-09 09:00:50 -0500172 stack.clipRect(gRects[i], SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000173 }
174
175 assert_count(reporter, stack, 4);
176
177 // bottom to top iteration
178 {
halcanary96fcdcc2015-08-27 07:41:13 -0700179 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000180
181 SkClipStack::B2TIter iter(stack);
182 int i;
183
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000184 for (i = 0, element = iter.next(); element; ++i, element = iter.next()) {
Brian Salomonf3b46e52017-08-30 11:37:57 -0400185 REPORTER_ASSERT(reporter, SkClipStack::Element::DeviceSpaceType::kRect ==
186 element->getDeviceSpaceType());
187 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == gRects[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000188 }
189
190 SkASSERT(i == 4);
191 }
192
193 // top to bottom iteration
194 {
halcanary96fcdcc2015-08-27 07:41:13 -0700195 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000196
197 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
198 int i;
199
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000200 for (i = 3, element = iter.prev(); element; --i, element = iter.prev()) {
Brian Salomonf3b46e52017-08-30 11:37:57 -0400201 REPORTER_ASSERT(reporter, SkClipStack::Element::DeviceSpaceType::kRect ==
202 element->getDeviceSpaceType());
203 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == gRects[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000204 }
205
206 SkASSERT(i == -1);
207 }
208
209 // skipToTopmost
210 {
halcanary96fcdcc2015-08-27 07:41:13 -0700211 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000212
213 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
214
Mike Reedc1f77742016-12-09 09:00:50 -0500215 element = iter.skipToTopmost(kUnion_SkClipOp);
Brian Salomonf3b46e52017-08-30 11:37:57 -0400216 REPORTER_ASSERT(reporter, SkClipStack::Element::DeviceSpaceType::kRect ==
217 element->getDeviceSpaceType());
218 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == gRects[3]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000219 }
220}
221
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000222// Exercise the SkClipStack's getConservativeBounds computation
Brian Salomonf3b46e52017-08-30 11:37:57 -0400223static void test_bounds(skiatest::Reporter* reporter,
224 SkClipStack::Element::DeviceSpaceType primType) {
robertphillips@google.com607fe072012-07-24 13:54:00 +0000225 static const int gNumCases = 20;
226 static const SkRect gAnswerRectsBW[gNumCases] = {
227 // A op B
228 { 40, 40, 50, 50 },
229 { 10, 10, 50, 50 },
230 { 10, 10, 80, 80 },
231 { 10, 10, 80, 80 },
232 { 40, 40, 80, 80 },
233
234 // invA op B
235 { 40, 40, 80, 80 },
236 { 0, 0, 100, 100 },
237 { 0, 0, 100, 100 },
238 { 0, 0, 100, 100 },
239 { 40, 40, 50, 50 },
240
241 // A op invB
242 { 10, 10, 50, 50 },
243 { 40, 40, 50, 50 },
244 { 0, 0, 100, 100 },
245 { 0, 0, 100, 100 },
246 { 0, 0, 100, 100 },
247
248 // invA op invB
249 { 0, 0, 100, 100 },
250 { 40, 40, 80, 80 },
251 { 0, 0, 100, 100 },
252 { 10, 10, 80, 80 },
253 { 10, 10, 50, 50 },
254 };
255
Mike Reedc1f77742016-12-09 09:00:50 -0500256 static const SkClipOp gOps[] = {
257 kIntersect_SkClipOp,
258 kDifference_SkClipOp,
259 kUnion_SkClipOp,
260 kXOR_SkClipOp,
261 kReverseDifference_SkClipOp
robertphillips@google.com607fe072012-07-24 13:54:00 +0000262 };
263
264 SkRect rectA, rectB;
265
Mike Reed92b33352019-08-24 19:39:13 -0400266 rectA.setLTRB(10, 10, 50, 50);
267 rectB.setLTRB(40, 40, 80, 80);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000268
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000269 SkRRect rrectA, rrectB;
270 rrectA.setOval(rectA);
271 rrectB.setRectXY(rectB, SkIntToScalar(1), SkIntToScalar(2));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000272
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000273 SkPath pathA, pathB;
274
275 pathA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
276 pathB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000277
278 SkClipStack stack;
robertphillips@google.com7b112892012-07-31 15:18:21 +0000279 SkRect devClipBound;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000280 bool isIntersectionOfRects = false;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000281
282 int testCase = 0;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400283 int numBitTests = SkClipStack::Element::DeviceSpaceType::kPath == primType ? 4 : 1;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000284 for (int invBits = 0; invBits < numBitTests; ++invBits) {
robertphillips@google.com607fe072012-07-24 13:54:00 +0000285 for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) {
286
287 stack.save();
288 bool doInvA = SkToBool(invBits & 1);
289 bool doInvB = SkToBool(invBits & 2);
290
Mike Reed7d34dc72019-11-26 12:17:17 -0500291 pathA.setFillType(doInvA ? SkPathFillType::kInverseEvenOdd :
292 SkPathFillType::kEvenOdd);
293 pathB.setFillType(doInvB ? SkPathFillType::kInverseEvenOdd :
294 SkPathFillType::kEvenOdd);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000295
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000296 switch (primType) {
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400297 case SkClipStack::Element::DeviceSpaceType::kShader:
Brian Salomonf3b46e52017-08-30 11:37:57 -0400298 case SkClipStack::Element::DeviceSpaceType::kEmpty:
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400299 SkDEBUGFAIL("Don't call this with kEmpty or kShader.");
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000300 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400301 case SkClipStack::Element::DeviceSpaceType::kRect:
Mike Reedc1f77742016-12-09 09:00:50 -0500302 stack.clipRect(rectA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400303 stack.clipRect(rectB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000304 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400305 case SkClipStack::Element::DeviceSpaceType::kRRect:
Mike Reedc1f77742016-12-09 09:00:50 -0500306 stack.clipRRect(rrectA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400307 stack.clipRRect(rrectB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000308 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400309 case SkClipStack::Element::DeviceSpaceType::kPath:
Mike Reedc1f77742016-12-09 09:00:50 -0500310 stack.clipPath(pathA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400311 stack.clipPath(pathB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000312 break;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000313 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000314
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000315 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000316 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000317
robertphillips@google.com7b112892012-07-31 15:18:21 +0000318 stack.getConservativeBounds(0, 0, 100, 100, &devClipBound,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000319 &isIntersectionOfRects);
320
Brian Salomonf3b46e52017-08-30 11:37:57 -0400321 if (SkClipStack::Element::DeviceSpaceType::kRect == primType) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000322 REPORTER_ASSERT(reporter, isIntersectionOfRects ==
Mike Reedc1f77742016-12-09 09:00:50 -0500323 (gOps[op] == kIntersect_SkClipOp));
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000324 } else {
325 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
326 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000327
328 SkASSERT(testCase < gNumCases);
robertphillips@google.com7b112892012-07-31 15:18:21 +0000329 REPORTER_ASSERT(reporter, devClipBound == gAnswerRectsBW[testCase]);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000330 ++testCase;
331
332 stack.restore();
333 }
334 }
335}
336
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000337// Test out 'isWideOpen' entry point
338static void test_isWideOpen(skiatest::Reporter* reporter) {
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000339 {
340 // Empty stack is wide open. Wide open stack means that gen id is wide open.
341 SkClipStack stack;
342 REPORTER_ASSERT(reporter, stack.isWideOpen());
343 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
344 }
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000345
346 SkRect rectA, rectB;
347
Mike Reed92b33352019-08-24 19:39:13 -0400348 rectA.setLTRB(10, 10, 40, 40);
349 rectB.setLTRB(50, 50, 80, 80);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000350
351 // Stack should initially be wide open
352 {
353 SkClipStack stack;
354
355 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000356 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000357 }
358
359 // Test out case where the user specifies a union that includes everything
360 {
361 SkClipStack stack;
362
363 SkPath clipA, clipB;
364
365 clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
Mike Reed7d34dc72019-11-26 12:17:17 -0500366 clipA.setFillType(SkPathFillType::kInverseEvenOdd);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000367
368 clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
Mike Reed7d34dc72019-11-26 12:17:17 -0500369 clipB.setFillType(SkPathFillType::kInverseEvenOdd);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000370
Mike Reedc1f77742016-12-09 09:00:50 -0500371 stack.clipPath(clipA, SkMatrix::I(), kReplace_SkClipOp, false);
372 stack.clipPath(clipB, SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000373
374 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000375 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000376 }
377
378 // Test out union w/ a wide open clip
379 {
380 SkClipStack stack;
381
Mike Reedc1f77742016-12-09 09:00:50 -0500382 stack.clipRect(rectA, SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000383
384 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000385 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000386 }
387
388 // Test out empty difference from a wide open clip
389 {
390 SkClipStack stack;
391
392 SkRect emptyRect;
393 emptyRect.setEmpty();
394
Mike Reedc1f77742016-12-09 09:00:50 -0500395 stack.clipRect(emptyRect, SkMatrix::I(), kDifference_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000396
397 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000398 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000399 }
400
401 // Test out return to wide open
402 {
403 SkClipStack stack;
404
405 stack.save();
406
Mike Reedc1f77742016-12-09 09:00:50 -0500407 stack.clipRect(rectA, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000408
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 stack.restore();
413
414 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000415 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000416 }
417}
418
bsalomon@google.com100abf42012-09-05 17:40:04 +0000419static int count(const SkClipStack& stack) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000420
421 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
422
halcanary96fcdcc2015-08-27 07:41:13 -0700423 const SkClipStack::Element* element = nullptr;
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000424 int count = 0;
425
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000426 for (element = iter.prev(); element; element = iter.prev(), ++count) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000427 }
428
429 return count;
430}
431
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000432static void test_rect_inverse_fill(skiatest::Reporter* reporter) {
433 // non-intersecting rectangles
434 SkRect rect = SkRect::MakeLTRB(0, 0, 10, 10);
435
436 SkPath path;
437 path.addRect(rect);
438 path.toggleInverseFillType();
439 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500440 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000441
442 SkRect bounds;
443 SkClipStack::BoundsType boundsType;
444 stack.getBounds(&bounds, &boundsType);
445 REPORTER_ASSERT(reporter, SkClipStack::kInsideOut_BoundsType == boundsType);
446 REPORTER_ASSERT(reporter, bounds == rect);
447}
448
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000449static void test_rect_replace(skiatest::Reporter* reporter) {
450 SkRect rect = SkRect::MakeWH(100, 100);
451 SkRect rect2 = SkRect::MakeXYWH(50, 50, 100, 100);
452
453 SkRect bound;
454 SkClipStack::BoundsType type;
455 bool isIntersectionOfRects;
456
457 // Adding a new rect with the replace operator should not increase
458 // the stack depth. BW replacing BW.
459 {
460 SkClipStack stack;
461 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500462 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000463 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500464 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000465 REPORTER_ASSERT(reporter, 1 == count(stack));
466 }
467
468 // Adding a new rect with the replace operator should not increase
469 // the stack depth. AA replacing AA.
470 {
471 SkClipStack stack;
472 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500473 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000474 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500475 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000476 REPORTER_ASSERT(reporter, 1 == count(stack));
477 }
478
479 // Adding a new rect with the replace operator should not increase
480 // the stack depth. BW replacing AA replacing BW.
481 {
482 SkClipStack stack;
483 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500484 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000485 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500486 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000487 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500488 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000489 REPORTER_ASSERT(reporter, 1 == count(stack));
490 }
491
492 // Make sure replace clip rects don't collapse too much.
493 {
494 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500495 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
496 stack.clipRect(rect2, SkMatrix::I(), kIntersect_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000497 REPORTER_ASSERT(reporter, 1 == count(stack));
498
499 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500500 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000501 REPORTER_ASSERT(reporter, 2 == count(stack));
502 stack.getBounds(&bound, &type, &isIntersectionOfRects);
503 REPORTER_ASSERT(reporter, bound == rect);
504 stack.restore();
505 REPORTER_ASSERT(reporter, 1 == count(stack));
506
507 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500508 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
509 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000510 REPORTER_ASSERT(reporter, 2 == count(stack));
511 stack.restore();
512 REPORTER_ASSERT(reporter, 1 == count(stack));
513
514 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500515 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
516 stack.clipRect(rect2, SkMatrix::I(), kIntersect_SkClipOp, false);
517 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000518 REPORTER_ASSERT(reporter, 2 == count(stack));
519 stack.restore();
520 REPORTER_ASSERT(reporter, 1 == count(stack));
521 }
522}
523
524// Simplified path-based version of test_rect_replace.
525static void test_path_replace(skiatest::Reporter* reporter) {
526 SkRect rect = SkRect::MakeWH(100, 100);
527 SkPath path;
528 path.addCircle(50, 50, 50);
529
530 // Replace operation doesn't grow the stack.
531 {
532 SkClipStack stack;
533 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500534 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000535 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500536 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000537 REPORTER_ASSERT(reporter, 1 == count(stack));
538 }
539
540 // Replacing rect with path.
541 {
542 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500543 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000544 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500545 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000546 REPORTER_ASSERT(reporter, 1 == count(stack));
547 }
548}
549
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000550// Test out SkClipStack's merging of rect clips. In particular exercise
551// merging of aa vs. bw rects.
552static void test_rect_merging(skiatest::Reporter* reporter) {
553
554 SkRect overlapLeft = SkRect::MakeLTRB(10, 10, 50, 50);
555 SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80);
556
557 SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90);
558 SkRect nestedChild = SkRect::MakeLTRB(40, 40, 60, 60);
559
560 SkRect bound;
561 SkClipStack::BoundsType type;
562 bool isIntersectionOfRects;
563
564 // all bw overlapping - should merge
565 {
566 SkClipStack stack;
567
Mike Reedc1f77742016-12-09 09:00:50 -0500568 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000569
Mike Reedc1f77742016-12-09 09:00:50 -0500570 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000571
572 REPORTER_ASSERT(reporter, 1 == count(stack));
573
574 stack.getBounds(&bound, &type, &isIntersectionOfRects);
575
576 REPORTER_ASSERT(reporter, isIntersectionOfRects);
577 }
578
579 // all aa overlapping - should merge
580 {
581 SkClipStack stack;
582
Mike Reedc1f77742016-12-09 09:00:50 -0500583 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000584
Mike Reedc1f77742016-12-09 09:00:50 -0500585 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000586
587 REPORTER_ASSERT(reporter, 1 == count(stack));
588
589 stack.getBounds(&bound, &type, &isIntersectionOfRects);
590
591 REPORTER_ASSERT(reporter, isIntersectionOfRects);
592 }
593
594 // mixed overlapping - should _not_ merge
595 {
596 SkClipStack stack;
597
Mike Reedc1f77742016-12-09 09:00:50 -0500598 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000599
Mike Reedc1f77742016-12-09 09:00:50 -0500600 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000601
602 REPORTER_ASSERT(reporter, 2 == count(stack));
603
604 stack.getBounds(&bound, &type, &isIntersectionOfRects);
605
606 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
607 }
608
609 // mixed nested (bw inside aa) - should merge
610 {
611 SkClipStack stack;
612
Mike Reedc1f77742016-12-09 09:00:50 -0500613 stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000614
Mike Reedc1f77742016-12-09 09:00:50 -0500615 stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000616
617 REPORTER_ASSERT(reporter, 1 == count(stack));
618
619 stack.getBounds(&bound, &type, &isIntersectionOfRects);
620
621 REPORTER_ASSERT(reporter, isIntersectionOfRects);
622 }
623
624 // mixed nested (aa inside bw) - should merge
625 {
626 SkClipStack stack;
627
Mike Reedc1f77742016-12-09 09:00:50 -0500628 stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000629
Mike Reedc1f77742016-12-09 09:00:50 -0500630 stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000631
632 REPORTER_ASSERT(reporter, 1 == count(stack));
633
634 stack.getBounds(&bound, &type, &isIntersectionOfRects);
635
636 REPORTER_ASSERT(reporter, isIntersectionOfRects);
637 }
638
639 // reverse nested (aa inside bw) - should _not_ merge
640 {
641 SkClipStack stack;
642
Mike Reedc1f77742016-12-09 09:00:50 -0500643 stack.clipRect(nestedChild, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000644
Mike Reedc1f77742016-12-09 09:00:50 -0500645 stack.clipRect(nestedParent, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000646
647 REPORTER_ASSERT(reporter, 2 == count(stack));
648
649 stack.getBounds(&bound, &type, &isIntersectionOfRects);
650
651 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
652 }
653}
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000654
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000655static void test_quickContains(skiatest::Reporter* reporter) {
656 SkRect testRect = SkRect::MakeLTRB(10, 10, 40, 40);
657 SkRect insideRect = SkRect::MakeLTRB(20, 20, 30, 30);
658 SkRect intersectingRect = SkRect::MakeLTRB(25, 25, 50, 50);
659 SkRect outsideRect = SkRect::MakeLTRB(0, 0, 50, 50);
660 SkRect nonIntersectingRect = SkRect::MakeLTRB(100, 100, 110, 110);
661
662 SkPath insideCircle;
663 insideCircle.addCircle(25, 25, 5);
664 SkPath intersectingCircle;
665 intersectingCircle.addCircle(25, 40, 10);
666 SkPath outsideCircle;
667 outsideCircle.addCircle(25, 25, 50);
668 SkPath nonIntersectingCircle;
669 nonIntersectingCircle.addCircle(100, 100, 5);
670
671 {
672 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500673 stack.clipRect(outsideRect, SkMatrix::I(), kDifference_SkClipOp, false);
674 // return false because quickContains currently does not care for kDifference_SkClipOp
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000675 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
676 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000677
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000678 // Replace Op tests
679 {
680 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500681 stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000682 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
683 }
684
685 {
686 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500687 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000688 stack.save(); // To prevent in-place substitution by replace OP
Mike Reedc1f77742016-12-09 09:00:50 -0500689 stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000690 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
691 stack.restore();
692 }
693
694 {
695 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500696 stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000697 stack.save(); // To prevent in-place substitution by replace OP
Mike Reedc1f77742016-12-09 09:00:50 -0500698 stack.clipRect(insideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000699 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
700 stack.restore();
701 }
702
703 // Verify proper traversal of multi-element clip
704 {
705 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500706 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000707 // Use a path for second clip to prevent in-place intersection
Mike Reedc1f77742016-12-09 09:00:50 -0500708 stack.clipPath(outsideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000709 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
710 }
711
712 // Intersect Op tests with rectangles
713 {
714 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500715 stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000716 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
717 }
718
719 {
720 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500721 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000722 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
723 }
724
725 {
726 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500727 stack.clipRect(intersectingRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000728 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
729 }
730
731 {
732 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500733 stack.clipRect(nonIntersectingRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000734 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
735 }
736
737 // Intersect Op tests with circle paths
738 {
739 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500740 stack.clipPath(outsideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000741 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
742 }
743
744 {
745 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500746 stack.clipPath(insideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000747 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
748 }
749
750 {
751 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500752 stack.clipPath(intersectingCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000753 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
754 }
755
756 {
757 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500758 stack.clipPath(nonIntersectingCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000759 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
760 }
761
762 // Intersect Op tests with inverse filled rectangles
763 {
764 SkClipStack stack;
765 SkPath path;
766 path.addRect(outsideRect);
767 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500768 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, 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(insideRect);
776 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500777 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, 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(intersectingRect);
785 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500786 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000787 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
788 }
789
790 {
791 SkClipStack stack;
792 SkPath path;
793 path.addRect(nonIntersectingRect);
794 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500795 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000796 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
797 }
798
799 // Intersect Op tests with inverse filled circles
800 {
801 SkClipStack stack;
802 SkPath path = outsideCircle;
803 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500804 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000805 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
806 }
807
808 {
809 SkClipStack stack;
810 SkPath path = insideCircle;
811 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500812 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000813 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
814 }
815
816 {
817 SkClipStack stack;
818 SkPath path = intersectingCircle;
819 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500820 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000821 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
822 }
823
824 {
825 SkClipStack stack;
826 SkPath path = nonIntersectingCircle;
827 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500828 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000829 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
830 }
831}
832
csmartdaltond50e2402016-07-22 08:39:06 -0700833static void set_region_to_stack(const SkClipStack& stack, const SkIRect& bounds, SkRegion* region) {
834 region->setRect(bounds);
835 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
836 while (const SkClipStack::Element *element = iter.next()) {
837 SkRegion elemRegion;
838 SkRegion boundsRgn(bounds);
839 SkPath path;
840
Brian Salomonf3b46e52017-08-30 11:37:57 -0400841 switch (element->getDeviceSpaceType()) {
842 case SkClipStack::Element::DeviceSpaceType::kEmpty:
csmartdaltond50e2402016-07-22 08:39:06 -0700843 elemRegion.setEmpty();
844 break;
845 default:
Brian Salomonf3b46e52017-08-30 11:37:57 -0400846 element->asDeviceSpacePath(&path);
csmartdaltond50e2402016-07-22 08:39:06 -0700847 elemRegion.setPath(path, boundsRgn);
848 break;
849 }
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400850
reed73603f32016-09-20 08:42:38 -0700851 region->op(elemRegion, (SkRegion::Op)element->getOp());
csmartdaltond50e2402016-07-22 08:39:06 -0700852 }
853}
854
855static void test_invfill_diff_bug(skiatest::Reporter* reporter) {
856 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500857 stack.clipRect({10, 10, 20, 20}, SkMatrix::I(), kIntersect_SkClipOp, false);
csmartdaltond50e2402016-07-22 08:39:06 -0700858
859 SkPath path;
860 path.addRect({30, 10, 40, 20});
Mike Reed7d34dc72019-11-26 12:17:17 -0500861 path.setFillType(SkPathFillType::kInverseWinding);
Mike Reedc1f77742016-12-09 09:00:50 -0500862 stack.clipPath(path, SkMatrix::I(), kDifference_SkClipOp, false);
csmartdaltond50e2402016-07-22 08:39:06 -0700863
864 REPORTER_ASSERT(reporter, SkClipStack::kEmptyGenID == stack.getTopmostGenID());
865
866 SkRect stackBounds;
867 SkClipStack::BoundsType stackBoundsType;
868 stack.getBounds(&stackBounds, &stackBoundsType);
869
870 REPORTER_ASSERT(reporter, stackBounds.isEmpty());
871 REPORTER_ASSERT(reporter, SkClipStack::kNormal_BoundsType == stackBoundsType);
872
873 SkRegion region;
874 set_region_to_stack(stack, {0, 0, 50, 30}, &region);
875
876 REPORTER_ASSERT(reporter, region.isEmpty());
877}
878
bsalomon@google.com51a62862012-11-26 21:19:43 +0000879///////////////////////////////////////////////////////////////////////////////////////////////////
880
bsalomon@google.com705e8402012-11-27 15:43:57 +0000881// Functions that add a shape to the clip stack. The shape is computed from a rectangle.
882// AA is always disabled since the clip stack reducer can cause changes in aa rasterization of the
883// stack. A fractional edge repeated in different elements may be rasterized fewer times using the
884// reduced stack.
885typedef void (*AddElementFunc) (const SkRect& rect,
886 bool invert,
Mike Reedc1f77742016-12-09 09:00:50 -0500887 SkClipOp op,
csmartdaltoncbecb082016-07-22 08:59:08 -0700888 SkClipStack* stack,
889 bool doAA);
bsalomon@google.coma4e13c82012-11-26 21:38:37 +0000890
Mike Reedc1f77742016-12-09 09:00:50 -0500891static void add_round_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700892 bool doAA) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000893 SkScalar rx = rect.width() / 10;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000894 SkScalar ry = rect.height() / 20;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000895 if (invert) {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000896 SkPath path;
897 path.addRoundRect(rect, rx, ry);
Mike Reed7d34dc72019-11-26 12:17:17 -0500898 path.setFillType(SkPathFillType::kInverseWinding);
Brian Salomona3b45d42016-10-03 11:36:16 -0400899 stack->clipPath(path, SkMatrix::I(), op, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000900 } else {
901 SkRRect rrect;
902 rrect.setRectXY(rect, rx, ry);
Brian Salomona3b45d42016-10-03 11:36:16 -0400903 stack->clipRRect(rrect, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000904 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000905};
906
Mike Reedc1f77742016-12-09 09:00:50 -0500907static void add_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700908 bool doAA) {
bsalomon@google.com705e8402012-11-27 15:43:57 +0000909 if (invert) {
910 SkPath path;
911 path.addRect(rect);
Mike Reed7d34dc72019-11-26 12:17:17 -0500912 path.setFillType(SkPathFillType::kInverseWinding);
Brian Salomona3b45d42016-10-03 11:36:16 -0400913 stack->clipPath(path, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000914 } else {
Brian Salomona3b45d42016-10-03 11:36:16 -0400915 stack->clipRect(rect, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000916 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000917};
918
Mike Reedc1f77742016-12-09 09:00:50 -0500919static void add_oval(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700920 bool doAA) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000921 SkPath path;
922 path.addOval(rect);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000923 if (invert) {
Mike Reed7d34dc72019-11-26 12:17:17 -0500924 path.setFillType(SkPathFillType::kInverseWinding);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000925 }
Brian Salomona3b45d42016-10-03 11:36:16 -0400926 stack->clipPath(path, SkMatrix::I(), op, doAA);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000927};
928
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400929static void add_shader(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
930 bool doAA) {
931 // invert, op, and doAA don't apply to shaders at the SkClipStack level; this is handled earlier
932 // in the SkCanvas->SkDevice stack. Use rect to produce unique gradients, however.
933 SkPoint corners[2] = { {rect.fLeft, rect.fTop}, {rect.fRight, rect.fBottom} };
934 SkColor colors[2] = { SK_ColorBLACK, SK_ColorTRANSPARENT };
935 auto gradient = SkGradientShader::MakeLinear(corners, colors, nullptr, 2, SkTileMode::kDecal);
936 stack->clipShader(std::move(gradient));
937}
Michael Ludwigde00dc92020-06-05 10:56:32 -0400938
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000939static void add_elem_to_stack(const SkClipStack::Element& element, SkClipStack* stack) {
Brian Salomonf3b46e52017-08-30 11:37:57 -0400940 switch (element.getDeviceSpaceType()) {
941 case SkClipStack::Element::DeviceSpaceType::kRect:
942 stack->clipRect(element.getDeviceSpaceRect(), SkMatrix::I(), element.getOp(),
943 element.isAA());
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000944 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400945 case SkClipStack::Element::DeviceSpaceType::kRRect:
946 stack->clipRRect(element.getDeviceSpaceRRect(), SkMatrix::I(), element.getOp(),
947 element.isAA());
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000948 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400949 case SkClipStack::Element::DeviceSpaceType::kPath:
950 stack->clipPath(element.getDeviceSpacePath(), SkMatrix::I(), element.getOp(),
951 element.isAA());
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000952 break;
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400953 case SkClipStack::Element::DeviceSpaceType::kShader:
954 SkDEBUGFAIL("Why did the reducer put this in the mask elements.");
955 stack->clipShader(element.refShader());
956 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400957 case SkClipStack::Element::DeviceSpaceType::kEmpty:
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000958 SkDEBUGFAIL("Why did the reducer produce an explicit empty.");
959 stack->clipEmpty();
960 break;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000961 }
962}
963
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400964static void test_reduced_clip_stack(skiatest::Reporter* reporter, bool enableClipShader) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000965 // We construct random clip stacks, reduce them, and then rasterize both versions to verify that
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000966 // they are equal.
bsalomon@google.com51a62862012-11-26 21:19:43 +0000967
968 // All the clip elements will be contained within these bounds.
csmartdaltond211e782016-08-15 11:17:19 -0700969 static const SkIRect kIBounds = SkIRect::MakeWH(100, 100);
970 static const SkRect kBounds = SkRect::Make(kIBounds);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000971
972 enum {
csmartdaltoncbecb082016-07-22 08:59:08 -0700973 kNumTests = 250,
bsalomon@google.com51a62862012-11-26 21:19:43 +0000974 kMinElemsPerTest = 1,
975 kMaxElemsPerTest = 50,
976 };
977
978 // min/max size of a clip element as a fraction of kBounds.
979 static const SkScalar kMinElemSizeFrac = SK_Scalar1 / 5;
980 static const SkScalar kMaxElemSizeFrac = SK_Scalar1;
981
Mike Reedc1f77742016-12-09 09:00:50 -0500982 static const SkClipOp kOps[] = {
983 kDifference_SkClipOp,
984 kIntersect_SkClipOp,
985 kUnion_SkClipOp,
986 kXOR_SkClipOp,
987 kReverseDifference_SkClipOp,
988 kReplace_SkClipOp,
bsalomon@google.com51a62862012-11-26 21:19:43 +0000989 };
990
991 // Replace operations short-circuit the optimizer. We want to make sure that we test this code
992 // path a little bit but we don't want it to prevent us from testing many longer traversals in
993 // the optimizer.
994 static const int kReplaceDiv = 4 * kMaxElemsPerTest;
995
bsalomon@google.com705e8402012-11-27 15:43:57 +0000996 // We want to test inverse fills. However, they are quite rare in practice so don't over do it.
997 static const SkScalar kFractionInverted = SK_Scalar1 / kMaxElemsPerTest;
998
csmartdaltoncbecb082016-07-22 08:59:08 -0700999 static const SkScalar kFractionAntialiased = 0.25;
1000
bsalomon@google.com51a62862012-11-26 21:19:43 +00001001 static const AddElementFunc kElementFuncs[] = {
1002 add_rect,
1003 add_round_rect,
1004 add_oval,
Michael Ludwig4e221bd2020-06-05 11:29:36 -04001005 add_shader
bsalomon@google.com51a62862012-11-26 21:19:43 +00001006 };
1007
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +00001008 SkRandom r;
bsalomon@google.com51a62862012-11-26 21:19:43 +00001009
1010 for (int i = 0; i < kNumTests; ++i) {
csmartdaltoncbecb082016-07-22 08:59:08 -07001011 SkString testCase;
1012 testCase.printf("Iteration %d", i);
1013
bsalomon@google.com51a62862012-11-26 21:19:43 +00001014 // Randomly generate a clip stack.
1015 SkClipStack stack;
1016 int numElems = r.nextRangeU(kMinElemsPerTest, kMaxElemsPerTest);
csmartdaltoncbecb082016-07-22 08:59:08 -07001017 bool doAA = r.nextBiasedBool(kFractionAntialiased);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001018 for (int e = 0; e < numElems; ++e) {
Michael Ludwig4e221bd2020-06-05 11:29:36 -04001019 size_t opLimit = enableClipShader ? ((size_t) kIntersect_SkClipOp + 1)
1020 : SK_ARRAY_COUNT(kOps);
1021 SkClipOp op = kOps[r.nextULessThan(opLimit)];
Mike Reedc1f77742016-12-09 09:00:50 -05001022 if (op == kReplace_SkClipOp) {
bsalomon@google.com51a62862012-11-26 21:19:43 +00001023 if (r.nextU() % kReplaceDiv) {
1024 --e;
1025 continue;
1026 }
1027 }
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +00001028
bsalomon@google.com51a62862012-11-26 21:19:43 +00001029 // saves can change the clip stack behavior when an element is added.
1030 bool doSave = r.nextBool();
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +00001031
bsalomon@google.com51a62862012-11-26 21:19:43 +00001032 SkSize size = SkSize::Make(
Mike Reeddf85c382017-02-14 10:59:19 -05001033 kBounds.width() * r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac),
1034 kBounds.height() * r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac));
bsalomon@google.com51a62862012-11-26 21:19:43 +00001035
csmartdaltoncbecb082016-07-22 08:59:08 -07001036 SkPoint xy = {r.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth),
1037 r.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight)};
bsalomon@google.com51a62862012-11-26 21:19:43 +00001038
csmartdaltoncbecb082016-07-22 08:59:08 -07001039 SkRect rect;
1040 if (doAA) {
1041 rect.setXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight);
1042 if (GrClip::IsPixelAligned(rect)) {
1043 // Don't create an element that may accidentally become not antialiased.
1044 rect.outset(0.5f, 0.5f);
1045 }
1046 SkASSERT(!GrClip::IsPixelAligned(rect));
1047 } else {
1048 rect.setXYWH(SkScalarFloorToScalar(xy.fX),
1049 SkScalarFloorToScalar(xy.fY),
1050 SkScalarCeilToScalar(size.fWidth),
1051 SkScalarCeilToScalar(size.fHeight));
1052 }
bsalomon@google.com51a62862012-11-26 21:19:43 +00001053
bsalomon@google.com705e8402012-11-27 15:43:57 +00001054 bool invert = r.nextBiasedBool(kFractionInverted);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001055
Michael Ludwig4e221bd2020-06-05 11:29:36 -04001056 size_t functionLimit = SK_ARRAY_COUNT(kElementFuncs);
1057 if (!enableClipShader) {
1058 functionLimit--;
1059 }
1060 kElementFuncs[r.nextULessThan(functionLimit)](rect, invert, op, &stack, doAA);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001061 if (doSave) {
1062 stack.save();
1063 }
1064 }
1065
Robert Phillipsf4f80112020-07-13 16:13:31 -04001066 sk_sp<GrDirectContext> context = GrDirectContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001067 const GrCaps* caps = context->priv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001068
Brian Salomonc3833b42018-07-09 18:23:58 +00001069 // Zero the memory we will new the GrReducedClip into. This ensures the elements gen ID
1070 // will be kInvalidGenID if left uninitialized.
1071 SkAlignedSTStorage<1, GrReducedClip> storage;
1072 memset(storage.get(), 0, sizeof(GrReducedClip));
Brian Salomon4dea72a2019-12-18 10:43:10 -05001073 static_assert(0 == SkClipStack::kInvalidGenID);
Brian Salomonc3833b42018-07-09 18:23:58 +00001074
csmartdalton77f2fae2016-08-08 09:55:06 -07001075 // Get the reduced version of the stack.
csmartdaltoncbecb082016-07-22 08:59:08 -07001076 SkRect queryBounds = kBounds;
1077 queryBounds.outset(kBounds.width() / 2, kBounds.height() / 2);
Ethan Nicholaseace9352018-10-15 20:09:54 +00001078 const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, queryBounds, caps);
bsalomon@google.coma4444302012-12-04 15:22:12 +00001079
Brian Salomonc3833b42018-07-09 18:23:58 +00001080 REPORTER_ASSERT(reporter,
1081 reduced->maskElements().isEmpty() ||
1082 SkClipStack::kInvalidGenID != reduced->maskGenID(),
1083 testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001084
Brian Salomonc3833b42018-07-09 18:23:58 +00001085 if (!reduced->maskElements().isEmpty()) {
1086 REPORTER_ASSERT(reporter, reduced->hasScissor(), testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001087 SkRect stackBounds;
1088 SkClipStack::BoundsType stackBoundsType;
1089 stack.getBounds(&stackBounds, &stackBoundsType);
Brian Salomonc3833b42018-07-09 18:23:58 +00001090 REPORTER_ASSERT(reporter, reduced->maskRequiresAA() == doAA, testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001091 }
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001092
bsalomon@google.com51a62862012-11-26 21:19:43 +00001093 // Build a new clip stack based on the reduced clip elements
1094 SkClipStack reducedStack;
Brian Salomonc3833b42018-07-09 18:23:58 +00001095 if (GrReducedClip::InitialState::kAllOut == reduced->initialState()) {
bsalomon@google.com51a62862012-11-26 21:19:43 +00001096 // whether the result is bounded or not, the whole plane should start outside the clip.
1097 reducedStack.clipEmpty();
1098 }
Brian Salomonc3833b42018-07-09 18:23:58 +00001099 for (ElementList::Iter iter(reduced->maskElements()); iter.get(); iter.next()) {
1100 add_elem_to_stack(*iter.get(), &reducedStack);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001101 }
Michael Ludwig4e221bd2020-06-05 11:29:36 -04001102 if (reduced->hasShader()) {
1103 REPORTER_ASSERT(reporter, enableClipShader);
1104 reducedStack.clipShader(reduced->shader());
1105 }
bsalomon@google.com51a62862012-11-26 21:19:43 +00001106
Brian Salomonc3833b42018-07-09 18:23:58 +00001107 SkIRect scissor = reduced->hasScissor() ? reduced->scissor() : kIBounds;
csmartdaltond211e782016-08-15 11:17:19 -07001108
bsalomon@google.com34cd70a2012-12-06 14:23:20 +00001109 // GrReducedClipStack assumes that the final result is clipped to the returned bounds
Chris Dalton79471932017-10-27 01:50:57 -06001110 reducedStack.clipDevRect(scissor, kIntersect_SkClipOp);
1111 stack.clipDevRect(scissor, kIntersect_SkClipOp);
bsalomon@google.com34cd70a2012-12-06 14:23:20 +00001112
bsalomon@google.com51a62862012-11-26 21:19:43 +00001113 // convert both the original stack and reduced stack to SkRegions and see if they're equal
bsalomon@google.com51a62862012-11-26 21:19:43 +00001114 SkRegion region;
Chris Dalton79471932017-10-27 01:50:57 -06001115 set_region_to_stack(stack, scissor, &region);
csmartdaltond50e2402016-07-22 08:39:06 -07001116
bsalomon@google.com51a62862012-11-26 21:19:43 +00001117 SkRegion reducedRegion;
Chris Dalton79471932017-10-27 01:50:57 -06001118 set_region_to_stack(reducedStack, scissor, &reducedRegion);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001119
Brian Salomon1c80e992018-01-29 09:50:47 -05001120 REPORTER_ASSERT(reporter, region == reducedRegion, testCase.c_str());
Brian Salomonc3833b42018-07-09 18:23:58 +00001121
1122 reduced->~GrReducedClip();
bsalomon@google.com51a62862012-11-26 21:19:43 +00001123 }
1124}
1125
halcanary4dbbd042016-06-07 17:21:10 -07001126#ifdef SK_BUILD_FOR_WIN
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001127 #define SUPPRESS_VISIBILITY_WARNING
1128#else
1129 #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden")))
1130#endif
1131
Brian Salomonc3833b42018-07-09 18:23:58 +00001132static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) {
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001133 {
1134 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001135 stack.clipRect(SkRect::MakeXYWH(0, 0, 100, 100), SkMatrix::I(), kReplace_SkClipOp,
Brian Salomona3b45d42016-10-03 11:36:16 -04001136 true);
1137 stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(50.3), SkScalar(50.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001138 kReplace_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001139 SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001140
Robert Phillipsf4f80112020-07-13 16:13:31 -04001141 sk_sp<GrDirectContext> context = GrDirectContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001142 const GrCaps* caps = context->priv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001143
Brian Salomonc3833b42018-07-09 18:23:58 +00001144 SkAlignedSTStorage<1, GrReducedClip> storage;
1145 memset(storage.get(), 0, sizeof(GrReducedClip));
Brian Salomon4dea72a2019-12-18 10:43:10 -05001146 static_assert(0 == SkClipStack::kInvalidGenID);
Ethan Nicholaseace9352018-10-15 20:09:54 +00001147 const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, bounds, caps);
Brian Salomonc3833b42018-07-09 18:23:58 +00001148
1149 REPORTER_ASSERT(reporter, reduced->maskElements().count() == 1);
1150 // Clips will be cached based on the generation id. Make sure the gen id is valid.
1151 REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reduced->maskGenID());
1152
1153 reduced->~GrReducedClip();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001154 }
1155 {
1156 SkClipStack stack;
1157
1158 // Create a clip with following 25.3, 25.3 boxes which are 25 apart:
1159 // A B
1160 // C D
1161
Brian Salomona3b45d42016-10-03 11:36:16 -04001162 stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001163 kReplace_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001164 uint32_t genIDA = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001165 stack.clipRect(SkRect::MakeXYWH(50, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001166 kUnion_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001167 uint32_t genIDB = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001168 stack.clipRect(SkRect::MakeXYWH(0, 50, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001169 kUnion_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001170 uint32_t genIDC = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001171 stack.clipRect(SkRect::MakeXYWH(50, 50, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001172 kUnion_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001173 uint32_t genIDD = stack.getTopmostGenID();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001174
1175
csmartdaltoncbecb082016-07-22 08:59:08 -07001176#define IXYWH SkIRect::MakeXYWH
1177#define XYWH SkRect::MakeXYWH
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001178
csmartdaltoncbecb082016-07-22 08:59:08 -07001179 SkIRect stackBounds = IXYWH(0, 0, 76, 76);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001180
1181 // The base test is to test each rect in two ways:
1182 // 1) The box dimensions. (Should reduce to "all in", no elements).
1183 // 2) A bit over the box dimensions.
1184 // In the case 2, test that the generation id is what is expected.
1185 // The rects are of fractional size so that case 2 never gets optimized to an empty element
1186 // list.
1187
1188 // Not passing in tighter bounds is tested for consistency.
1189 static const struct SUPPRESS_VISIBILITY_WARNING {
csmartdaltoncbecb082016-07-22 08:59:08 -07001190 SkRect testBounds;
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001191 int reducedClipCount;
Brian Salomonc3833b42018-07-09 18:23:58 +00001192 uint32_t reducedGenID;
csmartdaltoncbecb082016-07-22 08:59:08 -07001193 InitialState initialState;
1194 SkIRect clipIRect;
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001195 // parameter.
1196 } testCases[] = {
csmartdalton77f2fae2016-08-08 09:55:06 -07001197
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001198 // Rect A.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001199 { XYWH(0, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 0, 25, 25) },
1200 { 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 -06001201 { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::InitialState::kAllOut, IXYWH(0, 0, 26, 26)},
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001202
1203 // Rect B.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001204 { XYWH(50, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 25, 25) },
1205 { XYWH(50, 0, 25.3f, 25.3f), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 26, 26) },
csmartdalton77f2fae2016-08-08 09:55:06 -07001206 { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::InitialState::kAllOut, IXYWH(50, 0, 26, 27) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001207
1208 // Rect C.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001209 { XYWH(0, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 50, 25, 25) },
1210 { 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 -07001211 { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::InitialState::kAllOut, IXYWH(0, 50, 27, 26) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001212
1213 // Rect D.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001214 { XYWH(50, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 25, 25)},
1215 { XYWH(50.3f, 50.3f, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 26, 26)},
csmartdalton77f2fae2016-08-08 09:55:06 -07001216 { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(50, 50, 26, 26)},
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001217
1218 // Other tests:
csmartdalton77f2fae2016-08-08 09:55:06 -07001219 { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::InitialState::kAllOut, stackBounds },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001220
Michael Ludwigde00dc92020-06-05 10:56:32 -04001221 // Rect in the middle, touches none (so should not be drawn)
1222 { XYWH(26, 26, 24, 24), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllOut, SkIRect::MakeEmpty() },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001223
1224 // Rect in the middle, touches all the rects. GenID is the last rect.
csmartdalton77f2fae2016-08-08 09:55:06 -07001225 { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(24, 24, 27, 27) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001226 };
1227
1228#undef XYWH
csmartdaltoncbecb082016-07-22 08:59:08 -07001229#undef IXYWH
Robert Phillipsf4f80112020-07-13 16:13:31 -04001230 sk_sp<GrDirectContext> context = GrDirectContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001231 const GrCaps* caps = context->priv().caps();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001232
1233 for (size_t i = 0; i < SK_ARRAY_COUNT(testCases); ++i) {
Ethan Nicholaseace9352018-10-15 20:09:54 +00001234 const GrReducedClip reduced(stack, testCases[i].testBounds, caps);
Chris Dalton79471932017-10-27 01:50:57 -06001235 REPORTER_ASSERT(reporter, reduced.maskElements().count() ==
1236 testCases[i].reducedClipCount);
Brian Salomonc3833b42018-07-09 18:23:58 +00001237 if (reduced.maskElements().count()) {
1238 REPORTER_ASSERT(reporter, reduced.maskGenID() == testCases[i].reducedGenID);
csmartdalton8d3f92a2016-08-17 09:39:38 -07001239 }
csmartdalton77f2fae2016-08-08 09:55:06 -07001240 REPORTER_ASSERT(reporter, reduced.initialState() == testCases[i].initialState);
Michael Ludwigde00dc92020-06-05 10:56:32 -04001241
1242 bool expectsScissor = !testCases[i].clipIRect.isEmpty();
1243 REPORTER_ASSERT(reporter, expectsScissor == reduced.hasScissor());
1244 if (expectsScissor) {
1245 REPORTER_ASSERT(reporter, reduced.scissor() == testCases[i].clipIRect);
1246 }
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001247 }
1248 }
1249}
1250
1251static void test_reduced_clip_stack_no_aa_crash(skiatest::Reporter* reporter) {
1252 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001253 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 100, 100), kReplace_SkClipOp);
1254 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 50, 50), kReplace_SkClipOp);
csmartdaltoncbecb082016-07-22 08:59:08 -07001255 SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001256
Robert Phillipsf4f80112020-07-13 16:13:31 -04001257 sk_sp<GrDirectContext> context = GrDirectContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001258 const GrCaps* caps = context->priv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001259
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001260 // At the time, this would crash.
Ethan Nicholaseace9352018-10-15 20:09:54 +00001261 const GrReducedClip reduced(stack, bounds, caps);
Chris Dalton79471932017-10-27 01:50:57 -06001262 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001263}
1264
csmartdaltoncbecb082016-07-22 08:59:08 -07001265enum class ClipMethod {
1266 kSkipDraw,
1267 kIgnoreClip,
1268 kScissor,
1269 kAAElements
1270};
1271
1272static void test_aa_query(skiatest::Reporter* reporter, const SkString& testName,
1273 const SkClipStack& stack, const SkMatrix& queryXform,
1274 const SkRect& preXformQuery, ClipMethod expectedMethod,
1275 int numExpectedElems = 0) {
Robert Phillipsf4f80112020-07-13 16:13:31 -04001276 sk_sp<GrDirectContext> context = GrDirectContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001277 const GrCaps* caps = context->priv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001278
csmartdaltoncbecb082016-07-22 08:59:08 -07001279 SkRect queryBounds;
1280 queryXform.mapRect(&queryBounds, preXformQuery);
Ethan Nicholaseace9352018-10-15 20:09:54 +00001281 const GrReducedClip reduced(stack, queryBounds, caps);
csmartdaltoncbecb082016-07-22 08:59:08 -07001282
1283 SkClipStack::BoundsType stackBoundsType;
1284 SkRect stackBounds;
1285 stack.getBounds(&stackBounds, &stackBoundsType);
1286
1287 switch (expectedMethod) {
1288 case ClipMethod::kSkipDraw:
1289 SkASSERT(0 == numExpectedElems);
Brian Salomon1c80e992018-01-29 09:50:47 -05001290 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty(), testName.c_str());
1291 REPORTER_ASSERT(reporter,
1292 GrReducedClip::InitialState::kAllOut == reduced.initialState(),
1293 testName.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001294 return;
1295 case ClipMethod::kIgnoreClip:
1296 SkASSERT(0 == numExpectedElems);
Brian Salomon1c80e992018-01-29 09:50:47 -05001297 REPORTER_ASSERT(
1298 reporter,
1299 !reduced.hasScissor() || GrClip::IsInsideClip(reduced.scissor(), queryBounds),
1300 testName.c_str());
1301 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty(), testName.c_str());
1302 REPORTER_ASSERT(reporter,
1303 GrReducedClip::InitialState::kAllIn == reduced.initialState(),
1304 testName.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001305 return;
1306 case ClipMethod::kScissor: {
1307 SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType);
1308 SkASSERT(0 == numExpectedElems);
1309 SkIRect expectedScissor;
1310 stackBounds.round(&expectedScissor);
Brian Salomon1c80e992018-01-29 09:50:47 -05001311 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty(), testName.c_str());
1312 REPORTER_ASSERT(reporter, reduced.hasScissor(), testName.c_str());
1313 REPORTER_ASSERT(reporter, expectedScissor == reduced.scissor(), testName.c_str());
1314 REPORTER_ASSERT(reporter,
1315 GrReducedClip::InitialState::kAllIn == reduced.initialState(),
1316 testName.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001317 return;
1318 }
1319 case ClipMethod::kAAElements: {
1320 SkIRect expectedClipIBounds = GrClip::GetPixelIBounds(queryBounds);
1321 if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
1322 SkAssertResult(expectedClipIBounds.intersect(GrClip::GetPixelIBounds(stackBounds)));
1323 }
Brian Salomon1c80e992018-01-29 09:50:47 -05001324 REPORTER_ASSERT(reporter, numExpectedElems == reduced.maskElements().count(),
1325 testName.c_str());
1326 REPORTER_ASSERT(reporter, reduced.hasScissor(), testName.c_str());
1327 REPORTER_ASSERT(reporter, expectedClipIBounds == reduced.scissor(), testName.c_str());
1328 REPORTER_ASSERT(reporter,
1329 reduced.maskElements().isEmpty() || reduced.maskRequiresAA(),
1330 testName.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001331 break;
1332 }
1333 }
1334}
1335
1336static void test_reduced_clip_stack_aa(skiatest::Reporter* reporter) {
1337 constexpr SkScalar IL = 2, IT = 1, IR = 6, IB = 7; // Pixel aligned rect.
1338 constexpr SkScalar L = 2.2f, T = 1.7f, R = 5.8f, B = 7.3f; // Generic rect.
1339 constexpr SkScalar l = 3.3f, t = 2.8f, r = 4.7f, b = 6.2f; // Small rect contained in R.
1340
1341 SkRect alignedRect = {IL, IT, IR, IB};
1342 SkRect rect = {L, T, R, B};
1343 SkRect innerRect = {l, t, r, b};
1344
1345 SkMatrix m;
1346 m.setIdentity();
1347
1348 constexpr SkScalar kMinScale = 2.0001f;
1349 constexpr SkScalar kMaxScale = 3;
1350 constexpr int kNumIters = 8;
1351
1352 SkString name;
1353 SkRandom rand;
1354
1355 for (int i = 0; i < kNumIters; ++i) {
1356 // Pixel-aligned rect (iior=true).
1357 name.printf("Pixel-aligned rect test, iter %i", i);
1358 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001359 stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001360 test_aa_query(reporter, name, stack, m, {IL, IT, IR, IB}, ClipMethod::kIgnoreClip);
1361 test_aa_query(reporter, name, stack, m, {IL, IT-1, IR, IT}, ClipMethod::kSkipDraw);
csmartdalton77f2fae2016-08-08 09:55:06 -07001362 test_aa_query(reporter, name, stack, m, {IL, IT-2, IR, IB}, ClipMethod::kScissor);
csmartdaltoncbecb082016-07-22 08:59:08 -07001363
1364 // Rect (iior=true).
1365 name.printf("Rect test, iter %i", i);
1366 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001367 stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001368 test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kIgnoreClip);
1369 test_aa_query(reporter, name, stack, m, {L-.1f, T, L, B}, ClipMethod::kSkipDraw);
1370 test_aa_query(reporter, name, stack, m, {L-.1f, T, L+.1f, B}, ClipMethod::kAAElements, 1);
1371
1372 // Difference rect (iior=false, inside-out bounds).
1373 name.printf("Difference rect test, iter %i", i);
1374 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001375 stack.clipRect(rect, SkMatrix::I(), kDifference_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001376 test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kSkipDraw);
1377 test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T}, ClipMethod::kIgnoreClip);
1378 test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T+.1f}, ClipMethod::kAAElements, 1);
1379
1380 // Complex clip (iior=false, normal bounds).
1381 name.printf("Complex clip test, iter %i", i);
1382 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001383 stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true);
1384 stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001385 test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
1386 test_aa_query(reporter, name, stack, m, {r-.1f, t, R, b}, ClipMethod::kAAElements, 1);
1387 test_aa_query(reporter, name, stack, m, {r-.1f, t, R+.1f, b}, ClipMethod::kAAElements, 2);
1388 test_aa_query(reporter, name, stack, m, {r, t, R+.1f, b}, ClipMethod::kAAElements, 1);
1389 test_aa_query(reporter, name, stack, m, {r, t, R, b}, ClipMethod::kIgnoreClip);
1390 test_aa_query(reporter, name, stack, m, {R, T, R+.1f, B}, ClipMethod::kSkipDraw);
1391
1392 // Complex clip where outer rect is pixel aligned (iior=false, normal bounds).
1393 name.printf("Aligned Complex clip test, iter %i", i);
1394 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001395 stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true);
1396 stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001397 test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
1398 test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB}, ClipMethod::kAAElements, 1);
1399 test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB+.1f}, ClipMethod::kAAElements, 1);
1400 test_aa_query(reporter, name, stack, m, {l, b, r, IB+.1f}, ClipMethod::kAAElements, 0);
1401 test_aa_query(reporter, name, stack, m, {l, b, r, IB}, ClipMethod::kIgnoreClip);
1402 test_aa_query(reporter, name, stack, m, {IL, IB, IR, IB+.1f}, ClipMethod::kSkipDraw);
1403
1404 // Apply random transforms and try again. This ensures the clip stack reduction is hardened
1405 // against FP rounding error.
1406 SkScalar sx = rand.nextRangeScalar(kMinScale, kMaxScale);
1407 sx = SkScalarFloorToScalar(sx * alignedRect.width()) / alignedRect.width();
1408 SkScalar sy = rand.nextRangeScalar(kMinScale, kMaxScale);
1409 sy = SkScalarFloorToScalar(sy * alignedRect.height()) / alignedRect.height();
1410 SkScalar tx = SkScalarRoundToScalar(sx * alignedRect.x()) - sx * alignedRect.x();
1411 SkScalar ty = SkScalarRoundToScalar(sy * alignedRect.y()) - sy * alignedRect.y();
1412
Mike Reed1f607332020-05-21 12:11:27 -04001413 SkMatrix xform = SkMatrix::Scale(sx, sy);
csmartdaltoncbecb082016-07-22 08:59:08 -07001414 xform.postTranslate(tx, ty);
1415 xform.mapRect(&alignedRect);
1416 xform.mapRect(&rect);
1417 xform.mapRect(&innerRect);
1418 m.postConcat(xform);
1419 }
1420}
1421
Chris Dalton348060f2017-06-05 13:15:37 -06001422static void test_tiny_query_bounds_assertion_bug(skiatest::Reporter* reporter) {
1423 // https://bugs.chromium.org/p/skia/issues/detail?id=5990
1424 const SkRect clipBounds = SkRect::MakeXYWH(1.5f, 100, 1000, 1000);
1425
1426 SkClipStack rectStack;
1427 rectStack.clipRect(clipBounds, SkMatrix::I(), kIntersect_SkClipOp, true);
1428
1429 SkPath clipPath;
1430 clipPath.moveTo(clipBounds.left(), clipBounds.top());
1431 clipPath.quadTo(clipBounds.right(), clipBounds.top(),
1432 clipBounds.right(), clipBounds.bottom());
1433 clipPath.quadTo(clipBounds.left(), clipBounds.bottom(),
1434 clipBounds.left(), clipBounds.top());
1435 SkClipStack pathStack;
1436 pathStack.clipPath(clipPath, SkMatrix::I(), kIntersect_SkClipOp, true);
1437
Robert Phillipsf4f80112020-07-13 16:13:31 -04001438 sk_sp<GrDirectContext> context = GrDirectContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001439 const GrCaps* caps = context->priv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001440
Chris Dalton348060f2017-06-05 13:15:37 -06001441 for (const SkClipStack& stack : {rectStack, pathStack}) {
1442 for (SkRect queryBounds : {SkRect::MakeXYWH(53, 60, GrClip::kBoundsTolerance, 1000),
1443 SkRect::MakeXYWH(53, 60, GrClip::kBoundsTolerance/2, 1000),
1444 SkRect::MakeXYWH(53, 160, 1000, GrClip::kBoundsTolerance),
1445 SkRect::MakeXYWH(53, 160, 1000, GrClip::kBoundsTolerance/2)}) {
Ethan Nicholaseace9352018-10-15 20:09:54 +00001446 const GrReducedClip reduced(stack, queryBounds, caps);
Chris Dalton79471932017-10-27 01:50:57 -06001447 REPORTER_ASSERT(reporter, !reduced.hasScissor());
1448 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty());
Chris Dalton348060f2017-06-05 13:15:37 -06001449 REPORTER_ASSERT(reporter,
1450 GrReducedClip::InitialState::kAllOut == reduced.initialState());
1451 }
1452 }
1453}
1454
Brian Salomon1c0b05a2019-04-19 15:37:28 -04001455static void test_is_rrect_deep_rect_stack(skiatest::Reporter* reporter) {
1456 static constexpr SkRect kTargetBounds = SkRect::MakeWH(1000, 500);
1457 // All antialiased or all not antialiased.
1458 for (bool aa : {false, true}) {
1459 SkClipStack stack;
1460 for (int i = 0; i <= 100; ++i) {
1461 stack.save();
1462 stack.clipRect(SkRect::MakeLTRB(i, 0.5, kTargetBounds.width(), kTargetBounds.height()),
1463 SkMatrix::I(), SkClipOp::kIntersect, aa);
1464 }
1465 SkRRect rrect;
1466 bool isAA;
1467 SkRRect expected = SkRRect::MakeRect(
1468 SkRect::MakeLTRB(100, 0.5, kTargetBounds.width(), kTargetBounds.height()));
1469 if (stack.isRRect(kTargetBounds, &rrect, &isAA)) {
1470 REPORTER_ASSERT(reporter, rrect == expected);
1471 REPORTER_ASSERT(reporter, aa == isAA);
1472 } else {
1473 ERRORF(reporter, "Expected to be an rrect.");
1474 }
1475 }
1476 // Mixed AA and non-AA without simple containment.
1477 SkClipStack stack;
1478 for (int i = 0; i <= 100; ++i) {
1479 bool aa = i & 0b1;
1480 int j = 100 - i;
1481 stack.save();
1482 stack.clipRect(SkRect::MakeLTRB(i, j + 0.5, kTargetBounds.width(), kTargetBounds.height()),
1483 SkMatrix::I(), SkClipOp::kIntersect, aa);
1484 }
1485 SkRRect rrect;
1486 bool isAA;
1487 REPORTER_ASSERT(reporter, !stack.isRRect(kTargetBounds, &rrect, &isAA));
1488}
1489
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00001490DEF_TEST(ClipStack, reporter) {
reed@google.combdee9fc2011-02-22 20:17:43 +00001491 SkClipStack stack;
1492
robertphillips@google.com80214e22012-07-20 15:33:18 +00001493 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001494 assert_count(reporter, stack, 0);
1495
1496 static const SkIRect gRects[] = {
1497 { 0, 0, 100, 100 },
1498 { 25, 25, 125, 125 },
1499 { 0, 0, 1000, 1000 },
1500 { 0, 0, 75, 75 }
1501 };
1502 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
Mike Reedc1f77742016-12-09 09:00:50 -05001503 stack.clipDevRect(gRects[i], kIntersect_SkClipOp);
reed@google.combdee9fc2011-02-22 20:17:43 +00001504 }
1505
1506 // all of the above rects should have been intersected, leaving only 1 rect
robertphillips@google.com80214e22012-07-20 15:33:18 +00001507 SkClipStack::B2TIter iter(stack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001508 const SkClipStack::Element* element = iter.next();
epoger@google.com2047f002011-05-17 17:36:59 +00001509 SkRect answer;
Mike Reed92b33352019-08-24 19:39:13 -04001510 answer.setLTRB(25, 25, 75, 75);
reed@google.combdee9fc2011-02-22 20:17:43 +00001511
bsalomon49f085d2014-09-05 13:34:00 -07001512 REPORTER_ASSERT(reporter, element);
Brian Salomonf3b46e52017-08-30 11:37:57 -04001513 REPORTER_ASSERT(reporter,
1514 SkClipStack::Element::DeviceSpaceType::kRect == element->getDeviceSpaceType());
Mike Reedc1f77742016-12-09 09:00:50 -05001515 REPORTER_ASSERT(reporter, kIntersect_SkClipOp == element->getOp());
Brian Salomonf3b46e52017-08-30 11:37:57 -04001516 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == answer);
reed@google.combdee9fc2011-02-22 20:17:43 +00001517 // now check that we only had one in our iterator
1518 REPORTER_ASSERT(reporter, !iter.next());
1519
1520 stack.reset();
robertphillips@google.com80214e22012-07-20 15:33:18 +00001521 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001522 assert_count(reporter, stack, 0);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +00001523
1524 test_assign_and_comparison(reporter);
robertphillips@google.com80214e22012-07-20 15:33:18 +00001525 test_iterators(reporter);
Brian Salomonf3b46e52017-08-30 11:37:57 -04001526 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kRect);
1527 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kRRect);
1528 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kPath);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +00001529 test_isWideOpen(reporter);
robertphillips@google.com08eacc12012-08-02 12:49:00 +00001530 test_rect_merging(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001531 test_rect_replace(reporter);
junov@chromium.orgedf32d52012-12-10 14:57:54 +00001532 test_rect_inverse_fill(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001533 test_path_replace(reporter);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +00001534 test_quickContains(reporter);
csmartdaltond50e2402016-07-22 08:39:06 -07001535 test_invfill_diff_bug(reporter);
Brian Osmanc7ad40f2018-05-31 14:27:17 -04001536
Michael Ludwig4e221bd2020-06-05 11:29:36 -04001537 test_reduced_clip_stack(reporter, /* clipShader */ false);
1538 test_reduced_clip_stack(reporter, /* clipShader */ true);
Brian Salomonc3833b42018-07-09 18:23:58 +00001539 test_reduced_clip_stack_genid(reporter);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001540 test_reduced_clip_stack_no_aa_crash(reporter);
csmartdaltoncbecb082016-07-22 08:59:08 -07001541 test_reduced_clip_stack_aa(reporter);
Chris Dalton348060f2017-06-05 13:15:37 -06001542 test_tiny_query_bounds_assertion_bug(reporter);
Brian Salomon1c0b05a2019-04-19 15:37:28 -04001543 test_is_rrect_deep_rect_stack(reporter);
reed@google.combdee9fc2011-02-22 20:17:43 +00001544}
Brian Salomon19f0ed52017-01-06 13:54:58 -05001545
1546//////////////////////////////////////////////////////////////////////////////
1547
Robert Phillips8faa3402021-04-05 14:01:36 -04001548#include "src/gpu/SkGpuDevice.h"
1549
1550// For the GrClipStack case, this is covered in GrClipStack_SWMask
1551#if defined(SK_DISABLE_NEW_GR_CLIP_STACK)
1552
Robert Phillips4e105e22020-07-16 09:18:50 -04001553sk_sp<GrTextureProxy> GrClipStackClip::testingOnly_createClipMask(
1554 GrRecordingContext* context) const {
John Stilesfe0de302020-08-14 10:52:06 -04001555 const GrReducedClip reducedClip(*fStack, SkRect::MakeWH(512, 512), nullptr);
Greg Daniele32506b2020-02-10 16:00:54 -05001556 return this->createSoftwareClipMask(context, reducedClip, nullptr).asTextureProxyRef();
Brian Salomon19f0ed52017-01-06 13:54:58 -05001557}
1558
1559// Verify that clip masks are freed up when the clip state that generated them goes away.
1560DEF_GPUTEST_FOR_ALL_CONTEXTS(ClipMaskCache, reporter, ctxInfo) {
1561 // This test uses resource key tags which only function in debug builds.
1562#ifdef SK_DEBUG
Robert Phillips6d344c32020-07-06 10:56:46 -04001563 auto context = ctxInfo.directContext();
Brian Salomon19f0ed52017-01-06 13:54:58 -05001564 SkClipStack stack;
1565
1566 SkPath path;
1567 path.addCircle(10, 10, 8);
1568 path.addCircle(15, 15, 8);
Mike Reed7d34dc72019-11-26 12:17:17 -05001569 path.setFillType(SkPathFillType::kEvenOdd);
Brian Salomon19f0ed52017-01-06 13:54:58 -05001570
Michael Ludwige06a8972020-06-11 10:29:00 -04001571 SkIRect stackBounds = path.getBounds().roundOut();
1572
Brian Salomonc3833b42018-07-09 18:23:58 +00001573 static const char* kTag = GrClipStackClip::kMaskTestTag;
Robert Phillips9da87e02019-02-04 13:26:26 -05001574 GrResourceCache* cache = context->priv().getResourceCache();
Brian Salomon19f0ed52017-01-06 13:54:58 -05001575
1576 static constexpr int kN = 5;
1577
1578 for (int i = 0; i < kN; ++i) {
1579 SkMatrix m;
1580 m.setTranslate(0.5, 0.5);
1581 stack.save();
1582 stack.clipPath(path, m, SkClipOp::kIntersect, true);
Michael Ludwige06a8972020-06-11 10:29:00 -04001583 sk_sp<GrTextureProxy> mask =
1584 GrClipStackClip(stackBounds.size(), &stack).testingOnly_createClipMask(context);
Adlai Hollercc25d532021-02-10 13:58:34 +00001585 mask->instantiate(context->priv().resourceProvider());
Brian Salomonfd98c2c2018-07-31 17:25:29 -04001586 GrTexture* tex = mask->peekTexture();
Robert Phillips875218e2017-02-24 08:37:13 -05001587 REPORTER_ASSERT(reporter, 0 == strcmp(tex->getUniqueKey().tag(), kTag));
Brian Salomon19f0ed52017-01-06 13:54:58 -05001588 // Make sure mask isn't pinned in cache.
1589 mask.reset(nullptr);
Greg Daniel0a2464f2020-05-14 15:45:44 -04001590 context->flushAndSubmit();
Brian Salomon19f0ed52017-01-06 13:54:58 -05001591 REPORTER_ASSERT(reporter, i + 1 == cache->countUniqueKeysWithTag(kTag));
1592 }
1593
1594 for (int i = 0; i < kN; ++i) {
1595 stack.restore();
1596 cache->purgeAsNeeded();
1597 REPORTER_ASSERT(reporter, kN - (i + 1) == cache->countUniqueKeysWithTag(kTag));
1598 }
1599#endif
1600}
Robert Phillips8faa3402021-04-05 14:01:36 -04001601#endif // SK_DISABLE_NEW_GR_CLIP_STACK