blob: 1f0887ac2e3a78e878fe4288a029566a2d22181a [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"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050023#include "include/gpu/GrConfig.h"
24#include "include/gpu/GrContext.h"
25#include "include/gpu/GrTexture.h"
26#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"
34#include "src/gpu/GrContextPriv.h"
35#include "src/gpu/GrReducedClip.h"
36#include "src/gpu/GrResourceCache.h"
Greg Danielf91aeb22019-06-18 09:58:02 -040037#include "src/gpu/GrTextureProxy.h"
Ben Wagner9707a7e2019-05-06 17:17:19 -040038#include "tests/Test.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050039#include "tools/gpu/GrContextFactory.h"
csmartdaltoncbecb082016-07-22 08:59:08 -070040
Ben Wagnerb607a8f2018-03-12 13:46:21 -040041#include <cstring>
Ben Wagner9707a7e2019-05-06 17:17:19 -040042#include <initializer_list>
Ben Wagnerb607a8f2018-03-12 13:46:21 -040043#include <new>
44
Ben Wagner9707a7e2019-05-06 17:17:19 -040045class GrCaps;
46
47typedef GrReducedClip::ElementList ElementList;
48typedef GrReducedClip::InitialState InitialState;
49
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000050static void test_assign_and_comparison(skiatest::Reporter* reporter) {
51 SkClipStack s;
reed@google.comd9f2dea2011-10-12 14:43:27 +000052 bool doAA = false;
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000053
robertphillips@google.com80214e22012-07-20 15:33:18 +000054 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
55
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000056 // Build up a clip stack with a path, an empty clip, and a rect.
57 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000058 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
59
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000060 SkPath p;
61 p.moveTo(5, 6);
62 p.lineTo(7, 8);
63 p.lineTo(5, 9);
64 p.close();
Mike Reedc1f77742016-12-09 09:00:50 -050065 s.clipPath(p, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000066
67 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000068 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
69
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000070 SkRect r = SkRect::MakeLTRB(1, 2, 3, 4);
Mike Reedc1f77742016-12-09 09:00:50 -050071 s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000072 r = SkRect::MakeLTRB(10, 11, 12, 13);
Mike Reedc1f77742016-12-09 09:00:50 -050073 s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000074
75 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000076 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
77
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000078 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -050079 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000080
81 // Test that assignment works.
82 SkClipStack copy = s;
83 REPORTER_ASSERT(reporter, s == copy);
84
85 // Test that different save levels triggers not equal.
86 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000087 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000088 REPORTER_ASSERT(reporter, s != copy);
89
90 // Test that an equal, but not copied version is equal.
91 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000092 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000093 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -050094 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000095 REPORTER_ASSERT(reporter, s == copy);
96
97 // Test that a different op on one level triggers not equal.
98 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000099 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000100 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000101 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000102 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -0500103 s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000104 REPORTER_ASSERT(reporter, s != copy);
105
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000106 // Test that version constructed with rect-path rather than a rect is still considered equal.
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000107 s.restore();
108 s.save();
109 SkPath rp;
110 rp.addRect(r);
Mike Reedc1f77742016-12-09 09:00:50 -0500111 s.clipPath(rp, SkMatrix::I(), kUnion_SkClipOp, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000112 REPORTER_ASSERT(reporter, s == copy);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000113
114 // Test that different rects triggers not equal.
115 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000116 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000117 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000118 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
119
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000120 r = SkRect::MakeLTRB(24, 25, 26, 27);
Mike Reedc1f77742016-12-09 09:00:50 -0500121 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000122 REPORTER_ASSERT(reporter, s != copy);
123
124 // Sanity check
125 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) {
Brian Salomonf3b46e52017-08-30 11:37:57 -0400297 case SkClipStack::Element::DeviceSpaceType::kEmpty:
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000298 SkDEBUGFAIL("Don't call this with kEmpty.");
299 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400300 case SkClipStack::Element::DeviceSpaceType::kRect:
Mike Reedc1f77742016-12-09 09:00:50 -0500301 stack.clipRect(rectA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400302 stack.clipRect(rectB, 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::kRRect:
Mike Reedc1f77742016-12-09 09:00:50 -0500305 stack.clipRRect(rrectA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400306 stack.clipRRect(rrectB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000307 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400308 case SkClipStack::Element::DeviceSpaceType::kPath:
Mike Reedc1f77742016-12-09 09:00:50 -0500309 stack.clipPath(pathA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400310 stack.clipPath(pathB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000311 break;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000312 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000313
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000314 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000315 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000316
robertphillips@google.com7b112892012-07-31 15:18:21 +0000317 stack.getConservativeBounds(0, 0, 100, 100, &devClipBound,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000318 &isIntersectionOfRects);
319
Brian Salomonf3b46e52017-08-30 11:37:57 -0400320 if (SkClipStack::Element::DeviceSpaceType::kRect == primType) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000321 REPORTER_ASSERT(reporter, isIntersectionOfRects ==
Mike Reedc1f77742016-12-09 09:00:50 -0500322 (gOps[op] == kIntersect_SkClipOp));
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000323 } else {
324 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
325 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000326
327 SkASSERT(testCase < gNumCases);
robertphillips@google.com7b112892012-07-31 15:18:21 +0000328 REPORTER_ASSERT(reporter, devClipBound == gAnswerRectsBW[testCase]);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000329 ++testCase;
330
331 stack.restore();
332 }
333 }
334}
335
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000336// Test out 'isWideOpen' entry point
337static void test_isWideOpen(skiatest::Reporter* reporter) {
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000338 {
339 // Empty stack is wide open. Wide open stack means that gen id is wide open.
340 SkClipStack stack;
341 REPORTER_ASSERT(reporter, stack.isWideOpen());
342 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
343 }
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000344
345 SkRect rectA, rectB;
346
Mike Reed92b33352019-08-24 19:39:13 -0400347 rectA.setLTRB(10, 10, 40, 40);
348 rectB.setLTRB(50, 50, 80, 80);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000349
350 // Stack should initially be wide open
351 {
352 SkClipStack stack;
353
354 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000355 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000356 }
357
358 // Test out case where the user specifies a union that includes everything
359 {
360 SkClipStack stack;
361
362 SkPath clipA, clipB;
363
364 clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
Mike Reed7d34dc72019-11-26 12:17:17 -0500365 clipA.setFillType(SkPathFillType::kInverseEvenOdd);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000366
367 clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
Mike Reed7d34dc72019-11-26 12:17:17 -0500368 clipB.setFillType(SkPathFillType::kInverseEvenOdd);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000369
Mike Reedc1f77742016-12-09 09:00:50 -0500370 stack.clipPath(clipA, SkMatrix::I(), kReplace_SkClipOp, false);
371 stack.clipPath(clipB, SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000372
373 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000374 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000375 }
376
377 // Test out union w/ a wide open clip
378 {
379 SkClipStack stack;
380
Mike Reedc1f77742016-12-09 09:00:50 -0500381 stack.clipRect(rectA, SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000382
383 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000384 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000385 }
386
387 // Test out empty difference from a wide open clip
388 {
389 SkClipStack stack;
390
391 SkRect emptyRect;
392 emptyRect.setEmpty();
393
Mike Reedc1f77742016-12-09 09:00:50 -0500394 stack.clipRect(emptyRect, SkMatrix::I(), kDifference_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000395
396 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000397 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000398 }
399
400 // Test out return to wide open
401 {
402 SkClipStack stack;
403
404 stack.save();
405
Mike Reedc1f77742016-12-09 09:00:50 -0500406 stack.clipRect(rectA, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000407
408 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000409 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000410
411 stack.restore();
412
413 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000414 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000415 }
416}
417
bsalomon@google.com100abf42012-09-05 17:40:04 +0000418static int count(const SkClipStack& stack) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000419
420 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
421
halcanary96fcdcc2015-08-27 07:41:13 -0700422 const SkClipStack::Element* element = nullptr;
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000423 int count = 0;
424
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000425 for (element = iter.prev(); element; element = iter.prev(), ++count) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000426 }
427
428 return count;
429}
430
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000431static void test_rect_inverse_fill(skiatest::Reporter* reporter) {
432 // non-intersecting rectangles
433 SkRect rect = SkRect::MakeLTRB(0, 0, 10, 10);
434
435 SkPath path;
436 path.addRect(rect);
437 path.toggleInverseFillType();
438 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500439 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000440
441 SkRect bounds;
442 SkClipStack::BoundsType boundsType;
443 stack.getBounds(&bounds, &boundsType);
444 REPORTER_ASSERT(reporter, SkClipStack::kInsideOut_BoundsType == boundsType);
445 REPORTER_ASSERT(reporter, bounds == rect);
446}
447
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000448static void test_rect_replace(skiatest::Reporter* reporter) {
449 SkRect rect = SkRect::MakeWH(100, 100);
450 SkRect rect2 = SkRect::MakeXYWH(50, 50, 100, 100);
451
452 SkRect bound;
453 SkClipStack::BoundsType type;
454 bool isIntersectionOfRects;
455
456 // Adding a new rect with the replace operator should not increase
457 // the stack depth. BW replacing BW.
458 {
459 SkClipStack stack;
460 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500461 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000462 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500463 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000464 REPORTER_ASSERT(reporter, 1 == count(stack));
465 }
466
467 // Adding a new rect with the replace operator should not increase
468 // the stack depth. AA replacing AA.
469 {
470 SkClipStack stack;
471 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500472 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000473 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500474 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000475 REPORTER_ASSERT(reporter, 1 == count(stack));
476 }
477
478 // Adding a new rect with the replace operator should not increase
479 // the stack depth. BW replacing AA replacing BW.
480 {
481 SkClipStack stack;
482 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500483 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000484 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500485 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000486 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500487 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000488 REPORTER_ASSERT(reporter, 1 == count(stack));
489 }
490
491 // Make sure replace clip rects don't collapse too much.
492 {
493 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500494 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
495 stack.clipRect(rect2, SkMatrix::I(), kIntersect_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000496 REPORTER_ASSERT(reporter, 1 == count(stack));
497
498 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500499 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000500 REPORTER_ASSERT(reporter, 2 == count(stack));
501 stack.getBounds(&bound, &type, &isIntersectionOfRects);
502 REPORTER_ASSERT(reporter, bound == rect);
503 stack.restore();
504 REPORTER_ASSERT(reporter, 1 == count(stack));
505
506 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500507 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
508 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000509 REPORTER_ASSERT(reporter, 2 == count(stack));
510 stack.restore();
511 REPORTER_ASSERT(reporter, 1 == count(stack));
512
513 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500514 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
515 stack.clipRect(rect2, SkMatrix::I(), kIntersect_SkClipOp, false);
516 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000517 REPORTER_ASSERT(reporter, 2 == count(stack));
518 stack.restore();
519 REPORTER_ASSERT(reporter, 1 == count(stack));
520 }
521}
522
523// Simplified path-based version of test_rect_replace.
524static void test_path_replace(skiatest::Reporter* reporter) {
525 SkRect rect = SkRect::MakeWH(100, 100);
526 SkPath path;
527 path.addCircle(50, 50, 50);
528
529 // Replace operation doesn't grow the stack.
530 {
531 SkClipStack stack;
532 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500533 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000534 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500535 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000536 REPORTER_ASSERT(reporter, 1 == count(stack));
537 }
538
539 // Replacing rect with path.
540 {
541 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500542 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000543 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500544 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000545 REPORTER_ASSERT(reporter, 1 == count(stack));
546 }
547}
548
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000549// Test out SkClipStack's merging of rect clips. In particular exercise
550// merging of aa vs. bw rects.
551static void test_rect_merging(skiatest::Reporter* reporter) {
552
553 SkRect overlapLeft = SkRect::MakeLTRB(10, 10, 50, 50);
554 SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80);
555
556 SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90);
557 SkRect nestedChild = SkRect::MakeLTRB(40, 40, 60, 60);
558
559 SkRect bound;
560 SkClipStack::BoundsType type;
561 bool isIntersectionOfRects;
562
563 // all bw overlapping - should merge
564 {
565 SkClipStack stack;
566
Mike Reedc1f77742016-12-09 09:00:50 -0500567 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000568
Mike Reedc1f77742016-12-09 09:00:50 -0500569 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000570
571 REPORTER_ASSERT(reporter, 1 == count(stack));
572
573 stack.getBounds(&bound, &type, &isIntersectionOfRects);
574
575 REPORTER_ASSERT(reporter, isIntersectionOfRects);
576 }
577
578 // all aa overlapping - should merge
579 {
580 SkClipStack stack;
581
Mike Reedc1f77742016-12-09 09:00:50 -0500582 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000583
Mike Reedc1f77742016-12-09 09:00:50 -0500584 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, 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;
596
Mike Reedc1f77742016-12-09 09:00:50 -0500597 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000598
Mike Reedc1f77742016-12-09 09:00:50 -0500599 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000600
601 REPORTER_ASSERT(reporter, 2 == count(stack));
602
603 stack.getBounds(&bound, &type, &isIntersectionOfRects);
604
605 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
606 }
607
608 // mixed nested (bw inside aa) - should merge
609 {
610 SkClipStack stack;
611
Mike Reedc1f77742016-12-09 09:00:50 -0500612 stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000613
Mike Reedc1f77742016-12-09 09:00:50 -0500614 stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000615
616 REPORTER_ASSERT(reporter, 1 == count(stack));
617
618 stack.getBounds(&bound, &type, &isIntersectionOfRects);
619
620 REPORTER_ASSERT(reporter, isIntersectionOfRects);
621 }
622
623 // mixed nested (aa inside bw) - should merge
624 {
625 SkClipStack stack;
626
Mike Reedc1f77742016-12-09 09:00:50 -0500627 stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000628
Mike Reedc1f77742016-12-09 09:00:50 -0500629 stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000630
631 REPORTER_ASSERT(reporter, 1 == count(stack));
632
633 stack.getBounds(&bound, &type, &isIntersectionOfRects);
634
635 REPORTER_ASSERT(reporter, isIntersectionOfRects);
636 }
637
638 // reverse nested (aa inside bw) - should _not_ merge
639 {
640 SkClipStack stack;
641
Mike Reedc1f77742016-12-09 09:00:50 -0500642 stack.clipRect(nestedChild, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000643
Mike Reedc1f77742016-12-09 09:00:50 -0500644 stack.clipRect(nestedParent, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000645
646 REPORTER_ASSERT(reporter, 2 == count(stack));
647
648 stack.getBounds(&bound, &type, &isIntersectionOfRects);
649
650 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
651 }
652}
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000653
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000654static void test_quickContains(skiatest::Reporter* reporter) {
655 SkRect testRect = SkRect::MakeLTRB(10, 10, 40, 40);
656 SkRect insideRect = SkRect::MakeLTRB(20, 20, 30, 30);
657 SkRect intersectingRect = SkRect::MakeLTRB(25, 25, 50, 50);
658 SkRect outsideRect = SkRect::MakeLTRB(0, 0, 50, 50);
659 SkRect nonIntersectingRect = SkRect::MakeLTRB(100, 100, 110, 110);
660
661 SkPath insideCircle;
662 insideCircle.addCircle(25, 25, 5);
663 SkPath intersectingCircle;
664 intersectingCircle.addCircle(25, 40, 10);
665 SkPath outsideCircle;
666 outsideCircle.addCircle(25, 25, 50);
667 SkPath nonIntersectingCircle;
668 nonIntersectingCircle.addCircle(100, 100, 5);
669
670 {
671 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500672 stack.clipRect(outsideRect, SkMatrix::I(), kDifference_SkClipOp, false);
673 // return false because quickContains currently does not care for kDifference_SkClipOp
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000674 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
675 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000676
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000677 // Replace Op tests
678 {
679 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500680 stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000681 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
682 }
683
684 {
685 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500686 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000687 stack.save(); // To prevent in-place substitution by replace OP
Mike Reedc1f77742016-12-09 09:00:50 -0500688 stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000689 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
690 stack.restore();
691 }
692
693 {
694 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500695 stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000696 stack.save(); // To prevent in-place substitution by replace OP
Mike Reedc1f77742016-12-09 09:00:50 -0500697 stack.clipRect(insideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000698 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
699 stack.restore();
700 }
701
702 // Verify proper traversal of multi-element clip
703 {
704 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500705 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000706 // Use a path for second clip to prevent in-place intersection
Mike Reedc1f77742016-12-09 09:00:50 -0500707 stack.clipPath(outsideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000708 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
709 }
710
711 // Intersect Op tests with rectangles
712 {
713 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500714 stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000715 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
716 }
717
718 {
719 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500720 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000721 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
722 }
723
724 {
725 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500726 stack.clipRect(intersectingRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000727 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
728 }
729
730 {
731 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500732 stack.clipRect(nonIntersectingRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000733 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
734 }
735
736 // Intersect Op tests with circle paths
737 {
738 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500739 stack.clipPath(outsideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000740 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
741 }
742
743 {
744 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500745 stack.clipPath(insideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000746 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
747 }
748
749 {
750 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500751 stack.clipPath(intersectingCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000752 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
753 }
754
755 {
756 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500757 stack.clipPath(nonIntersectingCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000758 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
759 }
760
761 // Intersect Op tests with inverse filled rectangles
762 {
763 SkClipStack stack;
764 SkPath path;
765 path.addRect(outsideRect);
766 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500767 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000768 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
769 }
770
771 {
772 SkClipStack stack;
773 SkPath path;
774 path.addRect(insideRect);
775 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500776 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000777 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
778 }
779
780 {
781 SkClipStack stack;
782 SkPath path;
783 path.addRect(intersectingRect);
784 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500785 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000786 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
787 }
788
789 {
790 SkClipStack stack;
791 SkPath path;
792 path.addRect(nonIntersectingRect);
793 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500794 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000795 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
796 }
797
798 // Intersect Op tests with inverse filled circles
799 {
800 SkClipStack stack;
801 SkPath path = outsideCircle;
802 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500803 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, 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 = insideCircle;
810 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500811 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, 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 = intersectingCircle;
818 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500819 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000820 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
821 }
822
823 {
824 SkClipStack stack;
825 SkPath path = nonIntersectingCircle;
826 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500827 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000828 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
829 }
830}
831
csmartdaltond50e2402016-07-22 08:39:06 -0700832static void set_region_to_stack(const SkClipStack& stack, const SkIRect& bounds, SkRegion* region) {
833 region->setRect(bounds);
834 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
835 while (const SkClipStack::Element *element = iter.next()) {
836 SkRegion elemRegion;
837 SkRegion boundsRgn(bounds);
838 SkPath path;
839
Brian Salomonf3b46e52017-08-30 11:37:57 -0400840 switch (element->getDeviceSpaceType()) {
841 case SkClipStack::Element::DeviceSpaceType::kEmpty:
csmartdaltond50e2402016-07-22 08:39:06 -0700842 elemRegion.setEmpty();
843 break;
844 default:
Brian Salomonf3b46e52017-08-30 11:37:57 -0400845 element->asDeviceSpacePath(&path);
csmartdaltond50e2402016-07-22 08:39:06 -0700846 elemRegion.setPath(path, boundsRgn);
847 break;
848 }
reed73603f32016-09-20 08:42:38 -0700849 region->op(elemRegion, (SkRegion::Op)element->getOp());
csmartdaltond50e2402016-07-22 08:39:06 -0700850 }
851}
852
853static void test_invfill_diff_bug(skiatest::Reporter* reporter) {
854 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500855 stack.clipRect({10, 10, 20, 20}, SkMatrix::I(), kIntersect_SkClipOp, false);
csmartdaltond50e2402016-07-22 08:39:06 -0700856
857 SkPath path;
858 path.addRect({30, 10, 40, 20});
Mike Reed7d34dc72019-11-26 12:17:17 -0500859 path.setFillType(SkPathFillType::kInverseWinding);
Mike Reedc1f77742016-12-09 09:00:50 -0500860 stack.clipPath(path, SkMatrix::I(), kDifference_SkClipOp, false);
csmartdaltond50e2402016-07-22 08:39:06 -0700861
862 REPORTER_ASSERT(reporter, SkClipStack::kEmptyGenID == stack.getTopmostGenID());
863
864 SkRect stackBounds;
865 SkClipStack::BoundsType stackBoundsType;
866 stack.getBounds(&stackBounds, &stackBoundsType);
867
868 REPORTER_ASSERT(reporter, stackBounds.isEmpty());
869 REPORTER_ASSERT(reporter, SkClipStack::kNormal_BoundsType == stackBoundsType);
870
871 SkRegion region;
872 set_region_to_stack(stack, {0, 0, 50, 30}, &region);
873
874 REPORTER_ASSERT(reporter, region.isEmpty());
875}
876
bsalomon@google.com51a62862012-11-26 21:19:43 +0000877///////////////////////////////////////////////////////////////////////////////////////////////////
878
bsalomon@google.com705e8402012-11-27 15:43:57 +0000879// Functions that add a shape to the clip stack. The shape is computed from a rectangle.
880// AA is always disabled since the clip stack reducer can cause changes in aa rasterization of the
881// stack. A fractional edge repeated in different elements may be rasterized fewer times using the
882// reduced stack.
883typedef void (*AddElementFunc) (const SkRect& rect,
884 bool invert,
Mike Reedc1f77742016-12-09 09:00:50 -0500885 SkClipOp op,
csmartdaltoncbecb082016-07-22 08:59:08 -0700886 SkClipStack* stack,
887 bool doAA);
bsalomon@google.coma4e13c82012-11-26 21:38:37 +0000888
Mike Reedc1f77742016-12-09 09:00:50 -0500889static void add_round_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700890 bool doAA) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000891 SkScalar rx = rect.width() / 10;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000892 SkScalar ry = rect.height() / 20;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000893 if (invert) {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000894 SkPath path;
895 path.addRoundRect(rect, rx, ry);
Mike Reed7d34dc72019-11-26 12:17:17 -0500896 path.setFillType(SkPathFillType::kInverseWinding);
Brian Salomona3b45d42016-10-03 11:36:16 -0400897 stack->clipPath(path, SkMatrix::I(), op, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000898 } else {
899 SkRRect rrect;
900 rrect.setRectXY(rect, rx, ry);
Brian Salomona3b45d42016-10-03 11:36:16 -0400901 stack->clipRRect(rrect, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000902 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000903};
904
Mike Reedc1f77742016-12-09 09:00:50 -0500905static void add_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700906 bool doAA) {
bsalomon@google.com705e8402012-11-27 15:43:57 +0000907 if (invert) {
908 SkPath path;
909 path.addRect(rect);
Mike Reed7d34dc72019-11-26 12:17:17 -0500910 path.setFillType(SkPathFillType::kInverseWinding);
Brian Salomona3b45d42016-10-03 11:36:16 -0400911 stack->clipPath(path, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000912 } else {
Brian Salomona3b45d42016-10-03 11:36:16 -0400913 stack->clipRect(rect, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000914 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000915};
916
Mike Reedc1f77742016-12-09 09:00:50 -0500917static void add_oval(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700918 bool doAA) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000919 SkPath path;
920 path.addOval(rect);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000921 if (invert) {
Mike Reed7d34dc72019-11-26 12:17:17 -0500922 path.setFillType(SkPathFillType::kInverseWinding);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000923 }
Brian Salomona3b45d42016-10-03 11:36:16 -0400924 stack->clipPath(path, SkMatrix::I(), op, doAA);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000925};
926
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000927static void add_elem_to_stack(const SkClipStack::Element& element, SkClipStack* stack) {
Brian Salomonf3b46e52017-08-30 11:37:57 -0400928 switch (element.getDeviceSpaceType()) {
929 case SkClipStack::Element::DeviceSpaceType::kRect:
930 stack->clipRect(element.getDeviceSpaceRect(), SkMatrix::I(), element.getOp(),
931 element.isAA());
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000932 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400933 case SkClipStack::Element::DeviceSpaceType::kRRect:
934 stack->clipRRect(element.getDeviceSpaceRRect(), SkMatrix::I(), element.getOp(),
935 element.isAA());
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000936 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400937 case SkClipStack::Element::DeviceSpaceType::kPath:
938 stack->clipPath(element.getDeviceSpacePath(), SkMatrix::I(), element.getOp(),
939 element.isAA());
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000940 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400941 case SkClipStack::Element::DeviceSpaceType::kEmpty:
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000942 SkDEBUGFAIL("Why did the reducer produce an explicit empty.");
943 stack->clipEmpty();
944 break;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000945 }
946}
947
bsalomon@google.com51a62862012-11-26 21:19:43 +0000948static void test_reduced_clip_stack(skiatest::Reporter* reporter) {
949 // We construct random clip stacks, reduce them, and then rasterize both versions to verify that
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000950 // they are equal.
bsalomon@google.com51a62862012-11-26 21:19:43 +0000951
952 // All the clip elements will be contained within these bounds.
csmartdaltond211e782016-08-15 11:17:19 -0700953 static const SkIRect kIBounds = SkIRect::MakeWH(100, 100);
954 static const SkRect kBounds = SkRect::Make(kIBounds);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000955
956 enum {
csmartdaltoncbecb082016-07-22 08:59:08 -0700957 kNumTests = 250,
bsalomon@google.com51a62862012-11-26 21:19:43 +0000958 kMinElemsPerTest = 1,
959 kMaxElemsPerTest = 50,
960 };
961
962 // min/max size of a clip element as a fraction of kBounds.
963 static const SkScalar kMinElemSizeFrac = SK_Scalar1 / 5;
964 static const SkScalar kMaxElemSizeFrac = SK_Scalar1;
965
Mike Reedc1f77742016-12-09 09:00:50 -0500966 static const SkClipOp kOps[] = {
967 kDifference_SkClipOp,
968 kIntersect_SkClipOp,
969 kUnion_SkClipOp,
970 kXOR_SkClipOp,
971 kReverseDifference_SkClipOp,
972 kReplace_SkClipOp,
bsalomon@google.com51a62862012-11-26 21:19:43 +0000973 };
974
975 // Replace operations short-circuit the optimizer. We want to make sure that we test this code
976 // path a little bit but we don't want it to prevent us from testing many longer traversals in
977 // the optimizer.
978 static const int kReplaceDiv = 4 * kMaxElemsPerTest;
979
bsalomon@google.com705e8402012-11-27 15:43:57 +0000980 // We want to test inverse fills. However, they are quite rare in practice so don't over do it.
981 static const SkScalar kFractionInverted = SK_Scalar1 / kMaxElemsPerTest;
982
csmartdaltoncbecb082016-07-22 08:59:08 -0700983 static const SkScalar kFractionAntialiased = 0.25;
984
bsalomon@google.com51a62862012-11-26 21:19:43 +0000985 static const AddElementFunc kElementFuncs[] = {
986 add_rect,
987 add_round_rect,
988 add_oval,
989 };
990
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000991 SkRandom r;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000992
993 for (int i = 0; i < kNumTests; ++i) {
csmartdaltoncbecb082016-07-22 08:59:08 -0700994 SkString testCase;
995 testCase.printf("Iteration %d", i);
996
bsalomon@google.com51a62862012-11-26 21:19:43 +0000997 // Randomly generate a clip stack.
998 SkClipStack stack;
999 int numElems = r.nextRangeU(kMinElemsPerTest, kMaxElemsPerTest);
csmartdaltoncbecb082016-07-22 08:59:08 -07001000 bool doAA = r.nextBiasedBool(kFractionAntialiased);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001001 for (int e = 0; e < numElems; ++e) {
Mike Reedc1f77742016-12-09 09:00:50 -05001002 SkClipOp op = kOps[r.nextULessThan(SK_ARRAY_COUNT(kOps))];
1003 if (op == kReplace_SkClipOp) {
bsalomon@google.com51a62862012-11-26 21:19:43 +00001004 if (r.nextU() % kReplaceDiv) {
1005 --e;
1006 continue;
1007 }
1008 }
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +00001009
bsalomon@google.com51a62862012-11-26 21:19:43 +00001010 // saves can change the clip stack behavior when an element is added.
1011 bool doSave = r.nextBool();
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +00001012
bsalomon@google.com51a62862012-11-26 21:19:43 +00001013 SkSize size = SkSize::Make(
Mike Reeddf85c382017-02-14 10:59:19 -05001014 kBounds.width() * r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac),
1015 kBounds.height() * r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac));
bsalomon@google.com51a62862012-11-26 21:19:43 +00001016
csmartdaltoncbecb082016-07-22 08:59:08 -07001017 SkPoint xy = {r.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth),
1018 r.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight)};
bsalomon@google.com51a62862012-11-26 21:19:43 +00001019
csmartdaltoncbecb082016-07-22 08:59:08 -07001020 SkRect rect;
1021 if (doAA) {
1022 rect.setXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight);
1023 if (GrClip::IsPixelAligned(rect)) {
1024 // Don't create an element that may accidentally become not antialiased.
1025 rect.outset(0.5f, 0.5f);
1026 }
1027 SkASSERT(!GrClip::IsPixelAligned(rect));
1028 } else {
1029 rect.setXYWH(SkScalarFloorToScalar(xy.fX),
1030 SkScalarFloorToScalar(xy.fY),
1031 SkScalarCeilToScalar(size.fWidth),
1032 SkScalarCeilToScalar(size.fHeight));
1033 }
bsalomon@google.com51a62862012-11-26 21:19:43 +00001034
bsalomon@google.com705e8402012-11-27 15:43:57 +00001035 bool invert = r.nextBiasedBool(kFractionInverted);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001036
csmartdaltoncbecb082016-07-22 08:59:08 -07001037 kElementFuncs[r.nextULessThan(SK_ARRAY_COUNT(kElementFuncs))](rect, invert, op, &stack,
1038 doAA);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001039 if (doSave) {
1040 stack.save();
1041 }
1042 }
1043
Brian Salomon14471772017-12-05 10:35:15 -05001044 auto context = GrContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001045 const GrCaps* caps = context->priv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001046
Brian Salomonc3833b42018-07-09 18:23:58 +00001047 // Zero the memory we will new the GrReducedClip into. This ensures the elements gen ID
1048 // will be kInvalidGenID if left uninitialized.
1049 SkAlignedSTStorage<1, GrReducedClip> storage;
1050 memset(storage.get(), 0, sizeof(GrReducedClip));
Brian Salomon4dea72a2019-12-18 10:43:10 -05001051 static_assert(0 == SkClipStack::kInvalidGenID);
Brian Salomonc3833b42018-07-09 18:23:58 +00001052
csmartdalton77f2fae2016-08-08 09:55:06 -07001053 // Get the reduced version of the stack.
csmartdaltoncbecb082016-07-22 08:59:08 -07001054 SkRect queryBounds = kBounds;
1055 queryBounds.outset(kBounds.width() / 2, kBounds.height() / 2);
Ethan Nicholaseace9352018-10-15 20:09:54 +00001056 const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, queryBounds, caps);
bsalomon@google.coma4444302012-12-04 15:22:12 +00001057
Brian Salomonc3833b42018-07-09 18:23:58 +00001058 REPORTER_ASSERT(reporter,
1059 reduced->maskElements().isEmpty() ||
1060 SkClipStack::kInvalidGenID != reduced->maskGenID(),
1061 testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001062
Brian Salomonc3833b42018-07-09 18:23:58 +00001063 if (!reduced->maskElements().isEmpty()) {
1064 REPORTER_ASSERT(reporter, reduced->hasScissor(), testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001065 SkRect stackBounds;
1066 SkClipStack::BoundsType stackBoundsType;
1067 stack.getBounds(&stackBounds, &stackBoundsType);
Brian Salomonc3833b42018-07-09 18:23:58 +00001068 REPORTER_ASSERT(reporter, reduced->maskRequiresAA() == doAA, testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001069 }
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001070
bsalomon@google.com51a62862012-11-26 21:19:43 +00001071 // Build a new clip stack based on the reduced clip elements
1072 SkClipStack reducedStack;
Brian Salomonc3833b42018-07-09 18:23:58 +00001073 if (GrReducedClip::InitialState::kAllOut == reduced->initialState()) {
bsalomon@google.com51a62862012-11-26 21:19:43 +00001074 // whether the result is bounded or not, the whole plane should start outside the clip.
1075 reducedStack.clipEmpty();
1076 }
Brian Salomonc3833b42018-07-09 18:23:58 +00001077 for (ElementList::Iter iter(reduced->maskElements()); iter.get(); iter.next()) {
1078 add_elem_to_stack(*iter.get(), &reducedStack);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001079 }
bsalomon@google.com51a62862012-11-26 21:19:43 +00001080
Brian Salomonc3833b42018-07-09 18:23:58 +00001081 SkIRect scissor = reduced->hasScissor() ? reduced->scissor() : kIBounds;
csmartdaltond211e782016-08-15 11:17:19 -07001082
bsalomon@google.com34cd70a2012-12-06 14:23:20 +00001083 // GrReducedClipStack assumes that the final result is clipped to the returned bounds
Chris Dalton79471932017-10-27 01:50:57 -06001084 reducedStack.clipDevRect(scissor, kIntersect_SkClipOp);
1085 stack.clipDevRect(scissor, kIntersect_SkClipOp);
bsalomon@google.com34cd70a2012-12-06 14:23:20 +00001086
bsalomon@google.com51a62862012-11-26 21:19:43 +00001087 // convert both the original stack and reduced stack to SkRegions and see if they're equal
bsalomon@google.com51a62862012-11-26 21:19:43 +00001088 SkRegion region;
Chris Dalton79471932017-10-27 01:50:57 -06001089 set_region_to_stack(stack, scissor, &region);
csmartdaltond50e2402016-07-22 08:39:06 -07001090
bsalomon@google.com51a62862012-11-26 21:19:43 +00001091 SkRegion reducedRegion;
Chris Dalton79471932017-10-27 01:50:57 -06001092 set_region_to_stack(reducedStack, scissor, &reducedRegion);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001093
Brian Salomon1c80e992018-01-29 09:50:47 -05001094 REPORTER_ASSERT(reporter, region == reducedRegion, testCase.c_str());
Brian Salomonc3833b42018-07-09 18:23:58 +00001095
1096 reduced->~GrReducedClip();
bsalomon@google.com51a62862012-11-26 21:19:43 +00001097 }
1098}
1099
halcanary4dbbd042016-06-07 17:21:10 -07001100#ifdef SK_BUILD_FOR_WIN
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001101 #define SUPPRESS_VISIBILITY_WARNING
1102#else
1103 #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden")))
1104#endif
1105
Brian Salomonc3833b42018-07-09 18:23:58 +00001106static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) {
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001107 {
1108 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001109 stack.clipRect(SkRect::MakeXYWH(0, 0, 100, 100), SkMatrix::I(), kReplace_SkClipOp,
Brian Salomona3b45d42016-10-03 11:36:16 -04001110 true);
1111 stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(50.3), SkScalar(50.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001112 kReplace_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001113 SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001114
Brian Salomon14471772017-12-05 10:35:15 -05001115 auto context = GrContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001116 const GrCaps* caps = context->priv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001117
Brian Salomonc3833b42018-07-09 18:23:58 +00001118 SkAlignedSTStorage<1, GrReducedClip> storage;
1119 memset(storage.get(), 0, sizeof(GrReducedClip));
Brian Salomon4dea72a2019-12-18 10:43:10 -05001120 static_assert(0 == SkClipStack::kInvalidGenID);
Ethan Nicholaseace9352018-10-15 20:09:54 +00001121 const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, bounds, caps);
Brian Salomonc3833b42018-07-09 18:23:58 +00001122
1123 REPORTER_ASSERT(reporter, reduced->maskElements().count() == 1);
1124 // Clips will be cached based on the generation id. Make sure the gen id is valid.
1125 REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reduced->maskGenID());
1126
1127 reduced->~GrReducedClip();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001128 }
1129 {
1130 SkClipStack stack;
1131
1132 // Create a clip with following 25.3, 25.3 boxes which are 25 apart:
1133 // A B
1134 // C D
1135
Brian Salomona3b45d42016-10-03 11:36:16 -04001136 stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001137 kReplace_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001138 uint32_t genIDA = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001139 stack.clipRect(SkRect::MakeXYWH(50, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001140 kUnion_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001141 uint32_t genIDB = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001142 stack.clipRect(SkRect::MakeXYWH(0, 50, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001143 kUnion_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001144 uint32_t genIDC = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001145 stack.clipRect(SkRect::MakeXYWH(50, 50, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001146 kUnion_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001147 uint32_t genIDD = stack.getTopmostGenID();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001148
1149
csmartdaltoncbecb082016-07-22 08:59:08 -07001150#define IXYWH SkIRect::MakeXYWH
1151#define XYWH SkRect::MakeXYWH
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001152
csmartdaltoncbecb082016-07-22 08:59:08 -07001153 SkIRect stackBounds = IXYWH(0, 0, 76, 76);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001154
1155 // The base test is to test each rect in two ways:
1156 // 1) The box dimensions. (Should reduce to "all in", no elements).
1157 // 2) A bit over the box dimensions.
1158 // In the case 2, test that the generation id is what is expected.
1159 // The rects are of fractional size so that case 2 never gets optimized to an empty element
1160 // list.
1161
1162 // Not passing in tighter bounds is tested for consistency.
1163 static const struct SUPPRESS_VISIBILITY_WARNING {
csmartdaltoncbecb082016-07-22 08:59:08 -07001164 SkRect testBounds;
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001165 int reducedClipCount;
Brian Salomonc3833b42018-07-09 18:23:58 +00001166 uint32_t reducedGenID;
csmartdaltoncbecb082016-07-22 08:59:08 -07001167 InitialState initialState;
1168 SkIRect clipIRect;
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001169 // parameter.
1170 } testCases[] = {
csmartdalton77f2fae2016-08-08 09:55:06 -07001171
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001172 // Rect A.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001173 { XYWH(0, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 0, 25, 25) },
1174 { 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 -06001175 { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::InitialState::kAllOut, IXYWH(0, 0, 26, 26)},
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001176
1177 // Rect B.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001178 { XYWH(50, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 25, 25) },
1179 { XYWH(50, 0, 25.3f, 25.3f), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 26, 26) },
csmartdalton77f2fae2016-08-08 09:55:06 -07001180 { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::InitialState::kAllOut, IXYWH(50, 0, 26, 27) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001181
1182 // Rect C.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001183 { XYWH(0, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 50, 25, 25) },
1184 { 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 -07001185 { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::InitialState::kAllOut, IXYWH(0, 50, 27, 26) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001186
1187 // Rect D.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001188 { XYWH(50, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 25, 25)},
1189 { XYWH(50.3f, 50.3f, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 26, 26)},
csmartdalton77f2fae2016-08-08 09:55:06 -07001190 { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(50, 50, 26, 26)},
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001191
1192 // Other tests:
csmartdalton77f2fae2016-08-08 09:55:06 -07001193 { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::InitialState::kAllOut, stackBounds },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001194
1195 // Rect in the middle, touches none.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001196 { XYWH(26, 26, 24, 24), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllOut, IXYWH(26, 26, 24, 24) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001197
1198 // Rect in the middle, touches all the rects. GenID is the last rect.
csmartdalton77f2fae2016-08-08 09:55:06 -07001199 { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(24, 24, 27, 27) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001200 };
1201
1202#undef XYWH
csmartdaltoncbecb082016-07-22 08:59:08 -07001203#undef IXYWH
Brian Salomon14471772017-12-05 10:35:15 -05001204 auto context = GrContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001205 const GrCaps* caps = context->priv().caps();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001206
1207 for (size_t i = 0; i < SK_ARRAY_COUNT(testCases); ++i) {
Ethan Nicholaseace9352018-10-15 20:09:54 +00001208 const GrReducedClip reduced(stack, testCases[i].testBounds, caps);
Chris Dalton79471932017-10-27 01:50:57 -06001209 REPORTER_ASSERT(reporter, reduced.maskElements().count() ==
1210 testCases[i].reducedClipCount);
Brian Salomonc3833b42018-07-09 18:23:58 +00001211 SkASSERT(reduced.maskElements().count() == testCases[i].reducedClipCount);
1212 if (reduced.maskElements().count()) {
1213 REPORTER_ASSERT(reporter, reduced.maskGenID() == testCases[i].reducedGenID);
1214 SkASSERT(reduced.maskGenID() == testCases[i].reducedGenID);
csmartdalton8d3f92a2016-08-17 09:39:38 -07001215 }
csmartdalton77f2fae2016-08-08 09:55:06 -07001216 REPORTER_ASSERT(reporter, reduced.initialState() == testCases[i].initialState);
Brian Salomonc3833b42018-07-09 18:23:58 +00001217 SkASSERT(reduced.initialState() == testCases[i].initialState);
Chris Dalton79471932017-10-27 01:50:57 -06001218 REPORTER_ASSERT(reporter, reduced.hasScissor());
Brian Salomonc3833b42018-07-09 18:23:58 +00001219 SkASSERT(reduced.hasScissor());
Chris Dalton79471932017-10-27 01:50:57 -06001220 REPORTER_ASSERT(reporter, reduced.scissor() == testCases[i].clipIRect);
Brian Salomonc3833b42018-07-09 18:23:58 +00001221 SkASSERT(reduced.scissor() == testCases[i].clipIRect);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001222 }
1223 }
1224}
1225
1226static void test_reduced_clip_stack_no_aa_crash(skiatest::Reporter* reporter) {
1227 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001228 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 100, 100), kReplace_SkClipOp);
1229 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 50, 50), kReplace_SkClipOp);
csmartdaltoncbecb082016-07-22 08:59:08 -07001230 SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001231
Brian Salomon14471772017-12-05 10:35:15 -05001232 auto context = GrContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001233 const GrCaps* caps = context->priv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001234
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001235 // At the time, this would crash.
Ethan Nicholaseace9352018-10-15 20:09:54 +00001236 const GrReducedClip reduced(stack, bounds, caps);
Chris Dalton79471932017-10-27 01:50:57 -06001237 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001238}
1239
csmartdaltoncbecb082016-07-22 08:59:08 -07001240enum class ClipMethod {
1241 kSkipDraw,
1242 kIgnoreClip,
1243 kScissor,
1244 kAAElements
1245};
1246
1247static void test_aa_query(skiatest::Reporter* reporter, const SkString& testName,
1248 const SkClipStack& stack, const SkMatrix& queryXform,
1249 const SkRect& preXformQuery, ClipMethod expectedMethod,
1250 int numExpectedElems = 0) {
Brian Salomon14471772017-12-05 10:35:15 -05001251 auto context = GrContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001252 const GrCaps* caps = context->priv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001253
csmartdaltoncbecb082016-07-22 08:59:08 -07001254 SkRect queryBounds;
1255 queryXform.mapRect(&queryBounds, preXformQuery);
Ethan Nicholaseace9352018-10-15 20:09:54 +00001256 const GrReducedClip reduced(stack, queryBounds, caps);
csmartdaltoncbecb082016-07-22 08:59:08 -07001257
1258 SkClipStack::BoundsType stackBoundsType;
1259 SkRect stackBounds;
1260 stack.getBounds(&stackBounds, &stackBoundsType);
1261
1262 switch (expectedMethod) {
1263 case ClipMethod::kSkipDraw:
1264 SkASSERT(0 == numExpectedElems);
Brian Salomon1c80e992018-01-29 09:50:47 -05001265 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty(), testName.c_str());
1266 REPORTER_ASSERT(reporter,
1267 GrReducedClip::InitialState::kAllOut == reduced.initialState(),
1268 testName.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001269 return;
1270 case ClipMethod::kIgnoreClip:
1271 SkASSERT(0 == numExpectedElems);
Brian Salomon1c80e992018-01-29 09:50:47 -05001272 REPORTER_ASSERT(
1273 reporter,
1274 !reduced.hasScissor() || GrClip::IsInsideClip(reduced.scissor(), queryBounds),
1275 testName.c_str());
1276 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty(), testName.c_str());
1277 REPORTER_ASSERT(reporter,
1278 GrReducedClip::InitialState::kAllIn == reduced.initialState(),
1279 testName.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001280 return;
1281 case ClipMethod::kScissor: {
1282 SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType);
1283 SkASSERT(0 == numExpectedElems);
1284 SkIRect expectedScissor;
1285 stackBounds.round(&expectedScissor);
Brian Salomon1c80e992018-01-29 09:50:47 -05001286 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty(), testName.c_str());
1287 REPORTER_ASSERT(reporter, reduced.hasScissor(), testName.c_str());
1288 REPORTER_ASSERT(reporter, expectedScissor == reduced.scissor(), 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 }
1294 case ClipMethod::kAAElements: {
1295 SkIRect expectedClipIBounds = GrClip::GetPixelIBounds(queryBounds);
1296 if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
1297 SkAssertResult(expectedClipIBounds.intersect(GrClip::GetPixelIBounds(stackBounds)));
1298 }
Brian Salomon1c80e992018-01-29 09:50:47 -05001299 REPORTER_ASSERT(reporter, numExpectedElems == reduced.maskElements().count(),
1300 testName.c_str());
1301 REPORTER_ASSERT(reporter, reduced.hasScissor(), testName.c_str());
1302 REPORTER_ASSERT(reporter, expectedClipIBounds == reduced.scissor(), testName.c_str());
1303 REPORTER_ASSERT(reporter,
1304 reduced.maskElements().isEmpty() || reduced.maskRequiresAA(),
1305 testName.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001306 break;
1307 }
1308 }
1309}
1310
1311static void test_reduced_clip_stack_aa(skiatest::Reporter* reporter) {
1312 constexpr SkScalar IL = 2, IT = 1, IR = 6, IB = 7; // Pixel aligned rect.
1313 constexpr SkScalar L = 2.2f, T = 1.7f, R = 5.8f, B = 7.3f; // Generic rect.
1314 constexpr SkScalar l = 3.3f, t = 2.8f, r = 4.7f, b = 6.2f; // Small rect contained in R.
1315
1316 SkRect alignedRect = {IL, IT, IR, IB};
1317 SkRect rect = {L, T, R, B};
1318 SkRect innerRect = {l, t, r, b};
1319
1320 SkMatrix m;
1321 m.setIdentity();
1322
1323 constexpr SkScalar kMinScale = 2.0001f;
1324 constexpr SkScalar kMaxScale = 3;
1325 constexpr int kNumIters = 8;
1326
1327 SkString name;
1328 SkRandom rand;
1329
1330 for (int i = 0; i < kNumIters; ++i) {
1331 // Pixel-aligned rect (iior=true).
1332 name.printf("Pixel-aligned rect test, iter %i", i);
1333 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001334 stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001335 test_aa_query(reporter, name, stack, m, {IL, IT, IR, IB}, ClipMethod::kIgnoreClip);
1336 test_aa_query(reporter, name, stack, m, {IL, IT-1, IR, IT}, ClipMethod::kSkipDraw);
csmartdalton77f2fae2016-08-08 09:55:06 -07001337 test_aa_query(reporter, name, stack, m, {IL, IT-2, IR, IB}, ClipMethod::kScissor);
csmartdaltoncbecb082016-07-22 08:59:08 -07001338
1339 // Rect (iior=true).
1340 name.printf("Rect test, iter %i", i);
1341 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001342 stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001343 test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kIgnoreClip);
1344 test_aa_query(reporter, name, stack, m, {L-.1f, T, L, B}, ClipMethod::kSkipDraw);
1345 test_aa_query(reporter, name, stack, m, {L-.1f, T, L+.1f, B}, ClipMethod::kAAElements, 1);
1346
1347 // Difference rect (iior=false, inside-out bounds).
1348 name.printf("Difference rect test, iter %i", i);
1349 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001350 stack.clipRect(rect, SkMatrix::I(), kDifference_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001351 test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kSkipDraw);
1352 test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T}, ClipMethod::kIgnoreClip);
1353 test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T+.1f}, ClipMethod::kAAElements, 1);
1354
1355 // Complex clip (iior=false, normal bounds).
1356 name.printf("Complex clip test, iter %i", i);
1357 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001358 stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true);
1359 stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001360 test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
1361 test_aa_query(reporter, name, stack, m, {r-.1f, t, R, b}, ClipMethod::kAAElements, 1);
1362 test_aa_query(reporter, name, stack, m, {r-.1f, t, R+.1f, b}, ClipMethod::kAAElements, 2);
1363 test_aa_query(reporter, name, stack, m, {r, t, R+.1f, b}, ClipMethod::kAAElements, 1);
1364 test_aa_query(reporter, name, stack, m, {r, t, R, b}, ClipMethod::kIgnoreClip);
1365 test_aa_query(reporter, name, stack, m, {R, T, R+.1f, B}, ClipMethod::kSkipDraw);
1366
1367 // Complex clip where outer rect is pixel aligned (iior=false, normal bounds).
1368 name.printf("Aligned Complex clip test, iter %i", i);
1369 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001370 stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true);
1371 stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, 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, {l, b-.1f, r, IB}, ClipMethod::kAAElements, 1);
1374 test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB+.1f}, ClipMethod::kAAElements, 1);
1375 test_aa_query(reporter, name, stack, m, {l, b, r, IB+.1f}, ClipMethod::kAAElements, 0);
1376 test_aa_query(reporter, name, stack, m, {l, b, r, IB}, ClipMethod::kIgnoreClip);
1377 test_aa_query(reporter, name, stack, m, {IL, IB, IR, IB+.1f}, ClipMethod::kSkipDraw);
1378
1379 // Apply random transforms and try again. This ensures the clip stack reduction is hardened
1380 // against FP rounding error.
1381 SkScalar sx = rand.nextRangeScalar(kMinScale, kMaxScale);
1382 sx = SkScalarFloorToScalar(sx * alignedRect.width()) / alignedRect.width();
1383 SkScalar sy = rand.nextRangeScalar(kMinScale, kMaxScale);
1384 sy = SkScalarFloorToScalar(sy * alignedRect.height()) / alignedRect.height();
1385 SkScalar tx = SkScalarRoundToScalar(sx * alignedRect.x()) - sx * alignedRect.x();
1386 SkScalar ty = SkScalarRoundToScalar(sy * alignedRect.y()) - sy * alignedRect.y();
1387
1388 SkMatrix xform = SkMatrix::MakeScale(sx, sy);
1389 xform.postTranslate(tx, ty);
1390 xform.mapRect(&alignedRect);
1391 xform.mapRect(&rect);
1392 xform.mapRect(&innerRect);
1393 m.postConcat(xform);
1394 }
1395}
1396
Chris Dalton348060f2017-06-05 13:15:37 -06001397static void test_tiny_query_bounds_assertion_bug(skiatest::Reporter* reporter) {
1398 // https://bugs.chromium.org/p/skia/issues/detail?id=5990
1399 const SkRect clipBounds = SkRect::MakeXYWH(1.5f, 100, 1000, 1000);
1400
1401 SkClipStack rectStack;
1402 rectStack.clipRect(clipBounds, SkMatrix::I(), kIntersect_SkClipOp, true);
1403
1404 SkPath clipPath;
1405 clipPath.moveTo(clipBounds.left(), clipBounds.top());
1406 clipPath.quadTo(clipBounds.right(), clipBounds.top(),
1407 clipBounds.right(), clipBounds.bottom());
1408 clipPath.quadTo(clipBounds.left(), clipBounds.bottom(),
1409 clipBounds.left(), clipBounds.top());
1410 SkClipStack pathStack;
1411 pathStack.clipPath(clipPath, SkMatrix::I(), kIntersect_SkClipOp, true);
1412
Brian Salomon14471772017-12-05 10:35:15 -05001413 auto context = GrContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001414 const GrCaps* caps = context->priv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001415
Chris Dalton348060f2017-06-05 13:15:37 -06001416 for (const SkClipStack& stack : {rectStack, pathStack}) {
1417 for (SkRect queryBounds : {SkRect::MakeXYWH(53, 60, GrClip::kBoundsTolerance, 1000),
1418 SkRect::MakeXYWH(53, 60, GrClip::kBoundsTolerance/2, 1000),
1419 SkRect::MakeXYWH(53, 160, 1000, GrClip::kBoundsTolerance),
1420 SkRect::MakeXYWH(53, 160, 1000, GrClip::kBoundsTolerance/2)}) {
Ethan Nicholaseace9352018-10-15 20:09:54 +00001421 const GrReducedClip reduced(stack, queryBounds, caps);
Chris Dalton79471932017-10-27 01:50:57 -06001422 REPORTER_ASSERT(reporter, !reduced.hasScissor());
1423 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty());
Chris Dalton348060f2017-06-05 13:15:37 -06001424 REPORTER_ASSERT(reporter,
1425 GrReducedClip::InitialState::kAllOut == reduced.initialState());
1426 }
1427 }
1428}
1429
Brian Salomon1c0b05a2019-04-19 15:37:28 -04001430static void test_is_rrect_deep_rect_stack(skiatest::Reporter* reporter) {
1431 static constexpr SkRect kTargetBounds = SkRect::MakeWH(1000, 500);
1432 // All antialiased or all not antialiased.
1433 for (bool aa : {false, true}) {
1434 SkClipStack stack;
1435 for (int i = 0; i <= 100; ++i) {
1436 stack.save();
1437 stack.clipRect(SkRect::MakeLTRB(i, 0.5, kTargetBounds.width(), kTargetBounds.height()),
1438 SkMatrix::I(), SkClipOp::kIntersect, aa);
1439 }
1440 SkRRect rrect;
1441 bool isAA;
1442 SkRRect expected = SkRRect::MakeRect(
1443 SkRect::MakeLTRB(100, 0.5, kTargetBounds.width(), kTargetBounds.height()));
1444 if (stack.isRRect(kTargetBounds, &rrect, &isAA)) {
1445 REPORTER_ASSERT(reporter, rrect == expected);
1446 REPORTER_ASSERT(reporter, aa == isAA);
1447 } else {
1448 ERRORF(reporter, "Expected to be an rrect.");
1449 }
1450 }
1451 // Mixed AA and non-AA without simple containment.
1452 SkClipStack stack;
1453 for (int i = 0; i <= 100; ++i) {
1454 bool aa = i & 0b1;
1455 int j = 100 - i;
1456 stack.save();
1457 stack.clipRect(SkRect::MakeLTRB(i, j + 0.5, kTargetBounds.width(), kTargetBounds.height()),
1458 SkMatrix::I(), SkClipOp::kIntersect, aa);
1459 }
1460 SkRRect rrect;
1461 bool isAA;
1462 REPORTER_ASSERT(reporter, !stack.isRRect(kTargetBounds, &rrect, &isAA));
1463}
1464
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00001465DEF_TEST(ClipStack, reporter) {
reed@google.combdee9fc2011-02-22 20:17:43 +00001466 SkClipStack stack;
1467
robertphillips@google.com80214e22012-07-20 15:33:18 +00001468 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001469 assert_count(reporter, stack, 0);
1470
1471 static const SkIRect gRects[] = {
1472 { 0, 0, 100, 100 },
1473 { 25, 25, 125, 125 },
1474 { 0, 0, 1000, 1000 },
1475 { 0, 0, 75, 75 }
1476 };
1477 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
Mike Reedc1f77742016-12-09 09:00:50 -05001478 stack.clipDevRect(gRects[i], kIntersect_SkClipOp);
reed@google.combdee9fc2011-02-22 20:17:43 +00001479 }
1480
1481 // all of the above rects should have been intersected, leaving only 1 rect
robertphillips@google.com80214e22012-07-20 15:33:18 +00001482 SkClipStack::B2TIter iter(stack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001483 const SkClipStack::Element* element = iter.next();
epoger@google.com2047f002011-05-17 17:36:59 +00001484 SkRect answer;
Mike Reed92b33352019-08-24 19:39:13 -04001485 answer.setLTRB(25, 25, 75, 75);
reed@google.combdee9fc2011-02-22 20:17:43 +00001486
bsalomon49f085d2014-09-05 13:34:00 -07001487 REPORTER_ASSERT(reporter, element);
Brian Salomonf3b46e52017-08-30 11:37:57 -04001488 REPORTER_ASSERT(reporter,
1489 SkClipStack::Element::DeviceSpaceType::kRect == element->getDeviceSpaceType());
Mike Reedc1f77742016-12-09 09:00:50 -05001490 REPORTER_ASSERT(reporter, kIntersect_SkClipOp == element->getOp());
Brian Salomonf3b46e52017-08-30 11:37:57 -04001491 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == answer);
reed@google.combdee9fc2011-02-22 20:17:43 +00001492 // now check that we only had one in our iterator
1493 REPORTER_ASSERT(reporter, !iter.next());
1494
1495 stack.reset();
robertphillips@google.com80214e22012-07-20 15:33:18 +00001496 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001497 assert_count(reporter, stack, 0);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +00001498
1499 test_assign_and_comparison(reporter);
robertphillips@google.com80214e22012-07-20 15:33:18 +00001500 test_iterators(reporter);
Brian Salomonf3b46e52017-08-30 11:37:57 -04001501 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kRect);
1502 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kRRect);
1503 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kPath);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +00001504 test_isWideOpen(reporter);
robertphillips@google.com08eacc12012-08-02 12:49:00 +00001505 test_rect_merging(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001506 test_rect_replace(reporter);
junov@chromium.orgedf32d52012-12-10 14:57:54 +00001507 test_rect_inverse_fill(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001508 test_path_replace(reporter);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +00001509 test_quickContains(reporter);
csmartdaltond50e2402016-07-22 08:39:06 -07001510 test_invfill_diff_bug(reporter);
Brian Osmanc7ad40f2018-05-31 14:27:17 -04001511
bsalomon@google.comedb26fd2012-11-28 14:42:41 +00001512 test_reduced_clip_stack(reporter);
Brian Salomonc3833b42018-07-09 18:23:58 +00001513 test_reduced_clip_stack_genid(reporter);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001514 test_reduced_clip_stack_no_aa_crash(reporter);
csmartdaltoncbecb082016-07-22 08:59:08 -07001515 test_reduced_clip_stack_aa(reporter);
Chris Dalton348060f2017-06-05 13:15:37 -06001516 test_tiny_query_bounds_assertion_bug(reporter);
Brian Salomon1c0b05a2019-04-19 15:37:28 -04001517 test_is_rrect_deep_rect_stack(reporter);
reed@google.combdee9fc2011-02-22 20:17:43 +00001518}
Brian Salomon19f0ed52017-01-06 13:54:58 -05001519
1520//////////////////////////////////////////////////////////////////////////////
1521
Robert Phillips875218e2017-02-24 08:37:13 -05001522sk_sp<GrTextureProxy> GrClipStackClip::testingOnly_createClipMask(GrContext* context) const {
Brian Salomon19f0ed52017-01-06 13:54:58 -05001523 const GrReducedClip reducedClip(*fStack, SkRect::MakeWH(512, 512), 0);
Brian Osman5d034742017-09-11 13:38:55 -04001524 return this->createSoftwareClipMask(context, reducedClip, nullptr);
Brian Salomon19f0ed52017-01-06 13:54:58 -05001525}
1526
1527// Verify that clip masks are freed up when the clip state that generated them goes away.
1528DEF_GPUTEST_FOR_ALL_CONTEXTS(ClipMaskCache, reporter, ctxInfo) {
1529 // This test uses resource key tags which only function in debug builds.
1530#ifdef SK_DEBUG
1531 GrContext* context = ctxInfo.grContext();
1532 SkClipStack stack;
1533
1534 SkPath path;
1535 path.addCircle(10, 10, 8);
1536 path.addCircle(15, 15, 8);
Mike Reed7d34dc72019-11-26 12:17:17 -05001537 path.setFillType(SkPathFillType::kEvenOdd);
Brian Salomon19f0ed52017-01-06 13:54:58 -05001538
Brian Salomonc3833b42018-07-09 18:23:58 +00001539 static const char* kTag = GrClipStackClip::kMaskTestTag;
Robert Phillips9da87e02019-02-04 13:26:26 -05001540 GrResourceCache* cache = context->priv().getResourceCache();
Brian Salomon19f0ed52017-01-06 13:54:58 -05001541
1542 static constexpr int kN = 5;
1543
1544 for (int i = 0; i < kN; ++i) {
1545 SkMatrix m;
1546 m.setTranslate(0.5, 0.5);
1547 stack.save();
1548 stack.clipPath(path, m, SkClipOp::kIntersect, true);
Robert Phillips875218e2017-02-24 08:37:13 -05001549 sk_sp<GrTextureProxy> mask = GrClipStackClip(&stack).testingOnly_createClipMask(context);
Robert Phillips9da87e02019-02-04 13:26:26 -05001550 mask->instantiate(context->priv().resourceProvider());
Brian Salomonfd98c2c2018-07-31 17:25:29 -04001551 GrTexture* tex = mask->peekTexture();
Robert Phillips875218e2017-02-24 08:37:13 -05001552 REPORTER_ASSERT(reporter, 0 == strcmp(tex->getUniqueKey().tag(), kTag));
Brian Salomon19f0ed52017-01-06 13:54:58 -05001553 // Make sure mask isn't pinned in cache.
1554 mask.reset(nullptr);
1555 context->flush();
1556 REPORTER_ASSERT(reporter, i + 1 == cache->countUniqueKeysWithTag(kTag));
1557 }
1558
1559 for (int i = 0; i < kN; ++i) {
1560 stack.restore();
1561 cache->purgeAsNeeded();
1562 REPORTER_ASSERT(reporter, kN - (i + 1) == cache->countUniqueKeysWithTag(kTag));
1563 }
1564#endif
1565}