blob: e1aa148b3f9838c1050901a244ca422c5e3a676e [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()) {
161 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
162 REPORTER_ASSERT(reporter, element->getRect() == gRects[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000163 }
164
165 SkASSERT(i == 4);
166 }
167
168 // top to bottom iteration
169 {
halcanary96fcdcc2015-08-27 07:41:13 -0700170 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000171
172 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
173 int i;
174
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000175 for (i = 3, element = iter.prev(); element; --i, element = iter.prev()) {
176 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
177 REPORTER_ASSERT(reporter, element->getRect() == gRects[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000178 }
179
180 SkASSERT(i == -1);
181 }
182
183 // skipToTopmost
184 {
halcanary96fcdcc2015-08-27 07:41:13 -0700185 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000186
187 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
188
Mike Reedc1f77742016-12-09 09:00:50 -0500189 element = iter.skipToTopmost(kUnion_SkClipOp);
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000190 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
191 REPORTER_ASSERT(reporter, element->getRect() == gRects[3]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000192 }
193}
194
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000195// Exercise the SkClipStack's getConservativeBounds computation
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000196static void test_bounds(skiatest::Reporter* reporter, SkClipStack::Element::Type primType) {
robertphillips@google.com607fe072012-07-24 13:54:00 +0000197 static const int gNumCases = 20;
198 static const SkRect gAnswerRectsBW[gNumCases] = {
199 // A op B
200 { 40, 40, 50, 50 },
201 { 10, 10, 50, 50 },
202 { 10, 10, 80, 80 },
203 { 10, 10, 80, 80 },
204 { 40, 40, 80, 80 },
205
206 // invA op B
207 { 40, 40, 80, 80 },
208 { 0, 0, 100, 100 },
209 { 0, 0, 100, 100 },
210 { 0, 0, 100, 100 },
211 { 40, 40, 50, 50 },
212
213 // A op invB
214 { 10, 10, 50, 50 },
215 { 40, 40, 50, 50 },
216 { 0, 0, 100, 100 },
217 { 0, 0, 100, 100 },
218 { 0, 0, 100, 100 },
219
220 // invA op invB
221 { 0, 0, 100, 100 },
222 { 40, 40, 80, 80 },
223 { 0, 0, 100, 100 },
224 { 10, 10, 80, 80 },
225 { 10, 10, 50, 50 },
226 };
227
Mike Reedc1f77742016-12-09 09:00:50 -0500228 static const SkClipOp gOps[] = {
229 kIntersect_SkClipOp,
230 kDifference_SkClipOp,
231 kUnion_SkClipOp,
232 kXOR_SkClipOp,
233 kReverseDifference_SkClipOp
robertphillips@google.com607fe072012-07-24 13:54:00 +0000234 };
235
236 SkRect rectA, rectB;
237
238 rectA.iset(10, 10, 50, 50);
239 rectB.iset(40, 40, 80, 80);
240
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000241 SkRRect rrectA, rrectB;
242 rrectA.setOval(rectA);
243 rrectB.setRectXY(rectB, SkIntToScalar(1), SkIntToScalar(2));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000244
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000245 SkPath pathA, pathB;
246
247 pathA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
248 pathB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000249
250 SkClipStack stack;
robertphillips@google.com7b112892012-07-31 15:18:21 +0000251 SkRect devClipBound;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000252 bool isIntersectionOfRects = false;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000253
254 int testCase = 0;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000255 int numBitTests = SkClipStack::Element::kPath_Type == primType ? 4 : 1;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000256 for (int invBits = 0; invBits < numBitTests; ++invBits) {
robertphillips@google.com607fe072012-07-24 13:54:00 +0000257 for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) {
258
259 stack.save();
260 bool doInvA = SkToBool(invBits & 1);
261 bool doInvB = SkToBool(invBits & 2);
262
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000263 pathA.setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType :
robertphillips@google.com607fe072012-07-24 13:54:00 +0000264 SkPath::kEvenOdd_FillType);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000265 pathB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType :
robertphillips@google.com607fe072012-07-24 13:54:00 +0000266 SkPath::kEvenOdd_FillType);
267
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000268 switch (primType) {
269 case SkClipStack::Element::kEmpty_Type:
270 SkDEBUGFAIL("Don't call this with kEmpty.");
271 break;
272 case SkClipStack::Element::kRect_Type:
Mike Reedc1f77742016-12-09 09:00:50 -0500273 stack.clipRect(rectA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400274 stack.clipRect(rectB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000275 break;
276 case SkClipStack::Element::kRRect_Type:
Mike Reedc1f77742016-12-09 09:00:50 -0500277 stack.clipRRect(rrectA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400278 stack.clipRRect(rrectB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000279 break;
280 case SkClipStack::Element::kPath_Type:
Mike Reedc1f77742016-12-09 09:00:50 -0500281 stack.clipPath(pathA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400282 stack.clipPath(pathB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000283 break;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000284 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000285
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000286 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000287 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000288
robertphillips@google.com7b112892012-07-31 15:18:21 +0000289 stack.getConservativeBounds(0, 0, 100, 100, &devClipBound,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000290 &isIntersectionOfRects);
291
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000292 if (SkClipStack::Element::kRect_Type == primType) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000293 REPORTER_ASSERT(reporter, isIntersectionOfRects ==
Mike Reedc1f77742016-12-09 09:00:50 -0500294 (gOps[op] == kIntersect_SkClipOp));
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000295 } else {
296 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
297 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000298
299 SkASSERT(testCase < gNumCases);
robertphillips@google.com7b112892012-07-31 15:18:21 +0000300 REPORTER_ASSERT(reporter, devClipBound == gAnswerRectsBW[testCase]);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000301 ++testCase;
302
303 stack.restore();
304 }
305 }
306}
307
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000308// Test out 'isWideOpen' entry point
309static void test_isWideOpen(skiatest::Reporter* reporter) {
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000310 {
311 // Empty stack is wide open. Wide open stack means that gen id is wide open.
312 SkClipStack stack;
313 REPORTER_ASSERT(reporter, stack.isWideOpen());
314 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
315 }
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000316
317 SkRect rectA, rectB;
318
319 rectA.iset(10, 10, 40, 40);
320 rectB.iset(50, 50, 80, 80);
321
322 // Stack should initially be wide open
323 {
324 SkClipStack stack;
325
326 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000327 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000328 }
329
330 // Test out case where the user specifies a union that includes everything
331 {
332 SkClipStack stack;
333
334 SkPath clipA, clipB;
335
336 clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
337 clipA.setFillType(SkPath::kInverseEvenOdd_FillType);
338
339 clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
340 clipB.setFillType(SkPath::kInverseEvenOdd_FillType);
341
Mike Reedc1f77742016-12-09 09:00:50 -0500342 stack.clipPath(clipA, SkMatrix::I(), kReplace_SkClipOp, false);
343 stack.clipPath(clipB, SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000344
345 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000346 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000347 }
348
349 // Test out union w/ a wide open clip
350 {
351 SkClipStack stack;
352
Mike Reedc1f77742016-12-09 09:00:50 -0500353 stack.clipRect(rectA, SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000354
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 empty difference from a wide open clip
360 {
361 SkClipStack stack;
362
363 SkRect emptyRect;
364 emptyRect.setEmpty();
365
Mike Reedc1f77742016-12-09 09:00:50 -0500366 stack.clipRect(emptyRect, SkMatrix::I(), kDifference_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000367
368 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000369 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000370 }
371
372 // Test out return to wide open
373 {
374 SkClipStack stack;
375
376 stack.save();
377
Mike Reedc1f77742016-12-09 09:00:50 -0500378 stack.clipRect(rectA, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000379
380 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000381 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000382
383 stack.restore();
384
385 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000386 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000387 }
388}
389
bsalomon@google.com100abf42012-09-05 17:40:04 +0000390static int count(const SkClipStack& stack) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000391
392 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
393
halcanary96fcdcc2015-08-27 07:41:13 -0700394 const SkClipStack::Element* element = nullptr;
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000395 int count = 0;
396
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000397 for (element = iter.prev(); element; element = iter.prev(), ++count) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000398 ;
399 }
400
401 return count;
402}
403
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000404static void test_rect_inverse_fill(skiatest::Reporter* reporter) {
405 // non-intersecting rectangles
406 SkRect rect = SkRect::MakeLTRB(0, 0, 10, 10);
407
408 SkPath path;
409 path.addRect(rect);
410 path.toggleInverseFillType();
411 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500412 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000413
414 SkRect bounds;
415 SkClipStack::BoundsType boundsType;
416 stack.getBounds(&bounds, &boundsType);
417 REPORTER_ASSERT(reporter, SkClipStack::kInsideOut_BoundsType == boundsType);
418 REPORTER_ASSERT(reporter, bounds == rect);
419}
420
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000421static void test_rect_replace(skiatest::Reporter* reporter) {
422 SkRect rect = SkRect::MakeWH(100, 100);
423 SkRect rect2 = SkRect::MakeXYWH(50, 50, 100, 100);
424
425 SkRect bound;
426 SkClipStack::BoundsType type;
427 bool isIntersectionOfRects;
428
429 // Adding a new rect with the replace operator should not increase
430 // the stack depth. BW replacing BW.
431 {
432 SkClipStack stack;
433 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500434 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000435 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500436 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000437 REPORTER_ASSERT(reporter, 1 == count(stack));
438 }
439
440 // Adding a new rect with the replace operator should not increase
441 // the stack depth. AA replacing AA.
442 {
443 SkClipStack stack;
444 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500445 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000446 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500447 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000448 REPORTER_ASSERT(reporter, 1 == count(stack));
449 }
450
451 // Adding a new rect with the replace operator should not increase
452 // the stack depth. BW replacing AA replacing BW.
453 {
454 SkClipStack stack;
455 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500456 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000457 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500458 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000459 REPORTER_ASSERT(reporter, 1 == 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));
462 }
463
464 // Make sure replace clip rects don't collapse too much.
465 {
466 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500467 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
468 stack.clipRect(rect2, SkMatrix::I(), kIntersect_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000469 REPORTER_ASSERT(reporter, 1 == count(stack));
470
471 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500472 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000473 REPORTER_ASSERT(reporter, 2 == count(stack));
474 stack.getBounds(&bound, &type, &isIntersectionOfRects);
475 REPORTER_ASSERT(reporter, bound == rect);
476 stack.restore();
477 REPORTER_ASSERT(reporter, 1 == count(stack));
478
479 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500480 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
481 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000482 REPORTER_ASSERT(reporter, 2 == count(stack));
483 stack.restore();
484 REPORTER_ASSERT(reporter, 1 == count(stack));
485
486 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500487 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
488 stack.clipRect(rect2, SkMatrix::I(), kIntersect_SkClipOp, false);
489 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000490 REPORTER_ASSERT(reporter, 2 == count(stack));
491 stack.restore();
492 REPORTER_ASSERT(reporter, 1 == count(stack));
493 }
494}
495
496// Simplified path-based version of test_rect_replace.
497static void test_path_replace(skiatest::Reporter* reporter) {
498 SkRect rect = SkRect::MakeWH(100, 100);
499 SkPath path;
500 path.addCircle(50, 50, 50);
501
502 // Replace operation doesn't grow the stack.
503 {
504 SkClipStack stack;
505 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500506 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000507 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500508 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000509 REPORTER_ASSERT(reporter, 1 == count(stack));
510 }
511
512 // Replacing rect with path.
513 {
514 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500515 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000516 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500517 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000518 REPORTER_ASSERT(reporter, 1 == count(stack));
519 }
520}
521
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000522// Test out SkClipStack's merging of rect clips. In particular exercise
523// merging of aa vs. bw rects.
524static void test_rect_merging(skiatest::Reporter* reporter) {
525
526 SkRect overlapLeft = SkRect::MakeLTRB(10, 10, 50, 50);
527 SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80);
528
529 SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90);
530 SkRect nestedChild = SkRect::MakeLTRB(40, 40, 60, 60);
531
532 SkRect bound;
533 SkClipStack::BoundsType type;
534 bool isIntersectionOfRects;
535
536 // all bw overlapping - should merge
537 {
538 SkClipStack stack;
539
Mike Reedc1f77742016-12-09 09:00:50 -0500540 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000541
Mike Reedc1f77742016-12-09 09:00:50 -0500542 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000543
544 REPORTER_ASSERT(reporter, 1 == count(stack));
545
546 stack.getBounds(&bound, &type, &isIntersectionOfRects);
547
548 REPORTER_ASSERT(reporter, isIntersectionOfRects);
549 }
550
551 // all aa overlapping - should merge
552 {
553 SkClipStack stack;
554
Mike Reedc1f77742016-12-09 09:00:50 -0500555 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000556
Mike Reedc1f77742016-12-09 09:00:50 -0500557 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000558
559 REPORTER_ASSERT(reporter, 1 == count(stack));
560
561 stack.getBounds(&bound, &type, &isIntersectionOfRects);
562
563 REPORTER_ASSERT(reporter, isIntersectionOfRects);
564 }
565
566 // mixed overlapping - should _not_ merge
567 {
568 SkClipStack stack;
569
Mike Reedc1f77742016-12-09 09:00:50 -0500570 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000571
Mike Reedc1f77742016-12-09 09:00:50 -0500572 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000573
574 REPORTER_ASSERT(reporter, 2 == count(stack));
575
576 stack.getBounds(&bound, &type, &isIntersectionOfRects);
577
578 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
579 }
580
581 // mixed nested (bw inside aa) - should merge
582 {
583 SkClipStack stack;
584
Mike Reedc1f77742016-12-09 09:00:50 -0500585 stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000586
Mike Reedc1f77742016-12-09 09:00:50 -0500587 stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000588
589 REPORTER_ASSERT(reporter, 1 == count(stack));
590
591 stack.getBounds(&bound, &type, &isIntersectionOfRects);
592
593 REPORTER_ASSERT(reporter, isIntersectionOfRects);
594 }
595
596 // mixed nested (aa inside bw) - should merge
597 {
598 SkClipStack stack;
599
Mike Reedc1f77742016-12-09 09:00:50 -0500600 stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000601
Mike Reedc1f77742016-12-09 09:00:50 -0500602 stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000603
604 REPORTER_ASSERT(reporter, 1 == count(stack));
605
606 stack.getBounds(&bound, &type, &isIntersectionOfRects);
607
608 REPORTER_ASSERT(reporter, isIntersectionOfRects);
609 }
610
611 // reverse nested (aa inside bw) - should _not_ merge
612 {
613 SkClipStack stack;
614
Mike Reedc1f77742016-12-09 09:00:50 -0500615 stack.clipRect(nestedChild, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000616
Mike Reedc1f77742016-12-09 09:00:50 -0500617 stack.clipRect(nestedParent, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000618
619 REPORTER_ASSERT(reporter, 2 == count(stack));
620
621 stack.getBounds(&bound, &type, &isIntersectionOfRects);
622
623 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
624 }
625}
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000626
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000627static void test_quickContains(skiatest::Reporter* reporter) {
628 SkRect testRect = SkRect::MakeLTRB(10, 10, 40, 40);
629 SkRect insideRect = SkRect::MakeLTRB(20, 20, 30, 30);
630 SkRect intersectingRect = SkRect::MakeLTRB(25, 25, 50, 50);
631 SkRect outsideRect = SkRect::MakeLTRB(0, 0, 50, 50);
632 SkRect nonIntersectingRect = SkRect::MakeLTRB(100, 100, 110, 110);
633
634 SkPath insideCircle;
635 insideCircle.addCircle(25, 25, 5);
636 SkPath intersectingCircle;
637 intersectingCircle.addCircle(25, 40, 10);
638 SkPath outsideCircle;
639 outsideCircle.addCircle(25, 25, 50);
640 SkPath nonIntersectingCircle;
641 nonIntersectingCircle.addCircle(100, 100, 5);
642
643 {
644 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500645 stack.clipRect(outsideRect, SkMatrix::I(), kDifference_SkClipOp, false);
646 // return false because quickContains currently does not care for kDifference_SkClipOp
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000647 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
648 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000649
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000650 // Replace Op tests
651 {
652 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500653 stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000654 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
655 }
656
657 {
658 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500659 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000660 stack.save(); // To prevent in-place substitution by replace OP
Mike Reedc1f77742016-12-09 09:00:50 -0500661 stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000662 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
663 stack.restore();
664 }
665
666 {
667 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500668 stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000669 stack.save(); // To prevent in-place substitution by replace OP
Mike Reedc1f77742016-12-09 09:00:50 -0500670 stack.clipRect(insideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000671 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
672 stack.restore();
673 }
674
675 // Verify proper traversal of multi-element clip
676 {
677 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500678 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000679 // Use a path for second clip to prevent in-place intersection
Mike Reedc1f77742016-12-09 09:00:50 -0500680 stack.clipPath(outsideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000681 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
682 }
683
684 // Intersect Op tests with rectangles
685 {
686 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500687 stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000688 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
689 }
690
691 {
692 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500693 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000694 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
695 }
696
697 {
698 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500699 stack.clipRect(intersectingRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000700 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
701 }
702
703 {
704 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500705 stack.clipRect(nonIntersectingRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000706 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
707 }
708
709 // Intersect Op tests with circle paths
710 {
711 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500712 stack.clipPath(outsideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000713 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
714 }
715
716 {
717 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500718 stack.clipPath(insideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000719 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
720 }
721
722 {
723 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500724 stack.clipPath(intersectingCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000725 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
726 }
727
728 {
729 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500730 stack.clipPath(nonIntersectingCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000731 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
732 }
733
734 // Intersect Op tests with inverse filled rectangles
735 {
736 SkClipStack stack;
737 SkPath path;
738 path.addRect(outsideRect);
739 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500740 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000741 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
742 }
743
744 {
745 SkClipStack stack;
746 SkPath path;
747 path.addRect(insideRect);
748 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500749 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000750 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
751 }
752
753 {
754 SkClipStack stack;
755 SkPath path;
756 path.addRect(intersectingRect);
757 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500758 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000759 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
760 }
761
762 {
763 SkClipStack stack;
764 SkPath path;
765 path.addRect(nonIntersectingRect);
766 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500767 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000768 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
769 }
770
771 // Intersect Op tests with inverse filled circles
772 {
773 SkClipStack stack;
774 SkPath path = outsideCircle;
775 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500776 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000777 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
778 }
779
780 {
781 SkClipStack stack;
782 SkPath path = insideCircle;
783 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500784 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000785 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
786 }
787
788 {
789 SkClipStack stack;
790 SkPath path = intersectingCircle;
791 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500792 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000793 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
794 }
795
796 {
797 SkClipStack stack;
798 SkPath path = nonIntersectingCircle;
799 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500800 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000801 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
802 }
803}
804
csmartdaltond50e2402016-07-22 08:39:06 -0700805static void set_region_to_stack(const SkClipStack& stack, const SkIRect& bounds, SkRegion* region) {
806 region->setRect(bounds);
807 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
808 while (const SkClipStack::Element *element = iter.next()) {
809 SkRegion elemRegion;
810 SkRegion boundsRgn(bounds);
811 SkPath path;
812
813 switch (element->getType()) {
814 case SkClipStack::Element::kEmpty_Type:
815 elemRegion.setEmpty();
816 break;
817 default:
818 element->asPath(&path);
819 elemRegion.setPath(path, boundsRgn);
820 break;
821 }
reed73603f32016-09-20 08:42:38 -0700822 region->op(elemRegion, (SkRegion::Op)element->getOp());
csmartdaltond50e2402016-07-22 08:39:06 -0700823 }
824}
825
826static void test_invfill_diff_bug(skiatest::Reporter* reporter) {
827 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500828 stack.clipRect({10, 10, 20, 20}, SkMatrix::I(), kIntersect_SkClipOp, false);
csmartdaltond50e2402016-07-22 08:39:06 -0700829
830 SkPath path;
831 path.addRect({30, 10, 40, 20});
832 path.setFillType(SkPath::kInverseWinding_FillType);
Mike Reedc1f77742016-12-09 09:00:50 -0500833 stack.clipPath(path, SkMatrix::I(), kDifference_SkClipOp, false);
csmartdaltond50e2402016-07-22 08:39:06 -0700834
835 REPORTER_ASSERT(reporter, SkClipStack::kEmptyGenID == stack.getTopmostGenID());
836
837 SkRect stackBounds;
838 SkClipStack::BoundsType stackBoundsType;
839 stack.getBounds(&stackBounds, &stackBoundsType);
840
841 REPORTER_ASSERT(reporter, stackBounds.isEmpty());
842 REPORTER_ASSERT(reporter, SkClipStack::kNormal_BoundsType == stackBoundsType);
843
844 SkRegion region;
845 set_region_to_stack(stack, {0, 0, 50, 30}, &region);
846
847 REPORTER_ASSERT(reporter, region.isEmpty());
848}
849
bsalomon@google.com51a62862012-11-26 21:19:43 +0000850///////////////////////////////////////////////////////////////////////////////////////////////////
851
bsalomon@google.coma4e13c82012-11-26 21:38:37 +0000852#if SK_SUPPORT_GPU
bsalomon@google.com705e8402012-11-27 15:43:57 +0000853// Functions that add a shape to the clip stack. The shape is computed from a rectangle.
854// AA is always disabled since the clip stack reducer can cause changes in aa rasterization of the
855// stack. A fractional edge repeated in different elements may be rasterized fewer times using the
856// reduced stack.
857typedef void (*AddElementFunc) (const SkRect& rect,
858 bool invert,
Mike Reedc1f77742016-12-09 09:00:50 -0500859 SkClipOp op,
csmartdaltoncbecb082016-07-22 08:59:08 -0700860 SkClipStack* stack,
861 bool doAA);
bsalomon@google.coma4e13c82012-11-26 21:38:37 +0000862
Mike Reedc1f77742016-12-09 09:00:50 -0500863static void add_round_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700864 bool doAA) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000865 SkScalar rx = rect.width() / 10;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000866 SkScalar ry = rect.height() / 20;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000867 if (invert) {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000868 SkPath path;
869 path.addRoundRect(rect, rx, ry);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000870 path.setFillType(SkPath::kInverseWinding_FillType);
Brian Salomona3b45d42016-10-03 11:36:16 -0400871 stack->clipPath(path, SkMatrix::I(), op, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000872 } else {
873 SkRRect rrect;
874 rrect.setRectXY(rect, rx, ry);
Brian Salomona3b45d42016-10-03 11:36:16 -0400875 stack->clipRRect(rrect, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000876 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000877};
878
Mike Reedc1f77742016-12-09 09:00:50 -0500879static void add_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700880 bool doAA) {
bsalomon@google.com705e8402012-11-27 15:43:57 +0000881 if (invert) {
882 SkPath path;
883 path.addRect(rect);
884 path.setFillType(SkPath::kInverseWinding_FillType);
Brian Salomona3b45d42016-10-03 11:36:16 -0400885 stack->clipPath(path, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000886 } else {
Brian Salomona3b45d42016-10-03 11:36:16 -0400887 stack->clipRect(rect, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000888 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000889};
890
Mike Reedc1f77742016-12-09 09:00:50 -0500891static void add_oval(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700892 bool doAA) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000893 SkPath path;
894 path.addOval(rect);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000895 if (invert) {
896 path.setFillType(SkPath::kInverseWinding_FillType);
897 }
Brian Salomona3b45d42016-10-03 11:36:16 -0400898 stack->clipPath(path, SkMatrix::I(), op, doAA);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000899};
900
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000901static void add_elem_to_stack(const SkClipStack::Element& element, SkClipStack* stack) {
902 switch (element.getType()) {
903 case SkClipStack::Element::kRect_Type:
Brian Salomona3b45d42016-10-03 11:36:16 -0400904 stack->clipRect(element.getRect(), SkMatrix::I(), element.getOp(), element.isAA());
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000905 break;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000906 case SkClipStack::Element::kRRect_Type:
Brian Salomona3b45d42016-10-03 11:36:16 -0400907 stack->clipRRect(element.getRRect(), SkMatrix::I(), element.getOp(), element.isAA());
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000908 break;
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000909 case SkClipStack::Element::kPath_Type:
Brian Salomona3b45d42016-10-03 11:36:16 -0400910 stack->clipPath(element.getPath(), SkMatrix::I(), element.getOp(), element.isAA());
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000911 break;
912 case SkClipStack::Element::kEmpty_Type:
913 SkDEBUGFAIL("Why did the reducer produce an explicit empty.");
914 stack->clipEmpty();
915 break;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000916 }
917}
918
bsalomon@google.com51a62862012-11-26 21:19:43 +0000919static void test_reduced_clip_stack(skiatest::Reporter* reporter) {
920 // We construct random clip stacks, reduce them, and then rasterize both versions to verify that
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000921 // they are equal.
bsalomon@google.com51a62862012-11-26 21:19:43 +0000922
923 // All the clip elements will be contained within these bounds.
csmartdaltond211e782016-08-15 11:17:19 -0700924 static const SkIRect kIBounds = SkIRect::MakeWH(100, 100);
925 static const SkRect kBounds = SkRect::Make(kIBounds);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000926
927 enum {
csmartdaltoncbecb082016-07-22 08:59:08 -0700928 kNumTests = 250,
bsalomon@google.com51a62862012-11-26 21:19:43 +0000929 kMinElemsPerTest = 1,
930 kMaxElemsPerTest = 50,
931 };
932
933 // min/max size of a clip element as a fraction of kBounds.
934 static const SkScalar kMinElemSizeFrac = SK_Scalar1 / 5;
935 static const SkScalar kMaxElemSizeFrac = SK_Scalar1;
936
Mike Reedc1f77742016-12-09 09:00:50 -0500937 static const SkClipOp kOps[] = {
938 kDifference_SkClipOp,
939 kIntersect_SkClipOp,
940 kUnion_SkClipOp,
941 kXOR_SkClipOp,
942 kReverseDifference_SkClipOp,
943 kReplace_SkClipOp,
bsalomon@google.com51a62862012-11-26 21:19:43 +0000944 };
945
946 // Replace operations short-circuit the optimizer. We want to make sure that we test this code
947 // path a little bit but we don't want it to prevent us from testing many longer traversals in
948 // the optimizer.
949 static const int kReplaceDiv = 4 * kMaxElemsPerTest;
950
bsalomon@google.com705e8402012-11-27 15:43:57 +0000951 // We want to test inverse fills. However, they are quite rare in practice so don't over do it.
952 static const SkScalar kFractionInverted = SK_Scalar1 / kMaxElemsPerTest;
953
csmartdaltoncbecb082016-07-22 08:59:08 -0700954 static const SkScalar kFractionAntialiased = 0.25;
955
bsalomon@google.com51a62862012-11-26 21:19:43 +0000956 static const AddElementFunc kElementFuncs[] = {
957 add_rect,
958 add_round_rect,
959 add_oval,
960 };
961
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000962 SkRandom r;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000963
964 for (int i = 0; i < kNumTests; ++i) {
csmartdaltoncbecb082016-07-22 08:59:08 -0700965 SkString testCase;
966 testCase.printf("Iteration %d", i);
967
bsalomon@google.com51a62862012-11-26 21:19:43 +0000968 // Randomly generate a clip stack.
969 SkClipStack stack;
970 int numElems = r.nextRangeU(kMinElemsPerTest, kMaxElemsPerTest);
csmartdaltoncbecb082016-07-22 08:59:08 -0700971 bool doAA = r.nextBiasedBool(kFractionAntialiased);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000972 for (int e = 0; e < numElems; ++e) {
Mike Reedc1f77742016-12-09 09:00:50 -0500973 SkClipOp op = kOps[r.nextULessThan(SK_ARRAY_COUNT(kOps))];
974 if (op == kReplace_SkClipOp) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000975 if (r.nextU() % kReplaceDiv) {
976 --e;
977 continue;
978 }
979 }
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000980
bsalomon@google.com51a62862012-11-26 21:19:43 +0000981 // saves can change the clip stack behavior when an element is added.
982 bool doSave = r.nextBool();
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000983
bsalomon@google.com51a62862012-11-26 21:19:43 +0000984 SkSize size = SkSize::Make(
Mike Reeddf85c382017-02-14 10:59:19 -0500985 kBounds.width() * r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac),
986 kBounds.height() * r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac));
bsalomon@google.com51a62862012-11-26 21:19:43 +0000987
csmartdaltoncbecb082016-07-22 08:59:08 -0700988 SkPoint xy = {r.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth),
989 r.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight)};
bsalomon@google.com51a62862012-11-26 21:19:43 +0000990
csmartdaltoncbecb082016-07-22 08:59:08 -0700991 SkRect rect;
992 if (doAA) {
993 rect.setXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight);
994 if (GrClip::IsPixelAligned(rect)) {
995 // Don't create an element that may accidentally become not antialiased.
996 rect.outset(0.5f, 0.5f);
997 }
998 SkASSERT(!GrClip::IsPixelAligned(rect));
999 } else {
1000 rect.setXYWH(SkScalarFloorToScalar(xy.fX),
1001 SkScalarFloorToScalar(xy.fY),
1002 SkScalarCeilToScalar(size.fWidth),
1003 SkScalarCeilToScalar(size.fHeight));
1004 }
bsalomon@google.com51a62862012-11-26 21:19:43 +00001005
bsalomon@google.com705e8402012-11-27 15:43:57 +00001006 bool invert = r.nextBiasedBool(kFractionInverted);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001007
csmartdaltoncbecb082016-07-22 08:59:08 -07001008 kElementFuncs[r.nextULessThan(SK_ARRAY_COUNT(kElementFuncs))](rect, invert, op, &stack,
1009 doAA);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001010 if (doSave) {
1011 stack.save();
1012 }
1013 }
1014
csmartdalton8d3f92a2016-08-17 09:39:38 -07001015 // Zero the memory we will new the GrReducedClip into. This ensures the elements gen ID
1016 // will be kInvalidGenID if left uninitialized.
1017 SkAlignedSTStorage<1, GrReducedClip> storage;
1018 memset(storage.get(), 0, sizeof(GrReducedClip));
1019 GR_STATIC_ASSERT(0 == SkClipStack::kInvalidGenID);
1020
csmartdalton77f2fae2016-08-08 09:55:06 -07001021 // Get the reduced version of the stack.
csmartdaltoncbecb082016-07-22 08:59:08 -07001022 SkRect queryBounds = kBounds;
1023 queryBounds.outset(kBounds.width() / 2, kBounds.height() / 2);
csmartdalton8d3f92a2016-08-17 09:39:38 -07001024 const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, queryBounds);
bsalomon@google.coma4444302012-12-04 15:22:12 +00001025
csmartdalton8d3f92a2016-08-17 09:39:38 -07001026 REPORTER_ASSERT_MESSAGE(reporter,
1027 reduced->elements().isEmpty() ||
1028 SkClipStack::kInvalidGenID != reduced->elementsGenID(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001029 testCase.c_str());
1030
csmartdalton8d3f92a2016-08-17 09:39:38 -07001031 if (!reduced->elements().isEmpty()) {
1032 REPORTER_ASSERT_MESSAGE(reporter, reduced->hasIBounds(), testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001033 SkRect stackBounds;
1034 SkClipStack::BoundsType stackBoundsType;
1035 stack.getBounds(&stackBounds, &stackBoundsType);
1036 if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
1037 // Unless GrReducedClip starts doing some heroic tightening of the clip bounds, this
1038 // will be true since the stack bounds are completely contained inside the query.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001039 REPORTER_ASSERT_MESSAGE(reporter,
1040 GrClip::IsInsideClip(reduced->ibounds(), stackBounds),
csmartdaltoncbecb082016-07-22 08:59:08 -07001041 testCase.c_str());
1042 }
csmartdalton8d3f92a2016-08-17 09:39:38 -07001043 REPORTER_ASSERT_MESSAGE(reporter, reduced->requiresAA() == doAA, testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001044 }
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001045
bsalomon@google.com51a62862012-11-26 21:19:43 +00001046 // Build a new clip stack based on the reduced clip elements
1047 SkClipStack reducedStack;
csmartdalton8d3f92a2016-08-17 09:39:38 -07001048 if (GrReducedClip::InitialState::kAllOut == reduced->initialState()) {
bsalomon@google.com51a62862012-11-26 21:19:43 +00001049 // whether the result is bounded or not, the whole plane should start outside the clip.
1050 reducedStack.clipEmpty();
1051 }
csmartdalton8d3f92a2016-08-17 09:39:38 -07001052 for (ElementList::Iter iter(reduced->elements()); iter.get(); iter.next()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001053 add_elem_to_stack(*iter.get(), &reducedStack);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001054 }
bsalomon@google.com51a62862012-11-26 21:19:43 +00001055
csmartdalton8d3f92a2016-08-17 09:39:38 -07001056 SkIRect ibounds = reduced->hasIBounds() ? reduced->ibounds() : kIBounds;
csmartdaltond211e782016-08-15 11:17:19 -07001057
bsalomon@google.com34cd70a2012-12-06 14:23:20 +00001058 // GrReducedClipStack assumes that the final result is clipped to the returned bounds
Mike Reedc1f77742016-12-09 09:00:50 -05001059 reducedStack.clipDevRect(ibounds, kIntersect_SkClipOp);
1060 stack.clipDevRect(ibounds, kIntersect_SkClipOp);
bsalomon@google.com34cd70a2012-12-06 14:23:20 +00001061
bsalomon@google.com51a62862012-11-26 21:19:43 +00001062 // convert both the original stack and reduced stack to SkRegions and see if they're equal
bsalomon@google.com51a62862012-11-26 21:19:43 +00001063 SkRegion region;
csmartdaltond211e782016-08-15 11:17:19 -07001064 set_region_to_stack(stack, ibounds, &region);
csmartdaltond50e2402016-07-22 08:39:06 -07001065
bsalomon@google.com51a62862012-11-26 21:19:43 +00001066 SkRegion reducedRegion;
csmartdaltond211e782016-08-15 11:17:19 -07001067 set_region_to_stack(reducedStack, ibounds, &reducedRegion);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001068
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001069 REPORTER_ASSERT_MESSAGE(reporter, region == reducedRegion, testCase.c_str());
csmartdalton8d3f92a2016-08-17 09:39:38 -07001070
1071 reduced->~GrReducedClip();
bsalomon@google.com51a62862012-11-26 21:19:43 +00001072 }
1073}
1074
halcanary4dbbd042016-06-07 17:21:10 -07001075#ifdef SK_BUILD_FOR_WIN
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001076 #define SUPPRESS_VISIBILITY_WARNING
1077#else
1078 #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden")))
1079#endif
1080
1081static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) {
1082 {
1083 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001084 stack.clipRect(SkRect::MakeXYWH(0, 0, 100, 100), SkMatrix::I(), kReplace_SkClipOp,
Brian Salomona3b45d42016-10-03 11:36:16 -04001085 true);
1086 stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(50.3), SkScalar(50.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001087 kReplace_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001088 SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001089
csmartdalton8d3f92a2016-08-17 09:39:38 -07001090 SkAlignedSTStorage<1, GrReducedClip> storage;
1091 memset(storage.get(), 0, sizeof(GrReducedClip));
1092 GR_STATIC_ASSERT(0 == SkClipStack::kInvalidGenID);
1093 const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, bounds);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001094
csmartdalton8d3f92a2016-08-17 09:39:38 -07001095 REPORTER_ASSERT(reporter, reduced->elements().count() == 1);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001096 // Clips will be cached based on the generation id. Make sure the gen id is valid.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001097 REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reduced->elementsGenID());
1098
1099 reduced->~GrReducedClip();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001100 }
1101 {
1102 SkClipStack stack;
1103
1104 // Create a clip with following 25.3, 25.3 boxes which are 25 apart:
1105 // A B
1106 // C D
1107
Brian Salomona3b45d42016-10-03 11:36:16 -04001108 stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001109 kReplace_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001110 uint32_t genIDA = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001111 stack.clipRect(SkRect::MakeXYWH(50, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001112 kUnion_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001113 uint32_t genIDB = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001114 stack.clipRect(SkRect::MakeXYWH(0, 50, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001115 kUnion_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001116 uint32_t genIDC = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001117 stack.clipRect(SkRect::MakeXYWH(50, 50, 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 genIDD = stack.getTopmostGenID();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001120
1121
csmartdaltoncbecb082016-07-22 08:59:08 -07001122#define IXYWH SkIRect::MakeXYWH
1123#define XYWH SkRect::MakeXYWH
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001124
csmartdaltoncbecb082016-07-22 08:59:08 -07001125 SkIRect stackBounds = IXYWH(0, 0, 76, 76);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001126
1127 // The base test is to test each rect in two ways:
1128 // 1) The box dimensions. (Should reduce to "all in", no elements).
1129 // 2) A bit over the box dimensions.
1130 // In the case 2, test that the generation id is what is expected.
1131 // The rects are of fractional size so that case 2 never gets optimized to an empty element
1132 // list.
1133
1134 // Not passing in tighter bounds is tested for consistency.
1135 static const struct SUPPRESS_VISIBILITY_WARNING {
csmartdaltoncbecb082016-07-22 08:59:08 -07001136 SkRect testBounds;
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001137 int reducedClipCount;
Robert Phillips806be2d2017-06-28 15:23:59 -04001138 uint32_t reducedGenID;
csmartdaltoncbecb082016-07-22 08:59:08 -07001139 InitialState initialState;
1140 SkIRect clipIRect;
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001141 // parameter.
1142 } testCases[] = {
csmartdalton77f2fae2016-08-08 09:55:06 -07001143
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001144 // Rect A.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001145 { XYWH(0, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 0, 25, 25) },
1146 { XYWH(0.1f, 0.1f, 25.1f, 25.1f), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 0, 26, 26) },
csmartdalton77f2fae2016-08-08 09:55:06 -07001147 { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::InitialState::kAllOut, IXYWH(0, 0, 27, 27)},
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001148
1149 // Rect B.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001150 { XYWH(50, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 25, 25) },
1151 { XYWH(50, 0, 25.3f, 25.3f), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 26, 26) },
csmartdalton77f2fae2016-08-08 09:55:06 -07001152 { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::InitialState::kAllOut, IXYWH(50, 0, 26, 27) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001153
1154 // Rect C.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001155 { XYWH(0, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 50, 25, 25) },
1156 { 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 -07001157 { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::InitialState::kAllOut, IXYWH(0, 50, 27, 26) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001158
1159 // Rect D.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001160 { XYWH(50, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 25, 25)},
1161 { XYWH(50.3f, 50.3f, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 26, 26)},
csmartdalton77f2fae2016-08-08 09:55:06 -07001162 { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(50, 50, 26, 26)},
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001163
1164 // Other tests:
csmartdalton77f2fae2016-08-08 09:55:06 -07001165 { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::InitialState::kAllOut, stackBounds },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001166
1167 // Rect in the middle, touches none.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001168 { 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 +00001169
1170 // Rect in the middle, touches all the rects. GenID is the last rect.
csmartdalton77f2fae2016-08-08 09:55:06 -07001171 { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(24, 24, 27, 27) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001172 };
1173
1174#undef XYWH
csmartdaltoncbecb082016-07-22 08:59:08 -07001175#undef IXYWH
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001176
1177 for (size_t i = 0; i < SK_ARRAY_COUNT(testCases); ++i) {
csmartdalton77f2fae2016-08-08 09:55:06 -07001178 const GrReducedClip reduced(stack, testCases[i].testBounds);
1179 REPORTER_ASSERT(reporter, reduced.elements().count() == testCases[i].reducedClipCount);
1180 SkASSERT(reduced.elements().count() == testCases[i].reducedClipCount);
csmartdalton8d3f92a2016-08-17 09:39:38 -07001181 if (reduced.elements().count()) {
1182 REPORTER_ASSERT(reporter, reduced.elementsGenID() == testCases[i].reducedGenID);
1183 SkASSERT(reduced.elementsGenID() == testCases[i].reducedGenID);
1184 }
csmartdalton77f2fae2016-08-08 09:55:06 -07001185 REPORTER_ASSERT(reporter, reduced.initialState() == testCases[i].initialState);
1186 SkASSERT(reduced.initialState() == testCases[i].initialState);
csmartdaltond211e782016-08-15 11:17:19 -07001187 REPORTER_ASSERT(reporter, reduced.hasIBounds());
1188 SkASSERT(reduced.hasIBounds());
1189 REPORTER_ASSERT(reporter, reduced.ibounds() == testCases[i].clipIRect);
1190 SkASSERT(reduced.ibounds() == testCases[i].clipIRect);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001191 }
1192 }
1193}
1194
1195static void test_reduced_clip_stack_no_aa_crash(skiatest::Reporter* reporter) {
1196 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001197 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 100, 100), kReplace_SkClipOp);
1198 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 50, 50), kReplace_SkClipOp);
csmartdaltoncbecb082016-07-22 08:59:08 -07001199 SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001200
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001201 // At the time, this would crash.
csmartdalton77f2fae2016-08-08 09:55:06 -07001202 const GrReducedClip reduced(stack, bounds);
1203 REPORTER_ASSERT(reporter, reduced.elements().isEmpty());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001204}
1205
csmartdaltoncbecb082016-07-22 08:59:08 -07001206enum class ClipMethod {
1207 kSkipDraw,
1208 kIgnoreClip,
1209 kScissor,
1210 kAAElements
1211};
1212
1213static void test_aa_query(skiatest::Reporter* reporter, const SkString& testName,
1214 const SkClipStack& stack, const SkMatrix& queryXform,
1215 const SkRect& preXformQuery, ClipMethod expectedMethod,
1216 int numExpectedElems = 0) {
csmartdaltoncbecb082016-07-22 08:59:08 -07001217 SkRect queryBounds;
1218 queryXform.mapRect(&queryBounds, preXformQuery);
csmartdalton77f2fae2016-08-08 09:55:06 -07001219 const GrReducedClip reduced(stack, queryBounds);
csmartdaltoncbecb082016-07-22 08:59:08 -07001220
1221 SkClipStack::BoundsType stackBoundsType;
1222 SkRect stackBounds;
1223 stack.getBounds(&stackBounds, &stackBoundsType);
1224
1225 switch (expectedMethod) {
1226 case ClipMethod::kSkipDraw:
1227 SkASSERT(0 == numExpectedElems);
csmartdalton77f2fae2016-08-08 09:55:06 -07001228 REPORTER_ASSERT_MESSAGE(reporter, reduced.elements().isEmpty(), testName.c_str());
1229 REPORTER_ASSERT_MESSAGE(reporter,
1230 GrReducedClip::InitialState::kAllOut == reduced.initialState(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001231 testName.c_str());
1232 return;
1233 case ClipMethod::kIgnoreClip:
1234 SkASSERT(0 == numExpectedElems);
csmartdaltond211e782016-08-15 11:17:19 -07001235 REPORTER_ASSERT_MESSAGE(reporter,
1236 !reduced.hasIBounds() ||
1237 GrClip::IsInsideClip(reduced.ibounds(), queryBounds),
csmartdaltoncbecb082016-07-22 08:59:08 -07001238 testName.c_str());
csmartdalton77f2fae2016-08-08 09:55:06 -07001239 REPORTER_ASSERT_MESSAGE(reporter, reduced.elements().isEmpty(), testName.c_str());
1240 REPORTER_ASSERT_MESSAGE(reporter,
1241 GrReducedClip::InitialState::kAllIn == reduced.initialState(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001242 testName.c_str());
1243 return;
1244 case ClipMethod::kScissor: {
1245 SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType);
1246 SkASSERT(0 == numExpectedElems);
1247 SkIRect expectedScissor;
1248 stackBounds.round(&expectedScissor);
csmartdalton77f2fae2016-08-08 09:55:06 -07001249 REPORTER_ASSERT_MESSAGE(reporter, reduced.elements().isEmpty(), testName.c_str());
csmartdaltond211e782016-08-15 11:17:19 -07001250 REPORTER_ASSERT_MESSAGE(reporter, reduced.hasIBounds(), testName.c_str());
1251 REPORTER_ASSERT_MESSAGE(reporter, expectedScissor == reduced.ibounds(),
csmartdalton77f2fae2016-08-08 09:55:06 -07001252 testName.c_str());
1253 REPORTER_ASSERT_MESSAGE(reporter,
1254 GrReducedClip::InitialState::kAllIn == reduced.initialState(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001255 testName.c_str());
1256 return;
1257 }
1258 case ClipMethod::kAAElements: {
1259 SkIRect expectedClipIBounds = GrClip::GetPixelIBounds(queryBounds);
1260 if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
1261 SkAssertResult(expectedClipIBounds.intersect(GrClip::GetPixelIBounds(stackBounds)));
1262 }
csmartdalton77f2fae2016-08-08 09:55:06 -07001263 REPORTER_ASSERT_MESSAGE(reporter, numExpectedElems == reduced.elements().count(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001264 testName.c_str());
csmartdaltond211e782016-08-15 11:17:19 -07001265 REPORTER_ASSERT_MESSAGE(reporter, reduced.hasIBounds(), testName.c_str());
1266 REPORTER_ASSERT_MESSAGE(reporter, expectedClipIBounds == reduced.ibounds(),
csmartdalton77f2fae2016-08-08 09:55:06 -07001267 testName.c_str());
1268 REPORTER_ASSERT_MESSAGE(reporter, reduced.requiresAA() == !reduced.elements().isEmpty(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001269 testName.c_str());
1270 break;
1271 }
1272 }
1273}
1274
1275static void test_reduced_clip_stack_aa(skiatest::Reporter* reporter) {
1276 constexpr SkScalar IL = 2, IT = 1, IR = 6, IB = 7; // Pixel aligned rect.
1277 constexpr SkScalar L = 2.2f, T = 1.7f, R = 5.8f, B = 7.3f; // Generic rect.
1278 constexpr SkScalar l = 3.3f, t = 2.8f, r = 4.7f, b = 6.2f; // Small rect contained in R.
1279
1280 SkRect alignedRect = {IL, IT, IR, IB};
1281 SkRect rect = {L, T, R, B};
1282 SkRect innerRect = {l, t, r, b};
1283
1284 SkMatrix m;
1285 m.setIdentity();
1286
1287 constexpr SkScalar kMinScale = 2.0001f;
1288 constexpr SkScalar kMaxScale = 3;
1289 constexpr int kNumIters = 8;
1290
1291 SkString name;
1292 SkRandom rand;
1293
1294 for (int i = 0; i < kNumIters; ++i) {
1295 // Pixel-aligned rect (iior=true).
1296 name.printf("Pixel-aligned rect test, iter %i", i);
1297 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001298 stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001299 test_aa_query(reporter, name, stack, m, {IL, IT, IR, IB}, ClipMethod::kIgnoreClip);
1300 test_aa_query(reporter, name, stack, m, {IL, IT-1, IR, IT}, ClipMethod::kSkipDraw);
csmartdalton77f2fae2016-08-08 09:55:06 -07001301 test_aa_query(reporter, name, stack, m, {IL, IT-2, IR, IB}, ClipMethod::kScissor);
csmartdaltoncbecb082016-07-22 08:59:08 -07001302
1303 // Rect (iior=true).
1304 name.printf("Rect test, iter %i", i);
1305 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001306 stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001307 test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kIgnoreClip);
1308 test_aa_query(reporter, name, stack, m, {L-.1f, T, L, B}, ClipMethod::kSkipDraw);
1309 test_aa_query(reporter, name, stack, m, {L-.1f, T, L+.1f, B}, ClipMethod::kAAElements, 1);
1310
1311 // Difference rect (iior=false, inside-out bounds).
1312 name.printf("Difference rect test, iter %i", i);
1313 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001314 stack.clipRect(rect, SkMatrix::I(), kDifference_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001315 test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kSkipDraw);
1316 test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T}, ClipMethod::kIgnoreClip);
1317 test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T+.1f}, ClipMethod::kAAElements, 1);
1318
1319 // Complex clip (iior=false, normal bounds).
1320 name.printf("Complex clip test, iter %i", i);
1321 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001322 stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true);
1323 stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001324 test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
1325 test_aa_query(reporter, name, stack, m, {r-.1f, t, R, b}, ClipMethod::kAAElements, 1);
1326 test_aa_query(reporter, name, stack, m, {r-.1f, t, R+.1f, b}, ClipMethod::kAAElements, 2);
1327 test_aa_query(reporter, name, stack, m, {r, t, R+.1f, b}, ClipMethod::kAAElements, 1);
1328 test_aa_query(reporter, name, stack, m, {r, t, R, b}, ClipMethod::kIgnoreClip);
1329 test_aa_query(reporter, name, stack, m, {R, T, R+.1f, B}, ClipMethod::kSkipDraw);
1330
1331 // Complex clip where outer rect is pixel aligned (iior=false, normal bounds).
1332 name.printf("Aligned Complex clip test, iter %i", i);
1333 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001334 stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true);
1335 stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001336 test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
1337 test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB}, ClipMethod::kAAElements, 1);
1338 test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB+.1f}, ClipMethod::kAAElements, 1);
1339 test_aa_query(reporter, name, stack, m, {l, b, r, IB+.1f}, ClipMethod::kAAElements, 0);
1340 test_aa_query(reporter, name, stack, m, {l, b, r, IB}, ClipMethod::kIgnoreClip);
1341 test_aa_query(reporter, name, stack, m, {IL, IB, IR, IB+.1f}, ClipMethod::kSkipDraw);
1342
1343 // Apply random transforms and try again. This ensures the clip stack reduction is hardened
1344 // against FP rounding error.
1345 SkScalar sx = rand.nextRangeScalar(kMinScale, kMaxScale);
1346 sx = SkScalarFloorToScalar(sx * alignedRect.width()) / alignedRect.width();
1347 SkScalar sy = rand.nextRangeScalar(kMinScale, kMaxScale);
1348 sy = SkScalarFloorToScalar(sy * alignedRect.height()) / alignedRect.height();
1349 SkScalar tx = SkScalarRoundToScalar(sx * alignedRect.x()) - sx * alignedRect.x();
1350 SkScalar ty = SkScalarRoundToScalar(sy * alignedRect.y()) - sy * alignedRect.y();
1351
1352 SkMatrix xform = SkMatrix::MakeScale(sx, sy);
1353 xform.postTranslate(tx, ty);
1354 xform.mapRect(&alignedRect);
1355 xform.mapRect(&rect);
1356 xform.mapRect(&innerRect);
1357 m.postConcat(xform);
1358 }
1359}
1360
Chris Dalton348060f2017-06-05 13:15:37 -06001361static void test_tiny_query_bounds_assertion_bug(skiatest::Reporter* reporter) {
1362 // https://bugs.chromium.org/p/skia/issues/detail?id=5990
1363 const SkRect clipBounds = SkRect::MakeXYWH(1.5f, 100, 1000, 1000);
1364
1365 SkClipStack rectStack;
1366 rectStack.clipRect(clipBounds, SkMatrix::I(), kIntersect_SkClipOp, true);
1367
1368 SkPath clipPath;
1369 clipPath.moveTo(clipBounds.left(), clipBounds.top());
1370 clipPath.quadTo(clipBounds.right(), clipBounds.top(),
1371 clipBounds.right(), clipBounds.bottom());
1372 clipPath.quadTo(clipBounds.left(), clipBounds.bottom(),
1373 clipBounds.left(), clipBounds.top());
1374 SkClipStack pathStack;
1375 pathStack.clipPath(clipPath, SkMatrix::I(), kIntersect_SkClipOp, true);
1376
1377 for (const SkClipStack& stack : {rectStack, pathStack}) {
1378 for (SkRect queryBounds : {SkRect::MakeXYWH(53, 60, GrClip::kBoundsTolerance, 1000),
1379 SkRect::MakeXYWH(53, 60, GrClip::kBoundsTolerance/2, 1000),
1380 SkRect::MakeXYWH(53, 160, 1000, GrClip::kBoundsTolerance),
1381 SkRect::MakeXYWH(53, 160, 1000, GrClip::kBoundsTolerance/2)}) {
1382 const GrReducedClip reduced(stack, queryBounds);
1383 REPORTER_ASSERT(reporter, !reduced.hasIBounds());
1384 REPORTER_ASSERT(reporter, reduced.elements().isEmpty());
1385 REPORTER_ASSERT(reporter,
1386 GrReducedClip::InitialState::kAllOut == reduced.initialState());
1387 }
1388 }
1389}
1390
bsalomon@google.coma4e13c82012-11-26 21:38:37 +00001391#endif
bsalomon@google.com51a62862012-11-26 21:19:43 +00001392
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00001393DEF_TEST(ClipStack, reporter) {
reed@google.combdee9fc2011-02-22 20:17:43 +00001394 SkClipStack stack;
1395
robertphillips@google.com80214e22012-07-20 15:33:18 +00001396 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001397 assert_count(reporter, stack, 0);
1398
1399 static const SkIRect gRects[] = {
1400 { 0, 0, 100, 100 },
1401 { 25, 25, 125, 125 },
1402 { 0, 0, 1000, 1000 },
1403 { 0, 0, 75, 75 }
1404 };
1405 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
Mike Reedc1f77742016-12-09 09:00:50 -05001406 stack.clipDevRect(gRects[i], kIntersect_SkClipOp);
reed@google.combdee9fc2011-02-22 20:17:43 +00001407 }
1408
1409 // all of the above rects should have been intersected, leaving only 1 rect
robertphillips@google.com80214e22012-07-20 15:33:18 +00001410 SkClipStack::B2TIter iter(stack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001411 const SkClipStack::Element* element = iter.next();
epoger@google.com2047f002011-05-17 17:36:59 +00001412 SkRect answer;
1413 answer.iset(25, 25, 75, 75);
reed@google.combdee9fc2011-02-22 20:17:43 +00001414
bsalomon49f085d2014-09-05 13:34:00 -07001415 REPORTER_ASSERT(reporter, element);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001416 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
Mike Reedc1f77742016-12-09 09:00:50 -05001417 REPORTER_ASSERT(reporter, kIntersect_SkClipOp == element->getOp());
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001418 REPORTER_ASSERT(reporter, element->getRect() == answer);
reed@google.combdee9fc2011-02-22 20:17:43 +00001419 // now check that we only had one in our iterator
1420 REPORTER_ASSERT(reporter, !iter.next());
1421
1422 stack.reset();
robertphillips@google.com80214e22012-07-20 15:33:18 +00001423 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001424 assert_count(reporter, stack, 0);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +00001425
1426 test_assign_and_comparison(reporter);
robertphillips@google.com80214e22012-07-20 15:33:18 +00001427 test_iterators(reporter);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001428 test_bounds(reporter, SkClipStack::Element::kRect_Type);
1429 test_bounds(reporter, SkClipStack::Element::kRRect_Type);
1430 test_bounds(reporter, SkClipStack::Element::kPath_Type);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +00001431 test_isWideOpen(reporter);
robertphillips@google.com08eacc12012-08-02 12:49:00 +00001432 test_rect_merging(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001433 test_rect_replace(reporter);
junov@chromium.orgedf32d52012-12-10 14:57:54 +00001434 test_rect_inverse_fill(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001435 test_path_replace(reporter);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +00001436 test_quickContains(reporter);
csmartdaltond50e2402016-07-22 08:39:06 -07001437 test_invfill_diff_bug(reporter);
bsalomon@google.come7b3d292012-11-26 21:42:32 +00001438#if SK_SUPPORT_GPU
bsalomon@google.comedb26fd2012-11-28 14:42:41 +00001439 test_reduced_clip_stack(reporter);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001440 test_reduced_clip_stack_genid(reporter);
1441 test_reduced_clip_stack_no_aa_crash(reporter);
csmartdaltoncbecb082016-07-22 08:59:08 -07001442 test_reduced_clip_stack_aa(reporter);
Chris Dalton348060f2017-06-05 13:15:37 -06001443 test_tiny_query_bounds_assertion_bug(reporter);
bsalomon@google.come7b3d292012-11-26 21:42:32 +00001444#endif
reed@google.combdee9fc2011-02-22 20:17:43 +00001445}
Brian Salomon19f0ed52017-01-06 13:54:58 -05001446
1447//////////////////////////////////////////////////////////////////////////////
1448
1449#if SK_SUPPORT_GPU
Robert Phillips875218e2017-02-24 08:37:13 -05001450sk_sp<GrTextureProxy> GrClipStackClip::testingOnly_createClipMask(GrContext* context) const {
Brian Salomon19f0ed52017-01-06 13:54:58 -05001451 const GrReducedClip reducedClip(*fStack, SkRect::MakeWH(512, 512), 0);
1452 return this->createSoftwareClipMask(context, reducedClip);
1453}
1454
1455// Verify that clip masks are freed up when the clip state that generated them goes away.
1456DEF_GPUTEST_FOR_ALL_CONTEXTS(ClipMaskCache, reporter, ctxInfo) {
1457 // This test uses resource key tags which only function in debug builds.
1458#ifdef SK_DEBUG
1459 GrContext* context = ctxInfo.grContext();
1460 SkClipStack stack;
1461
1462 SkPath path;
1463 path.addCircle(10, 10, 8);
1464 path.addCircle(15, 15, 8);
1465 path.setFillType(SkPath::kEvenOdd_FillType);
1466
1467 static const char* kTag = GrClipStackClip::kMaskTestTag;
1468 GrResourceCache* cache = context->getResourceCache();
1469
1470 static constexpr int kN = 5;
1471
1472 for (int i = 0; i < kN; ++i) {
1473 SkMatrix m;
1474 m.setTranslate(0.5, 0.5);
1475 stack.save();
1476 stack.clipPath(path, m, SkClipOp::kIntersect, true);
Robert Phillips875218e2017-02-24 08:37:13 -05001477 sk_sp<GrTextureProxy> mask = GrClipStackClip(&stack).testingOnly_createClipMask(context);
Robert Phillipseee4d6e2017-06-05 09:26:07 -04001478 mask->instantiate(context->resourceProvider());
1479 GrTexture* tex = mask->priv().peekTexture();
Robert Phillips875218e2017-02-24 08:37:13 -05001480 REPORTER_ASSERT(reporter, 0 == strcmp(tex->getUniqueKey().tag(), kTag));
Brian Salomon19f0ed52017-01-06 13:54:58 -05001481 // Make sure mask isn't pinned in cache.
1482 mask.reset(nullptr);
1483 context->flush();
1484 REPORTER_ASSERT(reporter, i + 1 == cache->countUniqueKeysWithTag(kTag));
1485 }
1486
1487 for (int i = 0; i < kN; ++i) {
1488 stack.restore();
1489 cache->purgeAsNeeded();
1490 REPORTER_ASSERT(reporter, kN - (i + 1) == cache->countUniqueKeysWithTag(kTag));
1491 }
1492#endif
1493}
1494
Mike Reed3726a4a2017-01-19 11:36:41 -05001495#include "SkSurface.h"
1496DEF_GPUTEST_FOR_ALL_CONTEXTS(canvas_private_clipRgn, reporter, ctxInfo) {
1497 GrContext* context = ctxInfo.grContext();
1498
1499 const int w = 10;
1500 const int h = 10;
1501 SkImageInfo info = SkImageInfo::MakeN32Premul(w, h);
1502 sk_sp<SkSurface> surf = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info);
1503 SkCanvas* canvas = surf->getCanvas();
1504 SkRegion rgn;
1505
1506 canvas->temporary_internal_getRgnClip(&rgn);
1507 REPORTER_ASSERT(reporter, rgn.isRect());
1508 REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeWH(w, h));
1509
1510 canvas->save();
1511 canvas->clipRect(SkRect::MakeWH(5, 5), kDifference_SkClipOp);
1512 canvas->temporary_internal_getRgnClip(&rgn);
1513 REPORTER_ASSERT(reporter, rgn.isComplex());
1514 REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeWH(w, h));
1515 canvas->restore();
1516
1517 canvas->save();
1518 canvas->clipRRect(SkRRect::MakeOval(SkRect::MakeLTRB(3, 3, 7, 7)));
1519 canvas->temporary_internal_getRgnClip(&rgn);
1520 REPORTER_ASSERT(reporter, rgn.isComplex());
1521 REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeLTRB(3, 3, 7, 7));
1522 canvas->restore();
1523}
Brian Salomon19f0ed52017-01-06 13:54:58 -05001524#endif