blob: eeef831df1277e975fb004a9063ce9d287fcbe9e [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
csmartdaltoncbecb082016-07-22 08:59:08 -070030#if SK_SUPPORT_GPU
Ben Wagnerb607a8f2018-03-12 13:46:21 -040031#include "GrCaps.h"
32#include "GrClip.h"
Brian Salomon19f0ed52017-01-06 13:54:58 -050033#include "GrClipStackClip.h"
Ben Wagnerb607a8f2018-03-12 13:46:21 -040034#include "GrConfig.h"
35#include "GrContext.h"
36#include "GrContextFactory.h"
37#include "GrContextPriv.h"
csmartdaltoncbecb082016-07-22 08:59:08 -070038#include "GrReducedClip.h"
Brian Salomon19f0ed52017-01-06 13:54:58 -050039#include "GrResourceCache.h"
Ben Wagnerb607a8f2018-03-12 13:46:21 -040040#include "GrResourceKey.h"
Robert Phillipseee4d6e2017-06-05 09:26:07 -040041#include "GrSurfaceProxyPriv.h"
Robert Phillipsd9d84852017-06-09 10:48:29 -040042#include "GrTexture.h"
Mike Reed84dd8572017-03-08 22:21:00 -050043#include "GrTextureProxy.h"
csmartdaltoncbecb082016-07-22 08:59:08 -070044typedef GrReducedClip::ElementList ElementList;
45typedef GrReducedClip::InitialState InitialState;
46#endif
47
Ben Wagnerb607a8f2018-03-12 13:46:21 -040048#include <cstring>
49#include <new>
50
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000051static void test_assign_and_comparison(skiatest::Reporter* reporter) {
52 SkClipStack s;
reed@google.comd9f2dea2011-10-12 14:43:27 +000053 bool doAA = false;
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000054
robertphillips@google.com80214e22012-07-20 15:33:18 +000055 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
56
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000057 // Build up a clip stack with a path, an empty clip, and a rect.
58 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000059 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
60
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000061 SkPath p;
62 p.moveTo(5, 6);
63 p.lineTo(7, 8);
64 p.lineTo(5, 9);
65 p.close();
Mike Reedc1f77742016-12-09 09:00:50 -050066 s.clipPath(p, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000067
68 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000069 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
70
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000071 SkRect r = SkRect::MakeLTRB(1, 2, 3, 4);
Mike Reedc1f77742016-12-09 09:00:50 -050072 s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000073 r = SkRect::MakeLTRB(10, 11, 12, 13);
Mike Reedc1f77742016-12-09 09:00:50 -050074 s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000075
76 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000077 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
78
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000079 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -050080 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000081
82 // Test that assignment works.
83 SkClipStack copy = s;
84 REPORTER_ASSERT(reporter, s == copy);
85
86 // Test that different save levels triggers not equal.
87 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000088 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000089 REPORTER_ASSERT(reporter, s != copy);
90
91 // Test that an equal, but not copied version is equal.
92 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000093 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000094 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -050095 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000096 REPORTER_ASSERT(reporter, s == copy);
97
98 // Test that a different op on one level triggers not equal.
99 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000100 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000101 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000102 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000103 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -0500104 s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000105 REPORTER_ASSERT(reporter, s != copy);
106
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000107 // Test that version constructed with rect-path rather than a rect is still considered equal.
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000108 s.restore();
109 s.save();
110 SkPath rp;
111 rp.addRect(r);
Mike Reedc1f77742016-12-09 09:00:50 -0500112 s.clipPath(rp, SkMatrix::I(), kUnion_SkClipOp, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000113 REPORTER_ASSERT(reporter, s == copy);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000114
115 // Test that different rects triggers not equal.
116 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000117 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000118 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000119 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
120
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000121 r = SkRect::MakeLTRB(24, 25, 26, 27);
Mike Reedc1f77742016-12-09 09:00:50 -0500122 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000123 REPORTER_ASSERT(reporter, s != copy);
124
125 // Sanity check
126 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000127 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
128
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000129 copy.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000130 REPORTER_ASSERT(reporter, 2 == copy.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000131 REPORTER_ASSERT(reporter, s == copy);
132 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000133 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000134 copy.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000135 REPORTER_ASSERT(reporter, 1 == copy.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000136 REPORTER_ASSERT(reporter, s == copy);
137
138 // Test that different paths triggers not equal.
139 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000140 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000141 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000142 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
143
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000144 p.addRect(r);
Mike Reedc1f77742016-12-09 09:00:50 -0500145 s.clipPath(p, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000146 REPORTER_ASSERT(reporter, s != copy);
147}
reed@google.combdee9fc2011-02-22 20:17:43 +0000148
149static void assert_count(skiatest::Reporter* reporter, const SkClipStack& stack,
150 int count) {
robertphillips@google.com80214e22012-07-20 15:33:18 +0000151 SkClipStack::B2TIter iter(stack);
reed@google.combdee9fc2011-02-22 20:17:43 +0000152 int counter = 0;
153 while (iter.next()) {
154 counter += 1;
155 }
156 REPORTER_ASSERT(reporter, count == counter);
157}
158
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000159// Exercise the SkClipStack's bottom to top and bidirectional iterators
160// (including the skipToTopmost functionality)
robertphillips@google.com80214e22012-07-20 15:33:18 +0000161static void test_iterators(skiatest::Reporter* reporter) {
162 SkClipStack stack;
163
164 static const SkRect gRects[] = {
165 { 0, 0, 40, 40 },
166 { 60, 0, 100, 40 },
167 { 0, 60, 40, 100 },
168 { 60, 60, 100, 100 }
169 };
170
171 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
172 // the union op will prevent these from being fused together
Mike Reedc1f77742016-12-09 09:00:50 -0500173 stack.clipRect(gRects[i], SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000174 }
175
176 assert_count(reporter, stack, 4);
177
178 // bottom to top iteration
179 {
halcanary96fcdcc2015-08-27 07:41:13 -0700180 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000181
182 SkClipStack::B2TIter iter(stack);
183 int i;
184
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000185 for (i = 0, element = iter.next(); element; ++i, element = iter.next()) {
Brian Salomonf3b46e52017-08-30 11:37:57 -0400186 REPORTER_ASSERT(reporter, SkClipStack::Element::DeviceSpaceType::kRect ==
187 element->getDeviceSpaceType());
188 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == gRects[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000189 }
190
191 SkASSERT(i == 4);
192 }
193
194 // top to bottom iteration
195 {
halcanary96fcdcc2015-08-27 07:41:13 -0700196 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000197
198 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
199 int i;
200
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000201 for (i = 3, element = iter.prev(); element; --i, element = iter.prev()) {
Brian Salomonf3b46e52017-08-30 11:37:57 -0400202 REPORTER_ASSERT(reporter, SkClipStack::Element::DeviceSpaceType::kRect ==
203 element->getDeviceSpaceType());
204 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == gRects[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000205 }
206
207 SkASSERT(i == -1);
208 }
209
210 // skipToTopmost
211 {
halcanary96fcdcc2015-08-27 07:41:13 -0700212 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000213
214 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
215
Mike Reedc1f77742016-12-09 09:00:50 -0500216 element = iter.skipToTopmost(kUnion_SkClipOp);
Brian Salomonf3b46e52017-08-30 11:37:57 -0400217 REPORTER_ASSERT(reporter, SkClipStack::Element::DeviceSpaceType::kRect ==
218 element->getDeviceSpaceType());
219 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == gRects[3]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000220 }
221}
222
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000223// Exercise the SkClipStack's getConservativeBounds computation
Brian Salomonf3b46e52017-08-30 11:37:57 -0400224static void test_bounds(skiatest::Reporter* reporter,
225 SkClipStack::Element::DeviceSpaceType primType) {
robertphillips@google.com607fe072012-07-24 13:54:00 +0000226 static const int gNumCases = 20;
227 static const SkRect gAnswerRectsBW[gNumCases] = {
228 // A op B
229 { 40, 40, 50, 50 },
230 { 10, 10, 50, 50 },
231 { 10, 10, 80, 80 },
232 { 10, 10, 80, 80 },
233 { 40, 40, 80, 80 },
234
235 // invA op B
236 { 40, 40, 80, 80 },
237 { 0, 0, 100, 100 },
238 { 0, 0, 100, 100 },
239 { 0, 0, 100, 100 },
240 { 40, 40, 50, 50 },
241
242 // A op invB
243 { 10, 10, 50, 50 },
244 { 40, 40, 50, 50 },
245 { 0, 0, 100, 100 },
246 { 0, 0, 100, 100 },
247 { 0, 0, 100, 100 },
248
249 // invA op invB
250 { 0, 0, 100, 100 },
251 { 40, 40, 80, 80 },
252 { 0, 0, 100, 100 },
253 { 10, 10, 80, 80 },
254 { 10, 10, 50, 50 },
255 };
256
Mike Reedc1f77742016-12-09 09:00:50 -0500257 static const SkClipOp gOps[] = {
258 kIntersect_SkClipOp,
259 kDifference_SkClipOp,
260 kUnion_SkClipOp,
261 kXOR_SkClipOp,
262 kReverseDifference_SkClipOp
robertphillips@google.com607fe072012-07-24 13:54:00 +0000263 };
264
265 SkRect rectA, rectB;
266
267 rectA.iset(10, 10, 50, 50);
268 rectB.iset(40, 40, 80, 80);
269
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000270 SkRRect rrectA, rrectB;
271 rrectA.setOval(rectA);
272 rrectB.setRectXY(rectB, SkIntToScalar(1), SkIntToScalar(2));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000273
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000274 SkPath pathA, pathB;
275
276 pathA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
277 pathB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000278
279 SkClipStack stack;
robertphillips@google.com7b112892012-07-31 15:18:21 +0000280 SkRect devClipBound;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000281 bool isIntersectionOfRects = false;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000282
283 int testCase = 0;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400284 int numBitTests = SkClipStack::Element::DeviceSpaceType::kPath == primType ? 4 : 1;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000285 for (int invBits = 0; invBits < numBitTests; ++invBits) {
robertphillips@google.com607fe072012-07-24 13:54:00 +0000286 for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) {
287
288 stack.save();
289 bool doInvA = SkToBool(invBits & 1);
290 bool doInvB = SkToBool(invBits & 2);
291
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000292 pathA.setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType :
robertphillips@google.com607fe072012-07-24 13:54:00 +0000293 SkPath::kEvenOdd_FillType);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000294 pathB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType :
robertphillips@google.com607fe072012-07-24 13:54:00 +0000295 SkPath::kEvenOdd_FillType);
296
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000297 switch (primType) {
Brian Salomonf3b46e52017-08-30 11:37:57 -0400298 case SkClipStack::Element::DeviceSpaceType::kEmpty:
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000299 SkDEBUGFAIL("Don't call this with kEmpty.");
300 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400301 case SkClipStack::Element::DeviceSpaceType::kRect:
Mike Reedc1f77742016-12-09 09:00:50 -0500302 stack.clipRect(rectA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400303 stack.clipRect(rectB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000304 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400305 case SkClipStack::Element::DeviceSpaceType::kRRect:
Mike Reedc1f77742016-12-09 09:00:50 -0500306 stack.clipRRect(rrectA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400307 stack.clipRRect(rrectB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000308 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400309 case SkClipStack::Element::DeviceSpaceType::kPath:
Mike Reedc1f77742016-12-09 09:00:50 -0500310 stack.clipPath(pathA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400311 stack.clipPath(pathB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000312 break;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000313 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000314
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000315 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000316 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000317
robertphillips@google.com7b112892012-07-31 15:18:21 +0000318 stack.getConservativeBounds(0, 0, 100, 100, &devClipBound,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000319 &isIntersectionOfRects);
320
Brian Salomonf3b46e52017-08-30 11:37:57 -0400321 if (SkClipStack::Element::DeviceSpaceType::kRect == primType) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000322 REPORTER_ASSERT(reporter, isIntersectionOfRects ==
Mike Reedc1f77742016-12-09 09:00:50 -0500323 (gOps[op] == kIntersect_SkClipOp));
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000324 } else {
325 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
326 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000327
328 SkASSERT(testCase < gNumCases);
robertphillips@google.com7b112892012-07-31 15:18:21 +0000329 REPORTER_ASSERT(reporter, devClipBound == gAnswerRectsBW[testCase]);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000330 ++testCase;
331
332 stack.restore();
333 }
334 }
335}
336
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000337// Test out 'isWideOpen' entry point
338static void test_isWideOpen(skiatest::Reporter* reporter) {
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000339 {
340 // Empty stack is wide open. Wide open stack means that gen id is wide open.
341 SkClipStack stack;
342 REPORTER_ASSERT(reporter, stack.isWideOpen());
343 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
344 }
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000345
346 SkRect rectA, rectB;
347
348 rectA.iset(10, 10, 40, 40);
349 rectB.iset(50, 50, 80, 80);
350
351 // Stack should initially be wide open
352 {
353 SkClipStack stack;
354
355 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000356 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000357 }
358
359 // Test out case where the user specifies a union that includes everything
360 {
361 SkClipStack stack;
362
363 SkPath clipA, clipB;
364
365 clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
366 clipA.setFillType(SkPath::kInverseEvenOdd_FillType);
367
368 clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
369 clipB.setFillType(SkPath::kInverseEvenOdd_FillType);
370
Mike Reedc1f77742016-12-09 09:00:50 -0500371 stack.clipPath(clipA, SkMatrix::I(), kReplace_SkClipOp, false);
372 stack.clipPath(clipB, SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000373
374 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000375 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000376 }
377
378 // Test out union w/ a wide open clip
379 {
380 SkClipStack stack;
381
Mike Reedc1f77742016-12-09 09:00:50 -0500382 stack.clipRect(rectA, SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000383
384 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000385 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000386 }
387
388 // Test out empty difference from a wide open clip
389 {
390 SkClipStack stack;
391
392 SkRect emptyRect;
393 emptyRect.setEmpty();
394
Mike Reedc1f77742016-12-09 09:00:50 -0500395 stack.clipRect(emptyRect, SkMatrix::I(), kDifference_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000396
397 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000398 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000399 }
400
401 // Test out return to wide open
402 {
403 SkClipStack stack;
404
405 stack.save();
406
Mike Reedc1f77742016-12-09 09:00:50 -0500407 stack.clipRect(rectA, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000408
409 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000410 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000411
412 stack.restore();
413
414 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000415 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000416 }
417}
418
bsalomon@google.com100abf42012-09-05 17:40:04 +0000419static int count(const SkClipStack& stack) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000420
421 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
422
halcanary96fcdcc2015-08-27 07:41:13 -0700423 const SkClipStack::Element* element = nullptr;
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000424 int count = 0;
425
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000426 for (element = iter.prev(); element; element = iter.prev(), ++count) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000427 ;
428 }
429
430 return count;
431}
432
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000433static void test_rect_inverse_fill(skiatest::Reporter* reporter) {
434 // non-intersecting rectangles
435 SkRect rect = SkRect::MakeLTRB(0, 0, 10, 10);
436
437 SkPath path;
438 path.addRect(rect);
439 path.toggleInverseFillType();
440 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500441 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000442
443 SkRect bounds;
444 SkClipStack::BoundsType boundsType;
445 stack.getBounds(&bounds, &boundsType);
446 REPORTER_ASSERT(reporter, SkClipStack::kInsideOut_BoundsType == boundsType);
447 REPORTER_ASSERT(reporter, bounds == rect);
448}
449
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000450static void test_rect_replace(skiatest::Reporter* reporter) {
451 SkRect rect = SkRect::MakeWH(100, 100);
452 SkRect rect2 = SkRect::MakeXYWH(50, 50, 100, 100);
453
454 SkRect bound;
455 SkClipStack::BoundsType type;
456 bool isIntersectionOfRects;
457
458 // Adding a new rect with the replace operator should not increase
459 // the stack depth. BW replacing BW.
460 {
461 SkClipStack stack;
462 REPORTER_ASSERT(reporter, 0 == 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));
Mike Reedc1f77742016-12-09 09:00:50 -0500465 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000466 REPORTER_ASSERT(reporter, 1 == count(stack));
467 }
468
469 // Adding a new rect with the replace operator should not increase
470 // the stack depth. AA replacing AA.
471 {
472 SkClipStack stack;
473 REPORTER_ASSERT(reporter, 0 == 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));
Mike Reedc1f77742016-12-09 09:00:50 -0500476 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000477 REPORTER_ASSERT(reporter, 1 == count(stack));
478 }
479
480 // Adding a new rect with the replace operator should not increase
481 // the stack depth. BW replacing AA replacing BW.
482 {
483 SkClipStack stack;
484 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500485 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
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, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000488 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500489 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000490 REPORTER_ASSERT(reporter, 1 == count(stack));
491 }
492
493 // Make sure replace clip rects don't collapse too much.
494 {
495 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500496 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
497 stack.clipRect(rect2, SkMatrix::I(), kIntersect_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000498 REPORTER_ASSERT(reporter, 1 == count(stack));
499
500 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500501 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000502 REPORTER_ASSERT(reporter, 2 == count(stack));
503 stack.getBounds(&bound, &type, &isIntersectionOfRects);
504 REPORTER_ASSERT(reporter, bound == rect);
505 stack.restore();
506 REPORTER_ASSERT(reporter, 1 == count(stack));
507
508 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500509 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
510 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000511 REPORTER_ASSERT(reporter, 2 == count(stack));
512 stack.restore();
513 REPORTER_ASSERT(reporter, 1 == count(stack));
514
515 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500516 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
517 stack.clipRect(rect2, SkMatrix::I(), kIntersect_SkClipOp, false);
518 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000519 REPORTER_ASSERT(reporter, 2 == count(stack));
520 stack.restore();
521 REPORTER_ASSERT(reporter, 1 == count(stack));
522 }
523}
524
525// Simplified path-based version of test_rect_replace.
526static void test_path_replace(skiatest::Reporter* reporter) {
527 SkRect rect = SkRect::MakeWH(100, 100);
528 SkPath path;
529 path.addCircle(50, 50, 50);
530
531 // Replace operation doesn't grow the stack.
532 {
533 SkClipStack stack;
534 REPORTER_ASSERT(reporter, 0 == 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));
Mike Reedc1f77742016-12-09 09:00:50 -0500537 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000538 REPORTER_ASSERT(reporter, 1 == count(stack));
539 }
540
541 // Replacing rect with path.
542 {
543 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500544 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000545 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500546 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000547 REPORTER_ASSERT(reporter, 1 == count(stack));
548 }
549}
550
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000551// Test out SkClipStack's merging of rect clips. In particular exercise
552// merging of aa vs. bw rects.
553static void test_rect_merging(skiatest::Reporter* reporter) {
554
555 SkRect overlapLeft = SkRect::MakeLTRB(10, 10, 50, 50);
556 SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80);
557
558 SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90);
559 SkRect nestedChild = SkRect::MakeLTRB(40, 40, 60, 60);
560
561 SkRect bound;
562 SkClipStack::BoundsType type;
563 bool isIntersectionOfRects;
564
565 // all bw overlapping - should merge
566 {
567 SkClipStack stack;
568
Mike Reedc1f77742016-12-09 09:00:50 -0500569 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000570
Mike Reedc1f77742016-12-09 09:00:50 -0500571 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000572
573 REPORTER_ASSERT(reporter, 1 == count(stack));
574
575 stack.getBounds(&bound, &type, &isIntersectionOfRects);
576
577 REPORTER_ASSERT(reporter, isIntersectionOfRects);
578 }
579
580 // all aa overlapping - should merge
581 {
582 SkClipStack stack;
583
Mike Reedc1f77742016-12-09 09:00:50 -0500584 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000585
Mike Reedc1f77742016-12-09 09:00:50 -0500586 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000587
588 REPORTER_ASSERT(reporter, 1 == count(stack));
589
590 stack.getBounds(&bound, &type, &isIntersectionOfRects);
591
592 REPORTER_ASSERT(reporter, isIntersectionOfRects);
593 }
594
595 // mixed overlapping - should _not_ merge
596 {
597 SkClipStack stack;
598
Mike Reedc1f77742016-12-09 09:00:50 -0500599 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000600
Mike Reedc1f77742016-12-09 09:00:50 -0500601 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000602
603 REPORTER_ASSERT(reporter, 2 == count(stack));
604
605 stack.getBounds(&bound, &type, &isIntersectionOfRects);
606
607 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
608 }
609
610 // mixed nested (bw inside aa) - should merge
611 {
612 SkClipStack stack;
613
Mike Reedc1f77742016-12-09 09:00:50 -0500614 stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000615
Mike Reedc1f77742016-12-09 09:00:50 -0500616 stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000617
618 REPORTER_ASSERT(reporter, 1 == count(stack));
619
620 stack.getBounds(&bound, &type, &isIntersectionOfRects);
621
622 REPORTER_ASSERT(reporter, isIntersectionOfRects);
623 }
624
625 // mixed nested (aa inside bw) - should merge
626 {
627 SkClipStack stack;
628
Mike Reedc1f77742016-12-09 09:00:50 -0500629 stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000630
Mike Reedc1f77742016-12-09 09:00:50 -0500631 stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000632
633 REPORTER_ASSERT(reporter, 1 == count(stack));
634
635 stack.getBounds(&bound, &type, &isIntersectionOfRects);
636
637 REPORTER_ASSERT(reporter, isIntersectionOfRects);
638 }
639
640 // reverse nested (aa inside bw) - should _not_ merge
641 {
642 SkClipStack stack;
643
Mike Reedc1f77742016-12-09 09:00:50 -0500644 stack.clipRect(nestedChild, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000645
Mike Reedc1f77742016-12-09 09:00:50 -0500646 stack.clipRect(nestedParent, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000647
648 REPORTER_ASSERT(reporter, 2 == count(stack));
649
650 stack.getBounds(&bound, &type, &isIntersectionOfRects);
651
652 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
653 }
654}
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000655
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000656static void test_quickContains(skiatest::Reporter* reporter) {
657 SkRect testRect = SkRect::MakeLTRB(10, 10, 40, 40);
658 SkRect insideRect = SkRect::MakeLTRB(20, 20, 30, 30);
659 SkRect intersectingRect = SkRect::MakeLTRB(25, 25, 50, 50);
660 SkRect outsideRect = SkRect::MakeLTRB(0, 0, 50, 50);
661 SkRect nonIntersectingRect = SkRect::MakeLTRB(100, 100, 110, 110);
662
663 SkPath insideCircle;
664 insideCircle.addCircle(25, 25, 5);
665 SkPath intersectingCircle;
666 intersectingCircle.addCircle(25, 40, 10);
667 SkPath outsideCircle;
668 outsideCircle.addCircle(25, 25, 50);
669 SkPath nonIntersectingCircle;
670 nonIntersectingCircle.addCircle(100, 100, 5);
671
672 {
673 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500674 stack.clipRect(outsideRect, SkMatrix::I(), kDifference_SkClipOp, false);
675 // return false because quickContains currently does not care for kDifference_SkClipOp
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000676 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
677 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000678
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000679 // Replace Op tests
680 {
681 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500682 stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000683 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
684 }
685
686 {
687 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500688 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000689 stack.save(); // To prevent in-place substitution by replace OP
Mike Reedc1f77742016-12-09 09:00:50 -0500690 stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000691 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
692 stack.restore();
693 }
694
695 {
696 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500697 stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000698 stack.save(); // To prevent in-place substitution by replace OP
Mike Reedc1f77742016-12-09 09:00:50 -0500699 stack.clipRect(insideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000700 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
701 stack.restore();
702 }
703
704 // Verify proper traversal of multi-element clip
705 {
706 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500707 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000708 // Use a path for second clip to prevent in-place intersection
Mike Reedc1f77742016-12-09 09:00:50 -0500709 stack.clipPath(outsideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000710 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
711 }
712
713 // Intersect Op tests with rectangles
714 {
715 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500716 stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000717 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
718 }
719
720 {
721 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500722 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000723 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
724 }
725
726 {
727 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500728 stack.clipRect(intersectingRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000729 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
730 }
731
732 {
733 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500734 stack.clipRect(nonIntersectingRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000735 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
736 }
737
738 // Intersect Op tests with circle paths
739 {
740 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500741 stack.clipPath(outsideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000742 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
743 }
744
745 {
746 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500747 stack.clipPath(insideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000748 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
749 }
750
751 {
752 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500753 stack.clipPath(intersectingCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000754 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
755 }
756
757 {
758 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500759 stack.clipPath(nonIntersectingCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000760 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
761 }
762
763 // Intersect Op tests with inverse filled rectangles
764 {
765 SkClipStack stack;
766 SkPath path;
767 path.addRect(outsideRect);
768 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500769 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000770 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
771 }
772
773 {
774 SkClipStack stack;
775 SkPath path;
776 path.addRect(insideRect);
777 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500778 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000779 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
780 }
781
782 {
783 SkClipStack stack;
784 SkPath path;
785 path.addRect(intersectingRect);
786 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500787 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000788 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
789 }
790
791 {
792 SkClipStack stack;
793 SkPath path;
794 path.addRect(nonIntersectingRect);
795 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500796 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000797 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
798 }
799
800 // Intersect Op tests with inverse filled circles
801 {
802 SkClipStack stack;
803 SkPath path = outsideCircle;
804 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500805 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000806 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
807 }
808
809 {
810 SkClipStack stack;
811 SkPath path = insideCircle;
812 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500813 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000814 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
815 }
816
817 {
818 SkClipStack stack;
819 SkPath path = intersectingCircle;
820 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500821 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000822 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
823 }
824
825 {
826 SkClipStack stack;
827 SkPath path = nonIntersectingCircle;
828 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500829 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000830 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
831 }
832}
833
csmartdaltond50e2402016-07-22 08:39:06 -0700834static void set_region_to_stack(const SkClipStack& stack, const SkIRect& bounds, SkRegion* region) {
835 region->setRect(bounds);
836 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
837 while (const SkClipStack::Element *element = iter.next()) {
838 SkRegion elemRegion;
839 SkRegion boundsRgn(bounds);
840 SkPath path;
841
Brian Salomonf3b46e52017-08-30 11:37:57 -0400842 switch (element->getDeviceSpaceType()) {
843 case SkClipStack::Element::DeviceSpaceType::kEmpty:
csmartdaltond50e2402016-07-22 08:39:06 -0700844 elemRegion.setEmpty();
845 break;
846 default:
Brian Salomonf3b46e52017-08-30 11:37:57 -0400847 element->asDeviceSpacePath(&path);
csmartdaltond50e2402016-07-22 08:39:06 -0700848 elemRegion.setPath(path, boundsRgn);
849 break;
850 }
reed73603f32016-09-20 08:42:38 -0700851 region->op(elemRegion, (SkRegion::Op)element->getOp());
csmartdaltond50e2402016-07-22 08:39:06 -0700852 }
853}
854
855static void test_invfill_diff_bug(skiatest::Reporter* reporter) {
856 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500857 stack.clipRect({10, 10, 20, 20}, SkMatrix::I(), kIntersect_SkClipOp, false);
csmartdaltond50e2402016-07-22 08:39:06 -0700858
859 SkPath path;
860 path.addRect({30, 10, 40, 20});
861 path.setFillType(SkPath::kInverseWinding_FillType);
Mike Reedc1f77742016-12-09 09:00:50 -0500862 stack.clipPath(path, SkMatrix::I(), kDifference_SkClipOp, false);
csmartdaltond50e2402016-07-22 08:39:06 -0700863
864 REPORTER_ASSERT(reporter, SkClipStack::kEmptyGenID == stack.getTopmostGenID());
865
866 SkRect stackBounds;
867 SkClipStack::BoundsType stackBoundsType;
868 stack.getBounds(&stackBounds, &stackBoundsType);
869
870 REPORTER_ASSERT(reporter, stackBounds.isEmpty());
871 REPORTER_ASSERT(reporter, SkClipStack::kNormal_BoundsType == stackBoundsType);
872
873 SkRegion region;
874 set_region_to_stack(stack, {0, 0, 50, 30}, &region);
875
876 REPORTER_ASSERT(reporter, region.isEmpty());
877}
878
bsalomon@google.com51a62862012-11-26 21:19:43 +0000879///////////////////////////////////////////////////////////////////////////////////////////////////
880
bsalomon@google.coma4e13c82012-11-26 21:38:37 +0000881#if SK_SUPPORT_GPU
bsalomon@google.com705e8402012-11-27 15:43:57 +0000882// Functions that add a shape to the clip stack. The shape is computed from a rectangle.
883// AA is always disabled since the clip stack reducer can cause changes in aa rasterization of the
884// stack. A fractional edge repeated in different elements may be rasterized fewer times using the
885// reduced stack.
886typedef void (*AddElementFunc) (const SkRect& rect,
887 bool invert,
Mike Reedc1f77742016-12-09 09:00:50 -0500888 SkClipOp op,
csmartdaltoncbecb082016-07-22 08:59:08 -0700889 SkClipStack* stack,
890 bool doAA);
bsalomon@google.coma4e13c82012-11-26 21:38:37 +0000891
Mike Reedc1f77742016-12-09 09:00:50 -0500892static void add_round_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700893 bool doAA) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000894 SkScalar rx = rect.width() / 10;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000895 SkScalar ry = rect.height() / 20;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000896 if (invert) {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000897 SkPath path;
898 path.addRoundRect(rect, rx, ry);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000899 path.setFillType(SkPath::kInverseWinding_FillType);
Brian Salomona3b45d42016-10-03 11:36:16 -0400900 stack->clipPath(path, SkMatrix::I(), op, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000901 } else {
902 SkRRect rrect;
903 rrect.setRectXY(rect, rx, ry);
Brian Salomona3b45d42016-10-03 11:36:16 -0400904 stack->clipRRect(rrect, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000905 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000906};
907
Mike Reedc1f77742016-12-09 09:00:50 -0500908static void add_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700909 bool doAA) {
bsalomon@google.com705e8402012-11-27 15:43:57 +0000910 if (invert) {
911 SkPath path;
912 path.addRect(rect);
913 path.setFillType(SkPath::kInverseWinding_FillType);
Brian Salomona3b45d42016-10-03 11:36:16 -0400914 stack->clipPath(path, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000915 } else {
Brian Salomona3b45d42016-10-03 11:36:16 -0400916 stack->clipRect(rect, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000917 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000918};
919
Mike Reedc1f77742016-12-09 09:00:50 -0500920static void add_oval(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700921 bool doAA) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000922 SkPath path;
923 path.addOval(rect);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000924 if (invert) {
925 path.setFillType(SkPath::kInverseWinding_FillType);
926 }
Brian Salomona3b45d42016-10-03 11:36:16 -0400927 stack->clipPath(path, SkMatrix::I(), op, doAA);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000928};
929
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000930static void add_elem_to_stack(const SkClipStack::Element& element, SkClipStack* stack) {
Brian Salomonf3b46e52017-08-30 11:37:57 -0400931 switch (element.getDeviceSpaceType()) {
932 case SkClipStack::Element::DeviceSpaceType::kRect:
933 stack->clipRect(element.getDeviceSpaceRect(), SkMatrix::I(), element.getOp(),
934 element.isAA());
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000935 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400936 case SkClipStack::Element::DeviceSpaceType::kRRect:
937 stack->clipRRect(element.getDeviceSpaceRRect(), SkMatrix::I(), element.getOp(),
938 element.isAA());
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000939 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400940 case SkClipStack::Element::DeviceSpaceType::kPath:
941 stack->clipPath(element.getDeviceSpacePath(), SkMatrix::I(), element.getOp(),
942 element.isAA());
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000943 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400944 case SkClipStack::Element::DeviceSpaceType::kEmpty:
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000945 SkDEBUGFAIL("Why did the reducer produce an explicit empty.");
946 stack->clipEmpty();
947 break;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000948 }
949}
950
bsalomon@google.com51a62862012-11-26 21:19:43 +0000951static void test_reduced_clip_stack(skiatest::Reporter* reporter) {
952 // We construct random clip stacks, reduce them, and then rasterize both versions to verify that
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000953 // they are equal.
bsalomon@google.com51a62862012-11-26 21:19:43 +0000954
955 // All the clip elements will be contained within these bounds.
csmartdaltond211e782016-08-15 11:17:19 -0700956 static const SkIRect kIBounds = SkIRect::MakeWH(100, 100);
957 static const SkRect kBounds = SkRect::Make(kIBounds);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000958
959 enum {
csmartdaltoncbecb082016-07-22 08:59:08 -0700960 kNumTests = 250,
bsalomon@google.com51a62862012-11-26 21:19:43 +0000961 kMinElemsPerTest = 1,
962 kMaxElemsPerTest = 50,
963 };
964
965 // min/max size of a clip element as a fraction of kBounds.
966 static const SkScalar kMinElemSizeFrac = SK_Scalar1 / 5;
967 static const SkScalar kMaxElemSizeFrac = SK_Scalar1;
968
Mike Reedc1f77742016-12-09 09:00:50 -0500969 static const SkClipOp kOps[] = {
970 kDifference_SkClipOp,
971 kIntersect_SkClipOp,
972 kUnion_SkClipOp,
973 kXOR_SkClipOp,
974 kReverseDifference_SkClipOp,
975 kReplace_SkClipOp,
bsalomon@google.com51a62862012-11-26 21:19:43 +0000976 };
977
978 // Replace operations short-circuit the optimizer. We want to make sure that we test this code
979 // path a little bit but we don't want it to prevent us from testing many longer traversals in
980 // the optimizer.
981 static const int kReplaceDiv = 4 * kMaxElemsPerTest;
982
bsalomon@google.com705e8402012-11-27 15:43:57 +0000983 // We want to test inverse fills. However, they are quite rare in practice so don't over do it.
984 static const SkScalar kFractionInverted = SK_Scalar1 / kMaxElemsPerTest;
985
csmartdaltoncbecb082016-07-22 08:59:08 -0700986 static const SkScalar kFractionAntialiased = 0.25;
987
bsalomon@google.com51a62862012-11-26 21:19:43 +0000988 static const AddElementFunc kElementFuncs[] = {
989 add_rect,
990 add_round_rect,
991 add_oval,
992 };
993
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000994 SkRandom r;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000995
996 for (int i = 0; i < kNumTests; ++i) {
csmartdaltoncbecb082016-07-22 08:59:08 -0700997 SkString testCase;
998 testCase.printf("Iteration %d", i);
999
bsalomon@google.com51a62862012-11-26 21:19:43 +00001000 // Randomly generate a clip stack.
1001 SkClipStack stack;
1002 int numElems = r.nextRangeU(kMinElemsPerTest, kMaxElemsPerTest);
csmartdaltoncbecb082016-07-22 08:59:08 -07001003 bool doAA = r.nextBiasedBool(kFractionAntialiased);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001004 for (int e = 0; e < numElems; ++e) {
Mike Reedc1f77742016-12-09 09:00:50 -05001005 SkClipOp op = kOps[r.nextULessThan(SK_ARRAY_COUNT(kOps))];
1006 if (op == kReplace_SkClipOp) {
bsalomon@google.com51a62862012-11-26 21:19:43 +00001007 if (r.nextU() % kReplaceDiv) {
1008 --e;
1009 continue;
1010 }
1011 }
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +00001012
bsalomon@google.com51a62862012-11-26 21:19:43 +00001013 // saves can change the clip stack behavior when an element is added.
1014 bool doSave = r.nextBool();
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +00001015
bsalomon@google.com51a62862012-11-26 21:19:43 +00001016 SkSize size = SkSize::Make(
Mike Reeddf85c382017-02-14 10:59:19 -05001017 kBounds.width() * r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac),
1018 kBounds.height() * r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac));
bsalomon@google.com51a62862012-11-26 21:19:43 +00001019
csmartdaltoncbecb082016-07-22 08:59:08 -07001020 SkPoint xy = {r.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth),
1021 r.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight)};
bsalomon@google.com51a62862012-11-26 21:19:43 +00001022
csmartdaltoncbecb082016-07-22 08:59:08 -07001023 SkRect rect;
1024 if (doAA) {
1025 rect.setXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight);
1026 if (GrClip::IsPixelAligned(rect)) {
1027 // Don't create an element that may accidentally become not antialiased.
1028 rect.outset(0.5f, 0.5f);
1029 }
1030 SkASSERT(!GrClip::IsPixelAligned(rect));
1031 } else {
1032 rect.setXYWH(SkScalarFloorToScalar(xy.fX),
1033 SkScalarFloorToScalar(xy.fY),
1034 SkScalarCeilToScalar(size.fWidth),
1035 SkScalarCeilToScalar(size.fHeight));
1036 }
bsalomon@google.com51a62862012-11-26 21:19:43 +00001037
bsalomon@google.com705e8402012-11-27 15:43:57 +00001038 bool invert = r.nextBiasedBool(kFractionInverted);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001039
csmartdaltoncbecb082016-07-22 08:59:08 -07001040 kElementFuncs[r.nextULessThan(SK_ARRAY_COUNT(kElementFuncs))](rect, invert, op, &stack,
1041 doAA);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001042 if (doSave) {
1043 stack.save();
1044 }
1045 }
1046
Brian Salomon14471772017-12-05 10:35:15 -05001047 auto context = GrContext::MakeMock(nullptr);
1048 const auto* caps = context->caps()->shaderCaps();
1049
csmartdalton8d3f92a2016-08-17 09:39:38 -07001050 // Zero the memory we will new the GrReducedClip into. This ensures the elements gen ID
1051 // will be kInvalidGenID if left uninitialized.
1052 SkAlignedSTStorage<1, GrReducedClip> storage;
1053 memset(storage.get(), 0, sizeof(GrReducedClip));
1054 GR_STATIC_ASSERT(0 == SkClipStack::kInvalidGenID);
1055
csmartdalton77f2fae2016-08-08 09:55:06 -07001056 // Get the reduced version of the stack.
csmartdaltoncbecb082016-07-22 08:59:08 -07001057 SkRect queryBounds = kBounds;
1058 queryBounds.outset(kBounds.width() / 2, kBounds.height() / 2);
Brian Salomon14471772017-12-05 10:35:15 -05001059 const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, queryBounds, caps);
bsalomon@google.coma4444302012-12-04 15:22:12 +00001060
Brian Salomon1c80e992018-01-29 09:50:47 -05001061 REPORTER_ASSERT(reporter,
1062 reduced->maskElements().isEmpty() ||
Chris Dalton79471932017-10-27 01:50:57 -06001063 SkClipStack::kInvalidGenID != reduced->maskGenID(),
Brian Salomon1c80e992018-01-29 09:50:47 -05001064 testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001065
Chris Dalton79471932017-10-27 01:50:57 -06001066 if (!reduced->maskElements().isEmpty()) {
Brian Salomon1c80e992018-01-29 09:50:47 -05001067 REPORTER_ASSERT(reporter, reduced->hasScissor(), testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001068 SkRect stackBounds;
1069 SkClipStack::BoundsType stackBoundsType;
1070 stack.getBounds(&stackBounds, &stackBoundsType);
Brian Salomon1c80e992018-01-29 09:50:47 -05001071 REPORTER_ASSERT(reporter, reduced->maskRequiresAA() == doAA, testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001072 }
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001073
bsalomon@google.com51a62862012-11-26 21:19:43 +00001074 // Build a new clip stack based on the reduced clip elements
1075 SkClipStack reducedStack;
csmartdalton8d3f92a2016-08-17 09:39:38 -07001076 if (GrReducedClip::InitialState::kAllOut == reduced->initialState()) {
bsalomon@google.com51a62862012-11-26 21:19:43 +00001077 // whether the result is bounded or not, the whole plane should start outside the clip.
1078 reducedStack.clipEmpty();
1079 }
Chris Dalton79471932017-10-27 01:50:57 -06001080 for (ElementList::Iter iter(reduced->maskElements()); iter.get(); iter.next()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001081 add_elem_to_stack(*iter.get(), &reducedStack);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001082 }
bsalomon@google.com51a62862012-11-26 21:19:43 +00001083
Chris Dalton79471932017-10-27 01:50:57 -06001084 SkIRect scissor = reduced->hasScissor() ? reduced->scissor() : kIBounds;
csmartdaltond211e782016-08-15 11:17:19 -07001085
bsalomon@google.com34cd70a2012-12-06 14:23:20 +00001086 // GrReducedClipStack assumes that the final result is clipped to the returned bounds
Chris Dalton79471932017-10-27 01:50:57 -06001087 reducedStack.clipDevRect(scissor, kIntersect_SkClipOp);
1088 stack.clipDevRect(scissor, kIntersect_SkClipOp);
bsalomon@google.com34cd70a2012-12-06 14:23:20 +00001089
bsalomon@google.com51a62862012-11-26 21:19:43 +00001090 // convert both the original stack and reduced stack to SkRegions and see if they're equal
bsalomon@google.com51a62862012-11-26 21:19:43 +00001091 SkRegion region;
Chris Dalton79471932017-10-27 01:50:57 -06001092 set_region_to_stack(stack, scissor, &region);
csmartdaltond50e2402016-07-22 08:39:06 -07001093
bsalomon@google.com51a62862012-11-26 21:19:43 +00001094 SkRegion reducedRegion;
Chris Dalton79471932017-10-27 01:50:57 -06001095 set_region_to_stack(reducedStack, scissor, &reducedRegion);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001096
Brian Salomon1c80e992018-01-29 09:50:47 -05001097 REPORTER_ASSERT(reporter, region == reducedRegion, testCase.c_str());
csmartdalton8d3f92a2016-08-17 09:39:38 -07001098
1099 reduced->~GrReducedClip();
bsalomon@google.com51a62862012-11-26 21:19:43 +00001100 }
1101}
1102
halcanary4dbbd042016-06-07 17:21:10 -07001103#ifdef SK_BUILD_FOR_WIN
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001104 #define SUPPRESS_VISIBILITY_WARNING
1105#else
1106 #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden")))
1107#endif
1108
1109static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) {
1110 {
1111 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001112 stack.clipRect(SkRect::MakeXYWH(0, 0, 100, 100), SkMatrix::I(), kReplace_SkClipOp,
Brian Salomona3b45d42016-10-03 11:36:16 -04001113 true);
1114 stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(50.3), SkScalar(50.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001115 kReplace_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001116 SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001117
Brian Salomon14471772017-12-05 10:35:15 -05001118 auto context = GrContext::MakeMock(nullptr);
1119 const auto* caps = context->caps()->shaderCaps();
1120
csmartdalton8d3f92a2016-08-17 09:39:38 -07001121 SkAlignedSTStorage<1, GrReducedClip> storage;
1122 memset(storage.get(), 0, sizeof(GrReducedClip));
1123 GR_STATIC_ASSERT(0 == SkClipStack::kInvalidGenID);
Brian Salomon14471772017-12-05 10:35:15 -05001124 const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, bounds, caps);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001125
Chris Dalton79471932017-10-27 01:50:57 -06001126 REPORTER_ASSERT(reporter, reduced->maskElements().count() == 1);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001127 // Clips will be cached based on the generation id. Make sure the gen id is valid.
Chris Dalton79471932017-10-27 01:50:57 -06001128 REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reduced->maskGenID());
csmartdalton8d3f92a2016-08-17 09:39:38 -07001129
1130 reduced->~GrReducedClip();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001131 }
1132 {
1133 SkClipStack stack;
1134
1135 // Create a clip with following 25.3, 25.3 boxes which are 25 apart:
1136 // A B
1137 // C D
1138
Brian Salomona3b45d42016-10-03 11:36:16 -04001139 stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001140 kReplace_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001141 uint32_t genIDA = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001142 stack.clipRect(SkRect::MakeXYWH(50, 0, 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 genIDB = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001145 stack.clipRect(SkRect::MakeXYWH(0, 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 genIDC = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001148 stack.clipRect(SkRect::MakeXYWH(50, 50, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001149 kUnion_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001150 uint32_t genIDD = stack.getTopmostGenID();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001151
1152
csmartdaltoncbecb082016-07-22 08:59:08 -07001153#define IXYWH SkIRect::MakeXYWH
1154#define XYWH SkRect::MakeXYWH
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001155
csmartdaltoncbecb082016-07-22 08:59:08 -07001156 SkIRect stackBounds = IXYWH(0, 0, 76, 76);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001157
1158 // The base test is to test each rect in two ways:
1159 // 1) The box dimensions. (Should reduce to "all in", no elements).
1160 // 2) A bit over the box dimensions.
1161 // In the case 2, test that the generation id is what is expected.
1162 // The rects are of fractional size so that case 2 never gets optimized to an empty element
1163 // list.
1164
1165 // Not passing in tighter bounds is tested for consistency.
1166 static const struct SUPPRESS_VISIBILITY_WARNING {
csmartdaltoncbecb082016-07-22 08:59:08 -07001167 SkRect testBounds;
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001168 int reducedClipCount;
Robert Phillips806be2d2017-06-28 15:23:59 -04001169 uint32_t reducedGenID;
csmartdaltoncbecb082016-07-22 08:59:08 -07001170 InitialState initialState;
1171 SkIRect clipIRect;
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001172 // parameter.
1173 } testCases[] = {
csmartdalton77f2fae2016-08-08 09:55:06 -07001174
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001175 // Rect A.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001176 { XYWH(0, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 0, 25, 25) },
1177 { 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 -06001178 { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::InitialState::kAllOut, IXYWH(0, 0, 26, 26)},
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001179
1180 // Rect B.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001181 { XYWH(50, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 25, 25) },
1182 { XYWH(50, 0, 25.3f, 25.3f), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 26, 26) },
csmartdalton77f2fae2016-08-08 09:55:06 -07001183 { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::InitialState::kAllOut, IXYWH(50, 0, 26, 27) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001184
1185 // Rect C.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001186 { XYWH(0, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 50, 25, 25) },
1187 { 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 -07001188 { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::InitialState::kAllOut, IXYWH(0, 50, 27, 26) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001189
1190 // Rect D.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001191 { XYWH(50, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 25, 25)},
1192 { XYWH(50.3f, 50.3f, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 26, 26)},
csmartdalton77f2fae2016-08-08 09:55:06 -07001193 { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(50, 50, 26, 26)},
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001194
1195 // Other tests:
csmartdalton77f2fae2016-08-08 09:55:06 -07001196 { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::InitialState::kAllOut, stackBounds },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001197
1198 // Rect in the middle, touches none.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001199 { 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 +00001200
1201 // Rect in the middle, touches all the rects. GenID is the last rect.
csmartdalton77f2fae2016-08-08 09:55:06 -07001202 { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(24, 24, 27, 27) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001203 };
1204
1205#undef XYWH
csmartdaltoncbecb082016-07-22 08:59:08 -07001206#undef IXYWH
Brian Salomon14471772017-12-05 10:35:15 -05001207 auto context = GrContext::MakeMock(nullptr);
1208 const auto* caps = context->caps()->shaderCaps();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001209
1210 for (size_t i = 0; i < SK_ARRAY_COUNT(testCases); ++i) {
Brian Salomon14471772017-12-05 10:35:15 -05001211 const GrReducedClip reduced(stack, testCases[i].testBounds, caps);
Chris Dalton79471932017-10-27 01:50:57 -06001212 REPORTER_ASSERT(reporter, reduced.maskElements().count() ==
1213 testCases[i].reducedClipCount);
1214 SkASSERT(reduced.maskElements().count() == testCases[i].reducedClipCount);
1215 if (reduced.maskElements().count()) {
1216 REPORTER_ASSERT(reporter, reduced.maskGenID() == testCases[i].reducedGenID);
1217 SkASSERT(reduced.maskGenID() == testCases[i].reducedGenID);
csmartdalton8d3f92a2016-08-17 09:39:38 -07001218 }
csmartdalton77f2fae2016-08-08 09:55:06 -07001219 REPORTER_ASSERT(reporter, reduced.initialState() == testCases[i].initialState);
1220 SkASSERT(reduced.initialState() == testCases[i].initialState);
Chris Dalton79471932017-10-27 01:50:57 -06001221 REPORTER_ASSERT(reporter, reduced.hasScissor());
1222 SkASSERT(reduced.hasScissor());
1223 REPORTER_ASSERT(reporter, reduced.scissor() == testCases[i].clipIRect);
1224 SkASSERT(reduced.scissor() == testCases[i].clipIRect);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001225 }
1226 }
1227}
1228
1229static void test_reduced_clip_stack_no_aa_crash(skiatest::Reporter* reporter) {
1230 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001231 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 100, 100), kReplace_SkClipOp);
1232 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 50, 50), kReplace_SkClipOp);
csmartdaltoncbecb082016-07-22 08:59:08 -07001233 SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001234
Brian Salomon14471772017-12-05 10:35:15 -05001235 auto context = GrContext::MakeMock(nullptr);
1236 const auto* caps = context->caps()->shaderCaps();
1237
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001238 // At the time, this would crash.
Brian Salomon14471772017-12-05 10:35:15 -05001239 const GrReducedClip reduced(stack, bounds, caps);
Chris Dalton79471932017-10-27 01:50:57 -06001240 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001241}
1242
csmartdaltoncbecb082016-07-22 08:59:08 -07001243enum class ClipMethod {
1244 kSkipDraw,
1245 kIgnoreClip,
1246 kScissor,
1247 kAAElements
1248};
1249
1250static void test_aa_query(skiatest::Reporter* reporter, const SkString& testName,
1251 const SkClipStack& stack, const SkMatrix& queryXform,
1252 const SkRect& preXformQuery, ClipMethod expectedMethod,
1253 int numExpectedElems = 0) {
Brian Salomon14471772017-12-05 10:35:15 -05001254 auto context = GrContext::MakeMock(nullptr);
1255 const auto* caps = context->caps()->shaderCaps();
1256
csmartdaltoncbecb082016-07-22 08:59:08 -07001257 SkRect queryBounds;
1258 queryXform.mapRect(&queryBounds, preXformQuery);
Brian Salomon14471772017-12-05 10:35:15 -05001259 const GrReducedClip reduced(stack, queryBounds, caps);
csmartdaltoncbecb082016-07-22 08:59:08 -07001260
1261 SkClipStack::BoundsType stackBoundsType;
1262 SkRect stackBounds;
1263 stack.getBounds(&stackBounds, &stackBoundsType);
1264
1265 switch (expectedMethod) {
1266 case ClipMethod::kSkipDraw:
1267 SkASSERT(0 == numExpectedElems);
Brian Salomon1c80e992018-01-29 09:50:47 -05001268 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty(), testName.c_str());
1269 REPORTER_ASSERT(reporter,
1270 GrReducedClip::InitialState::kAllOut == reduced.initialState(),
1271 testName.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001272 return;
1273 case ClipMethod::kIgnoreClip:
1274 SkASSERT(0 == numExpectedElems);
Brian Salomon1c80e992018-01-29 09:50:47 -05001275 REPORTER_ASSERT(
1276 reporter,
1277 !reduced.hasScissor() || GrClip::IsInsideClip(reduced.scissor(), queryBounds),
1278 testName.c_str());
1279 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty(), testName.c_str());
1280 REPORTER_ASSERT(reporter,
1281 GrReducedClip::InitialState::kAllIn == reduced.initialState(),
1282 testName.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001283 return;
1284 case ClipMethod::kScissor: {
1285 SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType);
1286 SkASSERT(0 == numExpectedElems);
1287 SkIRect expectedScissor;
1288 stackBounds.round(&expectedScissor);
Brian Salomon1c80e992018-01-29 09:50:47 -05001289 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty(), testName.c_str());
1290 REPORTER_ASSERT(reporter, reduced.hasScissor(), testName.c_str());
1291 REPORTER_ASSERT(reporter, expectedScissor == reduced.scissor(), testName.c_str());
1292 REPORTER_ASSERT(reporter,
1293 GrReducedClip::InitialState::kAllIn == reduced.initialState(),
1294 testName.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001295 return;
1296 }
1297 case ClipMethod::kAAElements: {
1298 SkIRect expectedClipIBounds = GrClip::GetPixelIBounds(queryBounds);
1299 if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
1300 SkAssertResult(expectedClipIBounds.intersect(GrClip::GetPixelIBounds(stackBounds)));
1301 }
Brian Salomon1c80e992018-01-29 09:50:47 -05001302 REPORTER_ASSERT(reporter, numExpectedElems == reduced.maskElements().count(),
1303 testName.c_str());
1304 REPORTER_ASSERT(reporter, reduced.hasScissor(), testName.c_str());
1305 REPORTER_ASSERT(reporter, expectedClipIBounds == reduced.scissor(), testName.c_str());
1306 REPORTER_ASSERT(reporter,
1307 reduced.maskElements().isEmpty() || reduced.maskRequiresAA(),
1308 testName.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001309 break;
1310 }
1311 }
1312}
1313
1314static void test_reduced_clip_stack_aa(skiatest::Reporter* reporter) {
1315 constexpr SkScalar IL = 2, IT = 1, IR = 6, IB = 7; // Pixel aligned rect.
1316 constexpr SkScalar L = 2.2f, T = 1.7f, R = 5.8f, B = 7.3f; // Generic rect.
1317 constexpr SkScalar l = 3.3f, t = 2.8f, r = 4.7f, b = 6.2f; // Small rect contained in R.
1318
1319 SkRect alignedRect = {IL, IT, IR, IB};
1320 SkRect rect = {L, T, R, B};
1321 SkRect innerRect = {l, t, r, b};
1322
1323 SkMatrix m;
1324 m.setIdentity();
1325
1326 constexpr SkScalar kMinScale = 2.0001f;
1327 constexpr SkScalar kMaxScale = 3;
1328 constexpr int kNumIters = 8;
1329
1330 SkString name;
1331 SkRandom rand;
1332
1333 for (int i = 0; i < kNumIters; ++i) {
1334 // Pixel-aligned rect (iior=true).
1335 name.printf("Pixel-aligned rect test, iter %i", i);
1336 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001337 stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001338 test_aa_query(reporter, name, stack, m, {IL, IT, IR, IB}, ClipMethod::kIgnoreClip);
1339 test_aa_query(reporter, name, stack, m, {IL, IT-1, IR, IT}, ClipMethod::kSkipDraw);
csmartdalton77f2fae2016-08-08 09:55:06 -07001340 test_aa_query(reporter, name, stack, m, {IL, IT-2, IR, IB}, ClipMethod::kScissor);
csmartdaltoncbecb082016-07-22 08:59:08 -07001341
1342 // Rect (iior=true).
1343 name.printf("Rect test, iter %i", i);
1344 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001345 stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001346 test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kIgnoreClip);
1347 test_aa_query(reporter, name, stack, m, {L-.1f, T, L, B}, ClipMethod::kSkipDraw);
1348 test_aa_query(reporter, name, stack, m, {L-.1f, T, L+.1f, B}, ClipMethod::kAAElements, 1);
1349
1350 // Difference rect (iior=false, inside-out bounds).
1351 name.printf("Difference rect test, iter %i", i);
1352 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001353 stack.clipRect(rect, SkMatrix::I(), kDifference_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001354 test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kSkipDraw);
1355 test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T}, ClipMethod::kIgnoreClip);
1356 test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T+.1f}, ClipMethod::kAAElements, 1);
1357
1358 // Complex clip (iior=false, normal bounds).
1359 name.printf("Complex clip test, iter %i", i);
1360 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001361 stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true);
1362 stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001363 test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
1364 test_aa_query(reporter, name, stack, m, {r-.1f, t, R, b}, ClipMethod::kAAElements, 1);
1365 test_aa_query(reporter, name, stack, m, {r-.1f, t, R+.1f, b}, ClipMethod::kAAElements, 2);
1366 test_aa_query(reporter, name, stack, m, {r, t, R+.1f, b}, ClipMethod::kAAElements, 1);
1367 test_aa_query(reporter, name, stack, m, {r, t, R, b}, ClipMethod::kIgnoreClip);
1368 test_aa_query(reporter, name, stack, m, {R, T, R+.1f, B}, ClipMethod::kSkipDraw);
1369
1370 // Complex clip where outer rect is pixel aligned (iior=false, normal bounds).
1371 name.printf("Aligned Complex clip test, iter %i", i);
1372 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001373 stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true);
1374 stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001375 test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
1376 test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB}, ClipMethod::kAAElements, 1);
1377 test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB+.1f}, ClipMethod::kAAElements, 1);
1378 test_aa_query(reporter, name, stack, m, {l, b, r, IB+.1f}, ClipMethod::kAAElements, 0);
1379 test_aa_query(reporter, name, stack, m, {l, b, r, IB}, ClipMethod::kIgnoreClip);
1380 test_aa_query(reporter, name, stack, m, {IL, IB, IR, IB+.1f}, ClipMethod::kSkipDraw);
1381
1382 // Apply random transforms and try again. This ensures the clip stack reduction is hardened
1383 // against FP rounding error.
1384 SkScalar sx = rand.nextRangeScalar(kMinScale, kMaxScale);
1385 sx = SkScalarFloorToScalar(sx * alignedRect.width()) / alignedRect.width();
1386 SkScalar sy = rand.nextRangeScalar(kMinScale, kMaxScale);
1387 sy = SkScalarFloorToScalar(sy * alignedRect.height()) / alignedRect.height();
1388 SkScalar tx = SkScalarRoundToScalar(sx * alignedRect.x()) - sx * alignedRect.x();
1389 SkScalar ty = SkScalarRoundToScalar(sy * alignedRect.y()) - sy * alignedRect.y();
1390
1391 SkMatrix xform = SkMatrix::MakeScale(sx, sy);
1392 xform.postTranslate(tx, ty);
1393 xform.mapRect(&alignedRect);
1394 xform.mapRect(&rect);
1395 xform.mapRect(&innerRect);
1396 m.postConcat(xform);
1397 }
1398}
1399
Chris Dalton348060f2017-06-05 13:15:37 -06001400static void test_tiny_query_bounds_assertion_bug(skiatest::Reporter* reporter) {
1401 // https://bugs.chromium.org/p/skia/issues/detail?id=5990
1402 const SkRect clipBounds = SkRect::MakeXYWH(1.5f, 100, 1000, 1000);
1403
1404 SkClipStack rectStack;
1405 rectStack.clipRect(clipBounds, SkMatrix::I(), kIntersect_SkClipOp, true);
1406
1407 SkPath clipPath;
1408 clipPath.moveTo(clipBounds.left(), clipBounds.top());
1409 clipPath.quadTo(clipBounds.right(), clipBounds.top(),
1410 clipBounds.right(), clipBounds.bottom());
1411 clipPath.quadTo(clipBounds.left(), clipBounds.bottom(),
1412 clipBounds.left(), clipBounds.top());
1413 SkClipStack pathStack;
1414 pathStack.clipPath(clipPath, SkMatrix::I(), kIntersect_SkClipOp, true);
1415
Brian Salomon14471772017-12-05 10:35:15 -05001416 auto context = GrContext::MakeMock(nullptr);
1417 const auto* caps = context->caps()->shaderCaps();
1418
Chris Dalton348060f2017-06-05 13:15:37 -06001419 for (const SkClipStack& stack : {rectStack, pathStack}) {
1420 for (SkRect queryBounds : {SkRect::MakeXYWH(53, 60, GrClip::kBoundsTolerance, 1000),
1421 SkRect::MakeXYWH(53, 60, GrClip::kBoundsTolerance/2, 1000),
1422 SkRect::MakeXYWH(53, 160, 1000, GrClip::kBoundsTolerance),
1423 SkRect::MakeXYWH(53, 160, 1000, GrClip::kBoundsTolerance/2)}) {
Brian Salomon14471772017-12-05 10:35:15 -05001424 const GrReducedClip reduced(stack, queryBounds, caps);
Chris Dalton79471932017-10-27 01:50:57 -06001425 REPORTER_ASSERT(reporter, !reduced.hasScissor());
1426 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty());
Chris Dalton348060f2017-06-05 13:15:37 -06001427 REPORTER_ASSERT(reporter,
1428 GrReducedClip::InitialState::kAllOut == reduced.initialState());
1429 }
1430 }
1431}
1432
bsalomon@google.coma4e13c82012-11-26 21:38:37 +00001433#endif
bsalomon@google.com51a62862012-11-26 21:19:43 +00001434
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00001435DEF_TEST(ClipStack, reporter) {
reed@google.combdee9fc2011-02-22 20:17:43 +00001436 SkClipStack stack;
1437
robertphillips@google.com80214e22012-07-20 15:33:18 +00001438 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001439 assert_count(reporter, stack, 0);
1440
1441 static const SkIRect gRects[] = {
1442 { 0, 0, 100, 100 },
1443 { 25, 25, 125, 125 },
1444 { 0, 0, 1000, 1000 },
1445 { 0, 0, 75, 75 }
1446 };
1447 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
Mike Reedc1f77742016-12-09 09:00:50 -05001448 stack.clipDevRect(gRects[i], kIntersect_SkClipOp);
reed@google.combdee9fc2011-02-22 20:17:43 +00001449 }
1450
1451 // all of the above rects should have been intersected, leaving only 1 rect
robertphillips@google.com80214e22012-07-20 15:33:18 +00001452 SkClipStack::B2TIter iter(stack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001453 const SkClipStack::Element* element = iter.next();
epoger@google.com2047f002011-05-17 17:36:59 +00001454 SkRect answer;
1455 answer.iset(25, 25, 75, 75);
reed@google.combdee9fc2011-02-22 20:17:43 +00001456
bsalomon49f085d2014-09-05 13:34:00 -07001457 REPORTER_ASSERT(reporter, element);
Brian Salomonf3b46e52017-08-30 11:37:57 -04001458 REPORTER_ASSERT(reporter,
1459 SkClipStack::Element::DeviceSpaceType::kRect == element->getDeviceSpaceType());
Mike Reedc1f77742016-12-09 09:00:50 -05001460 REPORTER_ASSERT(reporter, kIntersect_SkClipOp == element->getOp());
Brian Salomonf3b46e52017-08-30 11:37:57 -04001461 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == answer);
reed@google.combdee9fc2011-02-22 20:17:43 +00001462 // now check that we only had one in our iterator
1463 REPORTER_ASSERT(reporter, !iter.next());
1464
1465 stack.reset();
robertphillips@google.com80214e22012-07-20 15:33:18 +00001466 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001467 assert_count(reporter, stack, 0);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +00001468
1469 test_assign_and_comparison(reporter);
robertphillips@google.com80214e22012-07-20 15:33:18 +00001470 test_iterators(reporter);
Brian Salomonf3b46e52017-08-30 11:37:57 -04001471 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kRect);
1472 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kRRect);
1473 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kPath);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +00001474 test_isWideOpen(reporter);
robertphillips@google.com08eacc12012-08-02 12:49:00 +00001475 test_rect_merging(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001476 test_rect_replace(reporter);
junov@chromium.orgedf32d52012-12-10 14:57:54 +00001477 test_rect_inverse_fill(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001478 test_path_replace(reporter);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +00001479 test_quickContains(reporter);
csmartdaltond50e2402016-07-22 08:39:06 -07001480 test_invfill_diff_bug(reporter);
bsalomon@google.come7b3d292012-11-26 21:42:32 +00001481#if SK_SUPPORT_GPU
bsalomon@google.comedb26fd2012-11-28 14:42:41 +00001482 test_reduced_clip_stack(reporter);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001483 test_reduced_clip_stack_genid(reporter);
1484 test_reduced_clip_stack_no_aa_crash(reporter);
csmartdaltoncbecb082016-07-22 08:59:08 -07001485 test_reduced_clip_stack_aa(reporter);
Chris Dalton348060f2017-06-05 13:15:37 -06001486 test_tiny_query_bounds_assertion_bug(reporter);
bsalomon@google.come7b3d292012-11-26 21:42:32 +00001487#endif
reed@google.combdee9fc2011-02-22 20:17:43 +00001488}
Brian Salomon19f0ed52017-01-06 13:54:58 -05001489
1490//////////////////////////////////////////////////////////////////////////////
1491
1492#if SK_SUPPORT_GPU
Robert Phillips875218e2017-02-24 08:37:13 -05001493sk_sp<GrTextureProxy> GrClipStackClip::testingOnly_createClipMask(GrContext* context) const {
Brian Salomon19f0ed52017-01-06 13:54:58 -05001494 const GrReducedClip reducedClip(*fStack, SkRect::MakeWH(512, 512), 0);
Brian Osman5d034742017-09-11 13:38:55 -04001495 return this->createSoftwareClipMask(context, reducedClip, nullptr);
Brian Salomon19f0ed52017-01-06 13:54:58 -05001496}
1497
1498// Verify that clip masks are freed up when the clip state that generated them goes away.
1499DEF_GPUTEST_FOR_ALL_CONTEXTS(ClipMaskCache, reporter, ctxInfo) {
1500 // This test uses resource key tags which only function in debug builds.
1501#ifdef SK_DEBUG
1502 GrContext* context = ctxInfo.grContext();
1503 SkClipStack stack;
1504
1505 SkPath path;
1506 path.addCircle(10, 10, 8);
1507 path.addCircle(15, 15, 8);
1508 path.setFillType(SkPath::kEvenOdd_FillType);
1509
1510 static const char* kTag = GrClipStackClip::kMaskTestTag;
Robert Phillips6be756b2018-01-16 15:07:54 -05001511 GrResourceCache* cache = context->contextPriv().getResourceCache();
Brian Salomon19f0ed52017-01-06 13:54:58 -05001512
1513 static constexpr int kN = 5;
1514
1515 for (int i = 0; i < kN; ++i) {
1516 SkMatrix m;
1517 m.setTranslate(0.5, 0.5);
1518 stack.save();
1519 stack.clipPath(path, m, SkClipOp::kIntersect, true);
Robert Phillips875218e2017-02-24 08:37:13 -05001520 sk_sp<GrTextureProxy> mask = GrClipStackClip(&stack).testingOnly_createClipMask(context);
Robert Phillips6be756b2018-01-16 15:07:54 -05001521 mask->instantiate(context->contextPriv().resourceProvider());
Robert Phillipseee4d6e2017-06-05 09:26:07 -04001522 GrTexture* tex = mask->priv().peekTexture();
Robert Phillips875218e2017-02-24 08:37:13 -05001523 REPORTER_ASSERT(reporter, 0 == strcmp(tex->getUniqueKey().tag(), kTag));
Brian Salomon19f0ed52017-01-06 13:54:58 -05001524 // Make sure mask isn't pinned in cache.
1525 mask.reset(nullptr);
1526 context->flush();
1527 REPORTER_ASSERT(reporter, i + 1 == cache->countUniqueKeysWithTag(kTag));
1528 }
1529
1530 for (int i = 0; i < kN; ++i) {
1531 stack.restore();
1532 cache->purgeAsNeeded();
1533 REPORTER_ASSERT(reporter, kN - (i + 1) == cache->countUniqueKeysWithTag(kTag));
1534 }
1535#endif
1536}
1537
Mike Reed3726a4a2017-01-19 11:36:41 -05001538DEF_GPUTEST_FOR_ALL_CONTEXTS(canvas_private_clipRgn, reporter, ctxInfo) {
1539 GrContext* context = ctxInfo.grContext();
1540
1541 const int w = 10;
1542 const int h = 10;
Brian Salomon8996e182017-07-05 17:01:48 -04001543 SkImageInfo info = SkImageInfo::Make(w, h, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
Mike Reed3726a4a2017-01-19 11:36:41 -05001544 sk_sp<SkSurface> surf = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info);
1545 SkCanvas* canvas = surf->getCanvas();
1546 SkRegion rgn;
1547
1548 canvas->temporary_internal_getRgnClip(&rgn);
1549 REPORTER_ASSERT(reporter, rgn.isRect());
1550 REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeWH(w, h));
1551
1552 canvas->save();
1553 canvas->clipRect(SkRect::MakeWH(5, 5), kDifference_SkClipOp);
1554 canvas->temporary_internal_getRgnClip(&rgn);
1555 REPORTER_ASSERT(reporter, rgn.isComplex());
1556 REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeWH(w, h));
1557 canvas->restore();
1558
1559 canvas->save();
1560 canvas->clipRRect(SkRRect::MakeOval(SkRect::MakeLTRB(3, 3, 7, 7)));
1561 canvas->temporary_internal_getRgnClip(&rgn);
1562 REPORTER_ASSERT(reporter, rgn.isComplex());
1563 REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeLTRB(3, 3, 7, 7));
1564 canvas->restore();
1565}
Brian Salomon19f0ed52017-01-06 13:54:58 -05001566#endif