blob: 70d7dcacee130e8b8c83a92b518458c3219bb8d5 [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
Ben Wagnerb607a8f2018-03-12 13:46:21 -04008#include "SkCanvas.h"
9#include "SkClipOp.h"
10#include "SkClipOpPriv.h"
reed@google.combdee9fc2011-02-22 20:17:43 +000011#include "SkClipStack.h"
Ben Wagnerb607a8f2018-03-12 13:46:21 -040012#include "SkImageInfo.h"
13#include "SkMatrix.h"
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000014#include "SkPath.h"
Ben Wagnerb607a8f2018-03-12 13:46:21 -040015#include "SkPoint.h"
16#include "SkRRect.h"
bsalomon@google.com51a62862012-11-26 21:19:43 +000017#include "SkRandom.h"
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000018#include "SkRect.h"
Ben Wagnerb607a8f2018-03-12 13:46:21 -040019#include "SkRefCnt.h"
bsalomon@google.com51a62862012-11-26 21:19:43 +000020#include "SkRegion.h"
Ben Wagnerb607a8f2018-03-12 13:46:21 -040021#include "SkScalar.h"
22#include "SkSize.h"
23#include "SkString.h"
24#include "SkSurface.h"
25#include "SkTLList.h"
26#include "SkTemplates.h"
27#include "SkTypes.h"
28#include "Test.h"
robertphillips@google.com80214e22012-07-20 15:33:18 +000029
Ben Wagnerb607a8f2018-03-12 13:46:21 -040030#include "GrCaps.h"
31#include "GrClip.h"
Brian Salomon19f0ed52017-01-06 13:54:58 -050032#include "GrClipStackClip.h"
Ben Wagnerb607a8f2018-03-12 13:46:21 -040033#include "GrConfig.h"
34#include "GrContext.h"
35#include "GrContextFactory.h"
36#include "GrContextPriv.h"
csmartdaltoncbecb082016-07-22 08:59:08 -070037#include "GrReducedClip.h"
Brian Salomon19f0ed52017-01-06 13:54:58 -050038#include "GrResourceCache.h"
Ben Wagnerb607a8f2018-03-12 13:46:21 -040039#include "GrResourceKey.h"
Robert Phillipseee4d6e2017-06-05 09:26:07 -040040#include "GrSurfaceProxyPriv.h"
Robert Phillipsd9d84852017-06-09 10:48:29 -040041#include "GrTexture.h"
Mike Reed84dd8572017-03-08 22:21:00 -050042#include "GrTextureProxy.h"
csmartdaltoncbecb082016-07-22 08:59:08 -070043typedef GrReducedClip::ElementList ElementList;
44typedef GrReducedClip::InitialState InitialState;
csmartdaltoncbecb082016-07-22 08:59:08 -070045
Ben Wagnerb607a8f2018-03-12 13:46:21 -040046#include <cstring>
47#include <new>
48
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000049static void test_assign_and_comparison(skiatest::Reporter* reporter) {
50 SkClipStack s;
reed@google.comd9f2dea2011-10-12 14:43:27 +000051 bool doAA = false;
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000052
robertphillips@google.com80214e22012-07-20 15:33:18 +000053 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
54
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000055 // Build up a clip stack with a path, an empty clip, and a rect.
56 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000057 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
58
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000059 SkPath p;
60 p.moveTo(5, 6);
61 p.lineTo(7, 8);
62 p.lineTo(5, 9);
63 p.close();
Mike Reedc1f77742016-12-09 09:00:50 -050064 s.clipPath(p, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000065
66 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000067 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
68
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000069 SkRect r = SkRect::MakeLTRB(1, 2, 3, 4);
Mike Reedc1f77742016-12-09 09:00:50 -050070 s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000071 r = SkRect::MakeLTRB(10, 11, 12, 13);
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
74 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000075 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
76
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000077 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -050078 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000079
80 // Test that assignment works.
81 SkClipStack copy = s;
82 REPORTER_ASSERT(reporter, s == copy);
83
84 // Test that different save levels triggers not equal.
85 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000086 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000087 REPORTER_ASSERT(reporter, s != copy);
88
89 // Test that an equal, but not copied version is equal.
90 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000091 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000092 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -050093 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000094 REPORTER_ASSERT(reporter, s == copy);
95
96 // Test that a different op on one level triggers not equal.
97 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000098 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000099 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000100 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000101 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -0500102 s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000103 REPORTER_ASSERT(reporter, s != copy);
104
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000105 // Test that version constructed with rect-path rather than a rect is still considered equal.
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000106 s.restore();
107 s.save();
108 SkPath rp;
109 rp.addRect(r);
Mike Reedc1f77742016-12-09 09:00:50 -0500110 s.clipPath(rp, SkMatrix::I(), kUnion_SkClipOp, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000111 REPORTER_ASSERT(reporter, s == copy);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000112
113 // Test that different rects triggers not equal.
114 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000115 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000116 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000117 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
118
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000119 r = SkRect::MakeLTRB(24, 25, 26, 27);
Mike Reedc1f77742016-12-09 09:00:50 -0500120 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000121 REPORTER_ASSERT(reporter, s != copy);
122
123 // Sanity check
124 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000125 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
126
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000127 copy.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000128 REPORTER_ASSERT(reporter, 2 == copy.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000129 REPORTER_ASSERT(reporter, s == copy);
130 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000131 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000132 copy.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000133 REPORTER_ASSERT(reporter, 1 == copy.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000134 REPORTER_ASSERT(reporter, s == copy);
135
136 // Test that different paths triggers not equal.
137 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000138 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000139 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000140 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
141
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000142 p.addRect(r);
Mike Reedc1f77742016-12-09 09:00:50 -0500143 s.clipPath(p, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000144 REPORTER_ASSERT(reporter, s != copy);
145}
reed@google.combdee9fc2011-02-22 20:17:43 +0000146
147static void assert_count(skiatest::Reporter* reporter, const SkClipStack& stack,
148 int count) {
robertphillips@google.com80214e22012-07-20 15:33:18 +0000149 SkClipStack::B2TIter iter(stack);
reed@google.combdee9fc2011-02-22 20:17:43 +0000150 int counter = 0;
151 while (iter.next()) {
152 counter += 1;
153 }
154 REPORTER_ASSERT(reporter, count == counter);
155}
156
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000157// Exercise the SkClipStack's bottom to top and bidirectional iterators
158// (including the skipToTopmost functionality)
robertphillips@google.com80214e22012-07-20 15:33:18 +0000159static void test_iterators(skiatest::Reporter* reporter) {
160 SkClipStack stack;
161
162 static const SkRect gRects[] = {
163 { 0, 0, 40, 40 },
164 { 60, 0, 100, 40 },
165 { 0, 60, 40, 100 },
166 { 60, 60, 100, 100 }
167 };
168
169 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
170 // the union op will prevent these from being fused together
Mike Reedc1f77742016-12-09 09:00:50 -0500171 stack.clipRect(gRects[i], SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000172 }
173
174 assert_count(reporter, stack, 4);
175
176 // bottom to top iteration
177 {
halcanary96fcdcc2015-08-27 07:41:13 -0700178 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000179
180 SkClipStack::B2TIter iter(stack);
181 int i;
182
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000183 for (i = 0, element = iter.next(); element; ++i, element = iter.next()) {
Brian Salomonf3b46e52017-08-30 11:37:57 -0400184 REPORTER_ASSERT(reporter, SkClipStack::Element::DeviceSpaceType::kRect ==
185 element->getDeviceSpaceType());
186 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == gRects[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000187 }
188
189 SkASSERT(i == 4);
190 }
191
192 // top to bottom iteration
193 {
halcanary96fcdcc2015-08-27 07:41:13 -0700194 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000195
196 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
197 int i;
198
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000199 for (i = 3, element = iter.prev(); element; --i, element = iter.prev()) {
Brian Salomonf3b46e52017-08-30 11:37:57 -0400200 REPORTER_ASSERT(reporter, SkClipStack::Element::DeviceSpaceType::kRect ==
201 element->getDeviceSpaceType());
202 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == gRects[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000203 }
204
205 SkASSERT(i == -1);
206 }
207
208 // skipToTopmost
209 {
halcanary96fcdcc2015-08-27 07:41:13 -0700210 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000211
212 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
213
Mike Reedc1f77742016-12-09 09:00:50 -0500214 element = iter.skipToTopmost(kUnion_SkClipOp);
Brian Salomonf3b46e52017-08-30 11:37:57 -0400215 REPORTER_ASSERT(reporter, SkClipStack::Element::DeviceSpaceType::kRect ==
216 element->getDeviceSpaceType());
217 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == gRects[3]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000218 }
219}
220
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000221// Exercise the SkClipStack's getConservativeBounds computation
Brian Salomonf3b46e52017-08-30 11:37:57 -0400222static void test_bounds(skiatest::Reporter* reporter,
223 SkClipStack::Element::DeviceSpaceType primType) {
robertphillips@google.com607fe072012-07-24 13:54:00 +0000224 static const int gNumCases = 20;
225 static const SkRect gAnswerRectsBW[gNumCases] = {
226 // A op B
227 { 40, 40, 50, 50 },
228 { 10, 10, 50, 50 },
229 { 10, 10, 80, 80 },
230 { 10, 10, 80, 80 },
231 { 40, 40, 80, 80 },
232
233 // invA op B
234 { 40, 40, 80, 80 },
235 { 0, 0, 100, 100 },
236 { 0, 0, 100, 100 },
237 { 0, 0, 100, 100 },
238 { 40, 40, 50, 50 },
239
240 // A op invB
241 { 10, 10, 50, 50 },
242 { 40, 40, 50, 50 },
243 { 0, 0, 100, 100 },
244 { 0, 0, 100, 100 },
245 { 0, 0, 100, 100 },
246
247 // invA op invB
248 { 0, 0, 100, 100 },
249 { 40, 40, 80, 80 },
250 { 0, 0, 100, 100 },
251 { 10, 10, 80, 80 },
252 { 10, 10, 50, 50 },
253 };
254
Mike Reedc1f77742016-12-09 09:00:50 -0500255 static const SkClipOp gOps[] = {
256 kIntersect_SkClipOp,
257 kDifference_SkClipOp,
258 kUnion_SkClipOp,
259 kXOR_SkClipOp,
260 kReverseDifference_SkClipOp
robertphillips@google.com607fe072012-07-24 13:54:00 +0000261 };
262
263 SkRect rectA, rectB;
264
265 rectA.iset(10, 10, 50, 50);
266 rectB.iset(40, 40, 80, 80);
267
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000268 SkRRect rrectA, rrectB;
269 rrectA.setOval(rectA);
270 rrectB.setRectXY(rectB, SkIntToScalar(1), SkIntToScalar(2));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000271
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000272 SkPath pathA, pathB;
273
274 pathA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
275 pathB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000276
277 SkClipStack stack;
robertphillips@google.com7b112892012-07-31 15:18:21 +0000278 SkRect devClipBound;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000279 bool isIntersectionOfRects = false;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000280
281 int testCase = 0;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400282 int numBitTests = SkClipStack::Element::DeviceSpaceType::kPath == primType ? 4 : 1;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000283 for (int invBits = 0; invBits < numBitTests; ++invBits) {
robertphillips@google.com607fe072012-07-24 13:54:00 +0000284 for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) {
285
286 stack.save();
287 bool doInvA = SkToBool(invBits & 1);
288 bool doInvB = SkToBool(invBits & 2);
289
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000290 pathA.setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType :
robertphillips@google.com607fe072012-07-24 13:54:00 +0000291 SkPath::kEvenOdd_FillType);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000292 pathB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType :
robertphillips@google.com607fe072012-07-24 13:54:00 +0000293 SkPath::kEvenOdd_FillType);
294
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000295 switch (primType) {
Brian Salomonf3b46e52017-08-30 11:37:57 -0400296 case SkClipStack::Element::DeviceSpaceType::kEmpty:
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000297 SkDEBUGFAIL("Don't call this with kEmpty.");
298 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400299 case SkClipStack::Element::DeviceSpaceType::kRect:
Mike Reedc1f77742016-12-09 09:00:50 -0500300 stack.clipRect(rectA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400301 stack.clipRect(rectB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000302 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400303 case SkClipStack::Element::DeviceSpaceType::kRRect:
Mike Reedc1f77742016-12-09 09:00:50 -0500304 stack.clipRRect(rrectA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400305 stack.clipRRect(rrectB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000306 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400307 case SkClipStack::Element::DeviceSpaceType::kPath:
Mike Reedc1f77742016-12-09 09:00:50 -0500308 stack.clipPath(pathA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400309 stack.clipPath(pathB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000310 break;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000311 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000312
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000313 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000314 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000315
robertphillips@google.com7b112892012-07-31 15:18:21 +0000316 stack.getConservativeBounds(0, 0, 100, 100, &devClipBound,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000317 &isIntersectionOfRects);
318
Brian Salomonf3b46e52017-08-30 11:37:57 -0400319 if (SkClipStack::Element::DeviceSpaceType::kRect == primType) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000320 REPORTER_ASSERT(reporter, isIntersectionOfRects ==
Mike Reedc1f77742016-12-09 09:00:50 -0500321 (gOps[op] == kIntersect_SkClipOp));
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000322 } else {
323 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
324 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000325
326 SkASSERT(testCase < gNumCases);
robertphillips@google.com7b112892012-07-31 15:18:21 +0000327 REPORTER_ASSERT(reporter, devClipBound == gAnswerRectsBW[testCase]);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000328 ++testCase;
329
330 stack.restore();
331 }
332 }
333}
334
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000335// Test out 'isWideOpen' entry point
336static void test_isWideOpen(skiatest::Reporter* reporter) {
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000337 {
338 // Empty stack is wide open. Wide open stack means that gen id is wide open.
339 SkClipStack stack;
340 REPORTER_ASSERT(reporter, stack.isWideOpen());
341 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
342 }
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000343
344 SkRect rectA, rectB;
345
346 rectA.iset(10, 10, 40, 40);
347 rectB.iset(50, 50, 80, 80);
348
349 // Stack should initially be wide open
350 {
351 SkClipStack stack;
352
353 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000354 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000355 }
356
357 // Test out case where the user specifies a union that includes everything
358 {
359 SkClipStack stack;
360
361 SkPath clipA, clipB;
362
363 clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
364 clipA.setFillType(SkPath::kInverseEvenOdd_FillType);
365
366 clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
367 clipB.setFillType(SkPath::kInverseEvenOdd_FillType);
368
Mike Reedc1f77742016-12-09 09:00:50 -0500369 stack.clipPath(clipA, SkMatrix::I(), kReplace_SkClipOp, false);
370 stack.clipPath(clipB, SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000371
372 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000373 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000374 }
375
376 // Test out union w/ a wide open clip
377 {
378 SkClipStack stack;
379
Mike Reedc1f77742016-12-09 09:00:50 -0500380 stack.clipRect(rectA, SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000381
382 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000383 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000384 }
385
386 // Test out empty difference from a wide open clip
387 {
388 SkClipStack stack;
389
390 SkRect emptyRect;
391 emptyRect.setEmpty();
392
Mike Reedc1f77742016-12-09 09:00:50 -0500393 stack.clipRect(emptyRect, SkMatrix::I(), kDifference_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000394
395 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000396 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000397 }
398
399 // Test out return to wide open
400 {
401 SkClipStack stack;
402
403 stack.save();
404
Mike Reedc1f77742016-12-09 09:00:50 -0500405 stack.clipRect(rectA, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000406
407 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000408 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000409
410 stack.restore();
411
412 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000413 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000414 }
415}
416
bsalomon@google.com100abf42012-09-05 17:40:04 +0000417static int count(const SkClipStack& stack) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000418
419 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
420
halcanary96fcdcc2015-08-27 07:41:13 -0700421 const SkClipStack::Element* element = nullptr;
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000422 int count = 0;
423
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000424 for (element = iter.prev(); element; element = iter.prev(), ++count) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000425 ;
426 }
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});
859 path.setFillType(SkPath::kInverseWinding_FillType);
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);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000896 path.setFillType(SkPath::kInverseWinding_FillType);
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);
910 path.setFillType(SkPath::kInverseWinding_FillType);
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) {
922 path.setFillType(SkPath::kInverseWinding_FillType);
923 }
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);
Brian Salomonc7fe0f72018-05-11 10:14:21 -04001045 const auto* caps = context->contextPriv().caps()->shaderCaps();
Brian Salomon14471772017-12-05 10:35:15 -05001046
csmartdalton8d3f92a2016-08-17 09:39:38 -07001047 // 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));
1051 GR_STATIC_ASSERT(0 == SkClipStack::kInvalidGenID);
1052
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);
Brian Salomon14471772017-12-05 10:35:15 -05001056 const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, queryBounds, caps);
bsalomon@google.coma4444302012-12-04 15:22:12 +00001057
Brian Salomon1c80e992018-01-29 09:50:47 -05001058 REPORTER_ASSERT(reporter,
1059 reduced->maskElements().isEmpty() ||
Chris Dalton79471932017-10-27 01:50:57 -06001060 SkClipStack::kInvalidGenID != reduced->maskGenID(),
Brian Salomon1c80e992018-01-29 09:50:47 -05001061 testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001062
Chris Dalton79471932017-10-27 01:50:57 -06001063 if (!reduced->maskElements().isEmpty()) {
Brian Salomon1c80e992018-01-29 09:50:47 -05001064 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 Salomon1c80e992018-01-29 09:50:47 -05001068 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;
csmartdalton8d3f92a2016-08-17 09:39:38 -07001073 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 }
Chris Dalton79471932017-10-27 01:50:57 -06001077 for (ElementList::Iter iter(reduced->maskElements()); iter.get(); iter.next()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001078 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
Chris Dalton79471932017-10-27 01:50:57 -06001081 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());
csmartdalton8d3f92a2016-08-17 09:39:38 -07001095
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
1106static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) {
1107 {
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);
Brian Salomonc7fe0f72018-05-11 10:14:21 -04001116 const auto* caps = context->contextPriv().caps()->shaderCaps();
Brian Salomon14471772017-12-05 10:35:15 -05001117
csmartdalton8d3f92a2016-08-17 09:39:38 -07001118 SkAlignedSTStorage<1, GrReducedClip> storage;
1119 memset(storage.get(), 0, sizeof(GrReducedClip));
1120 GR_STATIC_ASSERT(0 == SkClipStack::kInvalidGenID);
Brian Salomon14471772017-12-05 10:35:15 -05001121 const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, bounds, caps);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001122
Chris Dalton79471932017-10-27 01:50:57 -06001123 REPORTER_ASSERT(reporter, reduced->maskElements().count() == 1);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001124 // Clips will be cached based on the generation id. Make sure the gen id is valid.
Chris Dalton79471932017-10-27 01:50:57 -06001125 REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reduced->maskGenID());
csmartdalton8d3f92a2016-08-17 09:39:38 -07001126
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;
Robert Phillips806be2d2017-06-28 15:23:59 -04001166 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);
Brian Salomonc7fe0f72018-05-11 10:14:21 -04001205 const auto* caps = context->contextPriv().caps()->shaderCaps();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001206
1207 for (size_t i = 0; i < SK_ARRAY_COUNT(testCases); ++i) {
Brian Salomon14471772017-12-05 10:35:15 -05001208 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);
1211 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);
1217 SkASSERT(reduced.initialState() == testCases[i].initialState);
Chris Dalton79471932017-10-27 01:50:57 -06001218 REPORTER_ASSERT(reporter, reduced.hasScissor());
1219 SkASSERT(reduced.hasScissor());
1220 REPORTER_ASSERT(reporter, reduced.scissor() == testCases[i].clipIRect);
1221 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);
Brian Salomonc7fe0f72018-05-11 10:14:21 -04001233 const auto* caps = context->contextPriv().caps()->shaderCaps();
Brian Salomon14471772017-12-05 10:35:15 -05001234
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001235 // At the time, this would crash.
Brian Salomon14471772017-12-05 10:35:15 -05001236 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);
Brian Salomonc7fe0f72018-05-11 10:14:21 -04001252 const auto* caps = context->contextPriv().caps()->shaderCaps();
Brian Salomon14471772017-12-05 10:35:15 -05001253
csmartdaltoncbecb082016-07-22 08:59:08 -07001254 SkRect queryBounds;
1255 queryXform.mapRect(&queryBounds, preXformQuery);
Brian Salomon14471772017-12-05 10:35:15 -05001256 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);
Brian Salomonc7fe0f72018-05-11 10:14:21 -04001414 const auto* caps = context->contextPriv().caps()->shaderCaps();
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)}) {
Brian Salomon14471772017-12-05 10:35:15 -05001421 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
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00001430DEF_TEST(ClipStack, reporter) {
reed@google.combdee9fc2011-02-22 20:17:43 +00001431 SkClipStack stack;
1432
robertphillips@google.com80214e22012-07-20 15:33:18 +00001433 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001434 assert_count(reporter, stack, 0);
1435
1436 static const SkIRect gRects[] = {
1437 { 0, 0, 100, 100 },
1438 { 25, 25, 125, 125 },
1439 { 0, 0, 1000, 1000 },
1440 { 0, 0, 75, 75 }
1441 };
1442 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
Mike Reedc1f77742016-12-09 09:00:50 -05001443 stack.clipDevRect(gRects[i], kIntersect_SkClipOp);
reed@google.combdee9fc2011-02-22 20:17:43 +00001444 }
1445
1446 // all of the above rects should have been intersected, leaving only 1 rect
robertphillips@google.com80214e22012-07-20 15:33:18 +00001447 SkClipStack::B2TIter iter(stack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001448 const SkClipStack::Element* element = iter.next();
epoger@google.com2047f002011-05-17 17:36:59 +00001449 SkRect answer;
1450 answer.iset(25, 25, 75, 75);
reed@google.combdee9fc2011-02-22 20:17:43 +00001451
bsalomon49f085d2014-09-05 13:34:00 -07001452 REPORTER_ASSERT(reporter, element);
Brian Salomonf3b46e52017-08-30 11:37:57 -04001453 REPORTER_ASSERT(reporter,
1454 SkClipStack::Element::DeviceSpaceType::kRect == element->getDeviceSpaceType());
Mike Reedc1f77742016-12-09 09:00:50 -05001455 REPORTER_ASSERT(reporter, kIntersect_SkClipOp == element->getOp());
Brian Salomonf3b46e52017-08-30 11:37:57 -04001456 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == answer);
reed@google.combdee9fc2011-02-22 20:17:43 +00001457 // now check that we only had one in our iterator
1458 REPORTER_ASSERT(reporter, !iter.next());
1459
1460 stack.reset();
robertphillips@google.com80214e22012-07-20 15:33:18 +00001461 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001462 assert_count(reporter, stack, 0);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +00001463
1464 test_assign_and_comparison(reporter);
robertphillips@google.com80214e22012-07-20 15:33:18 +00001465 test_iterators(reporter);
Brian Salomonf3b46e52017-08-30 11:37:57 -04001466 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kRect);
1467 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kRRect);
1468 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kPath);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +00001469 test_isWideOpen(reporter);
robertphillips@google.com08eacc12012-08-02 12:49:00 +00001470 test_rect_merging(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001471 test_rect_replace(reporter);
junov@chromium.orgedf32d52012-12-10 14:57:54 +00001472 test_rect_inverse_fill(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001473 test_path_replace(reporter);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +00001474 test_quickContains(reporter);
csmartdaltond50e2402016-07-22 08:39:06 -07001475 test_invfill_diff_bug(reporter);
Brian Osmanc7ad40f2018-05-31 14:27:17 -04001476
bsalomon@google.comedb26fd2012-11-28 14:42:41 +00001477 test_reduced_clip_stack(reporter);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001478 test_reduced_clip_stack_genid(reporter);
1479 test_reduced_clip_stack_no_aa_crash(reporter);
csmartdaltoncbecb082016-07-22 08:59:08 -07001480 test_reduced_clip_stack_aa(reporter);
Chris Dalton348060f2017-06-05 13:15:37 -06001481 test_tiny_query_bounds_assertion_bug(reporter);
reed@google.combdee9fc2011-02-22 20:17:43 +00001482}
Brian Salomon19f0ed52017-01-06 13:54:58 -05001483
1484//////////////////////////////////////////////////////////////////////////////
1485
Robert Phillips875218e2017-02-24 08:37:13 -05001486sk_sp<GrTextureProxy> GrClipStackClip::testingOnly_createClipMask(GrContext* context) const {
Brian Salomon19f0ed52017-01-06 13:54:58 -05001487 const GrReducedClip reducedClip(*fStack, SkRect::MakeWH(512, 512), 0);
Brian Osman5d034742017-09-11 13:38:55 -04001488 return this->createSoftwareClipMask(context, reducedClip, nullptr);
Brian Salomon19f0ed52017-01-06 13:54:58 -05001489}
1490
1491// Verify that clip masks are freed up when the clip state that generated them goes away.
1492DEF_GPUTEST_FOR_ALL_CONTEXTS(ClipMaskCache, reporter, ctxInfo) {
1493 // This test uses resource key tags which only function in debug builds.
1494#ifdef SK_DEBUG
1495 GrContext* context = ctxInfo.grContext();
1496 SkClipStack stack;
1497
1498 SkPath path;
1499 path.addCircle(10, 10, 8);
1500 path.addCircle(15, 15, 8);
1501 path.setFillType(SkPath::kEvenOdd_FillType);
1502
1503 static const char* kTag = GrClipStackClip::kMaskTestTag;
Robert Phillips6be756b2018-01-16 15:07:54 -05001504 GrResourceCache* cache = context->contextPriv().getResourceCache();
Brian Salomon19f0ed52017-01-06 13:54:58 -05001505
1506 static constexpr int kN = 5;
1507
1508 for (int i = 0; i < kN; ++i) {
1509 SkMatrix m;
1510 m.setTranslate(0.5, 0.5);
1511 stack.save();
1512 stack.clipPath(path, m, SkClipOp::kIntersect, true);
Robert Phillips875218e2017-02-24 08:37:13 -05001513 sk_sp<GrTextureProxy> mask = GrClipStackClip(&stack).testingOnly_createClipMask(context);
Robert Phillips6be756b2018-01-16 15:07:54 -05001514 mask->instantiate(context->contextPriv().resourceProvider());
Robert Phillipseee4d6e2017-06-05 09:26:07 -04001515 GrTexture* tex = mask->priv().peekTexture();
Robert Phillips875218e2017-02-24 08:37:13 -05001516 REPORTER_ASSERT(reporter, 0 == strcmp(tex->getUniqueKey().tag(), kTag));
Brian Salomon19f0ed52017-01-06 13:54:58 -05001517 // Make sure mask isn't pinned in cache.
1518 mask.reset(nullptr);
1519 context->flush();
1520 REPORTER_ASSERT(reporter, i + 1 == cache->countUniqueKeysWithTag(kTag));
1521 }
1522
1523 for (int i = 0; i < kN; ++i) {
1524 stack.restore();
1525 cache->purgeAsNeeded();
1526 REPORTER_ASSERT(reporter, kN - (i + 1) == cache->countUniqueKeysWithTag(kTag));
1527 }
1528#endif
1529}
1530
Mike Reed3726a4a2017-01-19 11:36:41 -05001531DEF_GPUTEST_FOR_ALL_CONTEXTS(canvas_private_clipRgn, reporter, ctxInfo) {
1532 GrContext* context = ctxInfo.grContext();
1533
1534 const int w = 10;
1535 const int h = 10;
Brian Salomon8996e182017-07-05 17:01:48 -04001536 SkImageInfo info = SkImageInfo::Make(w, h, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
Mike Reed3726a4a2017-01-19 11:36:41 -05001537 sk_sp<SkSurface> surf = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info);
1538 SkCanvas* canvas = surf->getCanvas();
1539 SkRegion rgn;
1540
1541 canvas->temporary_internal_getRgnClip(&rgn);
1542 REPORTER_ASSERT(reporter, rgn.isRect());
1543 REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeWH(w, h));
1544
1545 canvas->save();
1546 canvas->clipRect(SkRect::MakeWH(5, 5), kDifference_SkClipOp);
1547 canvas->temporary_internal_getRgnClip(&rgn);
1548 REPORTER_ASSERT(reporter, rgn.isComplex());
1549 REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeWH(w, h));
1550 canvas->restore();
1551
1552 canvas->save();
1553 canvas->clipRRect(SkRRect::MakeOval(SkRect::MakeLTRB(3, 3, 7, 7)));
1554 canvas->temporary_internal_getRgnClip(&rgn);
1555 REPORTER_ASSERT(reporter, rgn.isComplex());
1556 REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeLTRB(3, 3, 7, 7));
1557 canvas->restore();
1558}