blob: 31dffcb6c201afa2faa3af790b2edc33c56b3540 [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"
Mike Reed84dd8572017-03-08 22:21:00 -050019#include "GrTextureProxy.h"
csmartdaltoncbecb082016-07-22 08:59:08 -070020typedef GrReducedClip::ElementList ElementList;
21typedef GrReducedClip::InitialState InitialState;
22#endif
23
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000024static void test_assign_and_comparison(skiatest::Reporter* reporter) {
25 SkClipStack s;
reed@google.comd9f2dea2011-10-12 14:43:27 +000026 bool doAA = false;
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000027
robertphillips@google.com80214e22012-07-20 15:33:18 +000028 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
29
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000030 // Build up a clip stack with a path, an empty clip, and a rect.
31 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000032 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
33
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000034 SkPath p;
35 p.moveTo(5, 6);
36 p.lineTo(7, 8);
37 p.lineTo(5, 9);
38 p.close();
Mike Reedc1f77742016-12-09 09:00:50 -050039 s.clipPath(p, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000040
41 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000042 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
43
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000044 SkRect r = SkRect::MakeLTRB(1, 2, 3, 4);
Mike Reedc1f77742016-12-09 09:00:50 -050045 s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000046 r = SkRect::MakeLTRB(10, 11, 12, 13);
Mike Reedc1f77742016-12-09 09:00:50 -050047 s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000048
49 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000050 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
51
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000052 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -050053 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000054
55 // Test that assignment works.
56 SkClipStack copy = s;
57 REPORTER_ASSERT(reporter, s == copy);
58
59 // Test that different save levels triggers not equal.
60 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000061 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000062 REPORTER_ASSERT(reporter, s != copy);
63
64 // Test that an equal, but not copied version is equal.
65 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000066 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000067 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -050068 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000069 REPORTER_ASSERT(reporter, s == copy);
70
71 // Test that a different op on one level triggers not equal.
72 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000073 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000074 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000075 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000076 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -050077 s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000078 REPORTER_ASSERT(reporter, s != copy);
79
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000080 // Test that version constructed with rect-path rather than a rect is still considered equal.
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000081 s.restore();
82 s.save();
83 SkPath rp;
84 rp.addRect(r);
Mike Reedc1f77742016-12-09 09:00:50 -050085 s.clipPath(rp, SkMatrix::I(), kUnion_SkClipOp, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000086 REPORTER_ASSERT(reporter, s == copy);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000087
88 // Test that different rects triggers not equal.
89 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000090 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000091 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000092 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
93
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000094 r = SkRect::MakeLTRB(24, 25, 26, 27);
Mike Reedc1f77742016-12-09 09:00:50 -050095 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000096 REPORTER_ASSERT(reporter, s != copy);
97
98 // Sanity check
99 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000100 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
101
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000102 copy.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000103 REPORTER_ASSERT(reporter, 2 == copy.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000104 REPORTER_ASSERT(reporter, s == copy);
105 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000106 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000107 copy.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000108 REPORTER_ASSERT(reporter, 1 == copy.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000109 REPORTER_ASSERT(reporter, s == copy);
110
111 // Test that different paths triggers not equal.
112 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000113 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000114 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000115 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
116
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000117 p.addRect(r);
Mike Reedc1f77742016-12-09 09:00:50 -0500118 s.clipPath(p, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000119 REPORTER_ASSERT(reporter, s != copy);
120}
reed@google.combdee9fc2011-02-22 20:17:43 +0000121
122static void assert_count(skiatest::Reporter* reporter, const SkClipStack& stack,
123 int count) {
robertphillips@google.com80214e22012-07-20 15:33:18 +0000124 SkClipStack::B2TIter iter(stack);
reed@google.combdee9fc2011-02-22 20:17:43 +0000125 int counter = 0;
126 while (iter.next()) {
127 counter += 1;
128 }
129 REPORTER_ASSERT(reporter, count == counter);
130}
131
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000132// Exercise the SkClipStack's bottom to top and bidirectional iterators
133// (including the skipToTopmost functionality)
robertphillips@google.com80214e22012-07-20 15:33:18 +0000134static void test_iterators(skiatest::Reporter* reporter) {
135 SkClipStack stack;
136
137 static const SkRect gRects[] = {
138 { 0, 0, 40, 40 },
139 { 60, 0, 100, 40 },
140 { 0, 60, 40, 100 },
141 { 60, 60, 100, 100 }
142 };
143
144 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
145 // the union op will prevent these from being fused together
Mike Reedc1f77742016-12-09 09:00:50 -0500146 stack.clipRect(gRects[i], SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000147 }
148
149 assert_count(reporter, stack, 4);
150
151 // bottom to top iteration
152 {
halcanary96fcdcc2015-08-27 07:41:13 -0700153 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000154
155 SkClipStack::B2TIter iter(stack);
156 int i;
157
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000158 for (i = 0, element = iter.next(); element; ++i, element = iter.next()) {
159 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
160 REPORTER_ASSERT(reporter, element->getRect() == gRects[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000161 }
162
163 SkASSERT(i == 4);
164 }
165
166 // top to bottom iteration
167 {
halcanary96fcdcc2015-08-27 07:41:13 -0700168 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000169
170 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
171 int i;
172
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000173 for (i = 3, element = iter.prev(); element; --i, element = iter.prev()) {
174 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
175 REPORTER_ASSERT(reporter, element->getRect() == gRects[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000176 }
177
178 SkASSERT(i == -1);
179 }
180
181 // skipToTopmost
182 {
halcanary96fcdcc2015-08-27 07:41:13 -0700183 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000184
185 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
186
Mike Reedc1f77742016-12-09 09:00:50 -0500187 element = iter.skipToTopmost(kUnion_SkClipOp);
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000188 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
189 REPORTER_ASSERT(reporter, element->getRect() == gRects[3]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000190 }
191}
192
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000193// Exercise the SkClipStack's getConservativeBounds computation
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000194static void test_bounds(skiatest::Reporter* reporter, SkClipStack::Element::Type primType) {
robertphillips@google.com607fe072012-07-24 13:54:00 +0000195 static const int gNumCases = 20;
196 static const SkRect gAnswerRectsBW[gNumCases] = {
197 // A op B
198 { 40, 40, 50, 50 },
199 { 10, 10, 50, 50 },
200 { 10, 10, 80, 80 },
201 { 10, 10, 80, 80 },
202 { 40, 40, 80, 80 },
203
204 // invA op B
205 { 40, 40, 80, 80 },
206 { 0, 0, 100, 100 },
207 { 0, 0, 100, 100 },
208 { 0, 0, 100, 100 },
209 { 40, 40, 50, 50 },
210
211 // A op invB
212 { 10, 10, 50, 50 },
213 { 40, 40, 50, 50 },
214 { 0, 0, 100, 100 },
215 { 0, 0, 100, 100 },
216 { 0, 0, 100, 100 },
217
218 // invA op invB
219 { 0, 0, 100, 100 },
220 { 40, 40, 80, 80 },
221 { 0, 0, 100, 100 },
222 { 10, 10, 80, 80 },
223 { 10, 10, 50, 50 },
224 };
225
Mike Reedc1f77742016-12-09 09:00:50 -0500226 static const SkClipOp gOps[] = {
227 kIntersect_SkClipOp,
228 kDifference_SkClipOp,
229 kUnion_SkClipOp,
230 kXOR_SkClipOp,
231 kReverseDifference_SkClipOp
robertphillips@google.com607fe072012-07-24 13:54:00 +0000232 };
233
234 SkRect rectA, rectB;
235
236 rectA.iset(10, 10, 50, 50);
237 rectB.iset(40, 40, 80, 80);
238
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000239 SkRRect rrectA, rrectB;
240 rrectA.setOval(rectA);
241 rrectB.setRectXY(rectB, SkIntToScalar(1), SkIntToScalar(2));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000242
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000243 SkPath pathA, pathB;
244
245 pathA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
246 pathB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000247
248 SkClipStack stack;
robertphillips@google.com7b112892012-07-31 15:18:21 +0000249 SkRect devClipBound;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000250 bool isIntersectionOfRects = false;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000251
252 int testCase = 0;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000253 int numBitTests = SkClipStack::Element::kPath_Type == primType ? 4 : 1;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000254 for (int invBits = 0; invBits < numBitTests; ++invBits) {
robertphillips@google.com607fe072012-07-24 13:54:00 +0000255 for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) {
256
257 stack.save();
258 bool doInvA = SkToBool(invBits & 1);
259 bool doInvB = SkToBool(invBits & 2);
260
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000261 pathA.setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType :
robertphillips@google.com607fe072012-07-24 13:54:00 +0000262 SkPath::kEvenOdd_FillType);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000263 pathB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType :
robertphillips@google.com607fe072012-07-24 13:54:00 +0000264 SkPath::kEvenOdd_FillType);
265
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000266 switch (primType) {
267 case SkClipStack::Element::kEmpty_Type:
268 SkDEBUGFAIL("Don't call this with kEmpty.");
269 break;
270 case SkClipStack::Element::kRect_Type:
Mike Reedc1f77742016-12-09 09:00:50 -0500271 stack.clipRect(rectA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400272 stack.clipRect(rectB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000273 break;
274 case SkClipStack::Element::kRRect_Type:
Mike Reedc1f77742016-12-09 09:00:50 -0500275 stack.clipRRect(rrectA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400276 stack.clipRRect(rrectB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000277 break;
278 case SkClipStack::Element::kPath_Type:
Mike Reedc1f77742016-12-09 09:00:50 -0500279 stack.clipPath(pathA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400280 stack.clipPath(pathB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000281 break;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000282 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000283
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000284 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000285 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000286
robertphillips@google.com7b112892012-07-31 15:18:21 +0000287 stack.getConservativeBounds(0, 0, 100, 100, &devClipBound,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000288 &isIntersectionOfRects);
289
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000290 if (SkClipStack::Element::kRect_Type == primType) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000291 REPORTER_ASSERT(reporter, isIntersectionOfRects ==
Mike Reedc1f77742016-12-09 09:00:50 -0500292 (gOps[op] == kIntersect_SkClipOp));
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000293 } else {
294 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
295 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000296
297 SkASSERT(testCase < gNumCases);
robertphillips@google.com7b112892012-07-31 15:18:21 +0000298 REPORTER_ASSERT(reporter, devClipBound == gAnswerRectsBW[testCase]);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000299 ++testCase;
300
301 stack.restore();
302 }
303 }
304}
305
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000306// Test out 'isWideOpen' entry point
307static void test_isWideOpen(skiatest::Reporter* reporter) {
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000308 {
309 // Empty stack is wide open. Wide open stack means that gen id is wide open.
310 SkClipStack stack;
311 REPORTER_ASSERT(reporter, stack.isWideOpen());
312 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
313 }
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000314
315 SkRect rectA, rectB;
316
317 rectA.iset(10, 10, 40, 40);
318 rectB.iset(50, 50, 80, 80);
319
320 // Stack should initially be wide open
321 {
322 SkClipStack stack;
323
324 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000325 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000326 }
327
328 // Test out case where the user specifies a union that includes everything
329 {
330 SkClipStack stack;
331
332 SkPath clipA, clipB;
333
334 clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
335 clipA.setFillType(SkPath::kInverseEvenOdd_FillType);
336
337 clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
338 clipB.setFillType(SkPath::kInverseEvenOdd_FillType);
339
Mike Reedc1f77742016-12-09 09:00:50 -0500340 stack.clipPath(clipA, SkMatrix::I(), kReplace_SkClipOp, false);
341 stack.clipPath(clipB, SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000342
343 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000344 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000345 }
346
347 // Test out union w/ a wide open clip
348 {
349 SkClipStack stack;
350
Mike Reedc1f77742016-12-09 09:00:50 -0500351 stack.clipRect(rectA, SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000352
353 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000354 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000355 }
356
357 // Test out empty difference from a wide open clip
358 {
359 SkClipStack stack;
360
361 SkRect emptyRect;
362 emptyRect.setEmpty();
363
Mike Reedc1f77742016-12-09 09:00:50 -0500364 stack.clipRect(emptyRect, SkMatrix::I(), kDifference_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000365
366 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000367 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000368 }
369
370 // Test out return to wide open
371 {
372 SkClipStack stack;
373
374 stack.save();
375
Mike Reedc1f77742016-12-09 09:00:50 -0500376 stack.clipRect(rectA, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000377
378 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000379 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000380
381 stack.restore();
382
383 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000384 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000385 }
386}
387
bsalomon@google.com100abf42012-09-05 17:40:04 +0000388static int count(const SkClipStack& stack) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000389
390 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
391
halcanary96fcdcc2015-08-27 07:41:13 -0700392 const SkClipStack::Element* element = nullptr;
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000393 int count = 0;
394
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000395 for (element = iter.prev(); element; element = iter.prev(), ++count) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000396 ;
397 }
398
399 return count;
400}
401
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000402static void test_rect_inverse_fill(skiatest::Reporter* reporter) {
403 // non-intersecting rectangles
404 SkRect rect = SkRect::MakeLTRB(0, 0, 10, 10);
405
406 SkPath path;
407 path.addRect(rect);
408 path.toggleInverseFillType();
409 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500410 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000411
412 SkRect bounds;
413 SkClipStack::BoundsType boundsType;
414 stack.getBounds(&bounds, &boundsType);
415 REPORTER_ASSERT(reporter, SkClipStack::kInsideOut_BoundsType == boundsType);
416 REPORTER_ASSERT(reporter, bounds == rect);
417}
418
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000419static void test_rect_replace(skiatest::Reporter* reporter) {
420 SkRect rect = SkRect::MakeWH(100, 100);
421 SkRect rect2 = SkRect::MakeXYWH(50, 50, 100, 100);
422
423 SkRect bound;
424 SkClipStack::BoundsType type;
425 bool isIntersectionOfRects;
426
427 // Adding a new rect with the replace operator should not increase
428 // the stack depth. BW replacing BW.
429 {
430 SkClipStack stack;
431 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500432 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000433 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500434 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000435 REPORTER_ASSERT(reporter, 1 == count(stack));
436 }
437
438 // Adding a new rect with the replace operator should not increase
439 // the stack depth. AA replacing AA.
440 {
441 SkClipStack stack;
442 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500443 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000444 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500445 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000446 REPORTER_ASSERT(reporter, 1 == count(stack));
447 }
448
449 // Adding a new rect with the replace operator should not increase
450 // the stack depth. BW replacing AA replacing BW.
451 {
452 SkClipStack stack;
453 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500454 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000455 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500456 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000457 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500458 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000459 REPORTER_ASSERT(reporter, 1 == count(stack));
460 }
461
462 // Make sure replace clip rects don't collapse too much.
463 {
464 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500465 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
466 stack.clipRect(rect2, SkMatrix::I(), kIntersect_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000467 REPORTER_ASSERT(reporter, 1 == count(stack));
468
469 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500470 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000471 REPORTER_ASSERT(reporter, 2 == count(stack));
472 stack.getBounds(&bound, &type, &isIntersectionOfRects);
473 REPORTER_ASSERT(reporter, bound == rect);
474 stack.restore();
475 REPORTER_ASSERT(reporter, 1 == count(stack));
476
477 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500478 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
479 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000480 REPORTER_ASSERT(reporter, 2 == count(stack));
481 stack.restore();
482 REPORTER_ASSERT(reporter, 1 == count(stack));
483
484 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500485 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
486 stack.clipRect(rect2, SkMatrix::I(), kIntersect_SkClipOp, false);
487 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000488 REPORTER_ASSERT(reporter, 2 == count(stack));
489 stack.restore();
490 REPORTER_ASSERT(reporter, 1 == count(stack));
491 }
492}
493
494// Simplified path-based version of test_rect_replace.
495static void test_path_replace(skiatest::Reporter* reporter) {
496 SkRect rect = SkRect::MakeWH(100, 100);
497 SkPath path;
498 path.addCircle(50, 50, 50);
499
500 // Replace operation doesn't grow the stack.
501 {
502 SkClipStack stack;
503 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500504 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000505 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500506 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000507 REPORTER_ASSERT(reporter, 1 == count(stack));
508 }
509
510 // Replacing rect with path.
511 {
512 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500513 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000514 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500515 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000516 REPORTER_ASSERT(reporter, 1 == count(stack));
517 }
518}
519
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000520// Test out SkClipStack's merging of rect clips. In particular exercise
521// merging of aa vs. bw rects.
522static void test_rect_merging(skiatest::Reporter* reporter) {
523
524 SkRect overlapLeft = SkRect::MakeLTRB(10, 10, 50, 50);
525 SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80);
526
527 SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90);
528 SkRect nestedChild = SkRect::MakeLTRB(40, 40, 60, 60);
529
530 SkRect bound;
531 SkClipStack::BoundsType type;
532 bool isIntersectionOfRects;
533
534 // all bw overlapping - should merge
535 {
536 SkClipStack stack;
537
Mike Reedc1f77742016-12-09 09:00:50 -0500538 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000539
Mike Reedc1f77742016-12-09 09:00:50 -0500540 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000541
542 REPORTER_ASSERT(reporter, 1 == count(stack));
543
544 stack.getBounds(&bound, &type, &isIntersectionOfRects);
545
546 REPORTER_ASSERT(reporter, isIntersectionOfRects);
547 }
548
549 // all aa overlapping - should merge
550 {
551 SkClipStack stack;
552
Mike Reedc1f77742016-12-09 09:00:50 -0500553 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000554
Mike Reedc1f77742016-12-09 09:00:50 -0500555 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000556
557 REPORTER_ASSERT(reporter, 1 == count(stack));
558
559 stack.getBounds(&bound, &type, &isIntersectionOfRects);
560
561 REPORTER_ASSERT(reporter, isIntersectionOfRects);
562 }
563
564 // mixed overlapping - should _not_ merge
565 {
566 SkClipStack stack;
567
Mike Reedc1f77742016-12-09 09:00:50 -0500568 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000569
Mike Reedc1f77742016-12-09 09:00:50 -0500570 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000571
572 REPORTER_ASSERT(reporter, 2 == count(stack));
573
574 stack.getBounds(&bound, &type, &isIntersectionOfRects);
575
576 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
577 }
578
579 // mixed nested (bw inside aa) - should merge
580 {
581 SkClipStack stack;
582
Mike Reedc1f77742016-12-09 09:00:50 -0500583 stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000584
Mike Reedc1f77742016-12-09 09:00:50 -0500585 stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000586
587 REPORTER_ASSERT(reporter, 1 == count(stack));
588
589 stack.getBounds(&bound, &type, &isIntersectionOfRects);
590
591 REPORTER_ASSERT(reporter, isIntersectionOfRects);
592 }
593
594 // mixed nested (aa inside bw) - should merge
595 {
596 SkClipStack stack;
597
Mike Reedc1f77742016-12-09 09:00:50 -0500598 stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000599
Mike Reedc1f77742016-12-09 09:00:50 -0500600 stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000601
602 REPORTER_ASSERT(reporter, 1 == count(stack));
603
604 stack.getBounds(&bound, &type, &isIntersectionOfRects);
605
606 REPORTER_ASSERT(reporter, isIntersectionOfRects);
607 }
608
609 // reverse nested (aa inside bw) - should _not_ merge
610 {
611 SkClipStack stack;
612
Mike Reedc1f77742016-12-09 09:00:50 -0500613 stack.clipRect(nestedChild, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000614
Mike Reedc1f77742016-12-09 09:00:50 -0500615 stack.clipRect(nestedParent, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000616
617 REPORTER_ASSERT(reporter, 2 == count(stack));
618
619 stack.getBounds(&bound, &type, &isIntersectionOfRects);
620
621 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
622 }
623}
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000624
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000625static void test_quickContains(skiatest::Reporter* reporter) {
626 SkRect testRect = SkRect::MakeLTRB(10, 10, 40, 40);
627 SkRect insideRect = SkRect::MakeLTRB(20, 20, 30, 30);
628 SkRect intersectingRect = SkRect::MakeLTRB(25, 25, 50, 50);
629 SkRect outsideRect = SkRect::MakeLTRB(0, 0, 50, 50);
630 SkRect nonIntersectingRect = SkRect::MakeLTRB(100, 100, 110, 110);
631
632 SkPath insideCircle;
633 insideCircle.addCircle(25, 25, 5);
634 SkPath intersectingCircle;
635 intersectingCircle.addCircle(25, 40, 10);
636 SkPath outsideCircle;
637 outsideCircle.addCircle(25, 25, 50);
638 SkPath nonIntersectingCircle;
639 nonIntersectingCircle.addCircle(100, 100, 5);
640
641 {
642 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500643 stack.clipRect(outsideRect, SkMatrix::I(), kDifference_SkClipOp, false);
644 // return false because quickContains currently does not care for kDifference_SkClipOp
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000645 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
646 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000647
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000648 // Replace Op tests
649 {
650 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500651 stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000652 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
653 }
654
655 {
656 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500657 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000658 stack.save(); // To prevent in-place substitution by replace OP
Mike Reedc1f77742016-12-09 09:00:50 -0500659 stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000660 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
661 stack.restore();
662 }
663
664 {
665 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500666 stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000667 stack.save(); // To prevent in-place substitution by replace OP
Mike Reedc1f77742016-12-09 09:00:50 -0500668 stack.clipRect(insideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000669 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
670 stack.restore();
671 }
672
673 // Verify proper traversal of multi-element clip
674 {
675 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500676 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000677 // Use a path for second clip to prevent in-place intersection
Mike Reedc1f77742016-12-09 09:00:50 -0500678 stack.clipPath(outsideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000679 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
680 }
681
682 // Intersect Op tests with rectangles
683 {
684 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500685 stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000686 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
687 }
688
689 {
690 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500691 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000692 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
693 }
694
695 {
696 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500697 stack.clipRect(intersectingRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000698 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
699 }
700
701 {
702 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500703 stack.clipRect(nonIntersectingRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000704 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
705 }
706
707 // Intersect Op tests with circle paths
708 {
709 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500710 stack.clipPath(outsideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000711 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
712 }
713
714 {
715 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500716 stack.clipPath(insideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000717 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
718 }
719
720 {
721 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500722 stack.clipPath(intersectingCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000723 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
724 }
725
726 {
727 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500728 stack.clipPath(nonIntersectingCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000729 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
730 }
731
732 // Intersect Op tests with inverse filled rectangles
733 {
734 SkClipStack stack;
735 SkPath path;
736 path.addRect(outsideRect);
737 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500738 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000739 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
740 }
741
742 {
743 SkClipStack stack;
744 SkPath path;
745 path.addRect(insideRect);
746 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500747 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000748 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
749 }
750
751 {
752 SkClipStack stack;
753 SkPath path;
754 path.addRect(intersectingRect);
755 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500756 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000757 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
758 }
759
760 {
761 SkClipStack stack;
762 SkPath path;
763 path.addRect(nonIntersectingRect);
764 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500765 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000766 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
767 }
768
769 // Intersect Op tests with inverse filled circles
770 {
771 SkClipStack stack;
772 SkPath path = outsideCircle;
773 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500774 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000775 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
776 }
777
778 {
779 SkClipStack stack;
780 SkPath path = insideCircle;
781 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500782 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000783 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
784 }
785
786 {
787 SkClipStack stack;
788 SkPath path = intersectingCircle;
789 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500790 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000791 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
792 }
793
794 {
795 SkClipStack stack;
796 SkPath path = nonIntersectingCircle;
797 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500798 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000799 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
800 }
801}
802
csmartdaltond50e2402016-07-22 08:39:06 -0700803static void set_region_to_stack(const SkClipStack& stack, const SkIRect& bounds, SkRegion* region) {
804 region->setRect(bounds);
805 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
806 while (const SkClipStack::Element *element = iter.next()) {
807 SkRegion elemRegion;
808 SkRegion boundsRgn(bounds);
809 SkPath path;
810
811 switch (element->getType()) {
812 case SkClipStack::Element::kEmpty_Type:
813 elemRegion.setEmpty();
814 break;
815 default:
816 element->asPath(&path);
817 elemRegion.setPath(path, boundsRgn);
818 break;
819 }
reed73603f32016-09-20 08:42:38 -0700820 region->op(elemRegion, (SkRegion::Op)element->getOp());
csmartdaltond50e2402016-07-22 08:39:06 -0700821 }
822}
823
824static void test_invfill_diff_bug(skiatest::Reporter* reporter) {
825 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500826 stack.clipRect({10, 10, 20, 20}, SkMatrix::I(), kIntersect_SkClipOp, false);
csmartdaltond50e2402016-07-22 08:39:06 -0700827
828 SkPath path;
829 path.addRect({30, 10, 40, 20});
830 path.setFillType(SkPath::kInverseWinding_FillType);
Mike Reedc1f77742016-12-09 09:00:50 -0500831 stack.clipPath(path, SkMatrix::I(), kDifference_SkClipOp, false);
csmartdaltond50e2402016-07-22 08:39:06 -0700832
833 REPORTER_ASSERT(reporter, SkClipStack::kEmptyGenID == stack.getTopmostGenID());
834
835 SkRect stackBounds;
836 SkClipStack::BoundsType stackBoundsType;
837 stack.getBounds(&stackBounds, &stackBoundsType);
838
839 REPORTER_ASSERT(reporter, stackBounds.isEmpty());
840 REPORTER_ASSERT(reporter, SkClipStack::kNormal_BoundsType == stackBoundsType);
841
842 SkRegion region;
843 set_region_to_stack(stack, {0, 0, 50, 30}, &region);
844
845 REPORTER_ASSERT(reporter, region.isEmpty());
846}
847
bsalomon@google.com51a62862012-11-26 21:19:43 +0000848///////////////////////////////////////////////////////////////////////////////////////////////////
849
bsalomon@google.coma4e13c82012-11-26 21:38:37 +0000850#if SK_SUPPORT_GPU
bsalomon@google.com705e8402012-11-27 15:43:57 +0000851// Functions that add a shape to the clip stack. The shape is computed from a rectangle.
852// AA is always disabled since the clip stack reducer can cause changes in aa rasterization of the
853// stack. A fractional edge repeated in different elements may be rasterized fewer times using the
854// reduced stack.
855typedef void (*AddElementFunc) (const SkRect& rect,
856 bool invert,
Mike Reedc1f77742016-12-09 09:00:50 -0500857 SkClipOp op,
csmartdaltoncbecb082016-07-22 08:59:08 -0700858 SkClipStack* stack,
859 bool doAA);
bsalomon@google.coma4e13c82012-11-26 21:38:37 +0000860
Mike Reedc1f77742016-12-09 09:00:50 -0500861static void add_round_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700862 bool doAA) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000863 SkScalar rx = rect.width() / 10;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000864 SkScalar ry = rect.height() / 20;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000865 if (invert) {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000866 SkPath path;
867 path.addRoundRect(rect, rx, ry);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000868 path.setFillType(SkPath::kInverseWinding_FillType);
Brian Salomona3b45d42016-10-03 11:36:16 -0400869 stack->clipPath(path, SkMatrix::I(), op, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000870 } else {
871 SkRRect rrect;
872 rrect.setRectXY(rect, rx, ry);
Brian Salomona3b45d42016-10-03 11:36:16 -0400873 stack->clipRRect(rrect, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000874 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000875};
876
Mike Reedc1f77742016-12-09 09:00:50 -0500877static void add_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700878 bool doAA) {
bsalomon@google.com705e8402012-11-27 15:43:57 +0000879 if (invert) {
880 SkPath path;
881 path.addRect(rect);
882 path.setFillType(SkPath::kInverseWinding_FillType);
Brian Salomona3b45d42016-10-03 11:36:16 -0400883 stack->clipPath(path, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000884 } else {
Brian Salomona3b45d42016-10-03 11:36:16 -0400885 stack->clipRect(rect, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000886 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000887};
888
Mike Reedc1f77742016-12-09 09:00:50 -0500889static void add_oval(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700890 bool doAA) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000891 SkPath path;
892 path.addOval(rect);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000893 if (invert) {
894 path.setFillType(SkPath::kInverseWinding_FillType);
895 }
Brian Salomona3b45d42016-10-03 11:36:16 -0400896 stack->clipPath(path, SkMatrix::I(), op, doAA);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000897};
898
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000899static void add_elem_to_stack(const SkClipStack::Element& element, SkClipStack* stack) {
900 switch (element.getType()) {
901 case SkClipStack::Element::kRect_Type:
Brian Salomona3b45d42016-10-03 11:36:16 -0400902 stack->clipRect(element.getRect(), SkMatrix::I(), element.getOp(), element.isAA());
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000903 break;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000904 case SkClipStack::Element::kRRect_Type:
Brian Salomona3b45d42016-10-03 11:36:16 -0400905 stack->clipRRect(element.getRRect(), SkMatrix::I(), element.getOp(), element.isAA());
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000906 break;
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000907 case SkClipStack::Element::kPath_Type:
Brian Salomona3b45d42016-10-03 11:36:16 -0400908 stack->clipPath(element.getPath(), SkMatrix::I(), element.getOp(), element.isAA());
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000909 break;
910 case SkClipStack::Element::kEmpty_Type:
911 SkDEBUGFAIL("Why did the reducer produce an explicit empty.");
912 stack->clipEmpty();
913 break;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000914 }
915}
916
bsalomon@google.com51a62862012-11-26 21:19:43 +0000917static void test_reduced_clip_stack(skiatest::Reporter* reporter) {
918 // We construct random clip stacks, reduce them, and then rasterize both versions to verify that
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000919 // they are equal.
bsalomon@google.com51a62862012-11-26 21:19:43 +0000920
921 // All the clip elements will be contained within these bounds.
csmartdaltond211e782016-08-15 11:17:19 -0700922 static const SkIRect kIBounds = SkIRect::MakeWH(100, 100);
923 static const SkRect kBounds = SkRect::Make(kIBounds);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000924
925 enum {
csmartdaltoncbecb082016-07-22 08:59:08 -0700926 kNumTests = 250,
bsalomon@google.com51a62862012-11-26 21:19:43 +0000927 kMinElemsPerTest = 1,
928 kMaxElemsPerTest = 50,
929 };
930
931 // min/max size of a clip element as a fraction of kBounds.
932 static const SkScalar kMinElemSizeFrac = SK_Scalar1 / 5;
933 static const SkScalar kMaxElemSizeFrac = SK_Scalar1;
934
Mike Reedc1f77742016-12-09 09:00:50 -0500935 static const SkClipOp kOps[] = {
936 kDifference_SkClipOp,
937 kIntersect_SkClipOp,
938 kUnion_SkClipOp,
939 kXOR_SkClipOp,
940 kReverseDifference_SkClipOp,
941 kReplace_SkClipOp,
bsalomon@google.com51a62862012-11-26 21:19:43 +0000942 };
943
944 // Replace operations short-circuit the optimizer. We want to make sure that we test this code
945 // path a little bit but we don't want it to prevent us from testing many longer traversals in
946 // the optimizer.
947 static const int kReplaceDiv = 4 * kMaxElemsPerTest;
948
bsalomon@google.com705e8402012-11-27 15:43:57 +0000949 // We want to test inverse fills. However, they are quite rare in practice so don't over do it.
950 static const SkScalar kFractionInverted = SK_Scalar1 / kMaxElemsPerTest;
951
csmartdaltoncbecb082016-07-22 08:59:08 -0700952 static const SkScalar kFractionAntialiased = 0.25;
953
bsalomon@google.com51a62862012-11-26 21:19:43 +0000954 static const AddElementFunc kElementFuncs[] = {
955 add_rect,
956 add_round_rect,
957 add_oval,
958 };
959
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000960 SkRandom r;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000961
962 for (int i = 0; i < kNumTests; ++i) {
csmartdaltoncbecb082016-07-22 08:59:08 -0700963 SkString testCase;
964 testCase.printf("Iteration %d", i);
965
bsalomon@google.com51a62862012-11-26 21:19:43 +0000966 // Randomly generate a clip stack.
967 SkClipStack stack;
968 int numElems = r.nextRangeU(kMinElemsPerTest, kMaxElemsPerTest);
csmartdaltoncbecb082016-07-22 08:59:08 -0700969 bool doAA = r.nextBiasedBool(kFractionAntialiased);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000970 for (int e = 0; e < numElems; ++e) {
Mike Reedc1f77742016-12-09 09:00:50 -0500971 SkClipOp op = kOps[r.nextULessThan(SK_ARRAY_COUNT(kOps))];
972 if (op == kReplace_SkClipOp) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000973 if (r.nextU() % kReplaceDiv) {
974 --e;
975 continue;
976 }
977 }
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000978
bsalomon@google.com51a62862012-11-26 21:19:43 +0000979 // saves can change the clip stack behavior when an element is added.
980 bool doSave = r.nextBool();
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000981
bsalomon@google.com51a62862012-11-26 21:19:43 +0000982 SkSize size = SkSize::Make(
Mike Reeddf85c382017-02-14 10:59:19 -0500983 kBounds.width() * r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac),
984 kBounds.height() * r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac));
bsalomon@google.com51a62862012-11-26 21:19:43 +0000985
csmartdaltoncbecb082016-07-22 08:59:08 -0700986 SkPoint xy = {r.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth),
987 r.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight)};
bsalomon@google.com51a62862012-11-26 21:19:43 +0000988
csmartdaltoncbecb082016-07-22 08:59:08 -0700989 SkRect rect;
990 if (doAA) {
991 rect.setXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight);
992 if (GrClip::IsPixelAligned(rect)) {
993 // Don't create an element that may accidentally become not antialiased.
994 rect.outset(0.5f, 0.5f);
995 }
996 SkASSERT(!GrClip::IsPixelAligned(rect));
997 } else {
998 rect.setXYWH(SkScalarFloorToScalar(xy.fX),
999 SkScalarFloorToScalar(xy.fY),
1000 SkScalarCeilToScalar(size.fWidth),
1001 SkScalarCeilToScalar(size.fHeight));
1002 }
bsalomon@google.com51a62862012-11-26 21:19:43 +00001003
bsalomon@google.com705e8402012-11-27 15:43:57 +00001004 bool invert = r.nextBiasedBool(kFractionInverted);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001005
csmartdaltoncbecb082016-07-22 08:59:08 -07001006 kElementFuncs[r.nextULessThan(SK_ARRAY_COUNT(kElementFuncs))](rect, invert, op, &stack,
1007 doAA);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001008 if (doSave) {
1009 stack.save();
1010 }
1011 }
1012
csmartdalton8d3f92a2016-08-17 09:39:38 -07001013 // Zero the memory we will new the GrReducedClip into. This ensures the elements gen ID
1014 // will be kInvalidGenID if left uninitialized.
1015 SkAlignedSTStorage<1, GrReducedClip> storage;
1016 memset(storage.get(), 0, sizeof(GrReducedClip));
1017 GR_STATIC_ASSERT(0 == SkClipStack::kInvalidGenID);
1018
csmartdalton77f2fae2016-08-08 09:55:06 -07001019 // Get the reduced version of the stack.
csmartdaltoncbecb082016-07-22 08:59:08 -07001020 SkRect queryBounds = kBounds;
1021 queryBounds.outset(kBounds.width() / 2, kBounds.height() / 2);
csmartdalton8d3f92a2016-08-17 09:39:38 -07001022 const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, queryBounds);
bsalomon@google.coma4444302012-12-04 15:22:12 +00001023
csmartdalton8d3f92a2016-08-17 09:39:38 -07001024 REPORTER_ASSERT_MESSAGE(reporter,
1025 reduced->elements().isEmpty() ||
1026 SkClipStack::kInvalidGenID != reduced->elementsGenID(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001027 testCase.c_str());
1028
csmartdalton8d3f92a2016-08-17 09:39:38 -07001029 if (!reduced->elements().isEmpty()) {
1030 REPORTER_ASSERT_MESSAGE(reporter, reduced->hasIBounds(), testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001031 SkRect stackBounds;
1032 SkClipStack::BoundsType stackBoundsType;
1033 stack.getBounds(&stackBounds, &stackBoundsType);
1034 if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
1035 // Unless GrReducedClip starts doing some heroic tightening of the clip bounds, this
1036 // will be true since the stack bounds are completely contained inside the query.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001037 REPORTER_ASSERT_MESSAGE(reporter,
1038 GrClip::IsInsideClip(reduced->ibounds(), stackBounds),
csmartdaltoncbecb082016-07-22 08:59:08 -07001039 testCase.c_str());
1040 }
csmartdalton8d3f92a2016-08-17 09:39:38 -07001041 REPORTER_ASSERT_MESSAGE(reporter, reduced->requiresAA() == doAA, testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001042 }
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001043
bsalomon@google.com51a62862012-11-26 21:19:43 +00001044 // Build a new clip stack based on the reduced clip elements
1045 SkClipStack reducedStack;
csmartdalton8d3f92a2016-08-17 09:39:38 -07001046 if (GrReducedClip::InitialState::kAllOut == reduced->initialState()) {
bsalomon@google.com51a62862012-11-26 21:19:43 +00001047 // whether the result is bounded or not, the whole plane should start outside the clip.
1048 reducedStack.clipEmpty();
1049 }
csmartdalton8d3f92a2016-08-17 09:39:38 -07001050 for (ElementList::Iter iter(reduced->elements()); iter.get(); iter.next()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001051 add_elem_to_stack(*iter.get(), &reducedStack);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001052 }
bsalomon@google.com51a62862012-11-26 21:19:43 +00001053
csmartdalton8d3f92a2016-08-17 09:39:38 -07001054 SkIRect ibounds = reduced->hasIBounds() ? reduced->ibounds() : kIBounds;
csmartdaltond211e782016-08-15 11:17:19 -07001055
bsalomon@google.com34cd70a2012-12-06 14:23:20 +00001056 // GrReducedClipStack assumes that the final result is clipped to the returned bounds
Mike Reedc1f77742016-12-09 09:00:50 -05001057 reducedStack.clipDevRect(ibounds, kIntersect_SkClipOp);
1058 stack.clipDevRect(ibounds, kIntersect_SkClipOp);
bsalomon@google.com34cd70a2012-12-06 14:23:20 +00001059
bsalomon@google.com51a62862012-11-26 21:19:43 +00001060 // convert both the original stack and reduced stack to SkRegions and see if they're equal
bsalomon@google.com51a62862012-11-26 21:19:43 +00001061 SkRegion region;
csmartdaltond211e782016-08-15 11:17:19 -07001062 set_region_to_stack(stack, ibounds, &region);
csmartdaltond50e2402016-07-22 08:39:06 -07001063
bsalomon@google.com51a62862012-11-26 21:19:43 +00001064 SkRegion reducedRegion;
csmartdaltond211e782016-08-15 11:17:19 -07001065 set_region_to_stack(reducedStack, ibounds, &reducedRegion);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001066
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001067 REPORTER_ASSERT_MESSAGE(reporter, region == reducedRegion, testCase.c_str());
csmartdalton8d3f92a2016-08-17 09:39:38 -07001068
1069 reduced->~GrReducedClip();
bsalomon@google.com51a62862012-11-26 21:19:43 +00001070 }
1071}
1072
halcanary4dbbd042016-06-07 17:21:10 -07001073#ifdef SK_BUILD_FOR_WIN
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001074 #define SUPPRESS_VISIBILITY_WARNING
1075#else
1076 #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden")))
1077#endif
1078
1079static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) {
1080 {
1081 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001082 stack.clipRect(SkRect::MakeXYWH(0, 0, 100, 100), SkMatrix::I(), kReplace_SkClipOp,
Brian Salomona3b45d42016-10-03 11:36:16 -04001083 true);
1084 stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(50.3), SkScalar(50.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001085 kReplace_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001086 SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001087
csmartdalton8d3f92a2016-08-17 09:39:38 -07001088 SkAlignedSTStorage<1, GrReducedClip> storage;
1089 memset(storage.get(), 0, sizeof(GrReducedClip));
1090 GR_STATIC_ASSERT(0 == SkClipStack::kInvalidGenID);
1091 const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, bounds);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001092
csmartdalton8d3f92a2016-08-17 09:39:38 -07001093 REPORTER_ASSERT(reporter, reduced->elements().count() == 1);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001094 // Clips will be cached based on the generation id. Make sure the gen id is valid.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001095 REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reduced->elementsGenID());
1096
1097 reduced->~GrReducedClip();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001098 }
1099 {
1100 SkClipStack stack;
1101
1102 // Create a clip with following 25.3, 25.3 boxes which are 25 apart:
1103 // A B
1104 // C D
1105
Brian Salomona3b45d42016-10-03 11:36:16 -04001106 stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001107 kReplace_SkClipOp, true);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001108 int32_t genIDA = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001109 stack.clipRect(SkRect::MakeXYWH(50, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001110 kUnion_SkClipOp, true);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001111 int32_t genIDB = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001112 stack.clipRect(SkRect::MakeXYWH(0, 50, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001113 kUnion_SkClipOp, true);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001114 int32_t genIDC = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001115 stack.clipRect(SkRect::MakeXYWH(50, 50, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001116 kUnion_SkClipOp, true);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001117 int32_t genIDD = stack.getTopmostGenID();
1118
1119
csmartdaltoncbecb082016-07-22 08:59:08 -07001120#define IXYWH SkIRect::MakeXYWH
1121#define XYWH SkRect::MakeXYWH
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001122
csmartdaltoncbecb082016-07-22 08:59:08 -07001123 SkIRect stackBounds = IXYWH(0, 0, 76, 76);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001124
1125 // The base test is to test each rect in two ways:
1126 // 1) The box dimensions. (Should reduce to "all in", no elements).
1127 // 2) A bit over the box dimensions.
1128 // In the case 2, test that the generation id is what is expected.
1129 // The rects are of fractional size so that case 2 never gets optimized to an empty element
1130 // list.
1131
1132 // Not passing in tighter bounds is tested for consistency.
1133 static const struct SUPPRESS_VISIBILITY_WARNING {
csmartdaltoncbecb082016-07-22 08:59:08 -07001134 SkRect testBounds;
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001135 int reducedClipCount;
1136 int32_t reducedGenID;
csmartdaltoncbecb082016-07-22 08:59:08 -07001137 InitialState initialState;
1138 SkIRect clipIRect;
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001139 // parameter.
1140 } testCases[] = {
csmartdalton77f2fae2016-08-08 09:55:06 -07001141
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001142 // Rect A.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001143 { XYWH(0, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 0, 25, 25) },
1144 { 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 -07001145 { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::InitialState::kAllOut, IXYWH(0, 0, 27, 27)},
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001146
1147 // Rect B.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001148 { XYWH(50, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 25, 25) },
1149 { XYWH(50, 0, 25.3f, 25.3f), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 26, 26) },
csmartdalton77f2fae2016-08-08 09:55:06 -07001150 { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::InitialState::kAllOut, IXYWH(50, 0, 26, 27) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001151
1152 // Rect C.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001153 { XYWH(0, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 50, 25, 25) },
1154 { 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 -07001155 { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::InitialState::kAllOut, IXYWH(0, 50, 27, 26) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001156
1157 // Rect D.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001158 { XYWH(50, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 25, 25)},
1159 { XYWH(50.3f, 50.3f, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 26, 26)},
csmartdalton77f2fae2016-08-08 09:55:06 -07001160 { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(50, 50, 26, 26)},
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001161
1162 // Other tests:
csmartdalton77f2fae2016-08-08 09:55:06 -07001163 { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::InitialState::kAllOut, stackBounds },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001164
1165 // Rect in the middle, touches none.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001166 { 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 +00001167
1168 // Rect in the middle, touches all the rects. GenID is the last rect.
csmartdalton77f2fae2016-08-08 09:55:06 -07001169 { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(24, 24, 27, 27) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001170 };
1171
1172#undef XYWH
csmartdaltoncbecb082016-07-22 08:59:08 -07001173#undef IXYWH
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001174
1175 for (size_t i = 0; i < SK_ARRAY_COUNT(testCases); ++i) {
csmartdalton77f2fae2016-08-08 09:55:06 -07001176 const GrReducedClip reduced(stack, testCases[i].testBounds);
1177 REPORTER_ASSERT(reporter, reduced.elements().count() == testCases[i].reducedClipCount);
1178 SkASSERT(reduced.elements().count() == testCases[i].reducedClipCount);
csmartdalton8d3f92a2016-08-17 09:39:38 -07001179 if (reduced.elements().count()) {
1180 REPORTER_ASSERT(reporter, reduced.elementsGenID() == testCases[i].reducedGenID);
1181 SkASSERT(reduced.elementsGenID() == testCases[i].reducedGenID);
1182 }
csmartdalton77f2fae2016-08-08 09:55:06 -07001183 REPORTER_ASSERT(reporter, reduced.initialState() == testCases[i].initialState);
1184 SkASSERT(reduced.initialState() == testCases[i].initialState);
csmartdaltond211e782016-08-15 11:17:19 -07001185 REPORTER_ASSERT(reporter, reduced.hasIBounds());
1186 SkASSERT(reduced.hasIBounds());
1187 REPORTER_ASSERT(reporter, reduced.ibounds() == testCases[i].clipIRect);
1188 SkASSERT(reduced.ibounds() == testCases[i].clipIRect);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001189 }
1190 }
1191}
1192
1193static void test_reduced_clip_stack_no_aa_crash(skiatest::Reporter* reporter) {
1194 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001195 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 100, 100), kReplace_SkClipOp);
1196 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 50, 50), kReplace_SkClipOp);
csmartdaltoncbecb082016-07-22 08:59:08 -07001197 SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001198
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001199 // At the time, this would crash.
csmartdalton77f2fae2016-08-08 09:55:06 -07001200 const GrReducedClip reduced(stack, bounds);
1201 REPORTER_ASSERT(reporter, reduced.elements().isEmpty());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001202}
1203
csmartdaltoncbecb082016-07-22 08:59:08 -07001204enum class ClipMethod {
1205 kSkipDraw,
1206 kIgnoreClip,
1207 kScissor,
1208 kAAElements
1209};
1210
1211static void test_aa_query(skiatest::Reporter* reporter, const SkString& testName,
1212 const SkClipStack& stack, const SkMatrix& queryXform,
1213 const SkRect& preXformQuery, ClipMethod expectedMethod,
1214 int numExpectedElems = 0) {
csmartdaltoncbecb082016-07-22 08:59:08 -07001215 SkRect queryBounds;
1216 queryXform.mapRect(&queryBounds, preXformQuery);
csmartdalton77f2fae2016-08-08 09:55:06 -07001217 const GrReducedClip reduced(stack, queryBounds);
csmartdaltoncbecb082016-07-22 08:59:08 -07001218
1219 SkClipStack::BoundsType stackBoundsType;
1220 SkRect stackBounds;
1221 stack.getBounds(&stackBounds, &stackBoundsType);
1222
1223 switch (expectedMethod) {
1224 case ClipMethod::kSkipDraw:
1225 SkASSERT(0 == numExpectedElems);
csmartdalton77f2fae2016-08-08 09:55:06 -07001226 REPORTER_ASSERT_MESSAGE(reporter, reduced.elements().isEmpty(), testName.c_str());
1227 REPORTER_ASSERT_MESSAGE(reporter,
1228 GrReducedClip::InitialState::kAllOut == reduced.initialState(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001229 testName.c_str());
1230 return;
1231 case ClipMethod::kIgnoreClip:
1232 SkASSERT(0 == numExpectedElems);
csmartdaltond211e782016-08-15 11:17:19 -07001233 REPORTER_ASSERT_MESSAGE(reporter,
1234 !reduced.hasIBounds() ||
1235 GrClip::IsInsideClip(reduced.ibounds(), queryBounds),
csmartdaltoncbecb082016-07-22 08:59:08 -07001236 testName.c_str());
csmartdalton77f2fae2016-08-08 09:55:06 -07001237 REPORTER_ASSERT_MESSAGE(reporter, reduced.elements().isEmpty(), testName.c_str());
1238 REPORTER_ASSERT_MESSAGE(reporter,
1239 GrReducedClip::InitialState::kAllIn == reduced.initialState(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001240 testName.c_str());
1241 return;
1242 case ClipMethod::kScissor: {
1243 SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType);
1244 SkASSERT(0 == numExpectedElems);
1245 SkIRect expectedScissor;
1246 stackBounds.round(&expectedScissor);
csmartdalton77f2fae2016-08-08 09:55:06 -07001247 REPORTER_ASSERT_MESSAGE(reporter, reduced.elements().isEmpty(), testName.c_str());
csmartdaltond211e782016-08-15 11:17:19 -07001248 REPORTER_ASSERT_MESSAGE(reporter, reduced.hasIBounds(), testName.c_str());
1249 REPORTER_ASSERT_MESSAGE(reporter, expectedScissor == reduced.ibounds(),
csmartdalton77f2fae2016-08-08 09:55:06 -07001250 testName.c_str());
1251 REPORTER_ASSERT_MESSAGE(reporter,
1252 GrReducedClip::InitialState::kAllIn == reduced.initialState(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001253 testName.c_str());
1254 return;
1255 }
1256 case ClipMethod::kAAElements: {
1257 SkIRect expectedClipIBounds = GrClip::GetPixelIBounds(queryBounds);
1258 if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
1259 SkAssertResult(expectedClipIBounds.intersect(GrClip::GetPixelIBounds(stackBounds)));
1260 }
csmartdalton77f2fae2016-08-08 09:55:06 -07001261 REPORTER_ASSERT_MESSAGE(reporter, numExpectedElems == reduced.elements().count(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001262 testName.c_str());
csmartdaltond211e782016-08-15 11:17:19 -07001263 REPORTER_ASSERT_MESSAGE(reporter, reduced.hasIBounds(), testName.c_str());
1264 REPORTER_ASSERT_MESSAGE(reporter, expectedClipIBounds == reduced.ibounds(),
csmartdalton77f2fae2016-08-08 09:55:06 -07001265 testName.c_str());
1266 REPORTER_ASSERT_MESSAGE(reporter, reduced.requiresAA() == !reduced.elements().isEmpty(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001267 testName.c_str());
1268 break;
1269 }
1270 }
1271}
1272
1273static void test_reduced_clip_stack_aa(skiatest::Reporter* reporter) {
1274 constexpr SkScalar IL = 2, IT = 1, IR = 6, IB = 7; // Pixel aligned rect.
1275 constexpr SkScalar L = 2.2f, T = 1.7f, R = 5.8f, B = 7.3f; // Generic rect.
1276 constexpr SkScalar l = 3.3f, t = 2.8f, r = 4.7f, b = 6.2f; // Small rect contained in R.
1277
1278 SkRect alignedRect = {IL, IT, IR, IB};
1279 SkRect rect = {L, T, R, B};
1280 SkRect innerRect = {l, t, r, b};
1281
1282 SkMatrix m;
1283 m.setIdentity();
1284
1285 constexpr SkScalar kMinScale = 2.0001f;
1286 constexpr SkScalar kMaxScale = 3;
1287 constexpr int kNumIters = 8;
1288
1289 SkString name;
1290 SkRandom rand;
1291
1292 for (int i = 0; i < kNumIters; ++i) {
1293 // Pixel-aligned rect (iior=true).
1294 name.printf("Pixel-aligned rect test, iter %i", i);
1295 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001296 stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001297 test_aa_query(reporter, name, stack, m, {IL, IT, IR, IB}, ClipMethod::kIgnoreClip);
1298 test_aa_query(reporter, name, stack, m, {IL, IT-1, IR, IT}, ClipMethod::kSkipDraw);
csmartdalton77f2fae2016-08-08 09:55:06 -07001299 test_aa_query(reporter, name, stack, m, {IL, IT-2, IR, IB}, ClipMethod::kScissor);
csmartdaltoncbecb082016-07-22 08:59:08 -07001300
1301 // Rect (iior=true).
1302 name.printf("Rect test, iter %i", i);
1303 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001304 stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001305 test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kIgnoreClip);
1306 test_aa_query(reporter, name, stack, m, {L-.1f, T, L, B}, ClipMethod::kSkipDraw);
1307 test_aa_query(reporter, name, stack, m, {L-.1f, T, L+.1f, B}, ClipMethod::kAAElements, 1);
1308
1309 // Difference rect (iior=false, inside-out bounds).
1310 name.printf("Difference rect test, iter %i", i);
1311 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001312 stack.clipRect(rect, SkMatrix::I(), kDifference_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001313 test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kSkipDraw);
1314 test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T}, ClipMethod::kIgnoreClip);
1315 test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T+.1f}, ClipMethod::kAAElements, 1);
1316
1317 // Complex clip (iior=false, normal bounds).
1318 name.printf("Complex clip test, iter %i", i);
1319 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001320 stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true);
1321 stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001322 test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
1323 test_aa_query(reporter, name, stack, m, {r-.1f, t, R, b}, ClipMethod::kAAElements, 1);
1324 test_aa_query(reporter, name, stack, m, {r-.1f, t, R+.1f, b}, ClipMethod::kAAElements, 2);
1325 test_aa_query(reporter, name, stack, m, {r, t, R+.1f, b}, ClipMethod::kAAElements, 1);
1326 test_aa_query(reporter, name, stack, m, {r, t, R, b}, ClipMethod::kIgnoreClip);
1327 test_aa_query(reporter, name, stack, m, {R, T, R+.1f, B}, ClipMethod::kSkipDraw);
1328
1329 // Complex clip where outer rect is pixel aligned (iior=false, normal bounds).
1330 name.printf("Aligned Complex clip test, iter %i", i);
1331 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001332 stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true);
1333 stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001334 test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
1335 test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB}, ClipMethod::kAAElements, 1);
1336 test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB+.1f}, ClipMethod::kAAElements, 1);
1337 test_aa_query(reporter, name, stack, m, {l, b, r, IB+.1f}, ClipMethod::kAAElements, 0);
1338 test_aa_query(reporter, name, stack, m, {l, b, r, IB}, ClipMethod::kIgnoreClip);
1339 test_aa_query(reporter, name, stack, m, {IL, IB, IR, IB+.1f}, ClipMethod::kSkipDraw);
1340
1341 // Apply random transforms and try again. This ensures the clip stack reduction is hardened
1342 // against FP rounding error.
1343 SkScalar sx = rand.nextRangeScalar(kMinScale, kMaxScale);
1344 sx = SkScalarFloorToScalar(sx * alignedRect.width()) / alignedRect.width();
1345 SkScalar sy = rand.nextRangeScalar(kMinScale, kMaxScale);
1346 sy = SkScalarFloorToScalar(sy * alignedRect.height()) / alignedRect.height();
1347 SkScalar tx = SkScalarRoundToScalar(sx * alignedRect.x()) - sx * alignedRect.x();
1348 SkScalar ty = SkScalarRoundToScalar(sy * alignedRect.y()) - sy * alignedRect.y();
1349
1350 SkMatrix xform = SkMatrix::MakeScale(sx, sy);
1351 xform.postTranslate(tx, ty);
1352 xform.mapRect(&alignedRect);
1353 xform.mapRect(&rect);
1354 xform.mapRect(&innerRect);
1355 m.postConcat(xform);
1356 }
1357}
1358
bsalomon@google.coma4e13c82012-11-26 21:38:37 +00001359#endif
bsalomon@google.com51a62862012-11-26 21:19:43 +00001360
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00001361DEF_TEST(ClipStack, reporter) {
reed@google.combdee9fc2011-02-22 20:17:43 +00001362 SkClipStack stack;
1363
robertphillips@google.com80214e22012-07-20 15:33:18 +00001364 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001365 assert_count(reporter, stack, 0);
1366
1367 static const SkIRect gRects[] = {
1368 { 0, 0, 100, 100 },
1369 { 25, 25, 125, 125 },
1370 { 0, 0, 1000, 1000 },
1371 { 0, 0, 75, 75 }
1372 };
1373 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
Mike Reedc1f77742016-12-09 09:00:50 -05001374 stack.clipDevRect(gRects[i], kIntersect_SkClipOp);
reed@google.combdee9fc2011-02-22 20:17:43 +00001375 }
1376
1377 // all of the above rects should have been intersected, leaving only 1 rect
robertphillips@google.com80214e22012-07-20 15:33:18 +00001378 SkClipStack::B2TIter iter(stack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001379 const SkClipStack::Element* element = iter.next();
epoger@google.com2047f002011-05-17 17:36:59 +00001380 SkRect answer;
1381 answer.iset(25, 25, 75, 75);
reed@google.combdee9fc2011-02-22 20:17:43 +00001382
bsalomon49f085d2014-09-05 13:34:00 -07001383 REPORTER_ASSERT(reporter, element);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001384 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
Mike Reedc1f77742016-12-09 09:00:50 -05001385 REPORTER_ASSERT(reporter, kIntersect_SkClipOp == element->getOp());
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001386 REPORTER_ASSERT(reporter, element->getRect() == answer);
reed@google.combdee9fc2011-02-22 20:17:43 +00001387 // now check that we only had one in our iterator
1388 REPORTER_ASSERT(reporter, !iter.next());
1389
1390 stack.reset();
robertphillips@google.com80214e22012-07-20 15:33:18 +00001391 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001392 assert_count(reporter, stack, 0);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +00001393
1394 test_assign_and_comparison(reporter);
robertphillips@google.com80214e22012-07-20 15:33:18 +00001395 test_iterators(reporter);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001396 test_bounds(reporter, SkClipStack::Element::kRect_Type);
1397 test_bounds(reporter, SkClipStack::Element::kRRect_Type);
1398 test_bounds(reporter, SkClipStack::Element::kPath_Type);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +00001399 test_isWideOpen(reporter);
robertphillips@google.com08eacc12012-08-02 12:49:00 +00001400 test_rect_merging(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001401 test_rect_replace(reporter);
junov@chromium.orgedf32d52012-12-10 14:57:54 +00001402 test_rect_inverse_fill(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001403 test_path_replace(reporter);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +00001404 test_quickContains(reporter);
csmartdaltond50e2402016-07-22 08:39:06 -07001405 test_invfill_diff_bug(reporter);
bsalomon@google.come7b3d292012-11-26 21:42:32 +00001406#if SK_SUPPORT_GPU
bsalomon@google.comedb26fd2012-11-28 14:42:41 +00001407 test_reduced_clip_stack(reporter);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001408 test_reduced_clip_stack_genid(reporter);
1409 test_reduced_clip_stack_no_aa_crash(reporter);
csmartdaltoncbecb082016-07-22 08:59:08 -07001410 test_reduced_clip_stack_aa(reporter);
bsalomon@google.come7b3d292012-11-26 21:42:32 +00001411#endif
reed@google.combdee9fc2011-02-22 20:17:43 +00001412}
Brian Salomon19f0ed52017-01-06 13:54:58 -05001413
1414//////////////////////////////////////////////////////////////////////////////
1415
1416#if SK_SUPPORT_GPU
Robert Phillips875218e2017-02-24 08:37:13 -05001417sk_sp<GrTextureProxy> GrClipStackClip::testingOnly_createClipMask(GrContext* context) const {
Brian Salomon19f0ed52017-01-06 13:54:58 -05001418 const GrReducedClip reducedClip(*fStack, SkRect::MakeWH(512, 512), 0);
1419 return this->createSoftwareClipMask(context, reducedClip);
1420}
1421
1422// Verify that clip masks are freed up when the clip state that generated them goes away.
1423DEF_GPUTEST_FOR_ALL_CONTEXTS(ClipMaskCache, reporter, ctxInfo) {
1424 // This test uses resource key tags which only function in debug builds.
1425#ifdef SK_DEBUG
1426 GrContext* context = ctxInfo.grContext();
1427 SkClipStack stack;
1428
1429 SkPath path;
1430 path.addCircle(10, 10, 8);
1431 path.addCircle(15, 15, 8);
1432 path.setFillType(SkPath::kEvenOdd_FillType);
1433
1434 static const char* kTag = GrClipStackClip::kMaskTestTag;
1435 GrResourceCache* cache = context->getResourceCache();
1436
1437 static constexpr int kN = 5;
1438
1439 for (int i = 0; i < kN; ++i) {
1440 SkMatrix m;
1441 m.setTranslate(0.5, 0.5);
1442 stack.save();
1443 stack.clipPath(path, m, SkClipOp::kIntersect, true);
Robert Phillips875218e2017-02-24 08:37:13 -05001444 sk_sp<GrTextureProxy> mask = GrClipStackClip(&stack).testingOnly_createClipMask(context);
Brian Osman32342f02017-03-04 08:12:46 -05001445 GrTexture* tex = mask->instantiate(context->resourceProvider());
Robert Phillips875218e2017-02-24 08:37:13 -05001446 REPORTER_ASSERT(reporter, 0 == strcmp(tex->getUniqueKey().tag(), kTag));
Brian Salomon19f0ed52017-01-06 13:54:58 -05001447 // Make sure mask isn't pinned in cache.
1448 mask.reset(nullptr);
1449 context->flush();
1450 REPORTER_ASSERT(reporter, i + 1 == cache->countUniqueKeysWithTag(kTag));
1451 }
1452
1453 for (int i = 0; i < kN; ++i) {
1454 stack.restore();
1455 cache->purgeAsNeeded();
1456 REPORTER_ASSERT(reporter, kN - (i + 1) == cache->countUniqueKeysWithTag(kTag));
1457 }
1458#endif
1459}
1460
Mike Reed3726a4a2017-01-19 11:36:41 -05001461#include "SkSurface.h"
1462DEF_GPUTEST_FOR_ALL_CONTEXTS(canvas_private_clipRgn, reporter, ctxInfo) {
1463 GrContext* context = ctxInfo.grContext();
1464
1465 const int w = 10;
1466 const int h = 10;
1467 SkImageInfo info = SkImageInfo::MakeN32Premul(w, h);
1468 sk_sp<SkSurface> surf = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info);
1469 SkCanvas* canvas = surf->getCanvas();
1470 SkRegion rgn;
1471
1472 canvas->temporary_internal_getRgnClip(&rgn);
1473 REPORTER_ASSERT(reporter, rgn.isRect());
1474 REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeWH(w, h));
1475
1476 canvas->save();
1477 canvas->clipRect(SkRect::MakeWH(5, 5), kDifference_SkClipOp);
1478 canvas->temporary_internal_getRgnClip(&rgn);
1479 REPORTER_ASSERT(reporter, rgn.isComplex());
1480 REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeWH(w, h));
1481 canvas->restore();
1482
1483 canvas->save();
1484 canvas->clipRRect(SkRRect::MakeOval(SkRect::MakeLTRB(3, 3, 7, 7)));
1485 canvas->temporary_internal_getRgnClip(&rgn);
1486 REPORTER_ASSERT(reporter, rgn.isComplex());
1487 REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeLTRB(3, 3, 7, 7));
1488 canvas->restore();
1489}
Brian Salomon19f0ed52017-01-06 13:54:58 -05001490#endif