blob: 8448f4cbe54a2cdb70042b6031211e059f1f4297 [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"
csmartdaltoncbecb082016-07-22 08:59:08 -070019typedef GrReducedClip::ElementList ElementList;
20typedef GrReducedClip::InitialState InitialState;
21#endif
22
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000023static void test_assign_and_comparison(skiatest::Reporter* reporter) {
24 SkClipStack s;
reed@google.comd9f2dea2011-10-12 14:43:27 +000025 bool doAA = false;
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000026
robertphillips@google.com80214e22012-07-20 15:33:18 +000027 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
28
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000029 // Build up a clip stack with a path, an empty clip, and a rect.
30 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000031 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
32
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000033 SkPath p;
34 p.moveTo(5, 6);
35 p.lineTo(7, 8);
36 p.lineTo(5, 9);
37 p.close();
Mike Reedc1f77742016-12-09 09:00:50 -050038 s.clipPath(p, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000039
40 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000041 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
42
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000043 SkRect r = SkRect::MakeLTRB(1, 2, 3, 4);
Mike Reedc1f77742016-12-09 09:00:50 -050044 s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000045 r = SkRect::MakeLTRB(10, 11, 12, 13);
Mike Reedc1f77742016-12-09 09:00:50 -050046 s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000047
48 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000049 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
50
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000051 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -050052 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000053
54 // Test that assignment works.
55 SkClipStack copy = s;
56 REPORTER_ASSERT(reporter, s == copy);
57
58 // Test that different save levels triggers not equal.
59 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000060 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000061 REPORTER_ASSERT(reporter, s != copy);
62
63 // Test that an equal, but not copied version is equal.
64 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000065 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000066 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -050067 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000068 REPORTER_ASSERT(reporter, s == copy);
69
70 // Test that a different op on one level triggers not equal.
71 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000072 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000073 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000074 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000075 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -050076 s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000077 REPORTER_ASSERT(reporter, s != copy);
78
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000079 // Test that version constructed with rect-path rather than a rect is still considered equal.
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000080 s.restore();
81 s.save();
82 SkPath rp;
83 rp.addRect(r);
Mike Reedc1f77742016-12-09 09:00:50 -050084 s.clipPath(rp, SkMatrix::I(), kUnion_SkClipOp, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000085 REPORTER_ASSERT(reporter, s == copy);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000086
87 // Test that different rects triggers not equal.
88 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000089 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000090 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000091 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
92
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000093 r = SkRect::MakeLTRB(24, 25, 26, 27);
Mike Reedc1f77742016-12-09 09:00:50 -050094 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000095 REPORTER_ASSERT(reporter, s != copy);
96
97 // Sanity check
98 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000099 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
100
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000101 copy.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000102 REPORTER_ASSERT(reporter, 2 == copy.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000103 REPORTER_ASSERT(reporter, s == copy);
104 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000105 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000106 copy.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000107 REPORTER_ASSERT(reporter, 1 == copy.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000108 REPORTER_ASSERT(reporter, s == copy);
109
110 // Test that different paths triggers not equal.
111 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000112 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000113 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000114 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
115
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000116 p.addRect(r);
Mike Reedc1f77742016-12-09 09:00:50 -0500117 s.clipPath(p, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000118 REPORTER_ASSERT(reporter, s != copy);
119}
reed@google.combdee9fc2011-02-22 20:17:43 +0000120
121static void assert_count(skiatest::Reporter* reporter, const SkClipStack& stack,
122 int count) {
robertphillips@google.com80214e22012-07-20 15:33:18 +0000123 SkClipStack::B2TIter iter(stack);
reed@google.combdee9fc2011-02-22 20:17:43 +0000124 int counter = 0;
125 while (iter.next()) {
126 counter += 1;
127 }
128 REPORTER_ASSERT(reporter, count == counter);
129}
130
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000131// Exercise the SkClipStack's bottom to top and bidirectional iterators
132// (including the skipToTopmost functionality)
robertphillips@google.com80214e22012-07-20 15:33:18 +0000133static void test_iterators(skiatest::Reporter* reporter) {
134 SkClipStack stack;
135
136 static const SkRect gRects[] = {
137 { 0, 0, 40, 40 },
138 { 60, 0, 100, 40 },
139 { 0, 60, 40, 100 },
140 { 60, 60, 100, 100 }
141 };
142
143 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
144 // the union op will prevent these from being fused together
Mike Reedc1f77742016-12-09 09:00:50 -0500145 stack.clipRect(gRects[i], SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000146 }
147
148 assert_count(reporter, stack, 4);
149
150 // bottom to top iteration
151 {
halcanary96fcdcc2015-08-27 07:41:13 -0700152 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000153
154 SkClipStack::B2TIter iter(stack);
155 int i;
156
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000157 for (i = 0, element = iter.next(); element; ++i, element = iter.next()) {
158 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
159 REPORTER_ASSERT(reporter, element->getRect() == gRects[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000160 }
161
162 SkASSERT(i == 4);
163 }
164
165 // top to bottom iteration
166 {
halcanary96fcdcc2015-08-27 07:41:13 -0700167 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000168
169 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
170 int i;
171
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000172 for (i = 3, element = iter.prev(); element; --i, element = iter.prev()) {
173 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
174 REPORTER_ASSERT(reporter, element->getRect() == gRects[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000175 }
176
177 SkASSERT(i == -1);
178 }
179
180 // skipToTopmost
181 {
halcanary96fcdcc2015-08-27 07:41:13 -0700182 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000183
184 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
185
Mike Reedc1f77742016-12-09 09:00:50 -0500186 element = iter.skipToTopmost(kUnion_SkClipOp);
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000187 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
188 REPORTER_ASSERT(reporter, element->getRect() == gRects[3]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000189 }
190}
191
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000192// Exercise the SkClipStack's getConservativeBounds computation
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000193static void test_bounds(skiatest::Reporter* reporter, SkClipStack::Element::Type primType) {
robertphillips@google.com607fe072012-07-24 13:54:00 +0000194 static const int gNumCases = 20;
195 static const SkRect gAnswerRectsBW[gNumCases] = {
196 // A op B
197 { 40, 40, 50, 50 },
198 { 10, 10, 50, 50 },
199 { 10, 10, 80, 80 },
200 { 10, 10, 80, 80 },
201 { 40, 40, 80, 80 },
202
203 // invA op B
204 { 40, 40, 80, 80 },
205 { 0, 0, 100, 100 },
206 { 0, 0, 100, 100 },
207 { 0, 0, 100, 100 },
208 { 40, 40, 50, 50 },
209
210 // A op invB
211 { 10, 10, 50, 50 },
212 { 40, 40, 50, 50 },
213 { 0, 0, 100, 100 },
214 { 0, 0, 100, 100 },
215 { 0, 0, 100, 100 },
216
217 // invA op invB
218 { 0, 0, 100, 100 },
219 { 40, 40, 80, 80 },
220 { 0, 0, 100, 100 },
221 { 10, 10, 80, 80 },
222 { 10, 10, 50, 50 },
223 };
224
Mike Reedc1f77742016-12-09 09:00:50 -0500225 static const SkClipOp gOps[] = {
226 kIntersect_SkClipOp,
227 kDifference_SkClipOp,
228 kUnion_SkClipOp,
229 kXOR_SkClipOp,
230 kReverseDifference_SkClipOp
robertphillips@google.com607fe072012-07-24 13:54:00 +0000231 };
232
233 SkRect rectA, rectB;
234
235 rectA.iset(10, 10, 50, 50);
236 rectB.iset(40, 40, 80, 80);
237
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000238 SkRRect rrectA, rrectB;
239 rrectA.setOval(rectA);
240 rrectB.setRectXY(rectB, SkIntToScalar(1), SkIntToScalar(2));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000241
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000242 SkPath pathA, pathB;
243
244 pathA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
245 pathB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000246
247 SkClipStack stack;
robertphillips@google.com7b112892012-07-31 15:18:21 +0000248 SkRect devClipBound;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000249 bool isIntersectionOfRects = false;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000250
251 int testCase = 0;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000252 int numBitTests = SkClipStack::Element::kPath_Type == primType ? 4 : 1;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000253 for (int invBits = 0; invBits < numBitTests; ++invBits) {
robertphillips@google.com607fe072012-07-24 13:54:00 +0000254 for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) {
255
256 stack.save();
257 bool doInvA = SkToBool(invBits & 1);
258 bool doInvB = SkToBool(invBits & 2);
259
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000260 pathA.setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType :
robertphillips@google.com607fe072012-07-24 13:54:00 +0000261 SkPath::kEvenOdd_FillType);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000262 pathB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType :
robertphillips@google.com607fe072012-07-24 13:54:00 +0000263 SkPath::kEvenOdd_FillType);
264
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000265 switch (primType) {
266 case SkClipStack::Element::kEmpty_Type:
267 SkDEBUGFAIL("Don't call this with kEmpty.");
268 break;
269 case SkClipStack::Element::kRect_Type:
Mike Reedc1f77742016-12-09 09:00:50 -0500270 stack.clipRect(rectA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400271 stack.clipRect(rectB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000272 break;
273 case SkClipStack::Element::kRRect_Type:
Mike Reedc1f77742016-12-09 09:00:50 -0500274 stack.clipRRect(rrectA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400275 stack.clipRRect(rrectB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000276 break;
277 case SkClipStack::Element::kPath_Type:
Mike Reedc1f77742016-12-09 09:00:50 -0500278 stack.clipPath(pathA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400279 stack.clipPath(pathB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000280 break;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000281 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000282
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000283 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000284 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000285
robertphillips@google.com7b112892012-07-31 15:18:21 +0000286 stack.getConservativeBounds(0, 0, 100, 100, &devClipBound,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000287 &isIntersectionOfRects);
288
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000289 if (SkClipStack::Element::kRect_Type == primType) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000290 REPORTER_ASSERT(reporter, isIntersectionOfRects ==
Mike Reedc1f77742016-12-09 09:00:50 -0500291 (gOps[op] == kIntersect_SkClipOp));
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000292 } else {
293 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
294 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000295
296 SkASSERT(testCase < gNumCases);
robertphillips@google.com7b112892012-07-31 15:18:21 +0000297 REPORTER_ASSERT(reporter, devClipBound == gAnswerRectsBW[testCase]);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000298 ++testCase;
299
300 stack.restore();
301 }
302 }
303}
304
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000305// Test out 'isWideOpen' entry point
306static void test_isWideOpen(skiatest::Reporter* reporter) {
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000307 {
308 // Empty stack is wide open. Wide open stack means that gen id is wide open.
309 SkClipStack stack;
310 REPORTER_ASSERT(reporter, stack.isWideOpen());
311 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
312 }
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000313
314 SkRect rectA, rectB;
315
316 rectA.iset(10, 10, 40, 40);
317 rectB.iset(50, 50, 80, 80);
318
319 // Stack should initially be wide open
320 {
321 SkClipStack stack;
322
323 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000324 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000325 }
326
327 // Test out case where the user specifies a union that includes everything
328 {
329 SkClipStack stack;
330
331 SkPath clipA, clipB;
332
333 clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
334 clipA.setFillType(SkPath::kInverseEvenOdd_FillType);
335
336 clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
337 clipB.setFillType(SkPath::kInverseEvenOdd_FillType);
338
Mike Reedc1f77742016-12-09 09:00:50 -0500339 stack.clipPath(clipA, SkMatrix::I(), kReplace_SkClipOp, false);
340 stack.clipPath(clipB, SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000341
342 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000343 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000344 }
345
346 // Test out union w/ a wide open clip
347 {
348 SkClipStack stack;
349
Mike Reedc1f77742016-12-09 09:00:50 -0500350 stack.clipRect(rectA, SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000351
352 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000353 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000354 }
355
356 // Test out empty difference from a wide open clip
357 {
358 SkClipStack stack;
359
360 SkRect emptyRect;
361 emptyRect.setEmpty();
362
Mike Reedc1f77742016-12-09 09:00:50 -0500363 stack.clipRect(emptyRect, SkMatrix::I(), kDifference_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000364
365 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000366 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000367 }
368
369 // Test out return to wide open
370 {
371 SkClipStack stack;
372
373 stack.save();
374
Mike Reedc1f77742016-12-09 09:00:50 -0500375 stack.clipRect(rectA, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000376
377 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000378 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000379
380 stack.restore();
381
382 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000383 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000384 }
385}
386
bsalomon@google.com100abf42012-09-05 17:40:04 +0000387static int count(const SkClipStack& stack) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000388
389 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
390
halcanary96fcdcc2015-08-27 07:41:13 -0700391 const SkClipStack::Element* element = nullptr;
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000392 int count = 0;
393
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000394 for (element = iter.prev(); element; element = iter.prev(), ++count) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000395 ;
396 }
397
398 return count;
399}
400
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000401static void test_rect_inverse_fill(skiatest::Reporter* reporter) {
402 // non-intersecting rectangles
403 SkRect rect = SkRect::MakeLTRB(0, 0, 10, 10);
404
405 SkPath path;
406 path.addRect(rect);
407 path.toggleInverseFillType();
408 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500409 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000410
411 SkRect bounds;
412 SkClipStack::BoundsType boundsType;
413 stack.getBounds(&bounds, &boundsType);
414 REPORTER_ASSERT(reporter, SkClipStack::kInsideOut_BoundsType == boundsType);
415 REPORTER_ASSERT(reporter, bounds == rect);
416}
417
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000418static void test_rect_replace(skiatest::Reporter* reporter) {
419 SkRect rect = SkRect::MakeWH(100, 100);
420 SkRect rect2 = SkRect::MakeXYWH(50, 50, 100, 100);
421
422 SkRect bound;
423 SkClipStack::BoundsType type;
424 bool isIntersectionOfRects;
425
426 // Adding a new rect with the replace operator should not increase
427 // the stack depth. BW replacing BW.
428 {
429 SkClipStack stack;
430 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500431 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000432 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500433 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000434 REPORTER_ASSERT(reporter, 1 == count(stack));
435 }
436
437 // Adding a new rect with the replace operator should not increase
438 // the stack depth. AA replacing AA.
439 {
440 SkClipStack stack;
441 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500442 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000443 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500444 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000445 REPORTER_ASSERT(reporter, 1 == count(stack));
446 }
447
448 // Adding a new rect with the replace operator should not increase
449 // the stack depth. BW replacing AA replacing BW.
450 {
451 SkClipStack stack;
452 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500453 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000454 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500455 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000456 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500457 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000458 REPORTER_ASSERT(reporter, 1 == count(stack));
459 }
460
461 // Make sure replace clip rects don't collapse too much.
462 {
463 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500464 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
465 stack.clipRect(rect2, SkMatrix::I(), kIntersect_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000466 REPORTER_ASSERT(reporter, 1 == count(stack));
467
468 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500469 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000470 REPORTER_ASSERT(reporter, 2 == count(stack));
471 stack.getBounds(&bound, &type, &isIntersectionOfRects);
472 REPORTER_ASSERT(reporter, bound == rect);
473 stack.restore();
474 REPORTER_ASSERT(reporter, 1 == count(stack));
475
476 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500477 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
478 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000479 REPORTER_ASSERT(reporter, 2 == count(stack));
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(rect2, SkMatrix::I(), kIntersect_SkClipOp, false);
486 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000487 REPORTER_ASSERT(reporter, 2 == count(stack));
488 stack.restore();
489 REPORTER_ASSERT(reporter, 1 == count(stack));
490 }
491}
492
493// Simplified path-based version of test_rect_replace.
494static void test_path_replace(skiatest::Reporter* reporter) {
495 SkRect rect = SkRect::MakeWH(100, 100);
496 SkPath path;
497 path.addCircle(50, 50, 50);
498
499 // Replace operation doesn't grow the stack.
500 {
501 SkClipStack stack;
502 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500503 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000504 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500505 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000506 REPORTER_ASSERT(reporter, 1 == count(stack));
507 }
508
509 // Replacing rect with path.
510 {
511 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500512 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000513 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500514 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000515 REPORTER_ASSERT(reporter, 1 == count(stack));
516 }
517}
518
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000519// Test out SkClipStack's merging of rect clips. In particular exercise
520// merging of aa vs. bw rects.
521static void test_rect_merging(skiatest::Reporter* reporter) {
522
523 SkRect overlapLeft = SkRect::MakeLTRB(10, 10, 50, 50);
524 SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80);
525
526 SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90);
527 SkRect nestedChild = SkRect::MakeLTRB(40, 40, 60, 60);
528
529 SkRect bound;
530 SkClipStack::BoundsType type;
531 bool isIntersectionOfRects;
532
533 // all bw overlapping - should merge
534 {
535 SkClipStack stack;
536
Mike Reedc1f77742016-12-09 09:00:50 -0500537 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000538
Mike Reedc1f77742016-12-09 09:00:50 -0500539 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000540
541 REPORTER_ASSERT(reporter, 1 == count(stack));
542
543 stack.getBounds(&bound, &type, &isIntersectionOfRects);
544
545 REPORTER_ASSERT(reporter, isIntersectionOfRects);
546 }
547
548 // all aa overlapping - should merge
549 {
550 SkClipStack stack;
551
Mike Reedc1f77742016-12-09 09:00:50 -0500552 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000553
Mike Reedc1f77742016-12-09 09:00:50 -0500554 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000555
556 REPORTER_ASSERT(reporter, 1 == count(stack));
557
558 stack.getBounds(&bound, &type, &isIntersectionOfRects);
559
560 REPORTER_ASSERT(reporter, isIntersectionOfRects);
561 }
562
563 // mixed overlapping - should _not_ merge
564 {
565 SkClipStack stack;
566
Mike Reedc1f77742016-12-09 09:00:50 -0500567 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000568
Mike Reedc1f77742016-12-09 09:00:50 -0500569 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000570
571 REPORTER_ASSERT(reporter, 2 == count(stack));
572
573 stack.getBounds(&bound, &type, &isIntersectionOfRects);
574
575 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
576 }
577
578 // mixed nested (bw inside aa) - should merge
579 {
580 SkClipStack stack;
581
Mike Reedc1f77742016-12-09 09:00:50 -0500582 stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000583
Mike Reedc1f77742016-12-09 09:00:50 -0500584 stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000585
586 REPORTER_ASSERT(reporter, 1 == count(stack));
587
588 stack.getBounds(&bound, &type, &isIntersectionOfRects);
589
590 REPORTER_ASSERT(reporter, isIntersectionOfRects);
591 }
592
593 // mixed nested (aa inside bw) - should merge
594 {
595 SkClipStack stack;
596
Mike Reedc1f77742016-12-09 09:00:50 -0500597 stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000598
Mike Reedc1f77742016-12-09 09:00:50 -0500599 stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000600
601 REPORTER_ASSERT(reporter, 1 == count(stack));
602
603 stack.getBounds(&bound, &type, &isIntersectionOfRects);
604
605 REPORTER_ASSERT(reporter, isIntersectionOfRects);
606 }
607
608 // reverse nested (aa inside bw) - should _not_ merge
609 {
610 SkClipStack stack;
611
Mike Reedc1f77742016-12-09 09:00:50 -0500612 stack.clipRect(nestedChild, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000613
Mike Reedc1f77742016-12-09 09:00:50 -0500614 stack.clipRect(nestedParent, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000615
616 REPORTER_ASSERT(reporter, 2 == count(stack));
617
618 stack.getBounds(&bound, &type, &isIntersectionOfRects);
619
620 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
621 }
622}
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000623
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000624static void test_quickContains(skiatest::Reporter* reporter) {
625 SkRect testRect = SkRect::MakeLTRB(10, 10, 40, 40);
626 SkRect insideRect = SkRect::MakeLTRB(20, 20, 30, 30);
627 SkRect intersectingRect = SkRect::MakeLTRB(25, 25, 50, 50);
628 SkRect outsideRect = SkRect::MakeLTRB(0, 0, 50, 50);
629 SkRect nonIntersectingRect = SkRect::MakeLTRB(100, 100, 110, 110);
630
631 SkPath insideCircle;
632 insideCircle.addCircle(25, 25, 5);
633 SkPath intersectingCircle;
634 intersectingCircle.addCircle(25, 40, 10);
635 SkPath outsideCircle;
636 outsideCircle.addCircle(25, 25, 50);
637 SkPath nonIntersectingCircle;
638 nonIntersectingCircle.addCircle(100, 100, 5);
639
640 {
641 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500642 stack.clipRect(outsideRect, SkMatrix::I(), kDifference_SkClipOp, false);
643 // return false because quickContains currently does not care for kDifference_SkClipOp
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000644 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
645 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000646
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000647 // Replace Op tests
648 {
649 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500650 stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000651 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
652 }
653
654 {
655 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500656 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000657 stack.save(); // To prevent in-place substitution by replace OP
Mike Reedc1f77742016-12-09 09:00:50 -0500658 stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000659 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
660 stack.restore();
661 }
662
663 {
664 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500665 stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000666 stack.save(); // To prevent in-place substitution by replace OP
Mike Reedc1f77742016-12-09 09:00:50 -0500667 stack.clipRect(insideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000668 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
669 stack.restore();
670 }
671
672 // Verify proper traversal of multi-element clip
673 {
674 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500675 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000676 // Use a path for second clip to prevent in-place intersection
Mike Reedc1f77742016-12-09 09:00:50 -0500677 stack.clipPath(outsideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000678 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
679 }
680
681 // Intersect Op tests with rectangles
682 {
683 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500684 stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000685 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
686 }
687
688 {
689 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500690 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000691 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
692 }
693
694 {
695 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500696 stack.clipRect(intersectingRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000697 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
698 }
699
700 {
701 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500702 stack.clipRect(nonIntersectingRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000703 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
704 }
705
706 // Intersect Op tests with circle paths
707 {
708 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500709 stack.clipPath(outsideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000710 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
711 }
712
713 {
714 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500715 stack.clipPath(insideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000716 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
717 }
718
719 {
720 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500721 stack.clipPath(intersectingCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000722 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
723 }
724
725 {
726 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500727 stack.clipPath(nonIntersectingCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000728 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
729 }
730
731 // Intersect Op tests with inverse filled rectangles
732 {
733 SkClipStack stack;
734 SkPath path;
735 path.addRect(outsideRect);
736 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500737 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000738 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
739 }
740
741 {
742 SkClipStack stack;
743 SkPath path;
744 path.addRect(insideRect);
745 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500746 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000747 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
748 }
749
750 {
751 SkClipStack stack;
752 SkPath path;
753 path.addRect(intersectingRect);
754 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500755 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000756 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
757 }
758
759 {
760 SkClipStack stack;
761 SkPath path;
762 path.addRect(nonIntersectingRect);
763 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500764 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000765 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
766 }
767
768 // Intersect Op tests with inverse filled circles
769 {
770 SkClipStack stack;
771 SkPath path = outsideCircle;
772 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500773 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000774 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
775 }
776
777 {
778 SkClipStack stack;
779 SkPath path = insideCircle;
780 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500781 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000782 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
783 }
784
785 {
786 SkClipStack stack;
787 SkPath path = intersectingCircle;
788 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500789 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000790 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
791 }
792
793 {
794 SkClipStack stack;
795 SkPath path = nonIntersectingCircle;
796 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500797 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000798 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
799 }
800}
801
csmartdaltond50e2402016-07-22 08:39:06 -0700802static void set_region_to_stack(const SkClipStack& stack, const SkIRect& bounds, SkRegion* region) {
803 region->setRect(bounds);
804 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
805 while (const SkClipStack::Element *element = iter.next()) {
806 SkRegion elemRegion;
807 SkRegion boundsRgn(bounds);
808 SkPath path;
809
810 switch (element->getType()) {
811 case SkClipStack::Element::kEmpty_Type:
812 elemRegion.setEmpty();
813 break;
814 default:
815 element->asPath(&path);
816 elemRegion.setPath(path, boundsRgn);
817 break;
818 }
reed73603f32016-09-20 08:42:38 -0700819 region->op(elemRegion, (SkRegion::Op)element->getOp());
csmartdaltond50e2402016-07-22 08:39:06 -0700820 }
821}
822
823static void test_invfill_diff_bug(skiatest::Reporter* reporter) {
824 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500825 stack.clipRect({10, 10, 20, 20}, SkMatrix::I(), kIntersect_SkClipOp, false);
csmartdaltond50e2402016-07-22 08:39:06 -0700826
827 SkPath path;
828 path.addRect({30, 10, 40, 20});
829 path.setFillType(SkPath::kInverseWinding_FillType);
Mike Reedc1f77742016-12-09 09:00:50 -0500830 stack.clipPath(path, SkMatrix::I(), kDifference_SkClipOp, false);
csmartdaltond50e2402016-07-22 08:39:06 -0700831
832 REPORTER_ASSERT(reporter, SkClipStack::kEmptyGenID == stack.getTopmostGenID());
833
834 SkRect stackBounds;
835 SkClipStack::BoundsType stackBoundsType;
836 stack.getBounds(&stackBounds, &stackBoundsType);
837
838 REPORTER_ASSERT(reporter, stackBounds.isEmpty());
839 REPORTER_ASSERT(reporter, SkClipStack::kNormal_BoundsType == stackBoundsType);
840
841 SkRegion region;
842 set_region_to_stack(stack, {0, 0, 50, 30}, &region);
843
844 REPORTER_ASSERT(reporter, region.isEmpty());
845}
846
bsalomon@google.com51a62862012-11-26 21:19:43 +0000847///////////////////////////////////////////////////////////////////////////////////////////////////
848
bsalomon@google.coma4e13c82012-11-26 21:38:37 +0000849#if SK_SUPPORT_GPU
bsalomon@google.com705e8402012-11-27 15:43:57 +0000850// Functions that add a shape to the clip stack. The shape is computed from a rectangle.
851// AA is always disabled since the clip stack reducer can cause changes in aa rasterization of the
852// stack. A fractional edge repeated in different elements may be rasterized fewer times using the
853// reduced stack.
854typedef void (*AddElementFunc) (const SkRect& rect,
855 bool invert,
Mike Reedc1f77742016-12-09 09:00:50 -0500856 SkClipOp op,
csmartdaltoncbecb082016-07-22 08:59:08 -0700857 SkClipStack* stack,
858 bool doAA);
bsalomon@google.coma4e13c82012-11-26 21:38:37 +0000859
Mike Reedc1f77742016-12-09 09:00:50 -0500860static void add_round_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700861 bool doAA) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000862 SkScalar rx = rect.width() / 10;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000863 SkScalar ry = rect.height() / 20;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000864 if (invert) {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000865 SkPath path;
866 path.addRoundRect(rect, rx, ry);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000867 path.setFillType(SkPath::kInverseWinding_FillType);
Brian Salomona3b45d42016-10-03 11:36:16 -0400868 stack->clipPath(path, SkMatrix::I(), op, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000869 } else {
870 SkRRect rrect;
871 rrect.setRectXY(rect, rx, ry);
Brian Salomona3b45d42016-10-03 11:36:16 -0400872 stack->clipRRect(rrect, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000873 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000874};
875
Mike Reedc1f77742016-12-09 09:00:50 -0500876static void add_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700877 bool doAA) {
bsalomon@google.com705e8402012-11-27 15:43:57 +0000878 if (invert) {
879 SkPath path;
880 path.addRect(rect);
881 path.setFillType(SkPath::kInverseWinding_FillType);
Brian Salomona3b45d42016-10-03 11:36:16 -0400882 stack->clipPath(path, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000883 } else {
Brian Salomona3b45d42016-10-03 11:36:16 -0400884 stack->clipRect(rect, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000885 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000886};
887
Mike Reedc1f77742016-12-09 09:00:50 -0500888static void add_oval(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700889 bool doAA) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000890 SkPath path;
891 path.addOval(rect);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000892 if (invert) {
893 path.setFillType(SkPath::kInverseWinding_FillType);
894 }
Brian Salomona3b45d42016-10-03 11:36:16 -0400895 stack->clipPath(path, SkMatrix::I(), op, doAA);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000896};
897
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000898static void add_elem_to_stack(const SkClipStack::Element& element, SkClipStack* stack) {
899 switch (element.getType()) {
900 case SkClipStack::Element::kRect_Type:
Brian Salomona3b45d42016-10-03 11:36:16 -0400901 stack->clipRect(element.getRect(), SkMatrix::I(), element.getOp(), element.isAA());
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000902 break;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000903 case SkClipStack::Element::kRRect_Type:
Brian Salomona3b45d42016-10-03 11:36:16 -0400904 stack->clipRRect(element.getRRect(), SkMatrix::I(), element.getOp(), element.isAA());
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000905 break;
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000906 case SkClipStack::Element::kPath_Type:
Brian Salomona3b45d42016-10-03 11:36:16 -0400907 stack->clipPath(element.getPath(), SkMatrix::I(), element.getOp(), element.isAA());
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000908 break;
909 case SkClipStack::Element::kEmpty_Type:
910 SkDEBUGFAIL("Why did the reducer produce an explicit empty.");
911 stack->clipEmpty();
912 break;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000913 }
914}
915
bsalomon@google.com51a62862012-11-26 21:19:43 +0000916static void test_reduced_clip_stack(skiatest::Reporter* reporter) {
917 // We construct random clip stacks, reduce them, and then rasterize both versions to verify that
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000918 // they are equal.
bsalomon@google.com51a62862012-11-26 21:19:43 +0000919
920 // All the clip elements will be contained within these bounds.
csmartdaltond211e782016-08-15 11:17:19 -0700921 static const SkIRect kIBounds = SkIRect::MakeWH(100, 100);
922 static const SkRect kBounds = SkRect::Make(kIBounds);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000923
924 enum {
csmartdaltoncbecb082016-07-22 08:59:08 -0700925 kNumTests = 250,
bsalomon@google.com51a62862012-11-26 21:19:43 +0000926 kMinElemsPerTest = 1,
927 kMaxElemsPerTest = 50,
928 };
929
930 // min/max size of a clip element as a fraction of kBounds.
931 static const SkScalar kMinElemSizeFrac = SK_Scalar1 / 5;
932 static const SkScalar kMaxElemSizeFrac = SK_Scalar1;
933
Mike Reedc1f77742016-12-09 09:00:50 -0500934 static const SkClipOp kOps[] = {
935 kDifference_SkClipOp,
936 kIntersect_SkClipOp,
937 kUnion_SkClipOp,
938 kXOR_SkClipOp,
939 kReverseDifference_SkClipOp,
940 kReplace_SkClipOp,
bsalomon@google.com51a62862012-11-26 21:19:43 +0000941 };
942
943 // Replace operations short-circuit the optimizer. We want to make sure that we test this code
944 // path a little bit but we don't want it to prevent us from testing many longer traversals in
945 // the optimizer.
946 static const int kReplaceDiv = 4 * kMaxElemsPerTest;
947
bsalomon@google.com705e8402012-11-27 15:43:57 +0000948 // We want to test inverse fills. However, they are quite rare in practice so don't over do it.
949 static const SkScalar kFractionInverted = SK_Scalar1 / kMaxElemsPerTest;
950
csmartdaltoncbecb082016-07-22 08:59:08 -0700951 static const SkScalar kFractionAntialiased = 0.25;
952
bsalomon@google.com51a62862012-11-26 21:19:43 +0000953 static const AddElementFunc kElementFuncs[] = {
954 add_rect,
955 add_round_rect,
956 add_oval,
957 };
958
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000959 SkRandom r;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000960
961 for (int i = 0; i < kNumTests; ++i) {
csmartdaltoncbecb082016-07-22 08:59:08 -0700962 SkString testCase;
963 testCase.printf("Iteration %d", i);
964
bsalomon@google.com51a62862012-11-26 21:19:43 +0000965 // Randomly generate a clip stack.
966 SkClipStack stack;
967 int numElems = r.nextRangeU(kMinElemsPerTest, kMaxElemsPerTest);
csmartdaltoncbecb082016-07-22 08:59:08 -0700968 bool doAA = r.nextBiasedBool(kFractionAntialiased);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000969 for (int e = 0; e < numElems; ++e) {
Mike Reedc1f77742016-12-09 09:00:50 -0500970 SkClipOp op = kOps[r.nextULessThan(SK_ARRAY_COUNT(kOps))];
971 if (op == kReplace_SkClipOp) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000972 if (r.nextU() % kReplaceDiv) {
973 --e;
974 continue;
975 }
976 }
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000977
bsalomon@google.com51a62862012-11-26 21:19:43 +0000978 // saves can change the clip stack behavior when an element is added.
979 bool doSave = r.nextBool();
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000980
bsalomon@google.com51a62862012-11-26 21:19:43 +0000981 SkSize size = SkSize::Make(
csmartdaltoncbecb082016-07-22 08:59:08 -0700982 SkScalarMul(kBounds.width(), r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac)),
983 SkScalarMul(kBounds.height(), r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac)));
bsalomon@google.com51a62862012-11-26 21:19:43 +0000984
csmartdaltoncbecb082016-07-22 08:59:08 -0700985 SkPoint xy = {r.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth),
986 r.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight)};
bsalomon@google.com51a62862012-11-26 21:19:43 +0000987
csmartdaltoncbecb082016-07-22 08:59:08 -0700988 SkRect rect;
989 if (doAA) {
990 rect.setXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight);
991 if (GrClip::IsPixelAligned(rect)) {
992 // Don't create an element that may accidentally become not antialiased.
993 rect.outset(0.5f, 0.5f);
994 }
995 SkASSERT(!GrClip::IsPixelAligned(rect));
996 } else {
997 rect.setXYWH(SkScalarFloorToScalar(xy.fX),
998 SkScalarFloorToScalar(xy.fY),
999 SkScalarCeilToScalar(size.fWidth),
1000 SkScalarCeilToScalar(size.fHeight));
1001 }
bsalomon@google.com51a62862012-11-26 21:19:43 +00001002
bsalomon@google.com705e8402012-11-27 15:43:57 +00001003 bool invert = r.nextBiasedBool(kFractionInverted);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001004
csmartdaltoncbecb082016-07-22 08:59:08 -07001005 kElementFuncs[r.nextULessThan(SK_ARRAY_COUNT(kElementFuncs))](rect, invert, op, &stack,
1006 doAA);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001007 if (doSave) {
1008 stack.save();
1009 }
1010 }
1011
csmartdalton8d3f92a2016-08-17 09:39:38 -07001012 // Zero the memory we will new the GrReducedClip into. This ensures the elements gen ID
1013 // will be kInvalidGenID if left uninitialized.
1014 SkAlignedSTStorage<1, GrReducedClip> storage;
1015 memset(storage.get(), 0, sizeof(GrReducedClip));
1016 GR_STATIC_ASSERT(0 == SkClipStack::kInvalidGenID);
1017
csmartdalton77f2fae2016-08-08 09:55:06 -07001018 // Get the reduced version of the stack.
csmartdaltoncbecb082016-07-22 08:59:08 -07001019 SkRect queryBounds = kBounds;
1020 queryBounds.outset(kBounds.width() / 2, kBounds.height() / 2);
csmartdalton8d3f92a2016-08-17 09:39:38 -07001021 const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, queryBounds);
bsalomon@google.coma4444302012-12-04 15:22:12 +00001022
csmartdalton8d3f92a2016-08-17 09:39:38 -07001023 REPORTER_ASSERT_MESSAGE(reporter,
1024 reduced->elements().isEmpty() ||
1025 SkClipStack::kInvalidGenID != reduced->elementsGenID(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001026 testCase.c_str());
1027
csmartdalton8d3f92a2016-08-17 09:39:38 -07001028 if (!reduced->elements().isEmpty()) {
1029 REPORTER_ASSERT_MESSAGE(reporter, reduced->hasIBounds(), testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001030 SkRect stackBounds;
1031 SkClipStack::BoundsType stackBoundsType;
1032 stack.getBounds(&stackBounds, &stackBoundsType);
1033 if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
1034 // Unless GrReducedClip starts doing some heroic tightening of the clip bounds, this
1035 // will be true since the stack bounds are completely contained inside the query.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001036 REPORTER_ASSERT_MESSAGE(reporter,
1037 GrClip::IsInsideClip(reduced->ibounds(), stackBounds),
csmartdaltoncbecb082016-07-22 08:59:08 -07001038 testCase.c_str());
1039 }
csmartdalton8d3f92a2016-08-17 09:39:38 -07001040 REPORTER_ASSERT_MESSAGE(reporter, reduced->requiresAA() == doAA, testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001041 }
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001042
bsalomon@google.com51a62862012-11-26 21:19:43 +00001043 // Build a new clip stack based on the reduced clip elements
1044 SkClipStack reducedStack;
csmartdalton8d3f92a2016-08-17 09:39:38 -07001045 if (GrReducedClip::InitialState::kAllOut == reduced->initialState()) {
bsalomon@google.com51a62862012-11-26 21:19:43 +00001046 // whether the result is bounded or not, the whole plane should start outside the clip.
1047 reducedStack.clipEmpty();
1048 }
csmartdalton8d3f92a2016-08-17 09:39:38 -07001049 for (ElementList::Iter iter(reduced->elements()); iter.get(); iter.next()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001050 add_elem_to_stack(*iter.get(), &reducedStack);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001051 }
bsalomon@google.com51a62862012-11-26 21:19:43 +00001052
csmartdalton8d3f92a2016-08-17 09:39:38 -07001053 SkIRect ibounds = reduced->hasIBounds() ? reduced->ibounds() : kIBounds;
csmartdaltond211e782016-08-15 11:17:19 -07001054
bsalomon@google.com34cd70a2012-12-06 14:23:20 +00001055 // GrReducedClipStack assumes that the final result is clipped to the returned bounds
Mike Reedc1f77742016-12-09 09:00:50 -05001056 reducedStack.clipDevRect(ibounds, kIntersect_SkClipOp);
1057 stack.clipDevRect(ibounds, kIntersect_SkClipOp);
bsalomon@google.com34cd70a2012-12-06 14:23:20 +00001058
bsalomon@google.com51a62862012-11-26 21:19:43 +00001059 // convert both the original stack and reduced stack to SkRegions and see if they're equal
bsalomon@google.com51a62862012-11-26 21:19:43 +00001060 SkRegion region;
csmartdaltond211e782016-08-15 11:17:19 -07001061 set_region_to_stack(stack, ibounds, &region);
csmartdaltond50e2402016-07-22 08:39:06 -07001062
bsalomon@google.com51a62862012-11-26 21:19:43 +00001063 SkRegion reducedRegion;
csmartdaltond211e782016-08-15 11:17:19 -07001064 set_region_to_stack(reducedStack, ibounds, &reducedRegion);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001065
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001066 REPORTER_ASSERT_MESSAGE(reporter, region == reducedRegion, testCase.c_str());
csmartdalton8d3f92a2016-08-17 09:39:38 -07001067
1068 reduced->~GrReducedClip();
bsalomon@google.com51a62862012-11-26 21:19:43 +00001069 }
1070}
1071
halcanary4dbbd042016-06-07 17:21:10 -07001072#ifdef SK_BUILD_FOR_WIN
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001073 #define SUPPRESS_VISIBILITY_WARNING
1074#else
1075 #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden")))
1076#endif
1077
1078static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) {
1079 {
1080 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001081 stack.clipRect(SkRect::MakeXYWH(0, 0, 100, 100), SkMatrix::I(), kReplace_SkClipOp,
Brian Salomona3b45d42016-10-03 11:36:16 -04001082 true);
1083 stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(50.3), SkScalar(50.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001084 kReplace_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001085 SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001086
csmartdalton8d3f92a2016-08-17 09:39:38 -07001087 SkAlignedSTStorage<1, GrReducedClip> storage;
1088 memset(storage.get(), 0, sizeof(GrReducedClip));
1089 GR_STATIC_ASSERT(0 == SkClipStack::kInvalidGenID);
1090 const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, bounds);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001091
csmartdalton8d3f92a2016-08-17 09:39:38 -07001092 REPORTER_ASSERT(reporter, reduced->elements().count() == 1);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001093 // Clips will be cached based on the generation id. Make sure the gen id is valid.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001094 REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reduced->elementsGenID());
1095
1096 reduced->~GrReducedClip();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001097 }
1098 {
1099 SkClipStack stack;
1100
1101 // Create a clip with following 25.3, 25.3 boxes which are 25 apart:
1102 // A B
1103 // C D
1104
Brian Salomona3b45d42016-10-03 11:36:16 -04001105 stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001106 kReplace_SkClipOp, true);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001107 int32_t genIDA = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001108 stack.clipRect(SkRect::MakeXYWH(50, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001109 kUnion_SkClipOp, true);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001110 int32_t genIDB = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001111 stack.clipRect(SkRect::MakeXYWH(0, 50, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001112 kUnion_SkClipOp, true);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001113 int32_t genIDC = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001114 stack.clipRect(SkRect::MakeXYWH(50, 50, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001115 kUnion_SkClipOp, true);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001116 int32_t genIDD = stack.getTopmostGenID();
1117
1118
csmartdaltoncbecb082016-07-22 08:59:08 -07001119#define IXYWH SkIRect::MakeXYWH
1120#define XYWH SkRect::MakeXYWH
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001121
csmartdaltoncbecb082016-07-22 08:59:08 -07001122 SkIRect stackBounds = IXYWH(0, 0, 76, 76);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001123
1124 // The base test is to test each rect in two ways:
1125 // 1) The box dimensions. (Should reduce to "all in", no elements).
1126 // 2) A bit over the box dimensions.
1127 // In the case 2, test that the generation id is what is expected.
1128 // The rects are of fractional size so that case 2 never gets optimized to an empty element
1129 // list.
1130
1131 // Not passing in tighter bounds is tested for consistency.
1132 static const struct SUPPRESS_VISIBILITY_WARNING {
csmartdaltoncbecb082016-07-22 08:59:08 -07001133 SkRect testBounds;
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001134 int reducedClipCount;
1135 int32_t reducedGenID;
csmartdaltoncbecb082016-07-22 08:59:08 -07001136 InitialState initialState;
1137 SkIRect clipIRect;
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001138 // parameter.
1139 } testCases[] = {
csmartdalton77f2fae2016-08-08 09:55:06 -07001140
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001141 // Rect A.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001142 { XYWH(0, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 0, 25, 25) },
1143 { 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 -07001144 { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::InitialState::kAllOut, IXYWH(0, 0, 27, 27)},
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001145
1146 // Rect B.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001147 { XYWH(50, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 25, 25) },
1148 { XYWH(50, 0, 25.3f, 25.3f), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 26, 26) },
csmartdalton77f2fae2016-08-08 09:55:06 -07001149 { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::InitialState::kAllOut, IXYWH(50, 0, 26, 27) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001150
1151 // Rect C.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001152 { XYWH(0, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 50, 25, 25) },
1153 { 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 -07001154 { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::InitialState::kAllOut, IXYWH(0, 50, 27, 26) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001155
1156 // Rect D.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001157 { XYWH(50, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 25, 25)},
1158 { XYWH(50.3f, 50.3f, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 26, 26)},
csmartdalton77f2fae2016-08-08 09:55:06 -07001159 { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(50, 50, 26, 26)},
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001160
1161 // Other tests:
csmartdalton77f2fae2016-08-08 09:55:06 -07001162 { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::InitialState::kAllOut, stackBounds },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001163
1164 // Rect in the middle, touches none.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001165 { 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 +00001166
1167 // Rect in the middle, touches all the rects. GenID is the last rect.
csmartdalton77f2fae2016-08-08 09:55:06 -07001168 { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(24, 24, 27, 27) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001169 };
1170
1171#undef XYWH
csmartdaltoncbecb082016-07-22 08:59:08 -07001172#undef IXYWH
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001173
1174 for (size_t i = 0; i < SK_ARRAY_COUNT(testCases); ++i) {
csmartdalton77f2fae2016-08-08 09:55:06 -07001175 const GrReducedClip reduced(stack, testCases[i].testBounds);
1176 REPORTER_ASSERT(reporter, reduced.elements().count() == testCases[i].reducedClipCount);
1177 SkASSERT(reduced.elements().count() == testCases[i].reducedClipCount);
csmartdalton8d3f92a2016-08-17 09:39:38 -07001178 if (reduced.elements().count()) {
1179 REPORTER_ASSERT(reporter, reduced.elementsGenID() == testCases[i].reducedGenID);
1180 SkASSERT(reduced.elementsGenID() == testCases[i].reducedGenID);
1181 }
csmartdalton77f2fae2016-08-08 09:55:06 -07001182 REPORTER_ASSERT(reporter, reduced.initialState() == testCases[i].initialState);
1183 SkASSERT(reduced.initialState() == testCases[i].initialState);
csmartdaltond211e782016-08-15 11:17:19 -07001184 REPORTER_ASSERT(reporter, reduced.hasIBounds());
1185 SkASSERT(reduced.hasIBounds());
1186 REPORTER_ASSERT(reporter, reduced.ibounds() == testCases[i].clipIRect);
1187 SkASSERT(reduced.ibounds() == testCases[i].clipIRect);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001188 }
1189 }
1190}
1191
1192static void test_reduced_clip_stack_no_aa_crash(skiatest::Reporter* reporter) {
1193 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001194 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 100, 100), kReplace_SkClipOp);
1195 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 50, 50), kReplace_SkClipOp);
csmartdaltoncbecb082016-07-22 08:59:08 -07001196 SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001197
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001198 // At the time, this would crash.
csmartdalton77f2fae2016-08-08 09:55:06 -07001199 const GrReducedClip reduced(stack, bounds);
1200 REPORTER_ASSERT(reporter, reduced.elements().isEmpty());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001201}
1202
csmartdaltoncbecb082016-07-22 08:59:08 -07001203enum class ClipMethod {
1204 kSkipDraw,
1205 kIgnoreClip,
1206 kScissor,
1207 kAAElements
1208};
1209
1210static void test_aa_query(skiatest::Reporter* reporter, const SkString& testName,
1211 const SkClipStack& stack, const SkMatrix& queryXform,
1212 const SkRect& preXformQuery, ClipMethod expectedMethod,
1213 int numExpectedElems = 0) {
csmartdaltoncbecb082016-07-22 08:59:08 -07001214 SkRect queryBounds;
1215 queryXform.mapRect(&queryBounds, preXformQuery);
csmartdalton77f2fae2016-08-08 09:55:06 -07001216 const GrReducedClip reduced(stack, queryBounds);
csmartdaltoncbecb082016-07-22 08:59:08 -07001217
1218 SkClipStack::BoundsType stackBoundsType;
1219 SkRect stackBounds;
1220 stack.getBounds(&stackBounds, &stackBoundsType);
1221
1222 switch (expectedMethod) {
1223 case ClipMethod::kSkipDraw:
1224 SkASSERT(0 == numExpectedElems);
csmartdalton77f2fae2016-08-08 09:55:06 -07001225 REPORTER_ASSERT_MESSAGE(reporter, reduced.elements().isEmpty(), testName.c_str());
1226 REPORTER_ASSERT_MESSAGE(reporter,
1227 GrReducedClip::InitialState::kAllOut == reduced.initialState(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001228 testName.c_str());
1229 return;
1230 case ClipMethod::kIgnoreClip:
1231 SkASSERT(0 == numExpectedElems);
csmartdaltond211e782016-08-15 11:17:19 -07001232 REPORTER_ASSERT_MESSAGE(reporter,
1233 !reduced.hasIBounds() ||
1234 GrClip::IsInsideClip(reduced.ibounds(), queryBounds),
csmartdaltoncbecb082016-07-22 08:59:08 -07001235 testName.c_str());
csmartdalton77f2fae2016-08-08 09:55:06 -07001236 REPORTER_ASSERT_MESSAGE(reporter, reduced.elements().isEmpty(), testName.c_str());
1237 REPORTER_ASSERT_MESSAGE(reporter,
1238 GrReducedClip::InitialState::kAllIn == reduced.initialState(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001239 testName.c_str());
1240 return;
1241 case ClipMethod::kScissor: {
1242 SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType);
1243 SkASSERT(0 == numExpectedElems);
1244 SkIRect expectedScissor;
1245 stackBounds.round(&expectedScissor);
csmartdalton77f2fae2016-08-08 09:55:06 -07001246 REPORTER_ASSERT_MESSAGE(reporter, reduced.elements().isEmpty(), testName.c_str());
csmartdaltond211e782016-08-15 11:17:19 -07001247 REPORTER_ASSERT_MESSAGE(reporter, reduced.hasIBounds(), testName.c_str());
1248 REPORTER_ASSERT_MESSAGE(reporter, expectedScissor == reduced.ibounds(),
csmartdalton77f2fae2016-08-08 09:55:06 -07001249 testName.c_str());
1250 REPORTER_ASSERT_MESSAGE(reporter,
1251 GrReducedClip::InitialState::kAllIn == reduced.initialState(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001252 testName.c_str());
1253 return;
1254 }
1255 case ClipMethod::kAAElements: {
1256 SkIRect expectedClipIBounds = GrClip::GetPixelIBounds(queryBounds);
1257 if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
1258 SkAssertResult(expectedClipIBounds.intersect(GrClip::GetPixelIBounds(stackBounds)));
1259 }
csmartdalton77f2fae2016-08-08 09:55:06 -07001260 REPORTER_ASSERT_MESSAGE(reporter, numExpectedElems == reduced.elements().count(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001261 testName.c_str());
csmartdaltond211e782016-08-15 11:17:19 -07001262 REPORTER_ASSERT_MESSAGE(reporter, reduced.hasIBounds(), testName.c_str());
1263 REPORTER_ASSERT_MESSAGE(reporter, expectedClipIBounds == reduced.ibounds(),
csmartdalton77f2fae2016-08-08 09:55:06 -07001264 testName.c_str());
1265 REPORTER_ASSERT_MESSAGE(reporter, reduced.requiresAA() == !reduced.elements().isEmpty(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001266 testName.c_str());
1267 break;
1268 }
1269 }
1270}
1271
1272static void test_reduced_clip_stack_aa(skiatest::Reporter* reporter) {
1273 constexpr SkScalar IL = 2, IT = 1, IR = 6, IB = 7; // Pixel aligned rect.
1274 constexpr SkScalar L = 2.2f, T = 1.7f, R = 5.8f, B = 7.3f; // Generic rect.
1275 constexpr SkScalar l = 3.3f, t = 2.8f, r = 4.7f, b = 6.2f; // Small rect contained in R.
1276
1277 SkRect alignedRect = {IL, IT, IR, IB};
1278 SkRect rect = {L, T, R, B};
1279 SkRect innerRect = {l, t, r, b};
1280
1281 SkMatrix m;
1282 m.setIdentity();
1283
1284 constexpr SkScalar kMinScale = 2.0001f;
1285 constexpr SkScalar kMaxScale = 3;
1286 constexpr int kNumIters = 8;
1287
1288 SkString name;
1289 SkRandom rand;
1290
1291 for (int i = 0; i < kNumIters; ++i) {
1292 // Pixel-aligned rect (iior=true).
1293 name.printf("Pixel-aligned rect test, iter %i", i);
1294 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001295 stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001296 test_aa_query(reporter, name, stack, m, {IL, IT, IR, IB}, ClipMethod::kIgnoreClip);
1297 test_aa_query(reporter, name, stack, m, {IL, IT-1, IR, IT}, ClipMethod::kSkipDraw);
csmartdalton77f2fae2016-08-08 09:55:06 -07001298 test_aa_query(reporter, name, stack, m, {IL, IT-2, IR, IB}, ClipMethod::kScissor);
csmartdaltoncbecb082016-07-22 08:59:08 -07001299
1300 // Rect (iior=true).
1301 name.printf("Rect test, iter %i", i);
1302 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001303 stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001304 test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kIgnoreClip);
1305 test_aa_query(reporter, name, stack, m, {L-.1f, T, L, B}, ClipMethod::kSkipDraw);
1306 test_aa_query(reporter, name, stack, m, {L-.1f, T, L+.1f, B}, ClipMethod::kAAElements, 1);
1307
1308 // Difference rect (iior=false, inside-out bounds).
1309 name.printf("Difference rect test, iter %i", i);
1310 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001311 stack.clipRect(rect, SkMatrix::I(), kDifference_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001312 test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kSkipDraw);
1313 test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T}, ClipMethod::kIgnoreClip);
1314 test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T+.1f}, ClipMethod::kAAElements, 1);
1315
1316 // Complex clip (iior=false, normal bounds).
1317 name.printf("Complex clip test, iter %i", i);
1318 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001319 stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true);
1320 stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001321 test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
1322 test_aa_query(reporter, name, stack, m, {r-.1f, t, R, b}, ClipMethod::kAAElements, 1);
1323 test_aa_query(reporter, name, stack, m, {r-.1f, t, R+.1f, b}, ClipMethod::kAAElements, 2);
1324 test_aa_query(reporter, name, stack, m, {r, t, R+.1f, b}, ClipMethod::kAAElements, 1);
1325 test_aa_query(reporter, name, stack, m, {r, t, R, b}, ClipMethod::kIgnoreClip);
1326 test_aa_query(reporter, name, stack, m, {R, T, R+.1f, B}, ClipMethod::kSkipDraw);
1327
1328 // Complex clip where outer rect is pixel aligned (iior=false, normal bounds).
1329 name.printf("Aligned Complex clip test, iter %i", i);
1330 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001331 stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true);
1332 stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001333 test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
1334 test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB}, ClipMethod::kAAElements, 1);
1335 test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB+.1f}, ClipMethod::kAAElements, 1);
1336 test_aa_query(reporter, name, stack, m, {l, b, r, IB+.1f}, ClipMethod::kAAElements, 0);
1337 test_aa_query(reporter, name, stack, m, {l, b, r, IB}, ClipMethod::kIgnoreClip);
1338 test_aa_query(reporter, name, stack, m, {IL, IB, IR, IB+.1f}, ClipMethod::kSkipDraw);
1339
1340 // Apply random transforms and try again. This ensures the clip stack reduction is hardened
1341 // against FP rounding error.
1342 SkScalar sx = rand.nextRangeScalar(kMinScale, kMaxScale);
1343 sx = SkScalarFloorToScalar(sx * alignedRect.width()) / alignedRect.width();
1344 SkScalar sy = rand.nextRangeScalar(kMinScale, kMaxScale);
1345 sy = SkScalarFloorToScalar(sy * alignedRect.height()) / alignedRect.height();
1346 SkScalar tx = SkScalarRoundToScalar(sx * alignedRect.x()) - sx * alignedRect.x();
1347 SkScalar ty = SkScalarRoundToScalar(sy * alignedRect.y()) - sy * alignedRect.y();
1348
1349 SkMatrix xform = SkMatrix::MakeScale(sx, sy);
1350 xform.postTranslate(tx, ty);
1351 xform.mapRect(&alignedRect);
1352 xform.mapRect(&rect);
1353 xform.mapRect(&innerRect);
1354 m.postConcat(xform);
1355 }
1356}
1357
bsalomon@google.coma4e13c82012-11-26 21:38:37 +00001358#endif
bsalomon@google.com51a62862012-11-26 21:19:43 +00001359
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00001360DEF_TEST(ClipStack, reporter) {
reed@google.combdee9fc2011-02-22 20:17:43 +00001361 SkClipStack stack;
1362
robertphillips@google.com80214e22012-07-20 15:33:18 +00001363 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001364 assert_count(reporter, stack, 0);
1365
1366 static const SkIRect gRects[] = {
1367 { 0, 0, 100, 100 },
1368 { 25, 25, 125, 125 },
1369 { 0, 0, 1000, 1000 },
1370 { 0, 0, 75, 75 }
1371 };
1372 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
Mike Reedc1f77742016-12-09 09:00:50 -05001373 stack.clipDevRect(gRects[i], kIntersect_SkClipOp);
reed@google.combdee9fc2011-02-22 20:17:43 +00001374 }
1375
1376 // all of the above rects should have been intersected, leaving only 1 rect
robertphillips@google.com80214e22012-07-20 15:33:18 +00001377 SkClipStack::B2TIter iter(stack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001378 const SkClipStack::Element* element = iter.next();
epoger@google.com2047f002011-05-17 17:36:59 +00001379 SkRect answer;
1380 answer.iset(25, 25, 75, 75);
reed@google.combdee9fc2011-02-22 20:17:43 +00001381
bsalomon49f085d2014-09-05 13:34:00 -07001382 REPORTER_ASSERT(reporter, element);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001383 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
Mike Reedc1f77742016-12-09 09:00:50 -05001384 REPORTER_ASSERT(reporter, kIntersect_SkClipOp == element->getOp());
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001385 REPORTER_ASSERT(reporter, element->getRect() == answer);
reed@google.combdee9fc2011-02-22 20:17:43 +00001386 // now check that we only had one in our iterator
1387 REPORTER_ASSERT(reporter, !iter.next());
1388
1389 stack.reset();
robertphillips@google.com80214e22012-07-20 15:33:18 +00001390 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001391 assert_count(reporter, stack, 0);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +00001392
1393 test_assign_and_comparison(reporter);
robertphillips@google.com80214e22012-07-20 15:33:18 +00001394 test_iterators(reporter);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001395 test_bounds(reporter, SkClipStack::Element::kRect_Type);
1396 test_bounds(reporter, SkClipStack::Element::kRRect_Type);
1397 test_bounds(reporter, SkClipStack::Element::kPath_Type);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +00001398 test_isWideOpen(reporter);
robertphillips@google.com08eacc12012-08-02 12:49:00 +00001399 test_rect_merging(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001400 test_rect_replace(reporter);
junov@chromium.orgedf32d52012-12-10 14:57:54 +00001401 test_rect_inverse_fill(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001402 test_path_replace(reporter);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +00001403 test_quickContains(reporter);
csmartdaltond50e2402016-07-22 08:39:06 -07001404 test_invfill_diff_bug(reporter);
bsalomon@google.come7b3d292012-11-26 21:42:32 +00001405#if SK_SUPPORT_GPU
bsalomon@google.comedb26fd2012-11-28 14:42:41 +00001406 test_reduced_clip_stack(reporter);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001407 test_reduced_clip_stack_genid(reporter);
1408 test_reduced_clip_stack_no_aa_crash(reporter);
csmartdaltoncbecb082016-07-22 08:59:08 -07001409 test_reduced_clip_stack_aa(reporter);
bsalomon@google.come7b3d292012-11-26 21:42:32 +00001410#endif
reed@google.combdee9fc2011-02-22 20:17:43 +00001411}
Brian Salomon19f0ed52017-01-06 13:54:58 -05001412
1413//////////////////////////////////////////////////////////////////////////////
1414
1415#if SK_SUPPORT_GPU
1416sk_sp<GrTexture> GrClipStackClip::testingOnly_createClipMask(GrContext* context) const {
1417 const GrReducedClip reducedClip(*fStack, SkRect::MakeWH(512, 512), 0);
1418 return this->createSoftwareClipMask(context, reducedClip);
1419}
1420
1421// Verify that clip masks are freed up when the clip state that generated them goes away.
1422DEF_GPUTEST_FOR_ALL_CONTEXTS(ClipMaskCache, reporter, ctxInfo) {
1423 // This test uses resource key tags which only function in debug builds.
1424#ifdef SK_DEBUG
1425 GrContext* context = ctxInfo.grContext();
1426 SkClipStack stack;
1427
1428 SkPath path;
1429 path.addCircle(10, 10, 8);
1430 path.addCircle(15, 15, 8);
1431 path.setFillType(SkPath::kEvenOdd_FillType);
1432
1433 static const char* kTag = GrClipStackClip::kMaskTestTag;
1434 GrResourceCache* cache = context->getResourceCache();
1435
1436 static constexpr int kN = 5;
1437
1438 for (int i = 0; i < kN; ++i) {
1439 SkMatrix m;
1440 m.setTranslate(0.5, 0.5);
1441 stack.save();
1442 stack.clipPath(path, m, SkClipOp::kIntersect, true);
1443 auto mask = GrClipStackClip(&stack).testingOnly_createClipMask(context);
1444 REPORTER_ASSERT(reporter, 0 == strcmp(mask->getUniqueKey().tag(), kTag));
1445 // Make sure mask isn't pinned in cache.
1446 mask.reset(nullptr);
1447 context->flush();
1448 REPORTER_ASSERT(reporter, i + 1 == cache->countUniqueKeysWithTag(kTag));
1449 }
1450
1451 for (int i = 0; i < kN; ++i) {
1452 stack.restore();
1453 cache->purgeAsNeeded();
1454 REPORTER_ASSERT(reporter, kN - (i + 1) == cache->countUniqueKeysWithTag(kTag));
1455 }
1456#endif
1457}
1458
Mike Reed3726a4a2017-01-19 11:36:41 -05001459#include "SkSurface.h"
1460DEF_GPUTEST_FOR_ALL_CONTEXTS(canvas_private_clipRgn, reporter, ctxInfo) {
1461 GrContext* context = ctxInfo.grContext();
1462
1463 const int w = 10;
1464 const int h = 10;
1465 SkImageInfo info = SkImageInfo::MakeN32Premul(w, h);
1466 sk_sp<SkSurface> surf = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info);
1467 SkCanvas* canvas = surf->getCanvas();
1468 SkRegion rgn;
1469
1470 canvas->temporary_internal_getRgnClip(&rgn);
1471 REPORTER_ASSERT(reporter, rgn.isRect());
1472 REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeWH(w, h));
1473
1474 canvas->save();
1475 canvas->clipRect(SkRect::MakeWH(5, 5), kDifference_SkClipOp);
1476 canvas->temporary_internal_getRgnClip(&rgn);
1477 REPORTER_ASSERT(reporter, rgn.isComplex());
1478 REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeWH(w, h));
1479 canvas->restore();
1480
1481 canvas->save();
1482 canvas->clipRRect(SkRRect::MakeOval(SkRect::MakeLTRB(3, 3, 7, 7)));
1483 canvas->temporary_internal_getRgnClip(&rgn);
1484 REPORTER_ASSERT(reporter, rgn.isComplex());
1485 REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeLTRB(3, 3, 7, 7));
1486 canvas->restore();
1487}
Brian Salomon19f0ed52017-01-06 13:54:58 -05001488#endif