blob: e0df2ca902f81cc23748289ab304dee10757cafa [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"
Mike Reed84dd8572017-03-08 22:21:00 -050020#include "GrTextureProxy.h"
csmartdaltoncbecb082016-07-22 08:59:08 -070021typedef GrReducedClip::ElementList ElementList;
22typedef GrReducedClip::InitialState InitialState;
23#endif
24
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000025static void test_assign_and_comparison(skiatest::Reporter* reporter) {
26 SkClipStack s;
reed@google.comd9f2dea2011-10-12 14:43:27 +000027 bool doAA = false;
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000028
robertphillips@google.com80214e22012-07-20 15:33:18 +000029 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
30
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000031 // Build up a clip stack with a path, an empty clip, and a rect.
32 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000033 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
34
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000035 SkPath p;
36 p.moveTo(5, 6);
37 p.lineTo(7, 8);
38 p.lineTo(5, 9);
39 p.close();
Mike Reedc1f77742016-12-09 09:00:50 -050040 s.clipPath(p, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000041
42 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000043 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
44
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000045 SkRect r = SkRect::MakeLTRB(1, 2, 3, 4);
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 r = SkRect::MakeLTRB(10, 11, 12, 13);
Mike Reedc1f77742016-12-09 09:00:50 -050048 s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000049
50 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000051 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
52
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000053 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -050054 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000055
56 // Test that assignment works.
57 SkClipStack copy = s;
58 REPORTER_ASSERT(reporter, s == copy);
59
60 // Test that different save levels triggers not equal.
61 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000062 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000063 REPORTER_ASSERT(reporter, s != copy);
64
65 // Test that an equal, but not copied version is equal.
66 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000067 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000068 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -050069 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000070 REPORTER_ASSERT(reporter, s == copy);
71
72 // Test that a different op on one level triggers not equal.
73 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000074 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000075 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000076 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000077 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -050078 s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000079 REPORTER_ASSERT(reporter, s != copy);
80
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000081 // Test that version constructed with rect-path rather than a rect is still considered equal.
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000082 s.restore();
83 s.save();
84 SkPath rp;
85 rp.addRect(r);
Mike Reedc1f77742016-12-09 09:00:50 -050086 s.clipPath(rp, SkMatrix::I(), kUnion_SkClipOp, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000087 REPORTER_ASSERT(reporter, s == copy);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000088
89 // Test that different rects triggers not equal.
90 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000091 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000092 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000093 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
94
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000095 r = SkRect::MakeLTRB(24, 25, 26, 27);
Mike Reedc1f77742016-12-09 09:00:50 -050096 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000097 REPORTER_ASSERT(reporter, s != copy);
98
99 // Sanity check
100 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000101 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
102
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000103 copy.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000104 REPORTER_ASSERT(reporter, 2 == copy.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000105 REPORTER_ASSERT(reporter, s == copy);
106 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000107 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000108 copy.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000109 REPORTER_ASSERT(reporter, 1 == copy.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000110 REPORTER_ASSERT(reporter, s == copy);
111
112 // Test that different paths triggers not equal.
113 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000114 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000115 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000116 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
117
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000118 p.addRect(r);
Mike Reedc1f77742016-12-09 09:00:50 -0500119 s.clipPath(p, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000120 REPORTER_ASSERT(reporter, s != copy);
121}
reed@google.combdee9fc2011-02-22 20:17:43 +0000122
123static void assert_count(skiatest::Reporter* reporter, const SkClipStack& stack,
124 int count) {
robertphillips@google.com80214e22012-07-20 15:33:18 +0000125 SkClipStack::B2TIter iter(stack);
reed@google.combdee9fc2011-02-22 20:17:43 +0000126 int counter = 0;
127 while (iter.next()) {
128 counter += 1;
129 }
130 REPORTER_ASSERT(reporter, count == counter);
131}
132
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000133// Exercise the SkClipStack's bottom to top and bidirectional iterators
134// (including the skipToTopmost functionality)
robertphillips@google.com80214e22012-07-20 15:33:18 +0000135static void test_iterators(skiatest::Reporter* reporter) {
136 SkClipStack stack;
137
138 static const SkRect gRects[] = {
139 { 0, 0, 40, 40 },
140 { 60, 0, 100, 40 },
141 { 0, 60, 40, 100 },
142 { 60, 60, 100, 100 }
143 };
144
145 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
146 // the union op will prevent these from being fused together
Mike Reedc1f77742016-12-09 09:00:50 -0500147 stack.clipRect(gRects[i], SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000148 }
149
150 assert_count(reporter, stack, 4);
151
152 // bottom to top iteration
153 {
halcanary96fcdcc2015-08-27 07:41:13 -0700154 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000155
156 SkClipStack::B2TIter iter(stack);
157 int i;
158
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000159 for (i = 0, element = iter.next(); element; ++i, element = iter.next()) {
160 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
161 REPORTER_ASSERT(reporter, element->getRect() == gRects[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000162 }
163
164 SkASSERT(i == 4);
165 }
166
167 // top to bottom iteration
168 {
halcanary96fcdcc2015-08-27 07:41:13 -0700169 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000170
171 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
172 int i;
173
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000174 for (i = 3, element = iter.prev(); element; --i, element = iter.prev()) {
175 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
176 REPORTER_ASSERT(reporter, element->getRect() == gRects[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000177 }
178
179 SkASSERT(i == -1);
180 }
181
182 // skipToTopmost
183 {
halcanary96fcdcc2015-08-27 07:41:13 -0700184 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000185
186 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
187
Mike Reedc1f77742016-12-09 09:00:50 -0500188 element = iter.skipToTopmost(kUnion_SkClipOp);
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000189 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
190 REPORTER_ASSERT(reporter, element->getRect() == gRects[3]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000191 }
192}
193
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000194// Exercise the SkClipStack's getConservativeBounds computation
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000195static void test_bounds(skiatest::Reporter* reporter, SkClipStack::Element::Type primType) {
robertphillips@google.com607fe072012-07-24 13:54:00 +0000196 static const int gNumCases = 20;
197 static const SkRect gAnswerRectsBW[gNumCases] = {
198 // A op B
199 { 40, 40, 50, 50 },
200 { 10, 10, 50, 50 },
201 { 10, 10, 80, 80 },
202 { 10, 10, 80, 80 },
203 { 40, 40, 80, 80 },
204
205 // invA op B
206 { 40, 40, 80, 80 },
207 { 0, 0, 100, 100 },
208 { 0, 0, 100, 100 },
209 { 0, 0, 100, 100 },
210 { 40, 40, 50, 50 },
211
212 // A op invB
213 { 10, 10, 50, 50 },
214 { 40, 40, 50, 50 },
215 { 0, 0, 100, 100 },
216 { 0, 0, 100, 100 },
217 { 0, 0, 100, 100 },
218
219 // invA op invB
220 { 0, 0, 100, 100 },
221 { 40, 40, 80, 80 },
222 { 0, 0, 100, 100 },
223 { 10, 10, 80, 80 },
224 { 10, 10, 50, 50 },
225 };
226
Mike Reedc1f77742016-12-09 09:00:50 -0500227 static const SkClipOp gOps[] = {
228 kIntersect_SkClipOp,
229 kDifference_SkClipOp,
230 kUnion_SkClipOp,
231 kXOR_SkClipOp,
232 kReverseDifference_SkClipOp
robertphillips@google.com607fe072012-07-24 13:54:00 +0000233 };
234
235 SkRect rectA, rectB;
236
237 rectA.iset(10, 10, 50, 50);
238 rectB.iset(40, 40, 80, 80);
239
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000240 SkRRect rrectA, rrectB;
241 rrectA.setOval(rectA);
242 rrectB.setRectXY(rectB, SkIntToScalar(1), SkIntToScalar(2));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000243
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000244 SkPath pathA, pathB;
245
246 pathA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
247 pathB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000248
249 SkClipStack stack;
robertphillips@google.com7b112892012-07-31 15:18:21 +0000250 SkRect devClipBound;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000251 bool isIntersectionOfRects = false;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000252
253 int testCase = 0;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000254 int numBitTests = SkClipStack::Element::kPath_Type == primType ? 4 : 1;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000255 for (int invBits = 0; invBits < numBitTests; ++invBits) {
robertphillips@google.com607fe072012-07-24 13:54:00 +0000256 for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) {
257
258 stack.save();
259 bool doInvA = SkToBool(invBits & 1);
260 bool doInvB = SkToBool(invBits & 2);
261
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000262 pathA.setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType :
robertphillips@google.com607fe072012-07-24 13:54:00 +0000263 SkPath::kEvenOdd_FillType);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000264 pathB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType :
robertphillips@google.com607fe072012-07-24 13:54:00 +0000265 SkPath::kEvenOdd_FillType);
266
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000267 switch (primType) {
268 case SkClipStack::Element::kEmpty_Type:
269 SkDEBUGFAIL("Don't call this with kEmpty.");
270 break;
271 case SkClipStack::Element::kRect_Type:
Mike Reedc1f77742016-12-09 09:00:50 -0500272 stack.clipRect(rectA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400273 stack.clipRect(rectB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000274 break;
275 case SkClipStack::Element::kRRect_Type:
Mike Reedc1f77742016-12-09 09:00:50 -0500276 stack.clipRRect(rrectA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400277 stack.clipRRect(rrectB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000278 break;
279 case SkClipStack::Element::kPath_Type:
Mike Reedc1f77742016-12-09 09:00:50 -0500280 stack.clipPath(pathA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400281 stack.clipPath(pathB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000282 break;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000283 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000284
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000285 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000286 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000287
robertphillips@google.com7b112892012-07-31 15:18:21 +0000288 stack.getConservativeBounds(0, 0, 100, 100, &devClipBound,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000289 &isIntersectionOfRects);
290
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000291 if (SkClipStack::Element::kRect_Type == primType) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000292 REPORTER_ASSERT(reporter, isIntersectionOfRects ==
Mike Reedc1f77742016-12-09 09:00:50 -0500293 (gOps[op] == kIntersect_SkClipOp));
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000294 } else {
295 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
296 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000297
298 SkASSERT(testCase < gNumCases);
robertphillips@google.com7b112892012-07-31 15:18:21 +0000299 REPORTER_ASSERT(reporter, devClipBound == gAnswerRectsBW[testCase]);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000300 ++testCase;
301
302 stack.restore();
303 }
304 }
305}
306
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000307// Test out 'isWideOpen' entry point
308static void test_isWideOpen(skiatest::Reporter* reporter) {
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000309 {
310 // Empty stack is wide open. Wide open stack means that gen id is wide open.
311 SkClipStack stack;
312 REPORTER_ASSERT(reporter, stack.isWideOpen());
313 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
314 }
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000315
316 SkRect rectA, rectB;
317
318 rectA.iset(10, 10, 40, 40);
319 rectB.iset(50, 50, 80, 80);
320
321 // Stack should initially be wide open
322 {
323 SkClipStack stack;
324
325 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000326 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000327 }
328
329 // Test out case where the user specifies a union that includes everything
330 {
331 SkClipStack stack;
332
333 SkPath clipA, clipB;
334
335 clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
336 clipA.setFillType(SkPath::kInverseEvenOdd_FillType);
337
338 clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
339 clipB.setFillType(SkPath::kInverseEvenOdd_FillType);
340
Mike Reedc1f77742016-12-09 09:00:50 -0500341 stack.clipPath(clipA, SkMatrix::I(), kReplace_SkClipOp, false);
342 stack.clipPath(clipB, SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000343
344 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000345 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000346 }
347
348 // Test out union w/ a wide open clip
349 {
350 SkClipStack stack;
351
Mike Reedc1f77742016-12-09 09:00:50 -0500352 stack.clipRect(rectA, SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000353
354 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000355 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000356 }
357
358 // Test out empty difference from a wide open clip
359 {
360 SkClipStack stack;
361
362 SkRect emptyRect;
363 emptyRect.setEmpty();
364
Mike Reedc1f77742016-12-09 09:00:50 -0500365 stack.clipRect(emptyRect, SkMatrix::I(), kDifference_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000366
367 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000368 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000369 }
370
371 // Test out return to wide open
372 {
373 SkClipStack stack;
374
375 stack.save();
376
Mike Reedc1f77742016-12-09 09:00:50 -0500377 stack.clipRect(rectA, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000378
379 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000380 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000381
382 stack.restore();
383
384 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000385 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000386 }
387}
388
bsalomon@google.com100abf42012-09-05 17:40:04 +0000389static int count(const SkClipStack& stack) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000390
391 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
392
halcanary96fcdcc2015-08-27 07:41:13 -0700393 const SkClipStack::Element* element = nullptr;
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000394 int count = 0;
395
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000396 for (element = iter.prev(); element; element = iter.prev(), ++count) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000397 ;
398 }
399
400 return count;
401}
402
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000403static void test_rect_inverse_fill(skiatest::Reporter* reporter) {
404 // non-intersecting rectangles
405 SkRect rect = SkRect::MakeLTRB(0, 0, 10, 10);
406
407 SkPath path;
408 path.addRect(rect);
409 path.toggleInverseFillType();
410 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500411 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000412
413 SkRect bounds;
414 SkClipStack::BoundsType boundsType;
415 stack.getBounds(&bounds, &boundsType);
416 REPORTER_ASSERT(reporter, SkClipStack::kInsideOut_BoundsType == boundsType);
417 REPORTER_ASSERT(reporter, bounds == rect);
418}
419
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000420static void test_rect_replace(skiatest::Reporter* reporter) {
421 SkRect rect = SkRect::MakeWH(100, 100);
422 SkRect rect2 = SkRect::MakeXYWH(50, 50, 100, 100);
423
424 SkRect bound;
425 SkClipStack::BoundsType type;
426 bool isIntersectionOfRects;
427
428 // Adding a new rect with the replace operator should not increase
429 // the stack depth. BW replacing BW.
430 {
431 SkClipStack stack;
432 REPORTER_ASSERT(reporter, 0 == 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));
Mike Reedc1f77742016-12-09 09:00:50 -0500435 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000436 REPORTER_ASSERT(reporter, 1 == count(stack));
437 }
438
439 // Adding a new rect with the replace operator should not increase
440 // the stack depth. AA replacing AA.
441 {
442 SkClipStack stack;
443 REPORTER_ASSERT(reporter, 0 == 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));
Mike Reedc1f77742016-12-09 09:00:50 -0500446 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000447 REPORTER_ASSERT(reporter, 1 == count(stack));
448 }
449
450 // Adding a new rect with the replace operator should not increase
451 // the stack depth. BW replacing AA replacing BW.
452 {
453 SkClipStack stack;
454 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500455 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
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, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000458 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500459 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000460 REPORTER_ASSERT(reporter, 1 == count(stack));
461 }
462
463 // Make sure replace clip rects don't collapse too much.
464 {
465 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500466 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
467 stack.clipRect(rect2, SkMatrix::I(), kIntersect_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000468 REPORTER_ASSERT(reporter, 1 == count(stack));
469
470 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500471 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000472 REPORTER_ASSERT(reporter, 2 == count(stack));
473 stack.getBounds(&bound, &type, &isIntersectionOfRects);
474 REPORTER_ASSERT(reporter, bound == rect);
475 stack.restore();
476 REPORTER_ASSERT(reporter, 1 == count(stack));
477
478 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500479 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
480 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000481 REPORTER_ASSERT(reporter, 2 == count(stack));
482 stack.restore();
483 REPORTER_ASSERT(reporter, 1 == count(stack));
484
485 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500486 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
487 stack.clipRect(rect2, SkMatrix::I(), kIntersect_SkClipOp, false);
488 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000489 REPORTER_ASSERT(reporter, 2 == count(stack));
490 stack.restore();
491 REPORTER_ASSERT(reporter, 1 == count(stack));
492 }
493}
494
495// Simplified path-based version of test_rect_replace.
496static void test_path_replace(skiatest::Reporter* reporter) {
497 SkRect rect = SkRect::MakeWH(100, 100);
498 SkPath path;
499 path.addCircle(50, 50, 50);
500
501 // Replace operation doesn't grow the stack.
502 {
503 SkClipStack stack;
504 REPORTER_ASSERT(reporter, 0 == 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));
Mike Reedc1f77742016-12-09 09:00:50 -0500507 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000508 REPORTER_ASSERT(reporter, 1 == count(stack));
509 }
510
511 // Replacing rect with path.
512 {
513 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500514 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000515 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500516 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000517 REPORTER_ASSERT(reporter, 1 == count(stack));
518 }
519}
520
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000521// Test out SkClipStack's merging of rect clips. In particular exercise
522// merging of aa vs. bw rects.
523static void test_rect_merging(skiatest::Reporter* reporter) {
524
525 SkRect overlapLeft = SkRect::MakeLTRB(10, 10, 50, 50);
526 SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80);
527
528 SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90);
529 SkRect nestedChild = SkRect::MakeLTRB(40, 40, 60, 60);
530
531 SkRect bound;
532 SkClipStack::BoundsType type;
533 bool isIntersectionOfRects;
534
535 // all bw overlapping - should merge
536 {
537 SkClipStack stack;
538
Mike Reedc1f77742016-12-09 09:00:50 -0500539 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000540
Mike Reedc1f77742016-12-09 09:00:50 -0500541 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000542
543 REPORTER_ASSERT(reporter, 1 == count(stack));
544
545 stack.getBounds(&bound, &type, &isIntersectionOfRects);
546
547 REPORTER_ASSERT(reporter, isIntersectionOfRects);
548 }
549
550 // all aa overlapping - should merge
551 {
552 SkClipStack stack;
553
Mike Reedc1f77742016-12-09 09:00:50 -0500554 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000555
Mike Reedc1f77742016-12-09 09:00:50 -0500556 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000557
558 REPORTER_ASSERT(reporter, 1 == count(stack));
559
560 stack.getBounds(&bound, &type, &isIntersectionOfRects);
561
562 REPORTER_ASSERT(reporter, isIntersectionOfRects);
563 }
564
565 // mixed overlapping - should _not_ merge
566 {
567 SkClipStack stack;
568
Mike Reedc1f77742016-12-09 09:00:50 -0500569 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000570
Mike Reedc1f77742016-12-09 09:00:50 -0500571 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000572
573 REPORTER_ASSERT(reporter, 2 == count(stack));
574
575 stack.getBounds(&bound, &type, &isIntersectionOfRects);
576
577 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
578 }
579
580 // mixed nested (bw inside aa) - should merge
581 {
582 SkClipStack stack;
583
Mike Reedc1f77742016-12-09 09:00:50 -0500584 stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000585
Mike Reedc1f77742016-12-09 09:00:50 -0500586 stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000587
588 REPORTER_ASSERT(reporter, 1 == count(stack));
589
590 stack.getBounds(&bound, &type, &isIntersectionOfRects);
591
592 REPORTER_ASSERT(reporter, isIntersectionOfRects);
593 }
594
595 // mixed nested (aa inside bw) - should merge
596 {
597 SkClipStack stack;
598
Mike Reedc1f77742016-12-09 09:00:50 -0500599 stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000600
Mike Reedc1f77742016-12-09 09:00:50 -0500601 stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000602
603 REPORTER_ASSERT(reporter, 1 == count(stack));
604
605 stack.getBounds(&bound, &type, &isIntersectionOfRects);
606
607 REPORTER_ASSERT(reporter, isIntersectionOfRects);
608 }
609
610 // reverse nested (aa inside bw) - should _not_ merge
611 {
612 SkClipStack stack;
613
Mike Reedc1f77742016-12-09 09:00:50 -0500614 stack.clipRect(nestedChild, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000615
Mike Reedc1f77742016-12-09 09:00:50 -0500616 stack.clipRect(nestedParent, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000617
618 REPORTER_ASSERT(reporter, 2 == count(stack));
619
620 stack.getBounds(&bound, &type, &isIntersectionOfRects);
621
622 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
623 }
624}
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000625
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000626static void test_quickContains(skiatest::Reporter* reporter) {
627 SkRect testRect = SkRect::MakeLTRB(10, 10, 40, 40);
628 SkRect insideRect = SkRect::MakeLTRB(20, 20, 30, 30);
629 SkRect intersectingRect = SkRect::MakeLTRB(25, 25, 50, 50);
630 SkRect outsideRect = SkRect::MakeLTRB(0, 0, 50, 50);
631 SkRect nonIntersectingRect = SkRect::MakeLTRB(100, 100, 110, 110);
632
633 SkPath insideCircle;
634 insideCircle.addCircle(25, 25, 5);
635 SkPath intersectingCircle;
636 intersectingCircle.addCircle(25, 40, 10);
637 SkPath outsideCircle;
638 outsideCircle.addCircle(25, 25, 50);
639 SkPath nonIntersectingCircle;
640 nonIntersectingCircle.addCircle(100, 100, 5);
641
642 {
643 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500644 stack.clipRect(outsideRect, SkMatrix::I(), kDifference_SkClipOp, false);
645 // return false because quickContains currently does not care for kDifference_SkClipOp
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000646 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
647 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000648
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000649 // Replace Op tests
650 {
651 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500652 stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000653 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
654 }
655
656 {
657 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500658 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000659 stack.save(); // To prevent in-place substitution by replace OP
Mike Reedc1f77742016-12-09 09:00:50 -0500660 stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000661 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
662 stack.restore();
663 }
664
665 {
666 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500667 stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000668 stack.save(); // To prevent in-place substitution by replace OP
Mike Reedc1f77742016-12-09 09:00:50 -0500669 stack.clipRect(insideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000670 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
671 stack.restore();
672 }
673
674 // Verify proper traversal of multi-element clip
675 {
676 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500677 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000678 // Use a path for second clip to prevent in-place intersection
Mike Reedc1f77742016-12-09 09:00:50 -0500679 stack.clipPath(outsideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000680 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
681 }
682
683 // Intersect Op tests with rectangles
684 {
685 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500686 stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000687 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
688 }
689
690 {
691 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500692 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000693 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
694 }
695
696 {
697 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500698 stack.clipRect(intersectingRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000699 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
700 }
701
702 {
703 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500704 stack.clipRect(nonIntersectingRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000705 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
706 }
707
708 // Intersect Op tests with circle paths
709 {
710 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500711 stack.clipPath(outsideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000712 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
713 }
714
715 {
716 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500717 stack.clipPath(insideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000718 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
719 }
720
721 {
722 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500723 stack.clipPath(intersectingCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000724 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
725 }
726
727 {
728 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500729 stack.clipPath(nonIntersectingCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000730 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
731 }
732
733 // Intersect Op tests with inverse filled rectangles
734 {
735 SkClipStack stack;
736 SkPath path;
737 path.addRect(outsideRect);
738 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500739 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000740 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
741 }
742
743 {
744 SkClipStack stack;
745 SkPath path;
746 path.addRect(insideRect);
747 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500748 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000749 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
750 }
751
752 {
753 SkClipStack stack;
754 SkPath path;
755 path.addRect(intersectingRect);
756 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500757 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000758 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
759 }
760
761 {
762 SkClipStack stack;
763 SkPath path;
764 path.addRect(nonIntersectingRect);
765 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500766 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000767 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
768 }
769
770 // Intersect Op tests with inverse filled circles
771 {
772 SkClipStack stack;
773 SkPath path = outsideCircle;
774 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500775 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000776 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
777 }
778
779 {
780 SkClipStack stack;
781 SkPath path = insideCircle;
782 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500783 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000784 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
785 }
786
787 {
788 SkClipStack stack;
789 SkPath path = intersectingCircle;
790 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500791 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000792 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
793 }
794
795 {
796 SkClipStack stack;
797 SkPath path = nonIntersectingCircle;
798 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500799 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000800 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
801 }
802}
803
csmartdaltond50e2402016-07-22 08:39:06 -0700804static void set_region_to_stack(const SkClipStack& stack, const SkIRect& bounds, SkRegion* region) {
805 region->setRect(bounds);
806 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
807 while (const SkClipStack::Element *element = iter.next()) {
808 SkRegion elemRegion;
809 SkRegion boundsRgn(bounds);
810 SkPath path;
811
812 switch (element->getType()) {
813 case SkClipStack::Element::kEmpty_Type:
814 elemRegion.setEmpty();
815 break;
816 default:
817 element->asPath(&path);
818 elemRegion.setPath(path, boundsRgn);
819 break;
820 }
reed73603f32016-09-20 08:42:38 -0700821 region->op(elemRegion, (SkRegion::Op)element->getOp());
csmartdaltond50e2402016-07-22 08:39:06 -0700822 }
823}
824
825static void test_invfill_diff_bug(skiatest::Reporter* reporter) {
826 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500827 stack.clipRect({10, 10, 20, 20}, SkMatrix::I(), kIntersect_SkClipOp, false);
csmartdaltond50e2402016-07-22 08:39:06 -0700828
829 SkPath path;
830 path.addRect({30, 10, 40, 20});
831 path.setFillType(SkPath::kInverseWinding_FillType);
Mike Reedc1f77742016-12-09 09:00:50 -0500832 stack.clipPath(path, SkMatrix::I(), kDifference_SkClipOp, false);
csmartdaltond50e2402016-07-22 08:39:06 -0700833
834 REPORTER_ASSERT(reporter, SkClipStack::kEmptyGenID == stack.getTopmostGenID());
835
836 SkRect stackBounds;
837 SkClipStack::BoundsType stackBoundsType;
838 stack.getBounds(&stackBounds, &stackBoundsType);
839
840 REPORTER_ASSERT(reporter, stackBounds.isEmpty());
841 REPORTER_ASSERT(reporter, SkClipStack::kNormal_BoundsType == stackBoundsType);
842
843 SkRegion region;
844 set_region_to_stack(stack, {0, 0, 50, 30}, &region);
845
846 REPORTER_ASSERT(reporter, region.isEmpty());
847}
848
bsalomon@google.com51a62862012-11-26 21:19:43 +0000849///////////////////////////////////////////////////////////////////////////////////////////////////
850
bsalomon@google.coma4e13c82012-11-26 21:38:37 +0000851#if SK_SUPPORT_GPU
bsalomon@google.com705e8402012-11-27 15:43:57 +0000852// Functions that add a shape to the clip stack. The shape is computed from a rectangle.
853// AA is always disabled since the clip stack reducer can cause changes in aa rasterization of the
854// stack. A fractional edge repeated in different elements may be rasterized fewer times using the
855// reduced stack.
856typedef void (*AddElementFunc) (const SkRect& rect,
857 bool invert,
Mike Reedc1f77742016-12-09 09:00:50 -0500858 SkClipOp op,
csmartdaltoncbecb082016-07-22 08:59:08 -0700859 SkClipStack* stack,
860 bool doAA);
bsalomon@google.coma4e13c82012-11-26 21:38:37 +0000861
Mike Reedc1f77742016-12-09 09:00:50 -0500862static void add_round_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700863 bool doAA) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000864 SkScalar rx = rect.width() / 10;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000865 SkScalar ry = rect.height() / 20;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000866 if (invert) {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000867 SkPath path;
868 path.addRoundRect(rect, rx, ry);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000869 path.setFillType(SkPath::kInverseWinding_FillType);
Brian Salomona3b45d42016-10-03 11:36:16 -0400870 stack->clipPath(path, SkMatrix::I(), op, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000871 } else {
872 SkRRect rrect;
873 rrect.setRectXY(rect, rx, ry);
Brian Salomona3b45d42016-10-03 11:36:16 -0400874 stack->clipRRect(rrect, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000875 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000876};
877
Mike Reedc1f77742016-12-09 09:00:50 -0500878static void add_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700879 bool doAA) {
bsalomon@google.com705e8402012-11-27 15:43:57 +0000880 if (invert) {
881 SkPath path;
882 path.addRect(rect);
883 path.setFillType(SkPath::kInverseWinding_FillType);
Brian Salomona3b45d42016-10-03 11:36:16 -0400884 stack->clipPath(path, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000885 } else {
Brian Salomona3b45d42016-10-03 11:36:16 -0400886 stack->clipRect(rect, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000887 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000888};
889
Mike Reedc1f77742016-12-09 09:00:50 -0500890static void add_oval(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700891 bool doAA) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000892 SkPath path;
893 path.addOval(rect);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000894 if (invert) {
895 path.setFillType(SkPath::kInverseWinding_FillType);
896 }
Brian Salomona3b45d42016-10-03 11:36:16 -0400897 stack->clipPath(path, SkMatrix::I(), op, doAA);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000898};
899
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000900static void add_elem_to_stack(const SkClipStack::Element& element, SkClipStack* stack) {
901 switch (element.getType()) {
902 case SkClipStack::Element::kRect_Type:
Brian Salomona3b45d42016-10-03 11:36:16 -0400903 stack->clipRect(element.getRect(), SkMatrix::I(), element.getOp(), element.isAA());
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000904 break;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000905 case SkClipStack::Element::kRRect_Type:
Brian Salomona3b45d42016-10-03 11:36:16 -0400906 stack->clipRRect(element.getRRect(), SkMatrix::I(), element.getOp(), element.isAA());
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000907 break;
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000908 case SkClipStack::Element::kPath_Type:
Brian Salomona3b45d42016-10-03 11:36:16 -0400909 stack->clipPath(element.getPath(), SkMatrix::I(), element.getOp(), element.isAA());
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000910 break;
911 case SkClipStack::Element::kEmpty_Type:
912 SkDEBUGFAIL("Why did the reducer produce an explicit empty.");
913 stack->clipEmpty();
914 break;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000915 }
916}
917
bsalomon@google.com51a62862012-11-26 21:19:43 +0000918static void test_reduced_clip_stack(skiatest::Reporter* reporter) {
919 // We construct random clip stacks, reduce them, and then rasterize both versions to verify that
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000920 // they are equal.
bsalomon@google.com51a62862012-11-26 21:19:43 +0000921
922 // All the clip elements will be contained within these bounds.
csmartdaltond211e782016-08-15 11:17:19 -0700923 static const SkIRect kIBounds = SkIRect::MakeWH(100, 100);
924 static const SkRect kBounds = SkRect::Make(kIBounds);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000925
926 enum {
csmartdaltoncbecb082016-07-22 08:59:08 -0700927 kNumTests = 250,
bsalomon@google.com51a62862012-11-26 21:19:43 +0000928 kMinElemsPerTest = 1,
929 kMaxElemsPerTest = 50,
930 };
931
932 // min/max size of a clip element as a fraction of kBounds.
933 static const SkScalar kMinElemSizeFrac = SK_Scalar1 / 5;
934 static const SkScalar kMaxElemSizeFrac = SK_Scalar1;
935
Mike Reedc1f77742016-12-09 09:00:50 -0500936 static const SkClipOp kOps[] = {
937 kDifference_SkClipOp,
938 kIntersect_SkClipOp,
939 kUnion_SkClipOp,
940 kXOR_SkClipOp,
941 kReverseDifference_SkClipOp,
942 kReplace_SkClipOp,
bsalomon@google.com51a62862012-11-26 21:19:43 +0000943 };
944
945 // Replace operations short-circuit the optimizer. We want to make sure that we test this code
946 // path a little bit but we don't want it to prevent us from testing many longer traversals in
947 // the optimizer.
948 static const int kReplaceDiv = 4 * kMaxElemsPerTest;
949
bsalomon@google.com705e8402012-11-27 15:43:57 +0000950 // We want to test inverse fills. However, they are quite rare in practice so don't over do it.
951 static const SkScalar kFractionInverted = SK_Scalar1 / kMaxElemsPerTest;
952
csmartdaltoncbecb082016-07-22 08:59:08 -0700953 static const SkScalar kFractionAntialiased = 0.25;
954
bsalomon@google.com51a62862012-11-26 21:19:43 +0000955 static const AddElementFunc kElementFuncs[] = {
956 add_rect,
957 add_round_rect,
958 add_oval,
959 };
960
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000961 SkRandom r;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000962
963 for (int i = 0; i < kNumTests; ++i) {
csmartdaltoncbecb082016-07-22 08:59:08 -0700964 SkString testCase;
965 testCase.printf("Iteration %d", i);
966
bsalomon@google.com51a62862012-11-26 21:19:43 +0000967 // Randomly generate a clip stack.
968 SkClipStack stack;
969 int numElems = r.nextRangeU(kMinElemsPerTest, kMaxElemsPerTest);
csmartdaltoncbecb082016-07-22 08:59:08 -0700970 bool doAA = r.nextBiasedBool(kFractionAntialiased);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000971 for (int e = 0; e < numElems; ++e) {
Mike Reedc1f77742016-12-09 09:00:50 -0500972 SkClipOp op = kOps[r.nextULessThan(SK_ARRAY_COUNT(kOps))];
973 if (op == kReplace_SkClipOp) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000974 if (r.nextU() % kReplaceDiv) {
975 --e;
976 continue;
977 }
978 }
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000979
bsalomon@google.com51a62862012-11-26 21:19:43 +0000980 // saves can change the clip stack behavior when an element is added.
981 bool doSave = r.nextBool();
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000982
bsalomon@google.com51a62862012-11-26 21:19:43 +0000983 SkSize size = SkSize::Make(
Mike Reeddf85c382017-02-14 10:59:19 -0500984 kBounds.width() * r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac),
985 kBounds.height() * r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac));
bsalomon@google.com51a62862012-11-26 21:19:43 +0000986
csmartdaltoncbecb082016-07-22 08:59:08 -0700987 SkPoint xy = {r.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth),
988 r.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight)};
bsalomon@google.com51a62862012-11-26 21:19:43 +0000989
csmartdaltoncbecb082016-07-22 08:59:08 -0700990 SkRect rect;
991 if (doAA) {
992 rect.setXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight);
993 if (GrClip::IsPixelAligned(rect)) {
994 // Don't create an element that may accidentally become not antialiased.
995 rect.outset(0.5f, 0.5f);
996 }
997 SkASSERT(!GrClip::IsPixelAligned(rect));
998 } else {
999 rect.setXYWH(SkScalarFloorToScalar(xy.fX),
1000 SkScalarFloorToScalar(xy.fY),
1001 SkScalarCeilToScalar(size.fWidth),
1002 SkScalarCeilToScalar(size.fHeight));
1003 }
bsalomon@google.com51a62862012-11-26 21:19:43 +00001004
bsalomon@google.com705e8402012-11-27 15:43:57 +00001005 bool invert = r.nextBiasedBool(kFractionInverted);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001006
csmartdaltoncbecb082016-07-22 08:59:08 -07001007 kElementFuncs[r.nextULessThan(SK_ARRAY_COUNT(kElementFuncs))](rect, invert, op, &stack,
1008 doAA);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001009 if (doSave) {
1010 stack.save();
1011 }
1012 }
1013
csmartdalton8d3f92a2016-08-17 09:39:38 -07001014 // Zero the memory we will new the GrReducedClip into. This ensures the elements gen ID
1015 // will be kInvalidGenID if left uninitialized.
1016 SkAlignedSTStorage<1, GrReducedClip> storage;
1017 memset(storage.get(), 0, sizeof(GrReducedClip));
1018 GR_STATIC_ASSERT(0 == SkClipStack::kInvalidGenID);
1019
csmartdalton77f2fae2016-08-08 09:55:06 -07001020 // Get the reduced version of the stack.
csmartdaltoncbecb082016-07-22 08:59:08 -07001021 SkRect queryBounds = kBounds;
1022 queryBounds.outset(kBounds.width() / 2, kBounds.height() / 2);
csmartdalton8d3f92a2016-08-17 09:39:38 -07001023 const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, queryBounds);
bsalomon@google.coma4444302012-12-04 15:22:12 +00001024
csmartdalton8d3f92a2016-08-17 09:39:38 -07001025 REPORTER_ASSERT_MESSAGE(reporter,
1026 reduced->elements().isEmpty() ||
1027 SkClipStack::kInvalidGenID != reduced->elementsGenID(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001028 testCase.c_str());
1029
csmartdalton8d3f92a2016-08-17 09:39:38 -07001030 if (!reduced->elements().isEmpty()) {
1031 REPORTER_ASSERT_MESSAGE(reporter, reduced->hasIBounds(), testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001032 SkRect stackBounds;
1033 SkClipStack::BoundsType stackBoundsType;
1034 stack.getBounds(&stackBounds, &stackBoundsType);
1035 if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
1036 // Unless GrReducedClip starts doing some heroic tightening of the clip bounds, this
1037 // will be true since the stack bounds are completely contained inside the query.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001038 REPORTER_ASSERT_MESSAGE(reporter,
1039 GrClip::IsInsideClip(reduced->ibounds(), stackBounds),
csmartdaltoncbecb082016-07-22 08:59:08 -07001040 testCase.c_str());
1041 }
csmartdalton8d3f92a2016-08-17 09:39:38 -07001042 REPORTER_ASSERT_MESSAGE(reporter, reduced->requiresAA() == doAA, testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001043 }
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001044
bsalomon@google.com51a62862012-11-26 21:19:43 +00001045 // Build a new clip stack based on the reduced clip elements
1046 SkClipStack reducedStack;
csmartdalton8d3f92a2016-08-17 09:39:38 -07001047 if (GrReducedClip::InitialState::kAllOut == reduced->initialState()) {
bsalomon@google.com51a62862012-11-26 21:19:43 +00001048 // whether the result is bounded or not, the whole plane should start outside the clip.
1049 reducedStack.clipEmpty();
1050 }
csmartdalton8d3f92a2016-08-17 09:39:38 -07001051 for (ElementList::Iter iter(reduced->elements()); iter.get(); iter.next()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001052 add_elem_to_stack(*iter.get(), &reducedStack);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001053 }
bsalomon@google.com51a62862012-11-26 21:19:43 +00001054
csmartdalton8d3f92a2016-08-17 09:39:38 -07001055 SkIRect ibounds = reduced->hasIBounds() ? reduced->ibounds() : kIBounds;
csmartdaltond211e782016-08-15 11:17:19 -07001056
bsalomon@google.com34cd70a2012-12-06 14:23:20 +00001057 // GrReducedClipStack assumes that the final result is clipped to the returned bounds
Mike Reedc1f77742016-12-09 09:00:50 -05001058 reducedStack.clipDevRect(ibounds, kIntersect_SkClipOp);
1059 stack.clipDevRect(ibounds, kIntersect_SkClipOp);
bsalomon@google.com34cd70a2012-12-06 14:23:20 +00001060
bsalomon@google.com51a62862012-11-26 21:19:43 +00001061 // convert both the original stack and reduced stack to SkRegions and see if they're equal
bsalomon@google.com51a62862012-11-26 21:19:43 +00001062 SkRegion region;
csmartdaltond211e782016-08-15 11:17:19 -07001063 set_region_to_stack(stack, ibounds, &region);
csmartdaltond50e2402016-07-22 08:39:06 -07001064
bsalomon@google.com51a62862012-11-26 21:19:43 +00001065 SkRegion reducedRegion;
csmartdaltond211e782016-08-15 11:17:19 -07001066 set_region_to_stack(reducedStack, ibounds, &reducedRegion);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001067
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001068 REPORTER_ASSERT_MESSAGE(reporter, region == reducedRegion, testCase.c_str());
csmartdalton8d3f92a2016-08-17 09:39:38 -07001069
1070 reduced->~GrReducedClip();
bsalomon@google.com51a62862012-11-26 21:19:43 +00001071 }
1072}
1073
halcanary4dbbd042016-06-07 17:21:10 -07001074#ifdef SK_BUILD_FOR_WIN
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001075 #define SUPPRESS_VISIBILITY_WARNING
1076#else
1077 #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden")))
1078#endif
1079
1080static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) {
1081 {
1082 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001083 stack.clipRect(SkRect::MakeXYWH(0, 0, 100, 100), SkMatrix::I(), kReplace_SkClipOp,
Brian Salomona3b45d42016-10-03 11:36:16 -04001084 true);
1085 stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(50.3), SkScalar(50.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001086 kReplace_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001087 SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001088
csmartdalton8d3f92a2016-08-17 09:39:38 -07001089 SkAlignedSTStorage<1, GrReducedClip> storage;
1090 memset(storage.get(), 0, sizeof(GrReducedClip));
1091 GR_STATIC_ASSERT(0 == SkClipStack::kInvalidGenID);
1092 const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, bounds);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001093
csmartdalton8d3f92a2016-08-17 09:39:38 -07001094 REPORTER_ASSERT(reporter, reduced->elements().count() == 1);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001095 // Clips will be cached based on the generation id. Make sure the gen id is valid.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001096 REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reduced->elementsGenID());
1097
1098 reduced->~GrReducedClip();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001099 }
1100 {
1101 SkClipStack stack;
1102
1103 // Create a clip with following 25.3, 25.3 boxes which are 25 apart:
1104 // A B
1105 // C D
1106
Brian Salomona3b45d42016-10-03 11:36:16 -04001107 stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001108 kReplace_SkClipOp, true);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001109 int32_t genIDA = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001110 stack.clipRect(SkRect::MakeXYWH(50, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001111 kUnion_SkClipOp, true);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001112 int32_t genIDB = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001113 stack.clipRect(SkRect::MakeXYWH(0, 50, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001114 kUnion_SkClipOp, true);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001115 int32_t genIDC = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001116 stack.clipRect(SkRect::MakeXYWH(50, 50, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001117 kUnion_SkClipOp, true);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001118 int32_t genIDD = stack.getTopmostGenID();
1119
1120
csmartdaltoncbecb082016-07-22 08:59:08 -07001121#define IXYWH SkIRect::MakeXYWH
1122#define XYWH SkRect::MakeXYWH
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001123
csmartdaltoncbecb082016-07-22 08:59:08 -07001124 SkIRect stackBounds = IXYWH(0, 0, 76, 76);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001125
1126 // The base test is to test each rect in two ways:
1127 // 1) The box dimensions. (Should reduce to "all in", no elements).
1128 // 2) A bit over the box dimensions.
1129 // In the case 2, test that the generation id is what is expected.
1130 // The rects are of fractional size so that case 2 never gets optimized to an empty element
1131 // list.
1132
1133 // Not passing in tighter bounds is tested for consistency.
1134 static const struct SUPPRESS_VISIBILITY_WARNING {
csmartdaltoncbecb082016-07-22 08:59:08 -07001135 SkRect testBounds;
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001136 int reducedClipCount;
1137 int32_t reducedGenID;
csmartdaltoncbecb082016-07-22 08:59:08 -07001138 InitialState initialState;
1139 SkIRect clipIRect;
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001140 // parameter.
1141 } testCases[] = {
csmartdalton77f2fae2016-08-08 09:55:06 -07001142
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001143 // Rect A.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001144 { XYWH(0, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 0, 25, 25) },
1145 { 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 -07001146 { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::InitialState::kAllOut, IXYWH(0, 0, 27, 27)},
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001147
1148 // Rect B.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001149 { XYWH(50, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 25, 25) },
1150 { XYWH(50, 0, 25.3f, 25.3f), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 26, 26) },
csmartdalton77f2fae2016-08-08 09:55:06 -07001151 { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::InitialState::kAllOut, IXYWH(50, 0, 26, 27) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001152
1153 // Rect C.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001154 { XYWH(0, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 50, 25, 25) },
1155 { 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 -07001156 { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::InitialState::kAllOut, IXYWH(0, 50, 27, 26) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001157
1158 // Rect D.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001159 { XYWH(50, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 25, 25)},
1160 { XYWH(50.3f, 50.3f, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 26, 26)},
csmartdalton77f2fae2016-08-08 09:55:06 -07001161 { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(50, 50, 26, 26)},
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001162
1163 // Other tests:
csmartdalton77f2fae2016-08-08 09:55:06 -07001164 { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::InitialState::kAllOut, stackBounds },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001165
1166 // Rect in the middle, touches none.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001167 { 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 +00001168
1169 // Rect in the middle, touches all the rects. GenID is the last rect.
csmartdalton77f2fae2016-08-08 09:55:06 -07001170 { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(24, 24, 27, 27) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001171 };
1172
1173#undef XYWH
csmartdaltoncbecb082016-07-22 08:59:08 -07001174#undef IXYWH
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001175
1176 for (size_t i = 0; i < SK_ARRAY_COUNT(testCases); ++i) {
csmartdalton77f2fae2016-08-08 09:55:06 -07001177 const GrReducedClip reduced(stack, testCases[i].testBounds);
1178 REPORTER_ASSERT(reporter, reduced.elements().count() == testCases[i].reducedClipCount);
1179 SkASSERT(reduced.elements().count() == testCases[i].reducedClipCount);
csmartdalton8d3f92a2016-08-17 09:39:38 -07001180 if (reduced.elements().count()) {
1181 REPORTER_ASSERT(reporter, reduced.elementsGenID() == testCases[i].reducedGenID);
1182 SkASSERT(reduced.elementsGenID() == testCases[i].reducedGenID);
1183 }
csmartdalton77f2fae2016-08-08 09:55:06 -07001184 REPORTER_ASSERT(reporter, reduced.initialState() == testCases[i].initialState);
1185 SkASSERT(reduced.initialState() == testCases[i].initialState);
csmartdaltond211e782016-08-15 11:17:19 -07001186 REPORTER_ASSERT(reporter, reduced.hasIBounds());
1187 SkASSERT(reduced.hasIBounds());
1188 REPORTER_ASSERT(reporter, reduced.ibounds() == testCases[i].clipIRect);
1189 SkASSERT(reduced.ibounds() == testCases[i].clipIRect);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001190 }
1191 }
1192}
1193
1194static void test_reduced_clip_stack_no_aa_crash(skiatest::Reporter* reporter) {
1195 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001196 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 100, 100), kReplace_SkClipOp);
1197 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 50, 50), kReplace_SkClipOp);
csmartdaltoncbecb082016-07-22 08:59:08 -07001198 SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001199
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001200 // At the time, this would crash.
csmartdalton77f2fae2016-08-08 09:55:06 -07001201 const GrReducedClip reduced(stack, bounds);
1202 REPORTER_ASSERT(reporter, reduced.elements().isEmpty());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001203}
1204
csmartdaltoncbecb082016-07-22 08:59:08 -07001205enum class ClipMethod {
1206 kSkipDraw,
1207 kIgnoreClip,
1208 kScissor,
1209 kAAElements
1210};
1211
1212static void test_aa_query(skiatest::Reporter* reporter, const SkString& testName,
1213 const SkClipStack& stack, const SkMatrix& queryXform,
1214 const SkRect& preXformQuery, ClipMethod expectedMethod,
1215 int numExpectedElems = 0) {
csmartdaltoncbecb082016-07-22 08:59:08 -07001216 SkRect queryBounds;
1217 queryXform.mapRect(&queryBounds, preXformQuery);
csmartdalton77f2fae2016-08-08 09:55:06 -07001218 const GrReducedClip reduced(stack, queryBounds);
csmartdaltoncbecb082016-07-22 08:59:08 -07001219
1220 SkClipStack::BoundsType stackBoundsType;
1221 SkRect stackBounds;
1222 stack.getBounds(&stackBounds, &stackBoundsType);
1223
1224 switch (expectedMethod) {
1225 case ClipMethod::kSkipDraw:
1226 SkASSERT(0 == numExpectedElems);
csmartdalton77f2fae2016-08-08 09:55:06 -07001227 REPORTER_ASSERT_MESSAGE(reporter, reduced.elements().isEmpty(), testName.c_str());
1228 REPORTER_ASSERT_MESSAGE(reporter,
1229 GrReducedClip::InitialState::kAllOut == reduced.initialState(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001230 testName.c_str());
1231 return;
1232 case ClipMethod::kIgnoreClip:
1233 SkASSERT(0 == numExpectedElems);
csmartdaltond211e782016-08-15 11:17:19 -07001234 REPORTER_ASSERT_MESSAGE(reporter,
1235 !reduced.hasIBounds() ||
1236 GrClip::IsInsideClip(reduced.ibounds(), queryBounds),
csmartdaltoncbecb082016-07-22 08:59:08 -07001237 testName.c_str());
csmartdalton77f2fae2016-08-08 09:55:06 -07001238 REPORTER_ASSERT_MESSAGE(reporter, reduced.elements().isEmpty(), testName.c_str());
1239 REPORTER_ASSERT_MESSAGE(reporter,
1240 GrReducedClip::InitialState::kAllIn == reduced.initialState(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001241 testName.c_str());
1242 return;
1243 case ClipMethod::kScissor: {
1244 SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType);
1245 SkASSERT(0 == numExpectedElems);
1246 SkIRect expectedScissor;
1247 stackBounds.round(&expectedScissor);
csmartdalton77f2fae2016-08-08 09:55:06 -07001248 REPORTER_ASSERT_MESSAGE(reporter, reduced.elements().isEmpty(), testName.c_str());
csmartdaltond211e782016-08-15 11:17:19 -07001249 REPORTER_ASSERT_MESSAGE(reporter, reduced.hasIBounds(), testName.c_str());
1250 REPORTER_ASSERT_MESSAGE(reporter, expectedScissor == reduced.ibounds(),
csmartdalton77f2fae2016-08-08 09:55:06 -07001251 testName.c_str());
1252 REPORTER_ASSERT_MESSAGE(reporter,
1253 GrReducedClip::InitialState::kAllIn == reduced.initialState(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001254 testName.c_str());
1255 return;
1256 }
1257 case ClipMethod::kAAElements: {
1258 SkIRect expectedClipIBounds = GrClip::GetPixelIBounds(queryBounds);
1259 if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
1260 SkAssertResult(expectedClipIBounds.intersect(GrClip::GetPixelIBounds(stackBounds)));
1261 }
csmartdalton77f2fae2016-08-08 09:55:06 -07001262 REPORTER_ASSERT_MESSAGE(reporter, numExpectedElems == reduced.elements().count(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001263 testName.c_str());
csmartdaltond211e782016-08-15 11:17:19 -07001264 REPORTER_ASSERT_MESSAGE(reporter, reduced.hasIBounds(), testName.c_str());
1265 REPORTER_ASSERT_MESSAGE(reporter, expectedClipIBounds == reduced.ibounds(),
csmartdalton77f2fae2016-08-08 09:55:06 -07001266 testName.c_str());
1267 REPORTER_ASSERT_MESSAGE(reporter, reduced.requiresAA() == !reduced.elements().isEmpty(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001268 testName.c_str());
1269 break;
1270 }
1271 }
1272}
1273
1274static void test_reduced_clip_stack_aa(skiatest::Reporter* reporter) {
1275 constexpr SkScalar IL = 2, IT = 1, IR = 6, IB = 7; // Pixel aligned rect.
1276 constexpr SkScalar L = 2.2f, T = 1.7f, R = 5.8f, B = 7.3f; // Generic rect.
1277 constexpr SkScalar l = 3.3f, t = 2.8f, r = 4.7f, b = 6.2f; // Small rect contained in R.
1278
1279 SkRect alignedRect = {IL, IT, IR, IB};
1280 SkRect rect = {L, T, R, B};
1281 SkRect innerRect = {l, t, r, b};
1282
1283 SkMatrix m;
1284 m.setIdentity();
1285
1286 constexpr SkScalar kMinScale = 2.0001f;
1287 constexpr SkScalar kMaxScale = 3;
1288 constexpr int kNumIters = 8;
1289
1290 SkString name;
1291 SkRandom rand;
1292
1293 for (int i = 0; i < kNumIters; ++i) {
1294 // Pixel-aligned rect (iior=true).
1295 name.printf("Pixel-aligned rect test, iter %i", i);
1296 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001297 stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001298 test_aa_query(reporter, name, stack, m, {IL, IT, IR, IB}, ClipMethod::kIgnoreClip);
1299 test_aa_query(reporter, name, stack, m, {IL, IT-1, IR, IT}, ClipMethod::kSkipDraw);
csmartdalton77f2fae2016-08-08 09:55:06 -07001300 test_aa_query(reporter, name, stack, m, {IL, IT-2, IR, IB}, ClipMethod::kScissor);
csmartdaltoncbecb082016-07-22 08:59:08 -07001301
1302 // Rect (iior=true).
1303 name.printf("Rect test, iter %i", i);
1304 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001305 stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001306 test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kIgnoreClip);
1307 test_aa_query(reporter, name, stack, m, {L-.1f, T, L, B}, ClipMethod::kSkipDraw);
1308 test_aa_query(reporter, name, stack, m, {L-.1f, T, L+.1f, B}, ClipMethod::kAAElements, 1);
1309
1310 // Difference rect (iior=false, inside-out bounds).
1311 name.printf("Difference rect test, iter %i", i);
1312 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001313 stack.clipRect(rect, SkMatrix::I(), kDifference_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001314 test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kSkipDraw);
1315 test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T}, ClipMethod::kIgnoreClip);
1316 test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T+.1f}, ClipMethod::kAAElements, 1);
1317
1318 // Complex clip (iior=false, normal bounds).
1319 name.printf("Complex clip test, iter %i", i);
1320 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001321 stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true);
1322 stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001323 test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
1324 test_aa_query(reporter, name, stack, m, {r-.1f, t, R, b}, ClipMethod::kAAElements, 1);
1325 test_aa_query(reporter, name, stack, m, {r-.1f, t, R+.1f, b}, ClipMethod::kAAElements, 2);
1326 test_aa_query(reporter, name, stack, m, {r, t, R+.1f, b}, ClipMethod::kAAElements, 1);
1327 test_aa_query(reporter, name, stack, m, {r, t, R, b}, ClipMethod::kIgnoreClip);
1328 test_aa_query(reporter, name, stack, m, {R, T, R+.1f, B}, ClipMethod::kSkipDraw);
1329
1330 // Complex clip where outer rect is pixel aligned (iior=false, normal bounds).
1331 name.printf("Aligned Complex clip test, iter %i", i);
1332 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001333 stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true);
1334 stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001335 test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
1336 test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB}, ClipMethod::kAAElements, 1);
1337 test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB+.1f}, ClipMethod::kAAElements, 1);
1338 test_aa_query(reporter, name, stack, m, {l, b, r, IB+.1f}, ClipMethod::kAAElements, 0);
1339 test_aa_query(reporter, name, stack, m, {l, b, r, IB}, ClipMethod::kIgnoreClip);
1340 test_aa_query(reporter, name, stack, m, {IL, IB, IR, IB+.1f}, ClipMethod::kSkipDraw);
1341
1342 // Apply random transforms and try again. This ensures the clip stack reduction is hardened
1343 // against FP rounding error.
1344 SkScalar sx = rand.nextRangeScalar(kMinScale, kMaxScale);
1345 sx = SkScalarFloorToScalar(sx * alignedRect.width()) / alignedRect.width();
1346 SkScalar sy = rand.nextRangeScalar(kMinScale, kMaxScale);
1347 sy = SkScalarFloorToScalar(sy * alignedRect.height()) / alignedRect.height();
1348 SkScalar tx = SkScalarRoundToScalar(sx * alignedRect.x()) - sx * alignedRect.x();
1349 SkScalar ty = SkScalarRoundToScalar(sy * alignedRect.y()) - sy * alignedRect.y();
1350
1351 SkMatrix xform = SkMatrix::MakeScale(sx, sy);
1352 xform.postTranslate(tx, ty);
1353 xform.mapRect(&alignedRect);
1354 xform.mapRect(&rect);
1355 xform.mapRect(&innerRect);
1356 m.postConcat(xform);
1357 }
1358}
1359
bsalomon@google.coma4e13c82012-11-26 21:38:37 +00001360#endif
bsalomon@google.com51a62862012-11-26 21:19:43 +00001361
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00001362DEF_TEST(ClipStack, reporter) {
reed@google.combdee9fc2011-02-22 20:17:43 +00001363 SkClipStack stack;
1364
robertphillips@google.com80214e22012-07-20 15:33:18 +00001365 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001366 assert_count(reporter, stack, 0);
1367
1368 static const SkIRect gRects[] = {
1369 { 0, 0, 100, 100 },
1370 { 25, 25, 125, 125 },
1371 { 0, 0, 1000, 1000 },
1372 { 0, 0, 75, 75 }
1373 };
1374 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
Mike Reedc1f77742016-12-09 09:00:50 -05001375 stack.clipDevRect(gRects[i], kIntersect_SkClipOp);
reed@google.combdee9fc2011-02-22 20:17:43 +00001376 }
1377
1378 // all of the above rects should have been intersected, leaving only 1 rect
robertphillips@google.com80214e22012-07-20 15:33:18 +00001379 SkClipStack::B2TIter iter(stack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001380 const SkClipStack::Element* element = iter.next();
epoger@google.com2047f002011-05-17 17:36:59 +00001381 SkRect answer;
1382 answer.iset(25, 25, 75, 75);
reed@google.combdee9fc2011-02-22 20:17:43 +00001383
bsalomon49f085d2014-09-05 13:34:00 -07001384 REPORTER_ASSERT(reporter, element);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001385 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
Mike Reedc1f77742016-12-09 09:00:50 -05001386 REPORTER_ASSERT(reporter, kIntersect_SkClipOp == element->getOp());
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001387 REPORTER_ASSERT(reporter, element->getRect() == answer);
reed@google.combdee9fc2011-02-22 20:17:43 +00001388 // now check that we only had one in our iterator
1389 REPORTER_ASSERT(reporter, !iter.next());
1390
1391 stack.reset();
robertphillips@google.com80214e22012-07-20 15:33:18 +00001392 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001393 assert_count(reporter, stack, 0);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +00001394
1395 test_assign_and_comparison(reporter);
robertphillips@google.com80214e22012-07-20 15:33:18 +00001396 test_iterators(reporter);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001397 test_bounds(reporter, SkClipStack::Element::kRect_Type);
1398 test_bounds(reporter, SkClipStack::Element::kRRect_Type);
1399 test_bounds(reporter, SkClipStack::Element::kPath_Type);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +00001400 test_isWideOpen(reporter);
robertphillips@google.com08eacc12012-08-02 12:49:00 +00001401 test_rect_merging(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001402 test_rect_replace(reporter);
junov@chromium.orgedf32d52012-12-10 14:57:54 +00001403 test_rect_inverse_fill(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001404 test_path_replace(reporter);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +00001405 test_quickContains(reporter);
csmartdaltond50e2402016-07-22 08:39:06 -07001406 test_invfill_diff_bug(reporter);
bsalomon@google.come7b3d292012-11-26 21:42:32 +00001407#if SK_SUPPORT_GPU
bsalomon@google.comedb26fd2012-11-28 14:42:41 +00001408 test_reduced_clip_stack(reporter);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001409 test_reduced_clip_stack_genid(reporter);
1410 test_reduced_clip_stack_no_aa_crash(reporter);
csmartdaltoncbecb082016-07-22 08:59:08 -07001411 test_reduced_clip_stack_aa(reporter);
bsalomon@google.come7b3d292012-11-26 21:42:32 +00001412#endif
reed@google.combdee9fc2011-02-22 20:17:43 +00001413}
Brian Salomon19f0ed52017-01-06 13:54:58 -05001414
1415//////////////////////////////////////////////////////////////////////////////
1416
1417#if SK_SUPPORT_GPU
Robert Phillips875218e2017-02-24 08:37:13 -05001418sk_sp<GrTextureProxy> GrClipStackClip::testingOnly_createClipMask(GrContext* context) const {
Brian Salomon19f0ed52017-01-06 13:54:58 -05001419 const GrReducedClip reducedClip(*fStack, SkRect::MakeWH(512, 512), 0);
1420 return this->createSoftwareClipMask(context, reducedClip);
1421}
1422
1423// Verify that clip masks are freed up when the clip state that generated them goes away.
1424DEF_GPUTEST_FOR_ALL_CONTEXTS(ClipMaskCache, reporter, ctxInfo) {
1425 // This test uses resource key tags which only function in debug builds.
1426#ifdef SK_DEBUG
1427 GrContext* context = ctxInfo.grContext();
1428 SkClipStack stack;
1429
1430 SkPath path;
1431 path.addCircle(10, 10, 8);
1432 path.addCircle(15, 15, 8);
1433 path.setFillType(SkPath::kEvenOdd_FillType);
1434
1435 static const char* kTag = GrClipStackClip::kMaskTestTag;
1436 GrResourceCache* cache = context->getResourceCache();
1437
1438 static constexpr int kN = 5;
1439
1440 for (int i = 0; i < kN; ++i) {
1441 SkMatrix m;
1442 m.setTranslate(0.5, 0.5);
1443 stack.save();
1444 stack.clipPath(path, m, SkClipOp::kIntersect, true);
Robert Phillips875218e2017-02-24 08:37:13 -05001445 sk_sp<GrTextureProxy> mask = GrClipStackClip(&stack).testingOnly_createClipMask(context);
Robert Phillipseee4d6e2017-06-05 09:26:07 -04001446 mask->instantiate(context->resourceProvider());
1447 GrTexture* tex = mask->priv().peekTexture();
Robert Phillips875218e2017-02-24 08:37:13 -05001448 REPORTER_ASSERT(reporter, 0 == strcmp(tex->getUniqueKey().tag(), kTag));
Brian Salomon19f0ed52017-01-06 13:54:58 -05001449 // Make sure mask isn't pinned in cache.
1450 mask.reset(nullptr);
1451 context->flush();
1452 REPORTER_ASSERT(reporter, i + 1 == cache->countUniqueKeysWithTag(kTag));
1453 }
1454
1455 for (int i = 0; i < kN; ++i) {
1456 stack.restore();
1457 cache->purgeAsNeeded();
1458 REPORTER_ASSERT(reporter, kN - (i + 1) == cache->countUniqueKeysWithTag(kTag));
1459 }
1460#endif
1461}
1462
Mike Reed3726a4a2017-01-19 11:36:41 -05001463#include "SkSurface.h"
1464DEF_GPUTEST_FOR_ALL_CONTEXTS(canvas_private_clipRgn, reporter, ctxInfo) {
1465 GrContext* context = ctxInfo.grContext();
1466
1467 const int w = 10;
1468 const int h = 10;
1469 SkImageInfo info = SkImageInfo::MakeN32Premul(w, h);
1470 sk_sp<SkSurface> surf = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info);
1471 SkCanvas* canvas = surf->getCanvas();
1472 SkRegion rgn;
1473
1474 canvas->temporary_internal_getRgnClip(&rgn);
1475 REPORTER_ASSERT(reporter, rgn.isRect());
1476 REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeWH(w, h));
1477
1478 canvas->save();
1479 canvas->clipRect(SkRect::MakeWH(5, 5), kDifference_SkClipOp);
1480 canvas->temporary_internal_getRgnClip(&rgn);
1481 REPORTER_ASSERT(reporter, rgn.isComplex());
1482 REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeWH(w, h));
1483 canvas->restore();
1484
1485 canvas->save();
1486 canvas->clipRRect(SkRRect::MakeOval(SkRect::MakeLTRB(3, 3, 7, 7)));
1487 canvas->temporary_internal_getRgnClip(&rgn);
1488 REPORTER_ASSERT(reporter, rgn.isComplex());
1489 REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeLTRB(3, 3, 7, 7));
1490 canvas->restore();
1491}
Brian Salomon19f0ed52017-01-06 13:54:58 -05001492#endif