blob: fe24bc14e6b0a498fad6afa014641a3ad7a143ab [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
reed@google.combdee9fc2011-02-22 20:17:43 +00008#include "Test.h"
9#include "SkClipStack.h"
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000010#include "SkPath.h"
bsalomon@google.com51a62862012-11-26 21:19:43 +000011#include "SkRandom.h"
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000012#include "SkRect.h"
bsalomon@google.com51a62862012-11-26 21:19:43 +000013#include "SkRegion.h"
robertphillips@google.com80214e22012-07-20 15:33:18 +000014
csmartdaltoncbecb082016-07-22 08:59:08 -070015#if SK_SUPPORT_GPU
Brian Salomon19f0ed52017-01-06 13:54:58 -050016#include "GrClipStackClip.h"
csmartdaltoncbecb082016-07-22 08:59:08 -070017#include "GrReducedClip.h"
Brian Salomon19f0ed52017-01-06 13:54:58 -050018#include "GrResourceCache.h"
Robert Phillipseee4d6e2017-06-05 09:26:07 -040019#include "GrSurfaceProxyPriv.h"
Robert Phillipsd9d84852017-06-09 10:48:29 -040020#include "GrTexture.h"
Mike Reed84dd8572017-03-08 22:21:00 -050021#include "GrTextureProxy.h"
csmartdaltoncbecb082016-07-22 08:59:08 -070022typedef GrReducedClip::ElementList ElementList;
23typedef GrReducedClip::InitialState InitialState;
24#endif
25
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000026static void test_assign_and_comparison(skiatest::Reporter* reporter) {
27 SkClipStack s;
reed@google.comd9f2dea2011-10-12 14:43:27 +000028 bool doAA = false;
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000029
robertphillips@google.com80214e22012-07-20 15:33:18 +000030 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
31
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000032 // Build up a clip stack with a path, an empty clip, and a rect.
33 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000034 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
35
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000036 SkPath p;
37 p.moveTo(5, 6);
38 p.lineTo(7, 8);
39 p.lineTo(5, 9);
40 p.close();
Mike Reedc1f77742016-12-09 09:00:50 -050041 s.clipPath(p, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000042
43 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000044 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
45
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000046 SkRect r = SkRect::MakeLTRB(1, 2, 3, 4);
Mike Reedc1f77742016-12-09 09:00:50 -050047 s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000048 r = SkRect::MakeLTRB(10, 11, 12, 13);
Mike Reedc1f77742016-12-09 09:00:50 -050049 s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000050
51 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000052 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
53
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000054 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -050055 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000056
57 // Test that assignment works.
58 SkClipStack copy = s;
59 REPORTER_ASSERT(reporter, s == copy);
60
61 // Test that different save levels triggers not equal.
62 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000063 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000064 REPORTER_ASSERT(reporter, s != copy);
65
66 // Test that an equal, but not copied version is equal.
67 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000068 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000069 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -050070 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000071 REPORTER_ASSERT(reporter, s == copy);
72
73 // Test that a different op on one level triggers not equal.
74 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000075 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000076 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000077 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000078 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -050079 s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000080 REPORTER_ASSERT(reporter, s != copy);
81
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000082 // Test that version constructed with rect-path rather than a rect is still considered equal.
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000083 s.restore();
84 s.save();
85 SkPath rp;
86 rp.addRect(r);
Mike Reedc1f77742016-12-09 09:00:50 -050087 s.clipPath(rp, SkMatrix::I(), kUnion_SkClipOp, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000088 REPORTER_ASSERT(reporter, s == copy);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000089
90 // Test that different rects triggers not equal.
91 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000092 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000093 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000094 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
95
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000096 r = SkRect::MakeLTRB(24, 25, 26, 27);
Mike Reedc1f77742016-12-09 09:00:50 -050097 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000098 REPORTER_ASSERT(reporter, s != copy);
99
100 // Sanity check
101 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000102 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
103
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000104 copy.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000105 REPORTER_ASSERT(reporter, 2 == copy.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000106 REPORTER_ASSERT(reporter, s == copy);
107 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000108 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000109 copy.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000110 REPORTER_ASSERT(reporter, 1 == copy.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000111 REPORTER_ASSERT(reporter, s == copy);
112
113 // Test that different paths triggers not equal.
114 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000115 REPORTER_ASSERT(reporter, 0 == 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, 1 == s.getSaveCount());
118
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000119 p.addRect(r);
Mike Reedc1f77742016-12-09 09:00:50 -0500120 s.clipPath(p, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000121 REPORTER_ASSERT(reporter, s != copy);
122}
reed@google.combdee9fc2011-02-22 20:17:43 +0000123
124static void assert_count(skiatest::Reporter* reporter, const SkClipStack& stack,
125 int count) {
robertphillips@google.com80214e22012-07-20 15:33:18 +0000126 SkClipStack::B2TIter iter(stack);
reed@google.combdee9fc2011-02-22 20:17:43 +0000127 int counter = 0;
128 while (iter.next()) {
129 counter += 1;
130 }
131 REPORTER_ASSERT(reporter, count == counter);
132}
133
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000134// Exercise the SkClipStack's bottom to top and bidirectional iterators
135// (including the skipToTopmost functionality)
robertphillips@google.com80214e22012-07-20 15:33:18 +0000136static void test_iterators(skiatest::Reporter* reporter) {
137 SkClipStack stack;
138
139 static const SkRect gRects[] = {
140 { 0, 0, 40, 40 },
141 { 60, 0, 100, 40 },
142 { 0, 60, 40, 100 },
143 { 60, 60, 100, 100 }
144 };
145
146 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
147 // the union op will prevent these from being fused together
Mike Reedc1f77742016-12-09 09:00:50 -0500148 stack.clipRect(gRects[i], SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000149 }
150
151 assert_count(reporter, stack, 4);
152
153 // bottom to top iteration
154 {
halcanary96fcdcc2015-08-27 07:41:13 -0700155 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000156
157 SkClipStack::B2TIter iter(stack);
158 int i;
159
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000160 for (i = 0, element = iter.next(); element; ++i, element = iter.next()) {
Brian Salomonf3b46e52017-08-30 11:37:57 -0400161 REPORTER_ASSERT(reporter, SkClipStack::Element::DeviceSpaceType::kRect ==
162 element->getDeviceSpaceType());
163 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == gRects[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000164 }
165
166 SkASSERT(i == 4);
167 }
168
169 // top to bottom iteration
170 {
halcanary96fcdcc2015-08-27 07:41:13 -0700171 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000172
173 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
174 int i;
175
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000176 for (i = 3, element = iter.prev(); element; --i, element = iter.prev()) {
Brian Salomonf3b46e52017-08-30 11:37:57 -0400177 REPORTER_ASSERT(reporter, SkClipStack::Element::DeviceSpaceType::kRect ==
178 element->getDeviceSpaceType());
179 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == gRects[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000180 }
181
182 SkASSERT(i == -1);
183 }
184
185 // skipToTopmost
186 {
halcanary96fcdcc2015-08-27 07:41:13 -0700187 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000188
189 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
190
Mike Reedc1f77742016-12-09 09:00:50 -0500191 element = iter.skipToTopmost(kUnion_SkClipOp);
Brian Salomonf3b46e52017-08-30 11:37:57 -0400192 REPORTER_ASSERT(reporter, SkClipStack::Element::DeviceSpaceType::kRect ==
193 element->getDeviceSpaceType());
194 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == gRects[3]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000195 }
196}
197
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000198// Exercise the SkClipStack's getConservativeBounds computation
Brian Salomonf3b46e52017-08-30 11:37:57 -0400199static void test_bounds(skiatest::Reporter* reporter,
200 SkClipStack::Element::DeviceSpaceType primType) {
robertphillips@google.com607fe072012-07-24 13:54:00 +0000201 static const int gNumCases = 20;
202 static const SkRect gAnswerRectsBW[gNumCases] = {
203 // A op B
204 { 40, 40, 50, 50 },
205 { 10, 10, 50, 50 },
206 { 10, 10, 80, 80 },
207 { 10, 10, 80, 80 },
208 { 40, 40, 80, 80 },
209
210 // invA op B
211 { 40, 40, 80, 80 },
212 { 0, 0, 100, 100 },
213 { 0, 0, 100, 100 },
214 { 0, 0, 100, 100 },
215 { 40, 40, 50, 50 },
216
217 // A op invB
218 { 10, 10, 50, 50 },
219 { 40, 40, 50, 50 },
220 { 0, 0, 100, 100 },
221 { 0, 0, 100, 100 },
222 { 0, 0, 100, 100 },
223
224 // invA op invB
225 { 0, 0, 100, 100 },
226 { 40, 40, 80, 80 },
227 { 0, 0, 100, 100 },
228 { 10, 10, 80, 80 },
229 { 10, 10, 50, 50 },
230 };
231
Mike Reedc1f77742016-12-09 09:00:50 -0500232 static const SkClipOp gOps[] = {
233 kIntersect_SkClipOp,
234 kDifference_SkClipOp,
235 kUnion_SkClipOp,
236 kXOR_SkClipOp,
237 kReverseDifference_SkClipOp
robertphillips@google.com607fe072012-07-24 13:54:00 +0000238 };
239
240 SkRect rectA, rectB;
241
242 rectA.iset(10, 10, 50, 50);
243 rectB.iset(40, 40, 80, 80);
244
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000245 SkRRect rrectA, rrectB;
246 rrectA.setOval(rectA);
247 rrectB.setRectXY(rectB, SkIntToScalar(1), SkIntToScalar(2));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000248
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000249 SkPath pathA, pathB;
250
251 pathA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
252 pathB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000253
254 SkClipStack stack;
robertphillips@google.com7b112892012-07-31 15:18:21 +0000255 SkRect devClipBound;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000256 bool isIntersectionOfRects = false;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000257
258 int testCase = 0;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400259 int numBitTests = SkClipStack::Element::DeviceSpaceType::kPath == primType ? 4 : 1;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000260 for (int invBits = 0; invBits < numBitTests; ++invBits) {
robertphillips@google.com607fe072012-07-24 13:54:00 +0000261 for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) {
262
263 stack.save();
264 bool doInvA = SkToBool(invBits & 1);
265 bool doInvB = SkToBool(invBits & 2);
266
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000267 pathA.setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType :
robertphillips@google.com607fe072012-07-24 13:54:00 +0000268 SkPath::kEvenOdd_FillType);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000269 pathB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType :
robertphillips@google.com607fe072012-07-24 13:54:00 +0000270 SkPath::kEvenOdd_FillType);
271
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000272 switch (primType) {
Brian Salomonf3b46e52017-08-30 11:37:57 -0400273 case SkClipStack::Element::DeviceSpaceType::kEmpty:
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000274 SkDEBUGFAIL("Don't call this with kEmpty.");
275 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400276 case SkClipStack::Element::DeviceSpaceType::kRect:
Mike Reedc1f77742016-12-09 09:00:50 -0500277 stack.clipRect(rectA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400278 stack.clipRect(rectB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000279 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400280 case SkClipStack::Element::DeviceSpaceType::kRRect:
Mike Reedc1f77742016-12-09 09:00:50 -0500281 stack.clipRRect(rrectA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400282 stack.clipRRect(rrectB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000283 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400284 case SkClipStack::Element::DeviceSpaceType::kPath:
Mike Reedc1f77742016-12-09 09:00:50 -0500285 stack.clipPath(pathA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400286 stack.clipPath(pathB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000287 break;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000288 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000289
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000290 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000291 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000292
robertphillips@google.com7b112892012-07-31 15:18:21 +0000293 stack.getConservativeBounds(0, 0, 100, 100, &devClipBound,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000294 &isIntersectionOfRects);
295
Brian Salomonf3b46e52017-08-30 11:37:57 -0400296 if (SkClipStack::Element::DeviceSpaceType::kRect == primType) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000297 REPORTER_ASSERT(reporter, isIntersectionOfRects ==
Mike Reedc1f77742016-12-09 09:00:50 -0500298 (gOps[op] == kIntersect_SkClipOp));
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000299 } else {
300 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
301 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000302
303 SkASSERT(testCase < gNumCases);
robertphillips@google.com7b112892012-07-31 15:18:21 +0000304 REPORTER_ASSERT(reporter, devClipBound == gAnswerRectsBW[testCase]);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000305 ++testCase;
306
307 stack.restore();
308 }
309 }
310}
311
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000312// Test out 'isWideOpen' entry point
313static void test_isWideOpen(skiatest::Reporter* reporter) {
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000314 {
315 // Empty stack is wide open. Wide open stack means that gen id is wide open.
316 SkClipStack stack;
317 REPORTER_ASSERT(reporter, stack.isWideOpen());
318 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
319 }
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000320
321 SkRect rectA, rectB;
322
323 rectA.iset(10, 10, 40, 40);
324 rectB.iset(50, 50, 80, 80);
325
326 // Stack should initially be wide open
327 {
328 SkClipStack stack;
329
330 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000331 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000332 }
333
334 // Test out case where the user specifies a union that includes everything
335 {
336 SkClipStack stack;
337
338 SkPath clipA, clipB;
339
340 clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
341 clipA.setFillType(SkPath::kInverseEvenOdd_FillType);
342
343 clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
344 clipB.setFillType(SkPath::kInverseEvenOdd_FillType);
345
Mike Reedc1f77742016-12-09 09:00:50 -0500346 stack.clipPath(clipA, SkMatrix::I(), kReplace_SkClipOp, false);
347 stack.clipPath(clipB, SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000348
349 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000350 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000351 }
352
353 // Test out union w/ a wide open clip
354 {
355 SkClipStack stack;
356
Mike Reedc1f77742016-12-09 09:00:50 -0500357 stack.clipRect(rectA, SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000358
359 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000360 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000361 }
362
363 // Test out empty difference from a wide open clip
364 {
365 SkClipStack stack;
366
367 SkRect emptyRect;
368 emptyRect.setEmpty();
369
Mike Reedc1f77742016-12-09 09:00:50 -0500370 stack.clipRect(emptyRect, SkMatrix::I(), kDifference_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 return to wide open
377 {
378 SkClipStack stack;
379
380 stack.save();
381
Mike Reedc1f77742016-12-09 09:00:50 -0500382 stack.clipRect(rectA, SkMatrix::I(), kReplace_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 stack.restore();
388
389 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000390 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000391 }
392}
393
bsalomon@google.com100abf42012-09-05 17:40:04 +0000394static int count(const SkClipStack& stack) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000395
396 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
397
halcanary96fcdcc2015-08-27 07:41:13 -0700398 const SkClipStack::Element* element = nullptr;
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000399 int count = 0;
400
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000401 for (element = iter.prev(); element; element = iter.prev(), ++count) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000402 ;
403 }
404
405 return count;
406}
407
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000408static void test_rect_inverse_fill(skiatest::Reporter* reporter) {
409 // non-intersecting rectangles
410 SkRect rect = SkRect::MakeLTRB(0, 0, 10, 10);
411
412 SkPath path;
413 path.addRect(rect);
414 path.toggleInverseFillType();
415 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500416 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000417
418 SkRect bounds;
419 SkClipStack::BoundsType boundsType;
420 stack.getBounds(&bounds, &boundsType);
421 REPORTER_ASSERT(reporter, SkClipStack::kInsideOut_BoundsType == boundsType);
422 REPORTER_ASSERT(reporter, bounds == rect);
423}
424
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000425static void test_rect_replace(skiatest::Reporter* reporter) {
426 SkRect rect = SkRect::MakeWH(100, 100);
427 SkRect rect2 = SkRect::MakeXYWH(50, 50, 100, 100);
428
429 SkRect bound;
430 SkClipStack::BoundsType type;
431 bool isIntersectionOfRects;
432
433 // Adding a new rect with the replace operator should not increase
434 // the stack depth. BW replacing BW.
435 {
436 SkClipStack stack;
437 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500438 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000439 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500440 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000441 REPORTER_ASSERT(reporter, 1 == count(stack));
442 }
443
444 // Adding a new rect with the replace operator should not increase
445 // the stack depth. AA replacing AA.
446 {
447 SkClipStack stack;
448 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500449 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000450 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500451 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000452 REPORTER_ASSERT(reporter, 1 == count(stack));
453 }
454
455 // Adding a new rect with the replace operator should not increase
456 // the stack depth. BW replacing AA replacing BW.
457 {
458 SkClipStack stack;
459 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500460 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000461 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500462 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000463 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500464 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000465 REPORTER_ASSERT(reporter, 1 == count(stack));
466 }
467
468 // Make sure replace clip rects don't collapse too much.
469 {
470 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500471 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
472 stack.clipRect(rect2, SkMatrix::I(), kIntersect_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000473 REPORTER_ASSERT(reporter, 1 == count(stack));
474
475 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500476 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000477 REPORTER_ASSERT(reporter, 2 == count(stack));
478 stack.getBounds(&bound, &type, &isIntersectionOfRects);
479 REPORTER_ASSERT(reporter, bound == rect);
480 stack.restore();
481 REPORTER_ASSERT(reporter, 1 == count(stack));
482
483 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500484 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
485 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000486 REPORTER_ASSERT(reporter, 2 == count(stack));
487 stack.restore();
488 REPORTER_ASSERT(reporter, 1 == count(stack));
489
490 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500491 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
492 stack.clipRect(rect2, SkMatrix::I(), kIntersect_SkClipOp, false);
493 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000494 REPORTER_ASSERT(reporter, 2 == count(stack));
495 stack.restore();
496 REPORTER_ASSERT(reporter, 1 == count(stack));
497 }
498}
499
500// Simplified path-based version of test_rect_replace.
501static void test_path_replace(skiatest::Reporter* reporter) {
502 SkRect rect = SkRect::MakeWH(100, 100);
503 SkPath path;
504 path.addCircle(50, 50, 50);
505
506 // Replace operation doesn't grow the stack.
507 {
508 SkClipStack stack;
509 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500510 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000511 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500512 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000513 REPORTER_ASSERT(reporter, 1 == count(stack));
514 }
515
516 // Replacing rect with path.
517 {
518 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500519 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000520 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500521 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000522 REPORTER_ASSERT(reporter, 1 == count(stack));
523 }
524}
525
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000526// Test out SkClipStack's merging of rect clips. In particular exercise
527// merging of aa vs. bw rects.
528static void test_rect_merging(skiatest::Reporter* reporter) {
529
530 SkRect overlapLeft = SkRect::MakeLTRB(10, 10, 50, 50);
531 SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80);
532
533 SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90);
534 SkRect nestedChild = SkRect::MakeLTRB(40, 40, 60, 60);
535
536 SkRect bound;
537 SkClipStack::BoundsType type;
538 bool isIntersectionOfRects;
539
540 // all bw overlapping - should merge
541 {
542 SkClipStack stack;
543
Mike Reedc1f77742016-12-09 09:00:50 -0500544 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000545
Mike Reedc1f77742016-12-09 09:00:50 -0500546 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000547
548 REPORTER_ASSERT(reporter, 1 == count(stack));
549
550 stack.getBounds(&bound, &type, &isIntersectionOfRects);
551
552 REPORTER_ASSERT(reporter, isIntersectionOfRects);
553 }
554
555 // all aa overlapping - should merge
556 {
557 SkClipStack stack;
558
Mike Reedc1f77742016-12-09 09:00:50 -0500559 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000560
Mike Reedc1f77742016-12-09 09:00:50 -0500561 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000562
563 REPORTER_ASSERT(reporter, 1 == count(stack));
564
565 stack.getBounds(&bound, &type, &isIntersectionOfRects);
566
567 REPORTER_ASSERT(reporter, isIntersectionOfRects);
568 }
569
570 // mixed overlapping - should _not_ merge
571 {
572 SkClipStack stack;
573
Mike Reedc1f77742016-12-09 09:00:50 -0500574 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000575
Mike Reedc1f77742016-12-09 09:00:50 -0500576 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000577
578 REPORTER_ASSERT(reporter, 2 == count(stack));
579
580 stack.getBounds(&bound, &type, &isIntersectionOfRects);
581
582 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
583 }
584
585 // mixed nested (bw inside aa) - should merge
586 {
587 SkClipStack stack;
588
Mike Reedc1f77742016-12-09 09:00:50 -0500589 stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000590
Mike Reedc1f77742016-12-09 09:00:50 -0500591 stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000592
593 REPORTER_ASSERT(reporter, 1 == count(stack));
594
595 stack.getBounds(&bound, &type, &isIntersectionOfRects);
596
597 REPORTER_ASSERT(reporter, isIntersectionOfRects);
598 }
599
600 // mixed nested (aa inside bw) - should merge
601 {
602 SkClipStack stack;
603
Mike Reedc1f77742016-12-09 09:00:50 -0500604 stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000605
Mike Reedc1f77742016-12-09 09:00:50 -0500606 stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000607
608 REPORTER_ASSERT(reporter, 1 == count(stack));
609
610 stack.getBounds(&bound, &type, &isIntersectionOfRects);
611
612 REPORTER_ASSERT(reporter, isIntersectionOfRects);
613 }
614
615 // reverse nested (aa inside bw) - should _not_ merge
616 {
617 SkClipStack stack;
618
Mike Reedc1f77742016-12-09 09:00:50 -0500619 stack.clipRect(nestedChild, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000620
Mike Reedc1f77742016-12-09 09:00:50 -0500621 stack.clipRect(nestedParent, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000622
623 REPORTER_ASSERT(reporter, 2 == count(stack));
624
625 stack.getBounds(&bound, &type, &isIntersectionOfRects);
626
627 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
628 }
629}
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000630
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000631static void test_quickContains(skiatest::Reporter* reporter) {
632 SkRect testRect = SkRect::MakeLTRB(10, 10, 40, 40);
633 SkRect insideRect = SkRect::MakeLTRB(20, 20, 30, 30);
634 SkRect intersectingRect = SkRect::MakeLTRB(25, 25, 50, 50);
635 SkRect outsideRect = SkRect::MakeLTRB(0, 0, 50, 50);
636 SkRect nonIntersectingRect = SkRect::MakeLTRB(100, 100, 110, 110);
637
638 SkPath insideCircle;
639 insideCircle.addCircle(25, 25, 5);
640 SkPath intersectingCircle;
641 intersectingCircle.addCircle(25, 40, 10);
642 SkPath outsideCircle;
643 outsideCircle.addCircle(25, 25, 50);
644 SkPath nonIntersectingCircle;
645 nonIntersectingCircle.addCircle(100, 100, 5);
646
647 {
648 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500649 stack.clipRect(outsideRect, SkMatrix::I(), kDifference_SkClipOp, false);
650 // return false because quickContains currently does not care for kDifference_SkClipOp
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000651 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
652 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000653
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000654 // Replace Op tests
655 {
656 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500657 stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000658 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
659 }
660
661 {
662 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500663 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000664 stack.save(); // To prevent in-place substitution by replace OP
Mike Reedc1f77742016-12-09 09:00:50 -0500665 stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000666 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
667 stack.restore();
668 }
669
670 {
671 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500672 stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000673 stack.save(); // To prevent in-place substitution by replace OP
Mike Reedc1f77742016-12-09 09:00:50 -0500674 stack.clipRect(insideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000675 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
676 stack.restore();
677 }
678
679 // Verify proper traversal of multi-element clip
680 {
681 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500682 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000683 // Use a path for second clip to prevent in-place intersection
Mike Reedc1f77742016-12-09 09:00:50 -0500684 stack.clipPath(outsideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000685 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
686 }
687
688 // Intersect Op tests with rectangles
689 {
690 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500691 stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000692 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
693 }
694
695 {
696 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500697 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000698 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
699 }
700
701 {
702 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500703 stack.clipRect(intersectingRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000704 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
705 }
706
707 {
708 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500709 stack.clipRect(nonIntersectingRect, 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 circle paths
714 {
715 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500716 stack.clipPath(outsideCircle, 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.clipPath(insideCircle, 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.clipPath(intersectingCircle, 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.clipPath(nonIntersectingCircle, 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 inverse filled rectangles
739 {
740 SkClipStack stack;
741 SkPath path;
742 path.addRect(outsideRect);
743 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500744 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000745 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
746 }
747
748 {
749 SkClipStack stack;
750 SkPath path;
751 path.addRect(insideRect);
752 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500753 stack.clipPath(path, 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;
759 SkPath path;
760 path.addRect(intersectingRect);
761 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500762 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000763 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
764 }
765
766 {
767 SkClipStack stack;
768 SkPath path;
769 path.addRect(nonIntersectingRect);
770 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500771 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000772 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
773 }
774
775 // Intersect Op tests with inverse filled circles
776 {
777 SkClipStack stack;
778 SkPath path = outsideCircle;
779 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500780 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000781 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
782 }
783
784 {
785 SkClipStack stack;
786 SkPath path = insideCircle;
787 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500788 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000789 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
790 }
791
792 {
793 SkClipStack stack;
794 SkPath path = intersectingCircle;
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, false == stack.quickContains(testRect));
798 }
799
800 {
801 SkClipStack stack;
802 SkPath path = nonIntersectingCircle;
803 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500804 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000805 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
806 }
807}
808
csmartdaltond50e2402016-07-22 08:39:06 -0700809static void set_region_to_stack(const SkClipStack& stack, const SkIRect& bounds, SkRegion* region) {
810 region->setRect(bounds);
811 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
812 while (const SkClipStack::Element *element = iter.next()) {
813 SkRegion elemRegion;
814 SkRegion boundsRgn(bounds);
815 SkPath path;
816
Brian Salomonf3b46e52017-08-30 11:37:57 -0400817 switch (element->getDeviceSpaceType()) {
818 case SkClipStack::Element::DeviceSpaceType::kEmpty:
csmartdaltond50e2402016-07-22 08:39:06 -0700819 elemRegion.setEmpty();
820 break;
821 default:
Brian Salomonf3b46e52017-08-30 11:37:57 -0400822 element->asDeviceSpacePath(&path);
csmartdaltond50e2402016-07-22 08:39:06 -0700823 elemRegion.setPath(path, boundsRgn);
824 break;
825 }
reed73603f32016-09-20 08:42:38 -0700826 region->op(elemRegion, (SkRegion::Op)element->getOp());
csmartdaltond50e2402016-07-22 08:39:06 -0700827 }
828}
829
830static void test_invfill_diff_bug(skiatest::Reporter* reporter) {
831 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500832 stack.clipRect({10, 10, 20, 20}, SkMatrix::I(), kIntersect_SkClipOp, false);
csmartdaltond50e2402016-07-22 08:39:06 -0700833
834 SkPath path;
835 path.addRect({30, 10, 40, 20});
836 path.setFillType(SkPath::kInverseWinding_FillType);
Mike Reedc1f77742016-12-09 09:00:50 -0500837 stack.clipPath(path, SkMatrix::I(), kDifference_SkClipOp, false);
csmartdaltond50e2402016-07-22 08:39:06 -0700838
839 REPORTER_ASSERT(reporter, SkClipStack::kEmptyGenID == stack.getTopmostGenID());
840
841 SkRect stackBounds;
842 SkClipStack::BoundsType stackBoundsType;
843 stack.getBounds(&stackBounds, &stackBoundsType);
844
845 REPORTER_ASSERT(reporter, stackBounds.isEmpty());
846 REPORTER_ASSERT(reporter, SkClipStack::kNormal_BoundsType == stackBoundsType);
847
848 SkRegion region;
849 set_region_to_stack(stack, {0, 0, 50, 30}, &region);
850
851 REPORTER_ASSERT(reporter, region.isEmpty());
852}
853
bsalomon@google.com51a62862012-11-26 21:19:43 +0000854///////////////////////////////////////////////////////////////////////////////////////////////////
855
bsalomon@google.coma4e13c82012-11-26 21:38:37 +0000856#if SK_SUPPORT_GPU
bsalomon@google.com705e8402012-11-27 15:43:57 +0000857// Functions that add a shape to the clip stack. The shape is computed from a rectangle.
858// AA is always disabled since the clip stack reducer can cause changes in aa rasterization of the
859// stack. A fractional edge repeated in different elements may be rasterized fewer times using the
860// reduced stack.
861typedef void (*AddElementFunc) (const SkRect& rect,
862 bool invert,
Mike Reedc1f77742016-12-09 09:00:50 -0500863 SkClipOp op,
csmartdaltoncbecb082016-07-22 08:59:08 -0700864 SkClipStack* stack,
865 bool doAA);
bsalomon@google.coma4e13c82012-11-26 21:38:37 +0000866
Mike Reedc1f77742016-12-09 09:00:50 -0500867static void add_round_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700868 bool doAA) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000869 SkScalar rx = rect.width() / 10;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000870 SkScalar ry = rect.height() / 20;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000871 if (invert) {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000872 SkPath path;
873 path.addRoundRect(rect, rx, ry);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000874 path.setFillType(SkPath::kInverseWinding_FillType);
Brian Salomona3b45d42016-10-03 11:36:16 -0400875 stack->clipPath(path, SkMatrix::I(), op, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000876 } else {
877 SkRRect rrect;
878 rrect.setRectXY(rect, rx, ry);
Brian Salomona3b45d42016-10-03 11:36:16 -0400879 stack->clipRRect(rrect, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000880 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000881};
882
Mike Reedc1f77742016-12-09 09:00:50 -0500883static void add_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700884 bool doAA) {
bsalomon@google.com705e8402012-11-27 15:43:57 +0000885 if (invert) {
886 SkPath path;
887 path.addRect(rect);
888 path.setFillType(SkPath::kInverseWinding_FillType);
Brian Salomona3b45d42016-10-03 11:36:16 -0400889 stack->clipPath(path, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000890 } else {
Brian Salomona3b45d42016-10-03 11:36:16 -0400891 stack->clipRect(rect, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000892 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000893};
894
Mike Reedc1f77742016-12-09 09:00:50 -0500895static void add_oval(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700896 bool doAA) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000897 SkPath path;
898 path.addOval(rect);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000899 if (invert) {
900 path.setFillType(SkPath::kInverseWinding_FillType);
901 }
Brian Salomona3b45d42016-10-03 11:36:16 -0400902 stack->clipPath(path, SkMatrix::I(), op, doAA);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000903};
904
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000905static void add_elem_to_stack(const SkClipStack::Element& element, SkClipStack* stack) {
Brian Salomonf3b46e52017-08-30 11:37:57 -0400906 switch (element.getDeviceSpaceType()) {
907 case SkClipStack::Element::DeviceSpaceType::kRect:
908 stack->clipRect(element.getDeviceSpaceRect(), SkMatrix::I(), element.getOp(),
909 element.isAA());
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000910 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400911 case SkClipStack::Element::DeviceSpaceType::kRRect:
912 stack->clipRRect(element.getDeviceSpaceRRect(), SkMatrix::I(), element.getOp(),
913 element.isAA());
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000914 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400915 case SkClipStack::Element::DeviceSpaceType::kPath:
916 stack->clipPath(element.getDeviceSpacePath(), SkMatrix::I(), element.getOp(),
917 element.isAA());
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000918 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400919 case SkClipStack::Element::DeviceSpaceType::kEmpty:
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000920 SkDEBUGFAIL("Why did the reducer produce an explicit empty.");
921 stack->clipEmpty();
922 break;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000923 }
924}
925
bsalomon@google.com51a62862012-11-26 21:19:43 +0000926static void test_reduced_clip_stack(skiatest::Reporter* reporter) {
927 // We construct random clip stacks, reduce them, and then rasterize both versions to verify that
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000928 // they are equal.
bsalomon@google.com51a62862012-11-26 21:19:43 +0000929
930 // All the clip elements will be contained within these bounds.
csmartdaltond211e782016-08-15 11:17:19 -0700931 static const SkIRect kIBounds = SkIRect::MakeWH(100, 100);
932 static const SkRect kBounds = SkRect::Make(kIBounds);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000933
934 enum {
csmartdaltoncbecb082016-07-22 08:59:08 -0700935 kNumTests = 250,
bsalomon@google.com51a62862012-11-26 21:19:43 +0000936 kMinElemsPerTest = 1,
937 kMaxElemsPerTest = 50,
938 };
939
940 // min/max size of a clip element as a fraction of kBounds.
941 static const SkScalar kMinElemSizeFrac = SK_Scalar1 / 5;
942 static const SkScalar kMaxElemSizeFrac = SK_Scalar1;
943
Mike Reedc1f77742016-12-09 09:00:50 -0500944 static const SkClipOp kOps[] = {
945 kDifference_SkClipOp,
946 kIntersect_SkClipOp,
947 kUnion_SkClipOp,
948 kXOR_SkClipOp,
949 kReverseDifference_SkClipOp,
950 kReplace_SkClipOp,
bsalomon@google.com51a62862012-11-26 21:19:43 +0000951 };
952
953 // Replace operations short-circuit the optimizer. We want to make sure that we test this code
954 // path a little bit but we don't want it to prevent us from testing many longer traversals in
955 // the optimizer.
956 static const int kReplaceDiv = 4 * kMaxElemsPerTest;
957
bsalomon@google.com705e8402012-11-27 15:43:57 +0000958 // We want to test inverse fills. However, they are quite rare in practice so don't over do it.
959 static const SkScalar kFractionInverted = SK_Scalar1 / kMaxElemsPerTest;
960
csmartdaltoncbecb082016-07-22 08:59:08 -0700961 static const SkScalar kFractionAntialiased = 0.25;
962
bsalomon@google.com51a62862012-11-26 21:19:43 +0000963 static const AddElementFunc kElementFuncs[] = {
964 add_rect,
965 add_round_rect,
966 add_oval,
967 };
968
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000969 SkRandom r;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000970
971 for (int i = 0; i < kNumTests; ++i) {
csmartdaltoncbecb082016-07-22 08:59:08 -0700972 SkString testCase;
973 testCase.printf("Iteration %d", i);
974
bsalomon@google.com51a62862012-11-26 21:19:43 +0000975 // Randomly generate a clip stack.
976 SkClipStack stack;
977 int numElems = r.nextRangeU(kMinElemsPerTest, kMaxElemsPerTest);
csmartdaltoncbecb082016-07-22 08:59:08 -0700978 bool doAA = r.nextBiasedBool(kFractionAntialiased);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000979 for (int e = 0; e < numElems; ++e) {
Mike Reedc1f77742016-12-09 09:00:50 -0500980 SkClipOp op = kOps[r.nextULessThan(SK_ARRAY_COUNT(kOps))];
981 if (op == kReplace_SkClipOp) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000982 if (r.nextU() % kReplaceDiv) {
983 --e;
984 continue;
985 }
986 }
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000987
bsalomon@google.com51a62862012-11-26 21:19:43 +0000988 // saves can change the clip stack behavior when an element is added.
989 bool doSave = r.nextBool();
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000990
bsalomon@google.com51a62862012-11-26 21:19:43 +0000991 SkSize size = SkSize::Make(
Mike Reeddf85c382017-02-14 10:59:19 -0500992 kBounds.width() * r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac),
993 kBounds.height() * r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac));
bsalomon@google.com51a62862012-11-26 21:19:43 +0000994
csmartdaltoncbecb082016-07-22 08:59:08 -0700995 SkPoint xy = {r.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth),
996 r.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight)};
bsalomon@google.com51a62862012-11-26 21:19:43 +0000997
csmartdaltoncbecb082016-07-22 08:59:08 -0700998 SkRect rect;
999 if (doAA) {
1000 rect.setXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight);
1001 if (GrClip::IsPixelAligned(rect)) {
1002 // Don't create an element that may accidentally become not antialiased.
1003 rect.outset(0.5f, 0.5f);
1004 }
1005 SkASSERT(!GrClip::IsPixelAligned(rect));
1006 } else {
1007 rect.setXYWH(SkScalarFloorToScalar(xy.fX),
1008 SkScalarFloorToScalar(xy.fY),
1009 SkScalarCeilToScalar(size.fWidth),
1010 SkScalarCeilToScalar(size.fHeight));
1011 }
bsalomon@google.com51a62862012-11-26 21:19:43 +00001012
bsalomon@google.com705e8402012-11-27 15:43:57 +00001013 bool invert = r.nextBiasedBool(kFractionInverted);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001014
csmartdaltoncbecb082016-07-22 08:59:08 -07001015 kElementFuncs[r.nextULessThan(SK_ARRAY_COUNT(kElementFuncs))](rect, invert, op, &stack,
1016 doAA);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001017 if (doSave) {
1018 stack.save();
1019 }
1020 }
1021
Brian Salomon14471772017-12-05 10:35:15 -05001022 auto context = GrContext::MakeMock(nullptr);
1023 const auto* caps = context->caps()->shaderCaps();
1024
csmartdalton8d3f92a2016-08-17 09:39:38 -07001025 // Zero the memory we will new the GrReducedClip into. This ensures the elements gen ID
1026 // will be kInvalidGenID if left uninitialized.
1027 SkAlignedSTStorage<1, GrReducedClip> storage;
1028 memset(storage.get(), 0, sizeof(GrReducedClip));
1029 GR_STATIC_ASSERT(0 == SkClipStack::kInvalidGenID);
1030
csmartdalton77f2fae2016-08-08 09:55:06 -07001031 // Get the reduced version of the stack.
csmartdaltoncbecb082016-07-22 08:59:08 -07001032 SkRect queryBounds = kBounds;
1033 queryBounds.outset(kBounds.width() / 2, kBounds.height() / 2);
Brian Salomon14471772017-12-05 10:35:15 -05001034 const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, queryBounds, caps);
bsalomon@google.coma4444302012-12-04 15:22:12 +00001035
csmartdalton8d3f92a2016-08-17 09:39:38 -07001036 REPORTER_ASSERT_MESSAGE(reporter,
Chris Dalton79471932017-10-27 01:50:57 -06001037 reduced->maskElements().isEmpty() ||
1038 SkClipStack::kInvalidGenID != reduced->maskGenID(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001039 testCase.c_str());
1040
Chris Dalton79471932017-10-27 01:50:57 -06001041 if (!reduced->maskElements().isEmpty()) {
1042 REPORTER_ASSERT_MESSAGE(reporter, reduced->hasScissor(), testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001043 SkRect stackBounds;
1044 SkClipStack::BoundsType stackBoundsType;
1045 stack.getBounds(&stackBounds, &stackBoundsType);
Chris Dalton79471932017-10-27 01:50:57 -06001046 REPORTER_ASSERT_MESSAGE(reporter, reduced->maskRequiresAA() == doAA, testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001047 }
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001048
bsalomon@google.com51a62862012-11-26 21:19:43 +00001049 // Build a new clip stack based on the reduced clip elements
1050 SkClipStack reducedStack;
csmartdalton8d3f92a2016-08-17 09:39:38 -07001051 if (GrReducedClip::InitialState::kAllOut == reduced->initialState()) {
bsalomon@google.com51a62862012-11-26 21:19:43 +00001052 // whether the result is bounded or not, the whole plane should start outside the clip.
1053 reducedStack.clipEmpty();
1054 }
Chris Dalton79471932017-10-27 01:50:57 -06001055 for (ElementList::Iter iter(reduced->maskElements()); iter.get(); iter.next()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001056 add_elem_to_stack(*iter.get(), &reducedStack);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001057 }
bsalomon@google.com51a62862012-11-26 21:19:43 +00001058
Chris Dalton79471932017-10-27 01:50:57 -06001059 SkIRect scissor = reduced->hasScissor() ? reduced->scissor() : kIBounds;
csmartdaltond211e782016-08-15 11:17:19 -07001060
bsalomon@google.com34cd70a2012-12-06 14:23:20 +00001061 // GrReducedClipStack assumes that the final result is clipped to the returned bounds
Chris Dalton79471932017-10-27 01:50:57 -06001062 reducedStack.clipDevRect(scissor, kIntersect_SkClipOp);
1063 stack.clipDevRect(scissor, kIntersect_SkClipOp);
bsalomon@google.com34cd70a2012-12-06 14:23:20 +00001064
bsalomon@google.com51a62862012-11-26 21:19:43 +00001065 // convert both the original stack and reduced stack to SkRegions and see if they're equal
bsalomon@google.com51a62862012-11-26 21:19:43 +00001066 SkRegion region;
Chris Dalton79471932017-10-27 01:50:57 -06001067 set_region_to_stack(stack, scissor, &region);
csmartdaltond50e2402016-07-22 08:39:06 -07001068
bsalomon@google.com51a62862012-11-26 21:19:43 +00001069 SkRegion reducedRegion;
Chris Dalton79471932017-10-27 01:50:57 -06001070 set_region_to_stack(reducedStack, scissor, &reducedRegion);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001071
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001072 REPORTER_ASSERT_MESSAGE(reporter, region == reducedRegion, testCase.c_str());
csmartdalton8d3f92a2016-08-17 09:39:38 -07001073
1074 reduced->~GrReducedClip();
bsalomon@google.com51a62862012-11-26 21:19:43 +00001075 }
1076}
1077
halcanary4dbbd042016-06-07 17:21:10 -07001078#ifdef SK_BUILD_FOR_WIN
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001079 #define SUPPRESS_VISIBILITY_WARNING
1080#else
1081 #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden")))
1082#endif
1083
1084static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) {
1085 {
1086 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001087 stack.clipRect(SkRect::MakeXYWH(0, 0, 100, 100), SkMatrix::I(), kReplace_SkClipOp,
Brian Salomona3b45d42016-10-03 11:36:16 -04001088 true);
1089 stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(50.3), SkScalar(50.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001090 kReplace_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001091 SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001092
Brian Salomon14471772017-12-05 10:35:15 -05001093 auto context = GrContext::MakeMock(nullptr);
1094 const auto* caps = context->caps()->shaderCaps();
1095
csmartdalton8d3f92a2016-08-17 09:39:38 -07001096 SkAlignedSTStorage<1, GrReducedClip> storage;
1097 memset(storage.get(), 0, sizeof(GrReducedClip));
1098 GR_STATIC_ASSERT(0 == SkClipStack::kInvalidGenID);
Brian Salomon14471772017-12-05 10:35:15 -05001099 const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, bounds, caps);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001100
Chris Dalton79471932017-10-27 01:50:57 -06001101 REPORTER_ASSERT(reporter, reduced->maskElements().count() == 1);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001102 // Clips will be cached based on the generation id. Make sure the gen id is valid.
Chris Dalton79471932017-10-27 01:50:57 -06001103 REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reduced->maskGenID());
csmartdalton8d3f92a2016-08-17 09:39:38 -07001104
1105 reduced->~GrReducedClip();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001106 }
1107 {
1108 SkClipStack stack;
1109
1110 // Create a clip with following 25.3, 25.3 boxes which are 25 apart:
1111 // A B
1112 // C D
1113
Brian Salomona3b45d42016-10-03 11:36:16 -04001114 stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001115 kReplace_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001116 uint32_t genIDA = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001117 stack.clipRect(SkRect::MakeXYWH(50, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001118 kUnion_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001119 uint32_t genIDB = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001120 stack.clipRect(SkRect::MakeXYWH(0, 50, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001121 kUnion_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001122 uint32_t genIDC = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001123 stack.clipRect(SkRect::MakeXYWH(50, 50, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001124 kUnion_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001125 uint32_t genIDD = stack.getTopmostGenID();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001126
1127
csmartdaltoncbecb082016-07-22 08:59:08 -07001128#define IXYWH SkIRect::MakeXYWH
1129#define XYWH SkRect::MakeXYWH
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001130
csmartdaltoncbecb082016-07-22 08:59:08 -07001131 SkIRect stackBounds = IXYWH(0, 0, 76, 76);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001132
1133 // The base test is to test each rect in two ways:
1134 // 1) The box dimensions. (Should reduce to "all in", no elements).
1135 // 2) A bit over the box dimensions.
1136 // In the case 2, test that the generation id is what is expected.
1137 // The rects are of fractional size so that case 2 never gets optimized to an empty element
1138 // list.
1139
1140 // Not passing in tighter bounds is tested for consistency.
1141 static const struct SUPPRESS_VISIBILITY_WARNING {
csmartdaltoncbecb082016-07-22 08:59:08 -07001142 SkRect testBounds;
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001143 int reducedClipCount;
Robert Phillips806be2d2017-06-28 15:23:59 -04001144 uint32_t reducedGenID;
csmartdaltoncbecb082016-07-22 08:59:08 -07001145 InitialState initialState;
1146 SkIRect clipIRect;
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001147 // parameter.
1148 } testCases[] = {
csmartdalton77f2fae2016-08-08 09:55:06 -07001149
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001150 // Rect A.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001151 { XYWH(0, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 0, 25, 25) },
1152 { 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 -06001153 { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::InitialState::kAllOut, IXYWH(0, 0, 26, 26)},
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001154
1155 // Rect B.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001156 { XYWH(50, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 25, 25) },
1157 { XYWH(50, 0, 25.3f, 25.3f), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 26, 26) },
csmartdalton77f2fae2016-08-08 09:55:06 -07001158 { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::InitialState::kAllOut, IXYWH(50, 0, 26, 27) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001159
1160 // Rect C.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001161 { XYWH(0, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 50, 25, 25) },
1162 { 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 -07001163 { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::InitialState::kAllOut, IXYWH(0, 50, 27, 26) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001164
1165 // Rect D.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001166 { XYWH(50, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 25, 25)},
1167 { XYWH(50.3f, 50.3f, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 26, 26)},
csmartdalton77f2fae2016-08-08 09:55:06 -07001168 { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(50, 50, 26, 26)},
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001169
1170 // Other tests:
csmartdalton77f2fae2016-08-08 09:55:06 -07001171 { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::InitialState::kAllOut, stackBounds },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001172
1173 // Rect in the middle, touches none.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001174 { 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 +00001175
1176 // Rect in the middle, touches all the rects. GenID is the last rect.
csmartdalton77f2fae2016-08-08 09:55:06 -07001177 { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(24, 24, 27, 27) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001178 };
1179
1180#undef XYWH
csmartdaltoncbecb082016-07-22 08:59:08 -07001181#undef IXYWH
Brian Salomon14471772017-12-05 10:35:15 -05001182 auto context = GrContext::MakeMock(nullptr);
1183 const auto* caps = context->caps()->shaderCaps();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001184
1185 for (size_t i = 0; i < SK_ARRAY_COUNT(testCases); ++i) {
Brian Salomon14471772017-12-05 10:35:15 -05001186 const GrReducedClip reduced(stack, testCases[i].testBounds, caps);
Chris Dalton79471932017-10-27 01:50:57 -06001187 REPORTER_ASSERT(reporter, reduced.maskElements().count() ==
1188 testCases[i].reducedClipCount);
1189 SkASSERT(reduced.maskElements().count() == testCases[i].reducedClipCount);
1190 if (reduced.maskElements().count()) {
1191 REPORTER_ASSERT(reporter, reduced.maskGenID() == testCases[i].reducedGenID);
1192 SkASSERT(reduced.maskGenID() == testCases[i].reducedGenID);
csmartdalton8d3f92a2016-08-17 09:39:38 -07001193 }
csmartdalton77f2fae2016-08-08 09:55:06 -07001194 REPORTER_ASSERT(reporter, reduced.initialState() == testCases[i].initialState);
1195 SkASSERT(reduced.initialState() == testCases[i].initialState);
Chris Dalton79471932017-10-27 01:50:57 -06001196 REPORTER_ASSERT(reporter, reduced.hasScissor());
1197 SkASSERT(reduced.hasScissor());
1198 REPORTER_ASSERT(reporter, reduced.scissor() == testCases[i].clipIRect);
1199 SkASSERT(reduced.scissor() == testCases[i].clipIRect);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001200 }
1201 }
1202}
1203
1204static void test_reduced_clip_stack_no_aa_crash(skiatest::Reporter* reporter) {
1205 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001206 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 100, 100), kReplace_SkClipOp);
1207 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 50, 50), kReplace_SkClipOp);
csmartdaltoncbecb082016-07-22 08:59:08 -07001208 SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001209
Brian Salomon14471772017-12-05 10:35:15 -05001210 auto context = GrContext::MakeMock(nullptr);
1211 const auto* caps = context->caps()->shaderCaps();
1212
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001213 // At the time, this would crash.
Brian Salomon14471772017-12-05 10:35:15 -05001214 const GrReducedClip reduced(stack, bounds, caps);
Chris Dalton79471932017-10-27 01:50:57 -06001215 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001216}
1217
csmartdaltoncbecb082016-07-22 08:59:08 -07001218enum class ClipMethod {
1219 kSkipDraw,
1220 kIgnoreClip,
1221 kScissor,
1222 kAAElements
1223};
1224
1225static void test_aa_query(skiatest::Reporter* reporter, const SkString& testName,
1226 const SkClipStack& stack, const SkMatrix& queryXform,
1227 const SkRect& preXformQuery, ClipMethod expectedMethod,
1228 int numExpectedElems = 0) {
Brian Salomon14471772017-12-05 10:35:15 -05001229 auto context = GrContext::MakeMock(nullptr);
1230 const auto* caps = context->caps()->shaderCaps();
1231
csmartdaltoncbecb082016-07-22 08:59:08 -07001232 SkRect queryBounds;
1233 queryXform.mapRect(&queryBounds, preXformQuery);
Brian Salomon14471772017-12-05 10:35:15 -05001234 const GrReducedClip reduced(stack, queryBounds, caps);
csmartdaltoncbecb082016-07-22 08:59:08 -07001235
1236 SkClipStack::BoundsType stackBoundsType;
1237 SkRect stackBounds;
1238 stack.getBounds(&stackBounds, &stackBoundsType);
1239
1240 switch (expectedMethod) {
1241 case ClipMethod::kSkipDraw:
1242 SkASSERT(0 == numExpectedElems);
Chris Dalton79471932017-10-27 01:50:57 -06001243 REPORTER_ASSERT_MESSAGE(reporter,
1244 reduced.maskElements().isEmpty(), testName.c_str());
csmartdalton77f2fae2016-08-08 09:55:06 -07001245 REPORTER_ASSERT_MESSAGE(reporter,
1246 GrReducedClip::InitialState::kAllOut == reduced.initialState(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001247 testName.c_str());
1248 return;
1249 case ClipMethod::kIgnoreClip:
1250 SkASSERT(0 == numExpectedElems);
csmartdaltond211e782016-08-15 11:17:19 -07001251 REPORTER_ASSERT_MESSAGE(reporter,
Chris Dalton79471932017-10-27 01:50:57 -06001252 !reduced.hasScissor() ||
1253 GrClip::IsInsideClip(reduced.scissor(), queryBounds),
csmartdaltoncbecb082016-07-22 08:59:08 -07001254 testName.c_str());
Chris Dalton79471932017-10-27 01:50:57 -06001255 REPORTER_ASSERT_MESSAGE(reporter, reduced.maskElements().isEmpty(), testName.c_str());
csmartdalton77f2fae2016-08-08 09:55:06 -07001256 REPORTER_ASSERT_MESSAGE(reporter,
1257 GrReducedClip::InitialState::kAllIn == reduced.initialState(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001258 testName.c_str());
1259 return;
1260 case ClipMethod::kScissor: {
1261 SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType);
1262 SkASSERT(0 == numExpectedElems);
1263 SkIRect expectedScissor;
1264 stackBounds.round(&expectedScissor);
Chris Dalton79471932017-10-27 01:50:57 -06001265 REPORTER_ASSERT_MESSAGE(reporter, reduced.maskElements().isEmpty(), testName.c_str());
1266 REPORTER_ASSERT_MESSAGE(reporter, reduced.hasScissor(), testName.c_str());
1267 REPORTER_ASSERT_MESSAGE(reporter, expectedScissor == reduced.scissor(),
csmartdalton77f2fae2016-08-08 09:55:06 -07001268 testName.c_str());
1269 REPORTER_ASSERT_MESSAGE(reporter,
1270 GrReducedClip::InitialState::kAllIn == reduced.initialState(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001271 testName.c_str());
1272 return;
1273 }
1274 case ClipMethod::kAAElements: {
1275 SkIRect expectedClipIBounds = GrClip::GetPixelIBounds(queryBounds);
1276 if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
1277 SkAssertResult(expectedClipIBounds.intersect(GrClip::GetPixelIBounds(stackBounds)));
1278 }
Chris Dalton79471932017-10-27 01:50:57 -06001279 REPORTER_ASSERT_MESSAGE(reporter, numExpectedElems == reduced.maskElements().count(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001280 testName.c_str());
Chris Dalton79471932017-10-27 01:50:57 -06001281 REPORTER_ASSERT_MESSAGE(reporter, reduced.hasScissor(), testName.c_str());
1282 REPORTER_ASSERT_MESSAGE(reporter, expectedClipIBounds == reduced.scissor(),
csmartdalton77f2fae2016-08-08 09:55:06 -07001283 testName.c_str());
Chris Dalton79471932017-10-27 01:50:57 -06001284 REPORTER_ASSERT_MESSAGE(reporter,
1285 reduced.maskElements().isEmpty() || reduced.maskRequiresAA(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001286 testName.c_str());
1287 break;
1288 }
1289 }
1290}
1291
1292static void test_reduced_clip_stack_aa(skiatest::Reporter* reporter) {
1293 constexpr SkScalar IL = 2, IT = 1, IR = 6, IB = 7; // Pixel aligned rect.
1294 constexpr SkScalar L = 2.2f, T = 1.7f, R = 5.8f, B = 7.3f; // Generic rect.
1295 constexpr SkScalar l = 3.3f, t = 2.8f, r = 4.7f, b = 6.2f; // Small rect contained in R.
1296
1297 SkRect alignedRect = {IL, IT, IR, IB};
1298 SkRect rect = {L, T, R, B};
1299 SkRect innerRect = {l, t, r, b};
1300
1301 SkMatrix m;
1302 m.setIdentity();
1303
1304 constexpr SkScalar kMinScale = 2.0001f;
1305 constexpr SkScalar kMaxScale = 3;
1306 constexpr int kNumIters = 8;
1307
1308 SkString name;
1309 SkRandom rand;
1310
1311 for (int i = 0; i < kNumIters; ++i) {
1312 // Pixel-aligned rect (iior=true).
1313 name.printf("Pixel-aligned rect test, iter %i", i);
1314 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001315 stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001316 test_aa_query(reporter, name, stack, m, {IL, IT, IR, IB}, ClipMethod::kIgnoreClip);
1317 test_aa_query(reporter, name, stack, m, {IL, IT-1, IR, IT}, ClipMethod::kSkipDraw);
csmartdalton77f2fae2016-08-08 09:55:06 -07001318 test_aa_query(reporter, name, stack, m, {IL, IT-2, IR, IB}, ClipMethod::kScissor);
csmartdaltoncbecb082016-07-22 08:59:08 -07001319
1320 // Rect (iior=true).
1321 name.printf("Rect test, iter %i", i);
1322 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001323 stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001324 test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kIgnoreClip);
1325 test_aa_query(reporter, name, stack, m, {L-.1f, T, L, B}, ClipMethod::kSkipDraw);
1326 test_aa_query(reporter, name, stack, m, {L-.1f, T, L+.1f, B}, ClipMethod::kAAElements, 1);
1327
1328 // Difference rect (iior=false, inside-out bounds).
1329 name.printf("Difference rect test, iter %i", i);
1330 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001331 stack.clipRect(rect, SkMatrix::I(), kDifference_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001332 test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kSkipDraw);
1333 test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T}, ClipMethod::kIgnoreClip);
1334 test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T+.1f}, ClipMethod::kAAElements, 1);
1335
1336 // Complex clip (iior=false, normal bounds).
1337 name.printf("Complex clip test, iter %i", i);
1338 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001339 stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true);
1340 stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001341 test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
1342 test_aa_query(reporter, name, stack, m, {r-.1f, t, R, b}, ClipMethod::kAAElements, 1);
1343 test_aa_query(reporter, name, stack, m, {r-.1f, t, R+.1f, b}, ClipMethod::kAAElements, 2);
1344 test_aa_query(reporter, name, stack, m, {r, t, R+.1f, b}, ClipMethod::kAAElements, 1);
1345 test_aa_query(reporter, name, stack, m, {r, t, R, b}, ClipMethod::kIgnoreClip);
1346 test_aa_query(reporter, name, stack, m, {R, T, R+.1f, B}, ClipMethod::kSkipDraw);
1347
1348 // Complex clip where outer rect is pixel aligned (iior=false, normal bounds).
1349 name.printf("Aligned Complex clip test, iter %i", i);
1350 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001351 stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true);
1352 stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001353 test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
1354 test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB}, ClipMethod::kAAElements, 1);
1355 test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB+.1f}, ClipMethod::kAAElements, 1);
1356 test_aa_query(reporter, name, stack, m, {l, b, r, IB+.1f}, ClipMethod::kAAElements, 0);
1357 test_aa_query(reporter, name, stack, m, {l, b, r, IB}, ClipMethod::kIgnoreClip);
1358 test_aa_query(reporter, name, stack, m, {IL, IB, IR, IB+.1f}, ClipMethod::kSkipDraw);
1359
1360 // Apply random transforms and try again. This ensures the clip stack reduction is hardened
1361 // against FP rounding error.
1362 SkScalar sx = rand.nextRangeScalar(kMinScale, kMaxScale);
1363 sx = SkScalarFloorToScalar(sx * alignedRect.width()) / alignedRect.width();
1364 SkScalar sy = rand.nextRangeScalar(kMinScale, kMaxScale);
1365 sy = SkScalarFloorToScalar(sy * alignedRect.height()) / alignedRect.height();
1366 SkScalar tx = SkScalarRoundToScalar(sx * alignedRect.x()) - sx * alignedRect.x();
1367 SkScalar ty = SkScalarRoundToScalar(sy * alignedRect.y()) - sy * alignedRect.y();
1368
1369 SkMatrix xform = SkMatrix::MakeScale(sx, sy);
1370 xform.postTranslate(tx, ty);
1371 xform.mapRect(&alignedRect);
1372 xform.mapRect(&rect);
1373 xform.mapRect(&innerRect);
1374 m.postConcat(xform);
1375 }
1376}
1377
Chris Dalton348060f2017-06-05 13:15:37 -06001378static void test_tiny_query_bounds_assertion_bug(skiatest::Reporter* reporter) {
1379 // https://bugs.chromium.org/p/skia/issues/detail?id=5990
1380 const SkRect clipBounds = SkRect::MakeXYWH(1.5f, 100, 1000, 1000);
1381
1382 SkClipStack rectStack;
1383 rectStack.clipRect(clipBounds, SkMatrix::I(), kIntersect_SkClipOp, true);
1384
1385 SkPath clipPath;
1386 clipPath.moveTo(clipBounds.left(), clipBounds.top());
1387 clipPath.quadTo(clipBounds.right(), clipBounds.top(),
1388 clipBounds.right(), clipBounds.bottom());
1389 clipPath.quadTo(clipBounds.left(), clipBounds.bottom(),
1390 clipBounds.left(), clipBounds.top());
1391 SkClipStack pathStack;
1392 pathStack.clipPath(clipPath, SkMatrix::I(), kIntersect_SkClipOp, true);
1393
Brian Salomon14471772017-12-05 10:35:15 -05001394 auto context = GrContext::MakeMock(nullptr);
1395 const auto* caps = context->caps()->shaderCaps();
1396
Chris Dalton348060f2017-06-05 13:15:37 -06001397 for (const SkClipStack& stack : {rectStack, pathStack}) {
1398 for (SkRect queryBounds : {SkRect::MakeXYWH(53, 60, GrClip::kBoundsTolerance, 1000),
1399 SkRect::MakeXYWH(53, 60, GrClip::kBoundsTolerance/2, 1000),
1400 SkRect::MakeXYWH(53, 160, 1000, GrClip::kBoundsTolerance),
1401 SkRect::MakeXYWH(53, 160, 1000, GrClip::kBoundsTolerance/2)}) {
Brian Salomon14471772017-12-05 10:35:15 -05001402 const GrReducedClip reduced(stack, queryBounds, caps);
Chris Dalton79471932017-10-27 01:50:57 -06001403 REPORTER_ASSERT(reporter, !reduced.hasScissor());
1404 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty());
Chris Dalton348060f2017-06-05 13:15:37 -06001405 REPORTER_ASSERT(reporter,
1406 GrReducedClip::InitialState::kAllOut == reduced.initialState());
1407 }
1408 }
1409}
1410
bsalomon@google.coma4e13c82012-11-26 21:38:37 +00001411#endif
bsalomon@google.com51a62862012-11-26 21:19:43 +00001412
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00001413DEF_TEST(ClipStack, reporter) {
reed@google.combdee9fc2011-02-22 20:17:43 +00001414 SkClipStack stack;
1415
robertphillips@google.com80214e22012-07-20 15:33:18 +00001416 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001417 assert_count(reporter, stack, 0);
1418
1419 static const SkIRect gRects[] = {
1420 { 0, 0, 100, 100 },
1421 { 25, 25, 125, 125 },
1422 { 0, 0, 1000, 1000 },
1423 { 0, 0, 75, 75 }
1424 };
1425 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
Mike Reedc1f77742016-12-09 09:00:50 -05001426 stack.clipDevRect(gRects[i], kIntersect_SkClipOp);
reed@google.combdee9fc2011-02-22 20:17:43 +00001427 }
1428
1429 // all of the above rects should have been intersected, leaving only 1 rect
robertphillips@google.com80214e22012-07-20 15:33:18 +00001430 SkClipStack::B2TIter iter(stack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001431 const SkClipStack::Element* element = iter.next();
epoger@google.com2047f002011-05-17 17:36:59 +00001432 SkRect answer;
1433 answer.iset(25, 25, 75, 75);
reed@google.combdee9fc2011-02-22 20:17:43 +00001434
bsalomon49f085d2014-09-05 13:34:00 -07001435 REPORTER_ASSERT(reporter, element);
Brian Salomonf3b46e52017-08-30 11:37:57 -04001436 REPORTER_ASSERT(reporter,
1437 SkClipStack::Element::DeviceSpaceType::kRect == element->getDeviceSpaceType());
Mike Reedc1f77742016-12-09 09:00:50 -05001438 REPORTER_ASSERT(reporter, kIntersect_SkClipOp == element->getOp());
Brian Salomonf3b46e52017-08-30 11:37:57 -04001439 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == answer);
reed@google.combdee9fc2011-02-22 20:17:43 +00001440 // now check that we only had one in our iterator
1441 REPORTER_ASSERT(reporter, !iter.next());
1442
1443 stack.reset();
robertphillips@google.com80214e22012-07-20 15:33:18 +00001444 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001445 assert_count(reporter, stack, 0);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +00001446
1447 test_assign_and_comparison(reporter);
robertphillips@google.com80214e22012-07-20 15:33:18 +00001448 test_iterators(reporter);
Brian Salomonf3b46e52017-08-30 11:37:57 -04001449 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kRect);
1450 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kRRect);
1451 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kPath);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +00001452 test_isWideOpen(reporter);
robertphillips@google.com08eacc12012-08-02 12:49:00 +00001453 test_rect_merging(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001454 test_rect_replace(reporter);
junov@chromium.orgedf32d52012-12-10 14:57:54 +00001455 test_rect_inverse_fill(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001456 test_path_replace(reporter);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +00001457 test_quickContains(reporter);
csmartdaltond50e2402016-07-22 08:39:06 -07001458 test_invfill_diff_bug(reporter);
bsalomon@google.come7b3d292012-11-26 21:42:32 +00001459#if SK_SUPPORT_GPU
bsalomon@google.comedb26fd2012-11-28 14:42:41 +00001460 test_reduced_clip_stack(reporter);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001461 test_reduced_clip_stack_genid(reporter);
1462 test_reduced_clip_stack_no_aa_crash(reporter);
csmartdaltoncbecb082016-07-22 08:59:08 -07001463 test_reduced_clip_stack_aa(reporter);
Chris Dalton348060f2017-06-05 13:15:37 -06001464 test_tiny_query_bounds_assertion_bug(reporter);
bsalomon@google.come7b3d292012-11-26 21:42:32 +00001465#endif
reed@google.combdee9fc2011-02-22 20:17:43 +00001466}
Brian Salomon19f0ed52017-01-06 13:54:58 -05001467
1468//////////////////////////////////////////////////////////////////////////////
1469
1470#if SK_SUPPORT_GPU
Robert Phillips875218e2017-02-24 08:37:13 -05001471sk_sp<GrTextureProxy> GrClipStackClip::testingOnly_createClipMask(GrContext* context) const {
Brian Salomon19f0ed52017-01-06 13:54:58 -05001472 const GrReducedClip reducedClip(*fStack, SkRect::MakeWH(512, 512), 0);
Brian Osman5d034742017-09-11 13:38:55 -04001473 return this->createSoftwareClipMask(context, reducedClip, nullptr);
Brian Salomon19f0ed52017-01-06 13:54:58 -05001474}
1475
1476// Verify that clip masks are freed up when the clip state that generated them goes away.
1477DEF_GPUTEST_FOR_ALL_CONTEXTS(ClipMaskCache, reporter, ctxInfo) {
1478 // This test uses resource key tags which only function in debug builds.
1479#ifdef SK_DEBUG
1480 GrContext* context = ctxInfo.grContext();
1481 SkClipStack stack;
1482
1483 SkPath path;
1484 path.addCircle(10, 10, 8);
1485 path.addCircle(15, 15, 8);
1486 path.setFillType(SkPath::kEvenOdd_FillType);
1487
1488 static const char* kTag = GrClipStackClip::kMaskTestTag;
1489 GrResourceCache* cache = context->getResourceCache();
1490
1491 static constexpr int kN = 5;
1492
1493 for (int i = 0; i < kN; ++i) {
1494 SkMatrix m;
1495 m.setTranslate(0.5, 0.5);
1496 stack.save();
1497 stack.clipPath(path, m, SkClipOp::kIntersect, true);
Robert Phillips875218e2017-02-24 08:37:13 -05001498 sk_sp<GrTextureProxy> mask = GrClipStackClip(&stack).testingOnly_createClipMask(context);
Robert Phillipseee4d6e2017-06-05 09:26:07 -04001499 mask->instantiate(context->resourceProvider());
1500 GrTexture* tex = mask->priv().peekTexture();
Robert Phillips875218e2017-02-24 08:37:13 -05001501 REPORTER_ASSERT(reporter, 0 == strcmp(tex->getUniqueKey().tag(), kTag));
Brian Salomon19f0ed52017-01-06 13:54:58 -05001502 // Make sure mask isn't pinned in cache.
1503 mask.reset(nullptr);
1504 context->flush();
1505 REPORTER_ASSERT(reporter, i + 1 == cache->countUniqueKeysWithTag(kTag));
1506 }
1507
1508 for (int i = 0; i < kN; ++i) {
1509 stack.restore();
1510 cache->purgeAsNeeded();
1511 REPORTER_ASSERT(reporter, kN - (i + 1) == cache->countUniqueKeysWithTag(kTag));
1512 }
1513#endif
1514}
1515
Mike Reed3726a4a2017-01-19 11:36:41 -05001516#include "SkSurface.h"
1517DEF_GPUTEST_FOR_ALL_CONTEXTS(canvas_private_clipRgn, reporter, ctxInfo) {
1518 GrContext* context = ctxInfo.grContext();
1519
1520 const int w = 10;
1521 const int h = 10;
Brian Salomon8996e182017-07-05 17:01:48 -04001522 SkImageInfo info = SkImageInfo::Make(w, h, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
Mike Reed3726a4a2017-01-19 11:36:41 -05001523 sk_sp<SkSurface> surf = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info);
1524 SkCanvas* canvas = surf->getCanvas();
1525 SkRegion rgn;
1526
1527 canvas->temporary_internal_getRgnClip(&rgn);
1528 REPORTER_ASSERT(reporter, rgn.isRect());
1529 REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeWH(w, h));
1530
1531 canvas->save();
1532 canvas->clipRect(SkRect::MakeWH(5, 5), kDifference_SkClipOp);
1533 canvas->temporary_internal_getRgnClip(&rgn);
1534 REPORTER_ASSERT(reporter, rgn.isComplex());
1535 REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeWH(w, h));
1536 canvas->restore();
1537
1538 canvas->save();
1539 canvas->clipRRect(SkRRect::MakeOval(SkRect::MakeLTRB(3, 3, 7, 7)));
1540 canvas->temporary_internal_getRgnClip(&rgn);
1541 REPORTER_ASSERT(reporter, rgn.isComplex());
1542 REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeLTRB(3, 3, 7, 7));
1543 canvas->restore();
1544}
Brian Salomon19f0ed52017-01-06 13:54:58 -05001545#endif