blob: 394835b369a53c36e4a886d1eed4b94eb8657cf3 [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
16#include "GrReducedClip.h"
17typedef GrReducedClip::ElementList ElementList;
18typedef GrReducedClip::InitialState InitialState;
19#endif
20
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000021static void test_assign_and_comparison(skiatest::Reporter* reporter) {
22 SkClipStack s;
reed@google.comd9f2dea2011-10-12 14:43:27 +000023 bool doAA = false;
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000024
robertphillips@google.com80214e22012-07-20 15:33:18 +000025 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
26
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000027 // Build up a clip stack with a path, an empty clip, and a rect.
28 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000029 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
30
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000031 SkPath p;
32 p.moveTo(5, 6);
33 p.lineTo(7, 8);
34 p.lineTo(5, 9);
35 p.close();
reed@google.comd9f2dea2011-10-12 14:43:27 +000036 s.clipDevPath(p, SkRegion::kIntersect_Op, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000037
38 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000039 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
40
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000041 SkRect r = SkRect::MakeLTRB(1, 2, 3, 4);
reed@google.comd9f2dea2011-10-12 14:43:27 +000042 s.clipDevRect(r, SkRegion::kIntersect_Op, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000043 r = SkRect::MakeLTRB(10, 11, 12, 13);
reed@google.comd9f2dea2011-10-12 14:43:27 +000044 s.clipDevRect(r, SkRegion::kIntersect_Op, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000045
46 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000047 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
48
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000049 r = SkRect::MakeLTRB(14, 15, 16, 17);
reed@google.comd9f2dea2011-10-12 14:43:27 +000050 s.clipDevRect(r, SkRegion::kUnion_Op, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000051
52 // Test that assignment works.
53 SkClipStack copy = s;
54 REPORTER_ASSERT(reporter, s == copy);
55
56 // Test that different save levels triggers not equal.
57 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000058 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000059 REPORTER_ASSERT(reporter, s != copy);
60
61 // Test that an equal, but not copied version is equal.
62 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000063 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000064 r = SkRect::MakeLTRB(14, 15, 16, 17);
reed@google.comd9f2dea2011-10-12 14:43:27 +000065 s.clipDevRect(r, SkRegion::kUnion_Op, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000066 REPORTER_ASSERT(reporter, s == copy);
67
68 // Test that a different op on one level triggers not equal.
69 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000070 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000071 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000072 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000073 r = SkRect::MakeLTRB(14, 15, 16, 17);
reed@google.comd9f2dea2011-10-12 14:43:27 +000074 s.clipDevRect(r, SkRegion::kIntersect_Op, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000075 REPORTER_ASSERT(reporter, s != copy);
76
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000077 // Test that version constructed with rect-path rather than a rect is still considered equal.
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000078 s.restore();
79 s.save();
80 SkPath rp;
81 rp.addRect(r);
reed@google.comd9f2dea2011-10-12 14:43:27 +000082 s.clipDevPath(rp, SkRegion::kUnion_Op, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000083 REPORTER_ASSERT(reporter, s == copy);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000084
85 // Test that different rects triggers not equal.
86 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000087 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000088 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000089 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
90
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000091 r = SkRect::MakeLTRB(24, 25, 26, 27);
reed@google.comd9f2dea2011-10-12 14:43:27 +000092 s.clipDevRect(r, SkRegion::kUnion_Op, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000093 REPORTER_ASSERT(reporter, s != copy);
94
95 // Sanity check
96 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000097 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
98
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000099 copy.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000100 REPORTER_ASSERT(reporter, 2 == copy.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000101 REPORTER_ASSERT(reporter, s == copy);
102 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000103 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000104 copy.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000105 REPORTER_ASSERT(reporter, 1 == copy.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000106 REPORTER_ASSERT(reporter, s == copy);
107
108 // Test that different paths triggers not equal.
109 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000110 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000111 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000112 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
113
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000114 p.addRect(r);
reed@google.comd9f2dea2011-10-12 14:43:27 +0000115 s.clipDevPath(p, SkRegion::kIntersect_Op, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000116 REPORTER_ASSERT(reporter, s != copy);
117}
reed@google.combdee9fc2011-02-22 20:17:43 +0000118
119static void assert_count(skiatest::Reporter* reporter, const SkClipStack& stack,
120 int count) {
robertphillips@google.com80214e22012-07-20 15:33:18 +0000121 SkClipStack::B2TIter iter(stack);
reed@google.combdee9fc2011-02-22 20:17:43 +0000122 int counter = 0;
123 while (iter.next()) {
124 counter += 1;
125 }
126 REPORTER_ASSERT(reporter, count == counter);
127}
128
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000129// Exercise the SkClipStack's bottom to top and bidirectional iterators
130// (including the skipToTopmost functionality)
robertphillips@google.com80214e22012-07-20 15:33:18 +0000131static void test_iterators(skiatest::Reporter* reporter) {
132 SkClipStack stack;
133
134 static const SkRect gRects[] = {
135 { 0, 0, 40, 40 },
136 { 60, 0, 100, 40 },
137 { 0, 60, 40, 100 },
138 { 60, 60, 100, 100 }
139 };
140
141 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
142 // the union op will prevent these from being fused together
143 stack.clipDevRect(gRects[i], SkRegion::kUnion_Op, false);
144 }
145
146 assert_count(reporter, stack, 4);
147
148 // bottom to top iteration
149 {
halcanary96fcdcc2015-08-27 07:41:13 -0700150 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000151
152 SkClipStack::B2TIter iter(stack);
153 int i;
154
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000155 for (i = 0, element = iter.next(); element; ++i, element = iter.next()) {
156 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
157 REPORTER_ASSERT(reporter, element->getRect() == gRects[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000158 }
159
160 SkASSERT(i == 4);
161 }
162
163 // top to bottom iteration
164 {
halcanary96fcdcc2015-08-27 07:41:13 -0700165 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000166
167 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
168 int i;
169
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000170 for (i = 3, element = iter.prev(); element; --i, element = iter.prev()) {
171 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
172 REPORTER_ASSERT(reporter, element->getRect() == gRects[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000173 }
174
175 SkASSERT(i == -1);
176 }
177
178 // skipToTopmost
179 {
halcanary96fcdcc2015-08-27 07:41:13 -0700180 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000181
182 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
183
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000184 element = iter.skipToTopmost(SkRegion::kUnion_Op);
185 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
186 REPORTER_ASSERT(reporter, element->getRect() == gRects[3]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000187 }
188}
189
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000190// Exercise the SkClipStack's getConservativeBounds computation
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000191static void test_bounds(skiatest::Reporter* reporter, SkClipStack::Element::Type primType) {
robertphillips@google.com607fe072012-07-24 13:54:00 +0000192 static const int gNumCases = 20;
193 static const SkRect gAnswerRectsBW[gNumCases] = {
194 // A op B
195 { 40, 40, 50, 50 },
196 { 10, 10, 50, 50 },
197 { 10, 10, 80, 80 },
198 { 10, 10, 80, 80 },
199 { 40, 40, 80, 80 },
200
201 // invA op B
202 { 40, 40, 80, 80 },
203 { 0, 0, 100, 100 },
204 { 0, 0, 100, 100 },
205 { 0, 0, 100, 100 },
206 { 40, 40, 50, 50 },
207
208 // A op invB
209 { 10, 10, 50, 50 },
210 { 40, 40, 50, 50 },
211 { 0, 0, 100, 100 },
212 { 0, 0, 100, 100 },
213 { 0, 0, 100, 100 },
214
215 // invA op invB
216 { 0, 0, 100, 100 },
217 { 40, 40, 80, 80 },
218 { 0, 0, 100, 100 },
219 { 10, 10, 80, 80 },
220 { 10, 10, 50, 50 },
221 };
222
223 static const SkRegion::Op gOps[] = {
224 SkRegion::kIntersect_Op,
225 SkRegion::kDifference_Op,
226 SkRegion::kUnion_Op,
227 SkRegion::kXOR_Op,
228 SkRegion::kReverseDifference_Op
229 };
230
231 SkRect rectA, rectB;
232
233 rectA.iset(10, 10, 50, 50);
234 rectB.iset(40, 40, 80, 80);
235
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000236 SkRRect rrectA, rrectB;
237 rrectA.setOval(rectA);
238 rrectB.setRectXY(rectB, SkIntToScalar(1), SkIntToScalar(2));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000239
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000240 SkPath pathA, pathB;
241
242 pathA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
243 pathB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000244
245 SkClipStack stack;
robertphillips@google.com7b112892012-07-31 15:18:21 +0000246 SkRect devClipBound;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000247 bool isIntersectionOfRects = false;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000248
249 int testCase = 0;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000250 int numBitTests = SkClipStack::Element::kPath_Type == primType ? 4 : 1;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000251 for (int invBits = 0; invBits < numBitTests; ++invBits) {
robertphillips@google.com607fe072012-07-24 13:54:00 +0000252 for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) {
253
254 stack.save();
255 bool doInvA = SkToBool(invBits & 1);
256 bool doInvB = SkToBool(invBits & 2);
257
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000258 pathA.setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType :
robertphillips@google.com607fe072012-07-24 13:54:00 +0000259 SkPath::kEvenOdd_FillType);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000260 pathB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType :
robertphillips@google.com607fe072012-07-24 13:54:00 +0000261 SkPath::kEvenOdd_FillType);
262
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000263 switch (primType) {
264 case SkClipStack::Element::kEmpty_Type:
265 SkDEBUGFAIL("Don't call this with kEmpty.");
266 break;
267 case SkClipStack::Element::kRect_Type:
268 stack.clipDevRect(rectA, SkRegion::kIntersect_Op, false);
269 stack.clipDevRect(rectB, gOps[op], false);
270 break;
271 case SkClipStack::Element::kRRect_Type:
272 stack.clipDevRRect(rrectA, SkRegion::kIntersect_Op, false);
273 stack.clipDevRRect(rrectB, gOps[op], false);
274 break;
275 case SkClipStack::Element::kPath_Type:
276 stack.clipDevPath(pathA, SkRegion::kIntersect_Op, false);
277 stack.clipDevPath(pathB, gOps[op], false);
278 break;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000279 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000280
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000281 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000282 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000283
robertphillips@google.com7b112892012-07-31 15:18:21 +0000284 stack.getConservativeBounds(0, 0, 100, 100, &devClipBound,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000285 &isIntersectionOfRects);
286
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000287 if (SkClipStack::Element::kRect_Type == primType) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000288 REPORTER_ASSERT(reporter, isIntersectionOfRects ==
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000289 (gOps[op] == SkRegion::kIntersect_Op));
290 } else {
291 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
292 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000293
294 SkASSERT(testCase < gNumCases);
robertphillips@google.com7b112892012-07-31 15:18:21 +0000295 REPORTER_ASSERT(reporter, devClipBound == gAnswerRectsBW[testCase]);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000296 ++testCase;
297
298 stack.restore();
299 }
300 }
301}
302
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000303// Test out 'isWideOpen' entry point
304static void test_isWideOpen(skiatest::Reporter* reporter) {
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000305 {
306 // Empty stack is wide open. Wide open stack means that gen id is wide open.
307 SkClipStack stack;
308 REPORTER_ASSERT(reporter, stack.isWideOpen());
309 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
310 }
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000311
312 SkRect rectA, rectB;
313
314 rectA.iset(10, 10, 40, 40);
315 rectB.iset(50, 50, 80, 80);
316
317 // Stack should initially be wide open
318 {
319 SkClipStack stack;
320
321 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000322 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000323 }
324
325 // Test out case where the user specifies a union that includes everything
326 {
327 SkClipStack stack;
328
329 SkPath clipA, clipB;
330
331 clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
332 clipA.setFillType(SkPath::kInverseEvenOdd_FillType);
333
334 clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
335 clipB.setFillType(SkPath::kInverseEvenOdd_FillType);
336
337 stack.clipDevPath(clipA, SkRegion::kReplace_Op, false);
338 stack.clipDevPath(clipB, SkRegion::kUnion_Op, false);
339
340 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000341 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000342 }
343
344 // Test out union w/ a wide open clip
345 {
346 SkClipStack stack;
347
348 stack.clipDevRect(rectA, SkRegion::kUnion_Op, false);
349
350 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000351 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000352 }
353
354 // Test out empty difference from a wide open clip
355 {
356 SkClipStack stack;
357
358 SkRect emptyRect;
359 emptyRect.setEmpty();
360
361 stack.clipDevRect(emptyRect, SkRegion::kDifference_Op, false);
362
363 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000364 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000365 }
366
367 // Test out return to wide open
368 {
369 SkClipStack stack;
370
371 stack.save();
372
373 stack.clipDevRect(rectA, SkRegion::kReplace_Op, false);
374
375 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000376 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000377
378 stack.restore();
379
380 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000381 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000382 }
383}
384
bsalomon@google.com100abf42012-09-05 17:40:04 +0000385static int count(const SkClipStack& stack) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000386
387 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
388
halcanary96fcdcc2015-08-27 07:41:13 -0700389 const SkClipStack::Element* element = nullptr;
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000390 int count = 0;
391
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000392 for (element = iter.prev(); element; element = iter.prev(), ++count) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000393 ;
394 }
395
396 return count;
397}
398
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000399static void test_rect_inverse_fill(skiatest::Reporter* reporter) {
400 // non-intersecting rectangles
401 SkRect rect = SkRect::MakeLTRB(0, 0, 10, 10);
402
403 SkPath path;
404 path.addRect(rect);
405 path.toggleInverseFillType();
406 SkClipStack stack;
407 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
408
409 SkRect bounds;
410 SkClipStack::BoundsType boundsType;
411 stack.getBounds(&bounds, &boundsType);
412 REPORTER_ASSERT(reporter, SkClipStack::kInsideOut_BoundsType == boundsType);
413 REPORTER_ASSERT(reporter, bounds == rect);
414}
415
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000416static void test_rect_replace(skiatest::Reporter* reporter) {
417 SkRect rect = SkRect::MakeWH(100, 100);
418 SkRect rect2 = SkRect::MakeXYWH(50, 50, 100, 100);
419
420 SkRect bound;
421 SkClipStack::BoundsType type;
422 bool isIntersectionOfRects;
423
424 // Adding a new rect with the replace operator should not increase
425 // the stack depth. BW replacing BW.
426 {
427 SkClipStack stack;
428 REPORTER_ASSERT(reporter, 0 == count(stack));
429 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
430 REPORTER_ASSERT(reporter, 1 == count(stack));
431 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
432 REPORTER_ASSERT(reporter, 1 == count(stack));
433 }
434
435 // Adding a new rect with the replace operator should not increase
436 // the stack depth. AA replacing AA.
437 {
438 SkClipStack stack;
439 REPORTER_ASSERT(reporter, 0 == count(stack));
440 stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
441 REPORTER_ASSERT(reporter, 1 == count(stack));
442 stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
443 REPORTER_ASSERT(reporter, 1 == count(stack));
444 }
445
446 // Adding a new rect with the replace operator should not increase
447 // the stack depth. BW replacing AA replacing BW.
448 {
449 SkClipStack stack;
450 REPORTER_ASSERT(reporter, 0 == count(stack));
451 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
452 REPORTER_ASSERT(reporter, 1 == count(stack));
453 stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
454 REPORTER_ASSERT(reporter, 1 == count(stack));
455 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
456 REPORTER_ASSERT(reporter, 1 == count(stack));
457 }
458
459 // Make sure replace clip rects don't collapse too much.
460 {
461 SkClipStack stack;
462 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
463 stack.clipDevRect(rect2, SkRegion::kIntersect_Op, false);
464 REPORTER_ASSERT(reporter, 1 == count(stack));
465
466 stack.save();
467 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
468 REPORTER_ASSERT(reporter, 2 == count(stack));
469 stack.getBounds(&bound, &type, &isIntersectionOfRects);
470 REPORTER_ASSERT(reporter, bound == rect);
471 stack.restore();
472 REPORTER_ASSERT(reporter, 1 == count(stack));
473
474 stack.save();
475 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
476 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
477 REPORTER_ASSERT(reporter, 2 == count(stack));
478 stack.restore();
479 REPORTER_ASSERT(reporter, 1 == count(stack));
480
481 stack.save();
482 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
483 stack.clipDevRect(rect2, SkRegion::kIntersect_Op, false);
484 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
485 REPORTER_ASSERT(reporter, 2 == count(stack));
486 stack.restore();
487 REPORTER_ASSERT(reporter, 1 == count(stack));
488 }
489}
490
491// Simplified path-based version of test_rect_replace.
492static void test_path_replace(skiatest::Reporter* reporter) {
493 SkRect rect = SkRect::MakeWH(100, 100);
494 SkPath path;
495 path.addCircle(50, 50, 50);
496
497 // Replace operation doesn't grow the stack.
498 {
499 SkClipStack stack;
500 REPORTER_ASSERT(reporter, 0 == count(stack));
501 stack.clipDevPath(path, SkRegion::kReplace_Op, false);
502 REPORTER_ASSERT(reporter, 1 == count(stack));
503 stack.clipDevPath(path, SkRegion::kReplace_Op, false);
504 REPORTER_ASSERT(reporter, 1 == count(stack));
505 }
506
507 // Replacing rect with path.
508 {
509 SkClipStack stack;
510 stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
511 REPORTER_ASSERT(reporter, 1 == count(stack));
512 stack.clipDevPath(path, SkRegion::kReplace_Op, true);
513 REPORTER_ASSERT(reporter, 1 == count(stack));
514 }
515}
516
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000517// Test out SkClipStack's merging of rect clips. In particular exercise
518// merging of aa vs. bw rects.
519static void test_rect_merging(skiatest::Reporter* reporter) {
520
521 SkRect overlapLeft = SkRect::MakeLTRB(10, 10, 50, 50);
522 SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80);
523
524 SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90);
525 SkRect nestedChild = SkRect::MakeLTRB(40, 40, 60, 60);
526
527 SkRect bound;
528 SkClipStack::BoundsType type;
529 bool isIntersectionOfRects;
530
531 // all bw overlapping - should merge
532 {
533 SkClipStack stack;
534
535 stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, false);
536
537 stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false);
538
539 REPORTER_ASSERT(reporter, 1 == count(stack));
540
541 stack.getBounds(&bound, &type, &isIntersectionOfRects);
542
543 REPORTER_ASSERT(reporter, isIntersectionOfRects);
544 }
545
546 // all aa overlapping - should merge
547 {
548 SkClipStack stack;
549
550 stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true);
551
552 stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, true);
553
554 REPORTER_ASSERT(reporter, 1 == count(stack));
555
556 stack.getBounds(&bound, &type, &isIntersectionOfRects);
557
558 REPORTER_ASSERT(reporter, isIntersectionOfRects);
559 }
560
561 // mixed overlapping - should _not_ merge
562 {
563 SkClipStack stack;
564
565 stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true);
566
567 stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false);
568
569 REPORTER_ASSERT(reporter, 2 == count(stack));
570
571 stack.getBounds(&bound, &type, &isIntersectionOfRects);
572
573 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
574 }
575
576 // mixed nested (bw inside aa) - should merge
577 {
578 SkClipStack stack;
579
580 stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, true);
581
582 stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, false);
583
584 REPORTER_ASSERT(reporter, 1 == count(stack));
585
586 stack.getBounds(&bound, &type, &isIntersectionOfRects);
587
588 REPORTER_ASSERT(reporter, isIntersectionOfRects);
589 }
590
591 // mixed nested (aa inside bw) - should merge
592 {
593 SkClipStack stack;
594
595 stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, false);
596
597 stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, true);
598
599 REPORTER_ASSERT(reporter, 1 == count(stack));
600
601 stack.getBounds(&bound, &type, &isIntersectionOfRects);
602
603 REPORTER_ASSERT(reporter, isIntersectionOfRects);
604 }
605
606 // reverse nested (aa inside bw) - should _not_ merge
607 {
608 SkClipStack stack;
609
610 stack.clipDevRect(nestedChild, SkRegion::kReplace_Op, false);
611
612 stack.clipDevRect(nestedParent, SkRegion::kIntersect_Op, true);
613
614 REPORTER_ASSERT(reporter, 2 == count(stack));
615
616 stack.getBounds(&bound, &type, &isIntersectionOfRects);
617
618 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
619 }
620}
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000621
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000622static void test_quickContains(skiatest::Reporter* reporter) {
623 SkRect testRect = SkRect::MakeLTRB(10, 10, 40, 40);
624 SkRect insideRect = SkRect::MakeLTRB(20, 20, 30, 30);
625 SkRect intersectingRect = SkRect::MakeLTRB(25, 25, 50, 50);
626 SkRect outsideRect = SkRect::MakeLTRB(0, 0, 50, 50);
627 SkRect nonIntersectingRect = SkRect::MakeLTRB(100, 100, 110, 110);
628
629 SkPath insideCircle;
630 insideCircle.addCircle(25, 25, 5);
631 SkPath intersectingCircle;
632 intersectingCircle.addCircle(25, 40, 10);
633 SkPath outsideCircle;
634 outsideCircle.addCircle(25, 25, 50);
635 SkPath nonIntersectingCircle;
636 nonIntersectingCircle.addCircle(100, 100, 5);
637
638 {
639 SkClipStack stack;
640 stack.clipDevRect(outsideRect, SkRegion::kDifference_Op, false);
641 // return false because quickContains currently does not care for kDifference_Op
642 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
643 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000644
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000645 // Replace Op tests
646 {
647 SkClipStack stack;
648 stack.clipDevRect(outsideRect, SkRegion::kReplace_Op, false);
649 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
650 }
651
652 {
653 SkClipStack stack;
654 stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
655 stack.save(); // To prevent in-place substitution by replace OP
656 stack.clipDevRect(outsideRect, SkRegion::kReplace_Op, false);
657 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
658 stack.restore();
659 }
660
661 {
662 SkClipStack stack;
663 stack.clipDevRect(outsideRect, SkRegion::kIntersect_Op, false);
664 stack.save(); // To prevent in-place substitution by replace OP
665 stack.clipDevRect(insideRect, SkRegion::kReplace_Op, false);
666 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
667 stack.restore();
668 }
669
670 // Verify proper traversal of multi-element clip
671 {
672 SkClipStack stack;
673 stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
674 // Use a path for second clip to prevent in-place intersection
675 stack.clipDevPath(outsideCircle, SkRegion::kIntersect_Op, false);
676 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
677 }
678
679 // Intersect Op tests with rectangles
680 {
681 SkClipStack stack;
682 stack.clipDevRect(outsideRect, SkRegion::kIntersect_Op, false);
683 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
684 }
685
686 {
687 SkClipStack stack;
688 stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
689 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
690 }
691
692 {
693 SkClipStack stack;
694 stack.clipDevRect(intersectingRect, SkRegion::kIntersect_Op, false);
695 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
696 }
697
698 {
699 SkClipStack stack;
700 stack.clipDevRect(nonIntersectingRect, SkRegion::kIntersect_Op, false);
701 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
702 }
703
704 // Intersect Op tests with circle paths
705 {
706 SkClipStack stack;
707 stack.clipDevPath(outsideCircle, SkRegion::kIntersect_Op, false);
708 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
709 }
710
711 {
712 SkClipStack stack;
713 stack.clipDevPath(insideCircle, SkRegion::kIntersect_Op, false);
714 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
715 }
716
717 {
718 SkClipStack stack;
719 stack.clipDevPath(intersectingCircle, SkRegion::kIntersect_Op, false);
720 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
721 }
722
723 {
724 SkClipStack stack;
725 stack.clipDevPath(nonIntersectingCircle, SkRegion::kIntersect_Op, false);
726 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
727 }
728
729 // Intersect Op tests with inverse filled rectangles
730 {
731 SkClipStack stack;
732 SkPath path;
733 path.addRect(outsideRect);
734 path.toggleInverseFillType();
735 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
736 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
737 }
738
739 {
740 SkClipStack stack;
741 SkPath path;
742 path.addRect(insideRect);
743 path.toggleInverseFillType();
744 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
745 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
746 }
747
748 {
749 SkClipStack stack;
750 SkPath path;
751 path.addRect(intersectingRect);
752 path.toggleInverseFillType();
753 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
754 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
755 }
756
757 {
758 SkClipStack stack;
759 SkPath path;
760 path.addRect(nonIntersectingRect);
761 path.toggleInverseFillType();
762 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
763 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
764 }
765
766 // Intersect Op tests with inverse filled circles
767 {
768 SkClipStack stack;
769 SkPath path = outsideCircle;
770 path.toggleInverseFillType();
771 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
772 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
773 }
774
775 {
776 SkClipStack stack;
777 SkPath path = insideCircle;
778 path.toggleInverseFillType();
779 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
780 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
781 }
782
783 {
784 SkClipStack stack;
785 SkPath path = intersectingCircle;
786 path.toggleInverseFillType();
787 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
788 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
789 }
790
791 {
792 SkClipStack stack;
793 SkPath path = nonIntersectingCircle;
794 path.toggleInverseFillType();
795 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
796 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
797 }
798}
799
csmartdaltond50e2402016-07-22 08:39:06 -0700800static void set_region_to_stack(const SkClipStack& stack, const SkIRect& bounds, SkRegion* region) {
801 region->setRect(bounds);
802 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
803 while (const SkClipStack::Element *element = iter.next()) {
804 SkRegion elemRegion;
805 SkRegion boundsRgn(bounds);
806 SkPath path;
807
808 switch (element->getType()) {
809 case SkClipStack::Element::kEmpty_Type:
810 elemRegion.setEmpty();
811 break;
812 default:
813 element->asPath(&path);
814 elemRegion.setPath(path, boundsRgn);
815 break;
816 }
817 region->op(elemRegion, element->getOp());
818 }
819}
820
821static void test_invfill_diff_bug(skiatest::Reporter* reporter) {
822 SkClipStack stack;
823 stack.clipDevRect({10, 10, 20, 20}, SkRegion::kIntersect_Op, false);
824
825 SkPath path;
826 path.addRect({30, 10, 40, 20});
827 path.setFillType(SkPath::kInverseWinding_FillType);
828 stack.clipDevPath(path, SkRegion::kDifference_Op, false);
829
830 REPORTER_ASSERT(reporter, SkClipStack::kEmptyGenID == stack.getTopmostGenID());
831
832 SkRect stackBounds;
833 SkClipStack::BoundsType stackBoundsType;
834 stack.getBounds(&stackBounds, &stackBoundsType);
835
836 REPORTER_ASSERT(reporter, stackBounds.isEmpty());
837 REPORTER_ASSERT(reporter, SkClipStack::kNormal_BoundsType == stackBoundsType);
838
839 SkRegion region;
840 set_region_to_stack(stack, {0, 0, 50, 30}, &region);
841
842 REPORTER_ASSERT(reporter, region.isEmpty());
843}
844
bsalomon@google.com51a62862012-11-26 21:19:43 +0000845///////////////////////////////////////////////////////////////////////////////////////////////////
846
bsalomon@google.coma4e13c82012-11-26 21:38:37 +0000847#if SK_SUPPORT_GPU
bsalomon@google.com705e8402012-11-27 15:43:57 +0000848// Functions that add a shape to the clip stack. The shape is computed from a rectangle.
849// AA is always disabled since the clip stack reducer can cause changes in aa rasterization of the
850// stack. A fractional edge repeated in different elements may be rasterized fewer times using the
851// reduced stack.
852typedef void (*AddElementFunc) (const SkRect& rect,
853 bool invert,
854 SkRegion::Op op,
csmartdaltoncbecb082016-07-22 08:59:08 -0700855 SkClipStack* stack,
856 bool doAA);
bsalomon@google.coma4e13c82012-11-26 21:38:37 +0000857
csmartdaltoncbecb082016-07-22 08:59:08 -0700858static void add_round_rect(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack,
859 bool doAA) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000860 SkScalar rx = rect.width() / 10;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000861 SkScalar ry = rect.height() / 20;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000862 if (invert) {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000863 SkPath path;
864 path.addRoundRect(rect, rx, ry);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000865 path.setFillType(SkPath::kInverseWinding_FillType);
csmartdaltoncbecb082016-07-22 08:59:08 -0700866 stack->clipDevPath(path, op, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000867 } else {
868 SkRRect rrect;
869 rrect.setRectXY(rect, rx, ry);
csmartdaltoncbecb082016-07-22 08:59:08 -0700870 stack->clipDevRRect(rrect, op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000871 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000872};
873
csmartdaltoncbecb082016-07-22 08:59:08 -0700874static void add_rect(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack,
875 bool doAA) {
bsalomon@google.com705e8402012-11-27 15:43:57 +0000876 if (invert) {
877 SkPath path;
878 path.addRect(rect);
879 path.setFillType(SkPath::kInverseWinding_FillType);
csmartdaltoncbecb082016-07-22 08:59:08 -0700880 stack->clipDevPath(path, op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000881 } else {
csmartdaltoncbecb082016-07-22 08:59:08 -0700882 stack->clipDevRect(rect, op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000883 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000884};
885
csmartdaltoncbecb082016-07-22 08:59:08 -0700886static void add_oval(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack,
887 bool doAA) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000888 SkPath path;
889 path.addOval(rect);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000890 if (invert) {
891 path.setFillType(SkPath::kInverseWinding_FillType);
892 }
csmartdaltoncbecb082016-07-22 08:59:08 -0700893 stack->clipDevPath(path, op, doAA);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000894};
895
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000896static void add_elem_to_stack(const SkClipStack::Element& element, SkClipStack* stack) {
897 switch (element.getType()) {
898 case SkClipStack::Element::kRect_Type:
899 stack->clipDevRect(element.getRect(), element.getOp(), element.isAA());
900 break;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000901 case SkClipStack::Element::kRRect_Type:
902 stack->clipDevRRect(element.getRRect(), element.getOp(), element.isAA());
903 break;
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000904 case SkClipStack::Element::kPath_Type:
905 stack->clipDevPath(element.getPath(), element.getOp(), element.isAA());
906 break;
907 case SkClipStack::Element::kEmpty_Type:
908 SkDEBUGFAIL("Why did the reducer produce an explicit empty.");
909 stack->clipEmpty();
910 break;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000911 }
912}
913
bsalomon@google.com51a62862012-11-26 21:19:43 +0000914static void test_reduced_clip_stack(skiatest::Reporter* reporter) {
915 // We construct random clip stacks, reduce them, and then rasterize both versions to verify that
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000916 // they are equal.
bsalomon@google.com51a62862012-11-26 21:19:43 +0000917
918 // All the clip elements will be contained within these bounds.
csmartdaltond211e782016-08-15 11:17:19 -0700919 static const SkIRect kIBounds = SkIRect::MakeWH(100, 100);
920 static const SkRect kBounds = SkRect::Make(kIBounds);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000921
922 enum {
csmartdaltoncbecb082016-07-22 08:59:08 -0700923 kNumTests = 250,
bsalomon@google.com51a62862012-11-26 21:19:43 +0000924 kMinElemsPerTest = 1,
925 kMaxElemsPerTest = 50,
926 };
927
928 // min/max size of a clip element as a fraction of kBounds.
929 static const SkScalar kMinElemSizeFrac = SK_Scalar1 / 5;
930 static const SkScalar kMaxElemSizeFrac = SK_Scalar1;
931
932 static const SkRegion::Op kOps[] = {
933 SkRegion::kDifference_Op,
934 SkRegion::kIntersect_Op,
935 SkRegion::kUnion_Op,
936 SkRegion::kXOR_Op,
937 SkRegion::kReverseDifference_Op,
938 SkRegion::kReplace_Op,
939 };
940
941 // Replace operations short-circuit the optimizer. We want to make sure that we test this code
942 // path a little bit but we don't want it to prevent us from testing many longer traversals in
943 // the optimizer.
944 static const int kReplaceDiv = 4 * kMaxElemsPerTest;
945
bsalomon@google.com705e8402012-11-27 15:43:57 +0000946 // We want to test inverse fills. However, they are quite rare in practice so don't over do it.
947 static const SkScalar kFractionInverted = SK_Scalar1 / kMaxElemsPerTest;
948
csmartdaltoncbecb082016-07-22 08:59:08 -0700949 static const SkScalar kFractionAntialiased = 0.25;
950
bsalomon@google.com51a62862012-11-26 21:19:43 +0000951 static const AddElementFunc kElementFuncs[] = {
952 add_rect,
953 add_round_rect,
954 add_oval,
955 };
956
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000957 SkRandom r;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000958
959 for (int i = 0; i < kNumTests; ++i) {
csmartdaltoncbecb082016-07-22 08:59:08 -0700960 SkString testCase;
961 testCase.printf("Iteration %d", i);
962
bsalomon@google.com51a62862012-11-26 21:19:43 +0000963 // Randomly generate a clip stack.
964 SkClipStack stack;
965 int numElems = r.nextRangeU(kMinElemsPerTest, kMaxElemsPerTest);
csmartdaltoncbecb082016-07-22 08:59:08 -0700966 bool doAA = r.nextBiasedBool(kFractionAntialiased);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000967 for (int e = 0; e < numElems; ++e) {
968 SkRegion::Op op = kOps[r.nextULessThan(SK_ARRAY_COUNT(kOps))];
969 if (op == SkRegion::kReplace_Op) {
970 if (r.nextU() % kReplaceDiv) {
971 --e;
972 continue;
973 }
974 }
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000975
bsalomon@google.com51a62862012-11-26 21:19:43 +0000976 // saves can change the clip stack behavior when an element is added.
977 bool doSave = r.nextBool();
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000978
bsalomon@google.com51a62862012-11-26 21:19:43 +0000979 SkSize size = SkSize::Make(
csmartdaltoncbecb082016-07-22 08:59:08 -0700980 SkScalarMul(kBounds.width(), r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac)),
981 SkScalarMul(kBounds.height(), r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac)));
bsalomon@google.com51a62862012-11-26 21:19:43 +0000982
csmartdaltoncbecb082016-07-22 08:59:08 -0700983 SkPoint xy = {r.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth),
984 r.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight)};
bsalomon@google.com51a62862012-11-26 21:19:43 +0000985
csmartdaltoncbecb082016-07-22 08:59:08 -0700986 SkRect rect;
987 if (doAA) {
988 rect.setXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight);
989 if (GrClip::IsPixelAligned(rect)) {
990 // Don't create an element that may accidentally become not antialiased.
991 rect.outset(0.5f, 0.5f);
992 }
993 SkASSERT(!GrClip::IsPixelAligned(rect));
994 } else {
995 rect.setXYWH(SkScalarFloorToScalar(xy.fX),
996 SkScalarFloorToScalar(xy.fY),
997 SkScalarCeilToScalar(size.fWidth),
998 SkScalarCeilToScalar(size.fHeight));
999 }
bsalomon@google.com51a62862012-11-26 21:19:43 +00001000
bsalomon@google.com705e8402012-11-27 15:43:57 +00001001 bool invert = r.nextBiasedBool(kFractionInverted);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001002
csmartdaltoncbecb082016-07-22 08:59:08 -07001003 kElementFuncs[r.nextULessThan(SK_ARRAY_COUNT(kElementFuncs))](rect, invert, op, &stack,
1004 doAA);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001005 if (doSave) {
1006 stack.save();
1007 }
1008 }
1009
csmartdalton8d3f92a2016-08-17 09:39:38 -07001010 // Zero the memory we will new the GrReducedClip into. This ensures the elements gen ID
1011 // will be kInvalidGenID if left uninitialized.
1012 SkAlignedSTStorage<1, GrReducedClip> storage;
1013 memset(storage.get(), 0, sizeof(GrReducedClip));
1014 GR_STATIC_ASSERT(0 == SkClipStack::kInvalidGenID);
1015
csmartdalton77f2fae2016-08-08 09:55:06 -07001016 // Get the reduced version of the stack.
csmartdaltoncbecb082016-07-22 08:59:08 -07001017 SkRect queryBounds = kBounds;
1018 queryBounds.outset(kBounds.width() / 2, kBounds.height() / 2);
csmartdalton8d3f92a2016-08-17 09:39:38 -07001019 const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, queryBounds);
bsalomon@google.coma4444302012-12-04 15:22:12 +00001020
csmartdalton8d3f92a2016-08-17 09:39:38 -07001021 REPORTER_ASSERT_MESSAGE(reporter,
1022 reduced->elements().isEmpty() ||
1023 SkClipStack::kInvalidGenID != reduced->elementsGenID(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001024 testCase.c_str());
1025
csmartdalton8d3f92a2016-08-17 09:39:38 -07001026 if (!reduced->elements().isEmpty()) {
1027 REPORTER_ASSERT_MESSAGE(reporter, reduced->hasIBounds(), testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001028 SkRect stackBounds;
1029 SkClipStack::BoundsType stackBoundsType;
1030 stack.getBounds(&stackBounds, &stackBoundsType);
1031 if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
1032 // Unless GrReducedClip starts doing some heroic tightening of the clip bounds, this
1033 // will be true since the stack bounds are completely contained inside the query.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001034 REPORTER_ASSERT_MESSAGE(reporter,
1035 GrClip::IsInsideClip(reduced->ibounds(), stackBounds),
csmartdaltoncbecb082016-07-22 08:59:08 -07001036 testCase.c_str());
1037 }
csmartdalton8d3f92a2016-08-17 09:39:38 -07001038 REPORTER_ASSERT_MESSAGE(reporter, reduced->requiresAA() == doAA, testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001039 }
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001040
bsalomon@google.com51a62862012-11-26 21:19:43 +00001041 // Build a new clip stack based on the reduced clip elements
1042 SkClipStack reducedStack;
csmartdalton8d3f92a2016-08-17 09:39:38 -07001043 if (GrReducedClip::InitialState::kAllOut == reduced->initialState()) {
bsalomon@google.com51a62862012-11-26 21:19:43 +00001044 // whether the result is bounded or not, the whole plane should start outside the clip.
1045 reducedStack.clipEmpty();
1046 }
csmartdalton8d3f92a2016-08-17 09:39:38 -07001047 for (ElementList::Iter iter(reduced->elements()); iter.get(); iter.next()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001048 add_elem_to_stack(*iter.get(), &reducedStack);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001049 }
bsalomon@google.com51a62862012-11-26 21:19:43 +00001050
csmartdalton8d3f92a2016-08-17 09:39:38 -07001051 SkIRect ibounds = reduced->hasIBounds() ? reduced->ibounds() : kIBounds;
csmartdaltond211e782016-08-15 11:17:19 -07001052
bsalomon@google.com34cd70a2012-12-06 14:23:20 +00001053 // GrReducedClipStack assumes that the final result is clipped to the returned bounds
csmartdaltond211e782016-08-15 11:17:19 -07001054 reducedStack.clipDevRect(ibounds, SkRegion::kIntersect_Op);
1055 stack.clipDevRect(ibounds, SkRegion::kIntersect_Op);
bsalomon@google.com34cd70a2012-12-06 14:23:20 +00001056
bsalomon@google.com51a62862012-11-26 21:19:43 +00001057 // convert both the original stack and reduced stack to SkRegions and see if they're equal
bsalomon@google.com51a62862012-11-26 21:19:43 +00001058 SkRegion region;
csmartdaltond211e782016-08-15 11:17:19 -07001059 set_region_to_stack(stack, ibounds, &region);
csmartdaltond50e2402016-07-22 08:39:06 -07001060
bsalomon@google.com51a62862012-11-26 21:19:43 +00001061 SkRegion reducedRegion;
csmartdaltond211e782016-08-15 11:17:19 -07001062 set_region_to_stack(reducedStack, ibounds, &reducedRegion);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001063
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001064 REPORTER_ASSERT_MESSAGE(reporter, region == reducedRegion, testCase.c_str());
csmartdalton8d3f92a2016-08-17 09:39:38 -07001065
1066 reduced->~GrReducedClip();
bsalomon@google.com51a62862012-11-26 21:19:43 +00001067 }
1068}
1069
halcanary4dbbd042016-06-07 17:21:10 -07001070#ifdef SK_BUILD_FOR_WIN
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001071 #define SUPPRESS_VISIBILITY_WARNING
1072#else
1073 #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden")))
1074#endif
1075
1076static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) {
1077 {
1078 SkClipStack stack;
1079 stack.clipDevRect(SkRect::MakeXYWH(0, 0, 100, 100), SkRegion::kReplace_Op, true);
1080 stack.clipDevRect(SkRect::MakeXYWH(0, 0, SkScalar(50.3), SkScalar(50.3)), SkRegion::kReplace_Op, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001081 SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001082
csmartdalton8d3f92a2016-08-17 09:39:38 -07001083 SkAlignedSTStorage<1, GrReducedClip> storage;
1084 memset(storage.get(), 0, sizeof(GrReducedClip));
1085 GR_STATIC_ASSERT(0 == SkClipStack::kInvalidGenID);
1086 const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, bounds);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001087
csmartdalton8d3f92a2016-08-17 09:39:38 -07001088 REPORTER_ASSERT(reporter, reduced->elements().count() == 1);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001089 // Clips will be cached based on the generation id. Make sure the gen id is valid.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001090 REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reduced->elementsGenID());
1091
1092 reduced->~GrReducedClip();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001093 }
1094 {
1095 SkClipStack stack;
1096
1097 // Create a clip with following 25.3, 25.3 boxes which are 25 apart:
1098 // A B
1099 // C D
1100
1101 stack.clipDevRect(SkRect::MakeXYWH(0, 0, SkScalar(25.3), SkScalar(25.3)), SkRegion::kReplace_Op, true);
1102 int32_t genIDA = stack.getTopmostGenID();
1103 stack.clipDevRect(SkRect::MakeXYWH(50, 0, SkScalar(25.3), SkScalar(25.3)), SkRegion::kUnion_Op, true);
1104 int32_t genIDB = stack.getTopmostGenID();
1105 stack.clipDevRect(SkRect::MakeXYWH(0, 50, SkScalar(25.3), SkScalar(25.3)), SkRegion::kUnion_Op, true);
1106 int32_t genIDC = stack.getTopmostGenID();
1107 stack.clipDevRect(SkRect::MakeXYWH(50, 50, SkScalar(25.3), SkScalar(25.3)), SkRegion::kUnion_Op, true);
1108 int32_t genIDD = stack.getTopmostGenID();
1109
1110
csmartdaltoncbecb082016-07-22 08:59:08 -07001111#define IXYWH SkIRect::MakeXYWH
1112#define XYWH SkRect::MakeXYWH
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001113
csmartdaltoncbecb082016-07-22 08:59:08 -07001114 SkIRect stackBounds = IXYWH(0, 0, 76, 76);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001115
1116 // The base test is to test each rect in two ways:
1117 // 1) The box dimensions. (Should reduce to "all in", no elements).
1118 // 2) A bit over the box dimensions.
1119 // In the case 2, test that the generation id is what is expected.
1120 // The rects are of fractional size so that case 2 never gets optimized to an empty element
1121 // list.
1122
1123 // Not passing in tighter bounds is tested for consistency.
1124 static const struct SUPPRESS_VISIBILITY_WARNING {
csmartdaltoncbecb082016-07-22 08:59:08 -07001125 SkRect testBounds;
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001126 int reducedClipCount;
1127 int32_t reducedGenID;
csmartdaltoncbecb082016-07-22 08:59:08 -07001128 InitialState initialState;
1129 SkIRect clipIRect;
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001130 // parameter.
1131 } testCases[] = {
csmartdalton77f2fae2016-08-08 09:55:06 -07001132
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001133 // Rect A.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001134 { XYWH(0, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 0, 25, 25) },
1135 { 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 -07001136 { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::InitialState::kAllOut, IXYWH(0, 0, 27, 27)},
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001137
1138 // Rect B.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001139 { XYWH(50, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 25, 25) },
1140 { XYWH(50, 0, 25.3f, 25.3f), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 26, 26) },
csmartdalton77f2fae2016-08-08 09:55:06 -07001141 { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::InitialState::kAllOut, IXYWH(50, 0, 26, 27) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001142
1143 // Rect C.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001144 { XYWH(0, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 50, 25, 25) },
1145 { 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 -07001146 { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::InitialState::kAllOut, IXYWH(0, 50, 27, 26) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001147
1148 // Rect D.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001149 { XYWH(50, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 25, 25)},
1150 { XYWH(50.3f, 50.3f, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 26, 26)},
csmartdalton77f2fae2016-08-08 09:55:06 -07001151 { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(50, 50, 26, 26)},
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001152
1153 // Other tests:
csmartdalton77f2fae2016-08-08 09:55:06 -07001154 { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::InitialState::kAllOut, stackBounds },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001155
1156 // Rect in the middle, touches none.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001157 { 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 +00001158
1159 // Rect in the middle, touches all the rects. GenID is the last rect.
csmartdalton77f2fae2016-08-08 09:55:06 -07001160 { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(24, 24, 27, 27) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001161 };
1162
1163#undef XYWH
csmartdaltoncbecb082016-07-22 08:59:08 -07001164#undef IXYWH
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001165
1166 for (size_t i = 0; i < SK_ARRAY_COUNT(testCases); ++i) {
csmartdalton77f2fae2016-08-08 09:55:06 -07001167 const GrReducedClip reduced(stack, testCases[i].testBounds);
1168 REPORTER_ASSERT(reporter, reduced.elements().count() == testCases[i].reducedClipCount);
1169 SkASSERT(reduced.elements().count() == testCases[i].reducedClipCount);
csmartdalton8d3f92a2016-08-17 09:39:38 -07001170 if (reduced.elements().count()) {
1171 REPORTER_ASSERT(reporter, reduced.elementsGenID() == testCases[i].reducedGenID);
1172 SkASSERT(reduced.elementsGenID() == testCases[i].reducedGenID);
1173 }
csmartdalton77f2fae2016-08-08 09:55:06 -07001174 REPORTER_ASSERT(reporter, reduced.initialState() == testCases[i].initialState);
1175 SkASSERT(reduced.initialState() == testCases[i].initialState);
csmartdaltond211e782016-08-15 11:17:19 -07001176 REPORTER_ASSERT(reporter, reduced.hasIBounds());
1177 SkASSERT(reduced.hasIBounds());
1178 REPORTER_ASSERT(reporter, reduced.ibounds() == testCases[i].clipIRect);
1179 SkASSERT(reduced.ibounds() == testCases[i].clipIRect);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001180 }
1181 }
1182}
1183
1184static void test_reduced_clip_stack_no_aa_crash(skiatest::Reporter* reporter) {
1185 SkClipStack stack;
1186 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 100, 100), SkRegion::kReplace_Op);
1187 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 50, 50), SkRegion::kReplace_Op);
csmartdaltoncbecb082016-07-22 08:59:08 -07001188 SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001189
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001190 // At the time, this would crash.
csmartdalton77f2fae2016-08-08 09:55:06 -07001191 const GrReducedClip reduced(stack, bounds);
1192 REPORTER_ASSERT(reporter, reduced.elements().isEmpty());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001193}
1194
csmartdaltoncbecb082016-07-22 08:59:08 -07001195enum class ClipMethod {
1196 kSkipDraw,
1197 kIgnoreClip,
1198 kScissor,
1199 kAAElements
1200};
1201
1202static void test_aa_query(skiatest::Reporter* reporter, const SkString& testName,
1203 const SkClipStack& stack, const SkMatrix& queryXform,
1204 const SkRect& preXformQuery, ClipMethod expectedMethod,
1205 int numExpectedElems = 0) {
csmartdaltoncbecb082016-07-22 08:59:08 -07001206 SkRect queryBounds;
1207 queryXform.mapRect(&queryBounds, preXformQuery);
csmartdalton77f2fae2016-08-08 09:55:06 -07001208 const GrReducedClip reduced(stack, queryBounds);
csmartdaltoncbecb082016-07-22 08:59:08 -07001209
1210 SkClipStack::BoundsType stackBoundsType;
1211 SkRect stackBounds;
1212 stack.getBounds(&stackBounds, &stackBoundsType);
1213
1214 switch (expectedMethod) {
1215 case ClipMethod::kSkipDraw:
1216 SkASSERT(0 == numExpectedElems);
csmartdalton77f2fae2016-08-08 09:55:06 -07001217 REPORTER_ASSERT_MESSAGE(reporter, reduced.elements().isEmpty(), testName.c_str());
1218 REPORTER_ASSERT_MESSAGE(reporter,
1219 GrReducedClip::InitialState::kAllOut == reduced.initialState(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001220 testName.c_str());
1221 return;
1222 case ClipMethod::kIgnoreClip:
1223 SkASSERT(0 == numExpectedElems);
csmartdaltond211e782016-08-15 11:17:19 -07001224 REPORTER_ASSERT_MESSAGE(reporter,
1225 !reduced.hasIBounds() ||
1226 GrClip::IsInsideClip(reduced.ibounds(), queryBounds),
csmartdaltoncbecb082016-07-22 08:59:08 -07001227 testName.c_str());
csmartdalton77f2fae2016-08-08 09:55:06 -07001228 REPORTER_ASSERT_MESSAGE(reporter, reduced.elements().isEmpty(), testName.c_str());
1229 REPORTER_ASSERT_MESSAGE(reporter,
1230 GrReducedClip::InitialState::kAllIn == reduced.initialState(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001231 testName.c_str());
1232 return;
1233 case ClipMethod::kScissor: {
1234 SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType);
1235 SkASSERT(0 == numExpectedElems);
1236 SkIRect expectedScissor;
1237 stackBounds.round(&expectedScissor);
csmartdalton77f2fae2016-08-08 09:55:06 -07001238 REPORTER_ASSERT_MESSAGE(reporter, reduced.elements().isEmpty(), testName.c_str());
csmartdaltond211e782016-08-15 11:17:19 -07001239 REPORTER_ASSERT_MESSAGE(reporter, reduced.hasIBounds(), testName.c_str());
1240 REPORTER_ASSERT_MESSAGE(reporter, expectedScissor == reduced.ibounds(),
csmartdalton77f2fae2016-08-08 09:55:06 -07001241 testName.c_str());
1242 REPORTER_ASSERT_MESSAGE(reporter,
1243 GrReducedClip::InitialState::kAllIn == reduced.initialState(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001244 testName.c_str());
1245 return;
1246 }
1247 case ClipMethod::kAAElements: {
1248 SkIRect expectedClipIBounds = GrClip::GetPixelIBounds(queryBounds);
1249 if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
1250 SkAssertResult(expectedClipIBounds.intersect(GrClip::GetPixelIBounds(stackBounds)));
1251 }
csmartdalton77f2fae2016-08-08 09:55:06 -07001252 REPORTER_ASSERT_MESSAGE(reporter, numExpectedElems == reduced.elements().count(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001253 testName.c_str());
csmartdaltond211e782016-08-15 11:17:19 -07001254 REPORTER_ASSERT_MESSAGE(reporter, reduced.hasIBounds(), testName.c_str());
1255 REPORTER_ASSERT_MESSAGE(reporter, expectedClipIBounds == reduced.ibounds(),
csmartdalton77f2fae2016-08-08 09:55:06 -07001256 testName.c_str());
1257 REPORTER_ASSERT_MESSAGE(reporter, reduced.requiresAA() == !reduced.elements().isEmpty(),
csmartdaltoncbecb082016-07-22 08:59:08 -07001258 testName.c_str());
1259 break;
1260 }
1261 }
1262}
1263
1264static void test_reduced_clip_stack_aa(skiatest::Reporter* reporter) {
1265 constexpr SkScalar IL = 2, IT = 1, IR = 6, IB = 7; // Pixel aligned rect.
1266 constexpr SkScalar L = 2.2f, T = 1.7f, R = 5.8f, B = 7.3f; // Generic rect.
1267 constexpr SkScalar l = 3.3f, t = 2.8f, r = 4.7f, b = 6.2f; // Small rect contained in R.
1268
1269 SkRect alignedRect = {IL, IT, IR, IB};
1270 SkRect rect = {L, T, R, B};
1271 SkRect innerRect = {l, t, r, b};
1272
1273 SkMatrix m;
1274 m.setIdentity();
1275
1276 constexpr SkScalar kMinScale = 2.0001f;
1277 constexpr SkScalar kMaxScale = 3;
1278 constexpr int kNumIters = 8;
1279
1280 SkString name;
1281 SkRandom rand;
1282
1283 for (int i = 0; i < kNumIters; ++i) {
1284 // Pixel-aligned rect (iior=true).
1285 name.printf("Pixel-aligned rect test, iter %i", i);
1286 SkClipStack stack;
1287 stack.clipDevRect(alignedRect, SkRegion::kIntersect_Op, true);
1288 test_aa_query(reporter, name, stack, m, {IL, IT, IR, IB}, ClipMethod::kIgnoreClip);
1289 test_aa_query(reporter, name, stack, m, {IL, IT-1, IR, IT}, ClipMethod::kSkipDraw);
csmartdalton77f2fae2016-08-08 09:55:06 -07001290 test_aa_query(reporter, name, stack, m, {IL, IT-2, IR, IB}, ClipMethod::kScissor);
csmartdaltoncbecb082016-07-22 08:59:08 -07001291
1292 // Rect (iior=true).
1293 name.printf("Rect test, iter %i", i);
1294 stack.reset();
1295 stack.clipDevRect(rect, SkRegion::kIntersect_Op, true);
1296 test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kIgnoreClip);
1297 test_aa_query(reporter, name, stack, m, {L-.1f, T, L, B}, ClipMethod::kSkipDraw);
1298 test_aa_query(reporter, name, stack, m, {L-.1f, T, L+.1f, B}, ClipMethod::kAAElements, 1);
1299
1300 // Difference rect (iior=false, inside-out bounds).
1301 name.printf("Difference rect test, iter %i", i);
1302 stack.reset();
1303 stack.clipDevRect(rect, SkRegion::kDifference_Op, true);
1304 test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kSkipDraw);
1305 test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T}, ClipMethod::kIgnoreClip);
1306 test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T+.1f}, ClipMethod::kAAElements, 1);
1307
1308 // Complex clip (iior=false, normal bounds).
1309 name.printf("Complex clip test, iter %i", i);
1310 stack.reset();
1311 stack.clipDevRect(rect, SkRegion::kIntersect_Op, true);
1312 stack.clipDevRect(innerRect, SkRegion::kXOR_Op, true);
1313 test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
1314 test_aa_query(reporter, name, stack, m, {r-.1f, t, R, b}, ClipMethod::kAAElements, 1);
1315 test_aa_query(reporter, name, stack, m, {r-.1f, t, R+.1f, b}, ClipMethod::kAAElements, 2);
1316 test_aa_query(reporter, name, stack, m, {r, t, R+.1f, b}, ClipMethod::kAAElements, 1);
1317 test_aa_query(reporter, name, stack, m, {r, t, R, b}, ClipMethod::kIgnoreClip);
1318 test_aa_query(reporter, name, stack, m, {R, T, R+.1f, B}, ClipMethod::kSkipDraw);
1319
1320 // Complex clip where outer rect is pixel aligned (iior=false, normal bounds).
1321 name.printf("Aligned Complex clip test, iter %i", i);
1322 stack.reset();
1323 stack.clipDevRect(alignedRect, SkRegion::kIntersect_Op, true);
1324 stack.clipDevRect(innerRect, SkRegion::kXOR_Op, true);
1325 test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
1326 test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB}, ClipMethod::kAAElements, 1);
1327 test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB+.1f}, ClipMethod::kAAElements, 1);
1328 test_aa_query(reporter, name, stack, m, {l, b, r, IB+.1f}, ClipMethod::kAAElements, 0);
1329 test_aa_query(reporter, name, stack, m, {l, b, r, IB}, ClipMethod::kIgnoreClip);
1330 test_aa_query(reporter, name, stack, m, {IL, IB, IR, IB+.1f}, ClipMethod::kSkipDraw);
1331
1332 // Apply random transforms and try again. This ensures the clip stack reduction is hardened
1333 // against FP rounding error.
1334 SkScalar sx = rand.nextRangeScalar(kMinScale, kMaxScale);
1335 sx = SkScalarFloorToScalar(sx * alignedRect.width()) / alignedRect.width();
1336 SkScalar sy = rand.nextRangeScalar(kMinScale, kMaxScale);
1337 sy = SkScalarFloorToScalar(sy * alignedRect.height()) / alignedRect.height();
1338 SkScalar tx = SkScalarRoundToScalar(sx * alignedRect.x()) - sx * alignedRect.x();
1339 SkScalar ty = SkScalarRoundToScalar(sy * alignedRect.y()) - sy * alignedRect.y();
1340
1341 SkMatrix xform = SkMatrix::MakeScale(sx, sy);
1342 xform.postTranslate(tx, ty);
1343 xform.mapRect(&alignedRect);
1344 xform.mapRect(&rect);
1345 xform.mapRect(&innerRect);
1346 m.postConcat(xform);
1347 }
1348}
1349
bsalomon@google.coma4e13c82012-11-26 21:38:37 +00001350#endif
bsalomon@google.com51a62862012-11-26 21:19:43 +00001351
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00001352DEF_TEST(ClipStack, reporter) {
reed@google.combdee9fc2011-02-22 20:17:43 +00001353 SkClipStack stack;
1354
robertphillips@google.com80214e22012-07-20 15:33:18 +00001355 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001356 assert_count(reporter, stack, 0);
1357
1358 static const SkIRect gRects[] = {
1359 { 0, 0, 100, 100 },
1360 { 25, 25, 125, 125 },
1361 { 0, 0, 1000, 1000 },
1362 { 0, 0, 75, 75 }
1363 };
1364 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
reed@google.comd9f2dea2011-10-12 14:43:27 +00001365 stack.clipDevRect(gRects[i], SkRegion::kIntersect_Op);
reed@google.combdee9fc2011-02-22 20:17:43 +00001366 }
1367
1368 // all of the above rects should have been intersected, leaving only 1 rect
robertphillips@google.com80214e22012-07-20 15:33:18 +00001369 SkClipStack::B2TIter iter(stack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001370 const SkClipStack::Element* element = iter.next();
epoger@google.com2047f002011-05-17 17:36:59 +00001371 SkRect answer;
1372 answer.iset(25, 25, 75, 75);
reed@google.combdee9fc2011-02-22 20:17:43 +00001373
bsalomon49f085d2014-09-05 13:34:00 -07001374 REPORTER_ASSERT(reporter, element);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001375 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
1376 REPORTER_ASSERT(reporter, SkRegion::kIntersect_Op == element->getOp());
1377 REPORTER_ASSERT(reporter, element->getRect() == answer);
reed@google.combdee9fc2011-02-22 20:17:43 +00001378 // now check that we only had one in our iterator
1379 REPORTER_ASSERT(reporter, !iter.next());
1380
1381 stack.reset();
robertphillips@google.com80214e22012-07-20 15:33:18 +00001382 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001383 assert_count(reporter, stack, 0);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +00001384
1385 test_assign_and_comparison(reporter);
robertphillips@google.com80214e22012-07-20 15:33:18 +00001386 test_iterators(reporter);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001387 test_bounds(reporter, SkClipStack::Element::kRect_Type);
1388 test_bounds(reporter, SkClipStack::Element::kRRect_Type);
1389 test_bounds(reporter, SkClipStack::Element::kPath_Type);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +00001390 test_isWideOpen(reporter);
robertphillips@google.com08eacc12012-08-02 12:49:00 +00001391 test_rect_merging(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001392 test_rect_replace(reporter);
junov@chromium.orgedf32d52012-12-10 14:57:54 +00001393 test_rect_inverse_fill(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001394 test_path_replace(reporter);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +00001395 test_quickContains(reporter);
csmartdaltond50e2402016-07-22 08:39:06 -07001396 test_invfill_diff_bug(reporter);
bsalomon@google.come7b3d292012-11-26 21:42:32 +00001397#if SK_SUPPORT_GPU
bsalomon@google.comedb26fd2012-11-28 14:42:41 +00001398 test_reduced_clip_stack(reporter);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001399 test_reduced_clip_stack_genid(reporter);
1400 test_reduced_clip_stack_no_aa_crash(reporter);
csmartdaltoncbecb082016-07-22 08:59:08 -07001401 test_reduced_clip_stack_aa(reporter);
bsalomon@google.come7b3d292012-11-26 21:42:32 +00001402#endif
reed@google.combdee9fc2011-02-22 20:17:43 +00001403}