blob: 05f9fcaaa3391f62faf4beab51de56aecc0cb1aa [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"
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00009#include "TestClassDef.h"
bsalomon@google.coma4e13c82012-11-26 21:38:37 +000010#if SK_SUPPORT_GPU
bsalomon@google.com170bd792012-12-05 22:26:11 +000011 #include "GrReducedClip.h"
bsalomon@google.coma4e13c82012-11-26 21:38:37 +000012#endif
reed@google.combdee9fc2011-02-22 20:17:43 +000013#include "SkClipStack.h"
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000014#include "SkPath.h"
bsalomon@google.com51a62862012-11-26 21:19:43 +000015#include "SkRandom.h"
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000016#include "SkRect.h"
bsalomon@google.com51a62862012-11-26 21:19:43 +000017#include "SkRegion.h"
robertphillips@google.com80214e22012-07-20 15:33:18 +000018
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000019static void test_assign_and_comparison(skiatest::Reporter* reporter) {
20 SkClipStack s;
reed@google.comd9f2dea2011-10-12 14:43:27 +000021 bool doAA = false;
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000022
robertphillips@google.com80214e22012-07-20 15:33:18 +000023 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
24
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000025 // Build up a clip stack with a path, an empty clip, and a rect.
26 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000027 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
28
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000029 SkPath p;
30 p.moveTo(5, 6);
31 p.lineTo(7, 8);
32 p.lineTo(5, 9);
33 p.close();
reed@google.comd9f2dea2011-10-12 14:43:27 +000034 s.clipDevPath(p, SkRegion::kIntersect_Op, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000035
36 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000037 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
38
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000039 SkRect r = SkRect::MakeLTRB(1, 2, 3, 4);
reed@google.comd9f2dea2011-10-12 14:43:27 +000040 s.clipDevRect(r, SkRegion::kIntersect_Op, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000041 r = SkRect::MakeLTRB(10, 11, 12, 13);
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
44 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000045 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
46
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000047 r = SkRect::MakeLTRB(14, 15, 16, 17);
reed@google.comd9f2dea2011-10-12 14:43:27 +000048 s.clipDevRect(r, SkRegion::kUnion_Op, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000049
50 // Test that assignment works.
51 SkClipStack copy = s;
52 REPORTER_ASSERT(reporter, s == copy);
53
54 // Test that different save levels triggers not equal.
55 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000056 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000057 REPORTER_ASSERT(reporter, s != copy);
58
59 // Test that an equal, but not copied version is equal.
60 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000061 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
62
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000063 r = SkRect::MakeLTRB(14, 15, 16, 17);
reed@google.comd9f2dea2011-10-12 14:43:27 +000064 s.clipDevRect(r, SkRegion::kUnion_Op, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000065 REPORTER_ASSERT(reporter, s == copy);
66
67 // Test that a different op on one level triggers not equal.
68 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000069 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000070 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000071 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
72
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
77 // Test that different state (clip type) triggers not equal.
tomhudson@google.com4c433722012-03-09 16:48:20 +000078 // NO LONGER VALID: if a path contains only a rect, we turn
79 // it into a bare rect for performance reasons (working
80 // around Chromium/JavaScript bad pattern).
81/*
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000082 s.restore();
83 s.save();
84 SkPath rp;
85 rp.addRect(r);
reed@google.comd9f2dea2011-10-12 14:43:27 +000086 s.clipDevPath(rp, SkRegion::kUnion_Op, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000087 REPORTER_ASSERT(reporter, s != copy);
tomhudson@google.com4c433722012-03-09 16:48:20 +000088*/
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000089
90 // Test that different rects triggers not equal.
91 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000092 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000093 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000094 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
95
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000096 r = SkRect::MakeLTRB(24, 25, 26, 27);
reed@google.comd9f2dea2011-10-12 14:43:27 +000097 s.clipDevRect(r, SkRegion::kUnion_Op, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000098 REPORTER_ASSERT(reporter, s != copy);
99
100 // Sanity check
101 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000102 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
103
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000104 copy.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000105 REPORTER_ASSERT(reporter, 2 == copy.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000106 REPORTER_ASSERT(reporter, s == copy);
107 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000108 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000109 copy.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000110 REPORTER_ASSERT(reporter, 1 == copy.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000111 REPORTER_ASSERT(reporter, s == copy);
112
113 // Test that different paths triggers not equal.
114 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000115 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000116 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000117 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
118
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000119 p.addRect(r);
reed@google.comd9f2dea2011-10-12 14:43:27 +0000120 s.clipDevPath(p, SkRegion::kIntersect_Op, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000121 REPORTER_ASSERT(reporter, s != copy);
122}
reed@google.combdee9fc2011-02-22 20:17:43 +0000123
124static void assert_count(skiatest::Reporter* reporter, const SkClipStack& stack,
125 int count) {
robertphillips@google.com80214e22012-07-20 15:33:18 +0000126 SkClipStack::B2TIter iter(stack);
reed@google.combdee9fc2011-02-22 20:17:43 +0000127 int counter = 0;
128 while (iter.next()) {
129 counter += 1;
130 }
131 REPORTER_ASSERT(reporter, count == counter);
132}
133
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000134// Exercise the SkClipStack's bottom to top and bidirectional iterators
135// (including the skipToTopmost functionality)
robertphillips@google.com80214e22012-07-20 15:33:18 +0000136static void test_iterators(skiatest::Reporter* reporter) {
137 SkClipStack stack;
138
139 static const SkRect gRects[] = {
140 { 0, 0, 40, 40 },
141 { 60, 0, 100, 40 },
142 { 0, 60, 40, 100 },
143 { 60, 60, 100, 100 }
144 };
145
146 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
147 // the union op will prevent these from being fused together
148 stack.clipDevRect(gRects[i], SkRegion::kUnion_Op, false);
149 }
150
151 assert_count(reporter, stack, 4);
152
153 // bottom to top iteration
154 {
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000155 const SkClipStack::Element* element = NULL;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000156
157 SkClipStack::B2TIter iter(stack);
158 int i;
159
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000160 for (i = 0, element = iter.next(); element; ++i, element = iter.next()) {
161 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
162 REPORTER_ASSERT(reporter, element->getRect() == gRects[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000163 }
164
165 SkASSERT(i == 4);
166 }
167
168 // top to bottom iteration
169 {
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000170 const SkClipStack::Element* element = NULL;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000171
172 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
173 int i;
174
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000175 for (i = 3, element = iter.prev(); element; --i, element = iter.prev()) {
176 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
177 REPORTER_ASSERT(reporter, element->getRect() == gRects[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000178 }
179
180 SkASSERT(i == -1);
181 }
182
183 // skipToTopmost
184 {
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000185 const SkClipStack::Element* element = NULL;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000186
187 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
188
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000189 element = iter.skipToTopmost(SkRegion::kUnion_Op);
190 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
191 REPORTER_ASSERT(reporter, element->getRect() == gRects[3]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000192 }
193}
194
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000195// Exercise the SkClipStack's getConservativeBounds computation
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000196static void test_bounds(skiatest::Reporter* reporter, bool useRects) {
robertphillips@google.com607fe072012-07-24 13:54:00 +0000197
198 static const int gNumCases = 20;
199 static const SkRect gAnswerRectsBW[gNumCases] = {
200 // A op B
201 { 40, 40, 50, 50 },
202 { 10, 10, 50, 50 },
203 { 10, 10, 80, 80 },
204 { 10, 10, 80, 80 },
205 { 40, 40, 80, 80 },
206
207 // invA op B
208 { 40, 40, 80, 80 },
209 { 0, 0, 100, 100 },
210 { 0, 0, 100, 100 },
211 { 0, 0, 100, 100 },
212 { 40, 40, 50, 50 },
213
214 // A op invB
215 { 10, 10, 50, 50 },
216 { 40, 40, 50, 50 },
217 { 0, 0, 100, 100 },
218 { 0, 0, 100, 100 },
219 { 0, 0, 100, 100 },
220
221 // invA op invB
222 { 0, 0, 100, 100 },
223 { 40, 40, 80, 80 },
224 { 0, 0, 100, 100 },
225 { 10, 10, 80, 80 },
226 { 10, 10, 50, 50 },
227 };
228
229 static const SkRegion::Op gOps[] = {
230 SkRegion::kIntersect_Op,
231 SkRegion::kDifference_Op,
232 SkRegion::kUnion_Op,
233 SkRegion::kXOR_Op,
234 SkRegion::kReverseDifference_Op
235 };
236
237 SkRect rectA, rectB;
238
239 rectA.iset(10, 10, 50, 50);
240 rectB.iset(40, 40, 80, 80);
241
242 SkPath clipA, clipB;
243
244 clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
245 clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
246
247 SkClipStack stack;
robertphillips@google.com7b112892012-07-31 15:18:21 +0000248 SkRect devClipBound;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000249 bool isIntersectionOfRects = false;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000250
251 int testCase = 0;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000252 int numBitTests = useRects ? 1 : 4;
253 for (int invBits = 0; invBits < numBitTests; ++invBits) {
robertphillips@google.com607fe072012-07-24 13:54:00 +0000254 for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) {
255
256 stack.save();
257 bool doInvA = SkToBool(invBits & 1);
258 bool doInvB = SkToBool(invBits & 2);
259
260 clipA.setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType :
261 SkPath::kEvenOdd_FillType);
262 clipB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType :
263 SkPath::kEvenOdd_FillType);
264
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000265 if (useRects) {
266 stack.clipDevRect(rectA, SkRegion::kIntersect_Op, false);
267 stack.clipDevRect(rectB, gOps[op], false);
268 } else {
269 stack.clipDevPath(clipA, SkRegion::kIntersect_Op, false);
270 stack.clipDevPath(clipB, gOps[op], false);
271 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000272
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000273 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000274 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000275
robertphillips@google.com7b112892012-07-31 15:18:21 +0000276 stack.getConservativeBounds(0, 0, 100, 100, &devClipBound,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000277 &isIntersectionOfRects);
278
279 if (useRects) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000280 REPORTER_ASSERT(reporter, isIntersectionOfRects ==
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000281 (gOps[op] == SkRegion::kIntersect_Op));
282 } else {
283 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
284 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000285
286 SkASSERT(testCase < gNumCases);
robertphillips@google.com7b112892012-07-31 15:18:21 +0000287 REPORTER_ASSERT(reporter, devClipBound == gAnswerRectsBW[testCase]);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000288 ++testCase;
289
290 stack.restore();
291 }
292 }
293}
294
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000295// Test out 'isWideOpen' entry point
296static void test_isWideOpen(skiatest::Reporter* reporter) {
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000297 {
298 // Empty stack is wide open. Wide open stack means that gen id is wide open.
299 SkClipStack stack;
300 REPORTER_ASSERT(reporter, stack.isWideOpen());
301 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
302 }
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000303
304 SkRect rectA, rectB;
305
306 rectA.iset(10, 10, 40, 40);
307 rectB.iset(50, 50, 80, 80);
308
309 // Stack should initially be wide open
310 {
311 SkClipStack stack;
312
313 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000314 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000315 }
316
317 // Test out case where the user specifies a union that includes everything
318 {
319 SkClipStack stack;
320
321 SkPath clipA, clipB;
322
323 clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
324 clipA.setFillType(SkPath::kInverseEvenOdd_FillType);
325
326 clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
327 clipB.setFillType(SkPath::kInverseEvenOdd_FillType);
328
329 stack.clipDevPath(clipA, SkRegion::kReplace_Op, false);
330 stack.clipDevPath(clipB, SkRegion::kUnion_Op, false);
331
332 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000333 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000334 }
335
336 // Test out union w/ a wide open clip
337 {
338 SkClipStack stack;
339
340 stack.clipDevRect(rectA, SkRegion::kUnion_Op, false);
341
342 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000343 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000344 }
345
346 // Test out empty difference from a wide open clip
347 {
348 SkClipStack stack;
349
350 SkRect emptyRect;
351 emptyRect.setEmpty();
352
353 stack.clipDevRect(emptyRect, SkRegion::kDifference_Op, false);
354
355 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000356 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000357 }
358
359 // Test out return to wide open
360 {
361 SkClipStack stack;
362
363 stack.save();
364
365 stack.clipDevRect(rectA, SkRegion::kReplace_Op, false);
366
367 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000368 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000369
370 stack.restore();
371
372 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000373 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000374 }
375}
376
bsalomon@google.com100abf42012-09-05 17:40:04 +0000377static int count(const SkClipStack& stack) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000378
379 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
380
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000381 const SkClipStack::Element* element = NULL;
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000382 int count = 0;
383
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000384 for (element = iter.prev(); element; element = iter.prev(), ++count) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000385 ;
386 }
387
388 return count;
389}
390
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000391static void test_rect_inverse_fill(skiatest::Reporter* reporter) {
392 // non-intersecting rectangles
393 SkRect rect = SkRect::MakeLTRB(0, 0, 10, 10);
394
395 SkPath path;
396 path.addRect(rect);
397 path.toggleInverseFillType();
398 SkClipStack stack;
399 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
400
401 SkRect bounds;
402 SkClipStack::BoundsType boundsType;
403 stack.getBounds(&bounds, &boundsType);
404 REPORTER_ASSERT(reporter, SkClipStack::kInsideOut_BoundsType == boundsType);
405 REPORTER_ASSERT(reporter, bounds == rect);
406}
407
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000408static void test_rect_replace(skiatest::Reporter* reporter) {
409 SkRect rect = SkRect::MakeWH(100, 100);
410 SkRect rect2 = SkRect::MakeXYWH(50, 50, 100, 100);
411
412 SkRect bound;
413 SkClipStack::BoundsType type;
414 bool isIntersectionOfRects;
415
416 // Adding a new rect with the replace operator should not increase
417 // the stack depth. BW replacing BW.
418 {
419 SkClipStack stack;
420 REPORTER_ASSERT(reporter, 0 == count(stack));
421 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
422 REPORTER_ASSERT(reporter, 1 == count(stack));
423 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
424 REPORTER_ASSERT(reporter, 1 == count(stack));
425 }
426
427 // Adding a new rect with the replace operator should not increase
428 // the stack depth. AA replacing AA.
429 {
430 SkClipStack stack;
431 REPORTER_ASSERT(reporter, 0 == count(stack));
432 stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
433 REPORTER_ASSERT(reporter, 1 == count(stack));
434 stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
435 REPORTER_ASSERT(reporter, 1 == count(stack));
436 }
437
438 // Adding a new rect with the replace operator should not increase
439 // the stack depth. BW replacing AA replacing BW.
440 {
441 SkClipStack stack;
442 REPORTER_ASSERT(reporter, 0 == count(stack));
443 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
444 REPORTER_ASSERT(reporter, 1 == count(stack));
445 stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
446 REPORTER_ASSERT(reporter, 1 == count(stack));
447 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
448 REPORTER_ASSERT(reporter, 1 == count(stack));
449 }
450
451 // Make sure replace clip rects don't collapse too much.
452 {
453 SkClipStack stack;
454 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
455 stack.clipDevRect(rect2, SkRegion::kIntersect_Op, false);
456 REPORTER_ASSERT(reporter, 1 == count(stack));
457
458 stack.save();
459 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
460 REPORTER_ASSERT(reporter, 2 == count(stack));
461 stack.getBounds(&bound, &type, &isIntersectionOfRects);
462 REPORTER_ASSERT(reporter, bound == rect);
463 stack.restore();
464 REPORTER_ASSERT(reporter, 1 == count(stack));
465
466 stack.save();
467 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
468 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
469 REPORTER_ASSERT(reporter, 2 == count(stack));
470 stack.restore();
471 REPORTER_ASSERT(reporter, 1 == count(stack));
472
473 stack.save();
474 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
475 stack.clipDevRect(rect2, SkRegion::kIntersect_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}
482
483// Simplified path-based version of test_rect_replace.
484static void test_path_replace(skiatest::Reporter* reporter) {
485 SkRect rect = SkRect::MakeWH(100, 100);
486 SkPath path;
487 path.addCircle(50, 50, 50);
488
489 // Replace operation doesn't grow the stack.
490 {
491 SkClipStack stack;
492 REPORTER_ASSERT(reporter, 0 == count(stack));
493 stack.clipDevPath(path, SkRegion::kReplace_Op, false);
494 REPORTER_ASSERT(reporter, 1 == count(stack));
495 stack.clipDevPath(path, SkRegion::kReplace_Op, false);
496 REPORTER_ASSERT(reporter, 1 == count(stack));
497 }
498
499 // Replacing rect with path.
500 {
501 SkClipStack stack;
502 stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
503 REPORTER_ASSERT(reporter, 1 == count(stack));
504 stack.clipDevPath(path, SkRegion::kReplace_Op, true);
505 REPORTER_ASSERT(reporter, 1 == count(stack));
506 }
507}
508
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000509// Test out SkClipStack's merging of rect clips. In particular exercise
510// merging of aa vs. bw rects.
511static void test_rect_merging(skiatest::Reporter* reporter) {
512
513 SkRect overlapLeft = SkRect::MakeLTRB(10, 10, 50, 50);
514 SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80);
515
516 SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90);
517 SkRect nestedChild = SkRect::MakeLTRB(40, 40, 60, 60);
518
519 SkRect bound;
520 SkClipStack::BoundsType type;
521 bool isIntersectionOfRects;
522
523 // all bw overlapping - should merge
524 {
525 SkClipStack stack;
526
527 stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, false);
528
529 stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false);
530
531 REPORTER_ASSERT(reporter, 1 == count(stack));
532
533 stack.getBounds(&bound, &type, &isIntersectionOfRects);
534
535 REPORTER_ASSERT(reporter, isIntersectionOfRects);
536 }
537
538 // all aa overlapping - should merge
539 {
540 SkClipStack stack;
541
542 stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true);
543
544 stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, true);
545
546 REPORTER_ASSERT(reporter, 1 == count(stack));
547
548 stack.getBounds(&bound, &type, &isIntersectionOfRects);
549
550 REPORTER_ASSERT(reporter, isIntersectionOfRects);
551 }
552
553 // mixed overlapping - should _not_ merge
554 {
555 SkClipStack stack;
556
557 stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true);
558
559 stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false);
560
561 REPORTER_ASSERT(reporter, 2 == count(stack));
562
563 stack.getBounds(&bound, &type, &isIntersectionOfRects);
564
565 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
566 }
567
568 // mixed nested (bw inside aa) - should merge
569 {
570 SkClipStack stack;
571
572 stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, true);
573
574 stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, false);
575
576 REPORTER_ASSERT(reporter, 1 == count(stack));
577
578 stack.getBounds(&bound, &type, &isIntersectionOfRects);
579
580 REPORTER_ASSERT(reporter, isIntersectionOfRects);
581 }
582
583 // mixed nested (aa inside bw) - should merge
584 {
585 SkClipStack stack;
586
587 stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, false);
588
589 stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, true);
590
591 REPORTER_ASSERT(reporter, 1 == count(stack));
592
593 stack.getBounds(&bound, &type, &isIntersectionOfRects);
594
595 REPORTER_ASSERT(reporter, isIntersectionOfRects);
596 }
597
598 // reverse nested (aa inside bw) - should _not_ merge
599 {
600 SkClipStack stack;
601
602 stack.clipDevRect(nestedChild, SkRegion::kReplace_Op, false);
603
604 stack.clipDevRect(nestedParent, SkRegion::kIntersect_Op, true);
605
606 REPORTER_ASSERT(reporter, 2 == count(stack));
607
608 stack.getBounds(&bound, &type, &isIntersectionOfRects);
609
610 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
611 }
612}
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000613
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000614static void test_quickContains(skiatest::Reporter* reporter) {
615 SkRect testRect = SkRect::MakeLTRB(10, 10, 40, 40);
616 SkRect insideRect = SkRect::MakeLTRB(20, 20, 30, 30);
617 SkRect intersectingRect = SkRect::MakeLTRB(25, 25, 50, 50);
618 SkRect outsideRect = SkRect::MakeLTRB(0, 0, 50, 50);
619 SkRect nonIntersectingRect = SkRect::MakeLTRB(100, 100, 110, 110);
620
621 SkPath insideCircle;
622 insideCircle.addCircle(25, 25, 5);
623 SkPath intersectingCircle;
624 intersectingCircle.addCircle(25, 40, 10);
625 SkPath outsideCircle;
626 outsideCircle.addCircle(25, 25, 50);
627 SkPath nonIntersectingCircle;
628 nonIntersectingCircle.addCircle(100, 100, 5);
629
630 {
631 SkClipStack stack;
632 stack.clipDevRect(outsideRect, SkRegion::kDifference_Op, false);
633 // return false because quickContains currently does not care for kDifference_Op
634 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
635 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000636
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000637 // Replace Op tests
638 {
639 SkClipStack stack;
640 stack.clipDevRect(outsideRect, SkRegion::kReplace_Op, false);
641 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
642 }
643
644 {
645 SkClipStack stack;
646 stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
647 stack.save(); // To prevent in-place substitution by replace OP
648 stack.clipDevRect(outsideRect, SkRegion::kReplace_Op, false);
649 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
650 stack.restore();
651 }
652
653 {
654 SkClipStack stack;
655 stack.clipDevRect(outsideRect, SkRegion::kIntersect_Op, false);
656 stack.save(); // To prevent in-place substitution by replace OP
657 stack.clipDevRect(insideRect, SkRegion::kReplace_Op, false);
658 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
659 stack.restore();
660 }
661
662 // Verify proper traversal of multi-element clip
663 {
664 SkClipStack stack;
665 stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
666 // Use a path for second clip to prevent in-place intersection
667 stack.clipDevPath(outsideCircle, SkRegion::kIntersect_Op, false);
668 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
669 }
670
671 // Intersect Op tests with rectangles
672 {
673 SkClipStack stack;
674 stack.clipDevRect(outsideRect, SkRegion::kIntersect_Op, false);
675 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
676 }
677
678 {
679 SkClipStack stack;
680 stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
681 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
682 }
683
684 {
685 SkClipStack stack;
686 stack.clipDevRect(intersectingRect, SkRegion::kIntersect_Op, false);
687 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
688 }
689
690 {
691 SkClipStack stack;
692 stack.clipDevRect(nonIntersectingRect, SkRegion::kIntersect_Op, false);
693 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
694 }
695
696 // Intersect Op tests with circle paths
697 {
698 SkClipStack stack;
699 stack.clipDevPath(outsideCircle, SkRegion::kIntersect_Op, false);
700 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
701 }
702
703 {
704 SkClipStack stack;
705 stack.clipDevPath(insideCircle, SkRegion::kIntersect_Op, false);
706 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
707 }
708
709 {
710 SkClipStack stack;
711 stack.clipDevPath(intersectingCircle, SkRegion::kIntersect_Op, false);
712 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
713 }
714
715 {
716 SkClipStack stack;
717 stack.clipDevPath(nonIntersectingCircle, SkRegion::kIntersect_Op, false);
718 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
719 }
720
721 // Intersect Op tests with inverse filled rectangles
722 {
723 SkClipStack stack;
724 SkPath path;
725 path.addRect(outsideRect);
726 path.toggleInverseFillType();
727 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
728 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
729 }
730
731 {
732 SkClipStack stack;
733 SkPath path;
734 path.addRect(insideRect);
735 path.toggleInverseFillType();
736 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
737 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
738 }
739
740 {
741 SkClipStack stack;
742 SkPath path;
743 path.addRect(intersectingRect);
744 path.toggleInverseFillType();
745 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
746 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
747 }
748
749 {
750 SkClipStack stack;
751 SkPath path;
752 path.addRect(nonIntersectingRect);
753 path.toggleInverseFillType();
754 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
755 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
756 }
757
758 // Intersect Op tests with inverse filled circles
759 {
760 SkClipStack stack;
761 SkPath path = outsideCircle;
762 path.toggleInverseFillType();
763 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
764 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
765 }
766
767 {
768 SkClipStack stack;
769 SkPath path = insideCircle;
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 = intersectingCircle;
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 = nonIntersectingCircle;
786 path.toggleInverseFillType();
787 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
788 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
789 }
790}
791
bsalomon@google.com51a62862012-11-26 21:19:43 +0000792///////////////////////////////////////////////////////////////////////////////////////////////////
793
bsalomon@google.coma4e13c82012-11-26 21:38:37 +0000794#if SK_SUPPORT_GPU
bsalomon@google.com705e8402012-11-27 15:43:57 +0000795// Functions that add a shape to the clip stack. The shape is computed from a rectangle.
796// AA is always disabled since the clip stack reducer can cause changes in aa rasterization of the
797// stack. A fractional edge repeated in different elements may be rasterized fewer times using the
798// reduced stack.
799typedef void (*AddElementFunc) (const SkRect& rect,
800 bool invert,
801 SkRegion::Op op,
802 SkClipStack* stack);
bsalomon@google.coma4e13c82012-11-26 21:38:37 +0000803
bsalomon@google.com705e8402012-11-27 15:43:57 +0000804static void add_round_rect(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000805 SkPath path;
806 SkScalar rx = rect.width() / 10;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000807 SkScalar ry = rect.height() / 20;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000808 path.addRoundRect(rect, rx, ry);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000809 if (invert) {
810 path.setFillType(SkPath::kInverseWinding_FillType);
811 }
812 stack->clipDevPath(path, op, false);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000813};
814
bsalomon@google.com705e8402012-11-27 15:43:57 +0000815static void add_rect(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) {
816 if (invert) {
817 SkPath path;
818 path.addRect(rect);
819 path.setFillType(SkPath::kInverseWinding_FillType);
820 stack->clipDevPath(path, op, false);
821 } else {
822 stack->clipDevRect(rect, op, false);
823 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000824};
825
bsalomon@google.com705e8402012-11-27 15:43:57 +0000826static void add_oval(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000827 SkPath path;
828 path.addOval(rect);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000829 if (invert) {
830 path.setFillType(SkPath::kInverseWinding_FillType);
831 }
832 stack->clipDevPath(path, op, false);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000833};
834
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000835static void add_elem_to_stack(const SkClipStack::Element& element, SkClipStack* stack) {
836 switch (element.getType()) {
837 case SkClipStack::Element::kRect_Type:
838 stack->clipDevRect(element.getRect(), element.getOp(), element.isAA());
839 break;
840 case SkClipStack::Element::kPath_Type:
841 stack->clipDevPath(element.getPath(), element.getOp(), element.isAA());
842 break;
843 case SkClipStack::Element::kEmpty_Type:
844 SkDEBUGFAIL("Why did the reducer produce an explicit empty.");
845 stack->clipEmpty();
846 break;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000847 }
848}
849
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000850static void add_elem_to_region(const SkClipStack::Element& element,
bsalomon@google.com51a62862012-11-26 21:19:43 +0000851 const SkIRect& bounds,
852 SkRegion* region) {
853 SkRegion elemRegion;
854 SkRegion boundsRgn(bounds);
855
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000856 switch (element.getType()) {
857 case SkClipStack::Element::kRect_Type: {
858 SkPath path;
859 path.addRect(element.getRect());
860 elemRegion.setPath(path, boundsRgn);
861 break;
862 }
863 case SkClipStack::Element::kPath_Type:
864 elemRegion.setPath(element.getPath(), boundsRgn);
865 break;
866 case SkClipStack::Element::kEmpty_Type:
skia.committer@gmail.com73b140a2012-12-05 02:01:21 +0000867 //
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000868 region->setEmpty();
869 return;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000870 }
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000871 region->op(elemRegion, element.getOp());
bsalomon@google.com51a62862012-11-26 21:19:43 +0000872}
873
bsalomon@google.com51a62862012-11-26 21:19:43 +0000874static void test_reduced_clip_stack(skiatest::Reporter* reporter) {
875 // We construct random clip stacks, reduce them, and then rasterize both versions to verify that
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000876 // they are equal.
bsalomon@google.com51a62862012-11-26 21:19:43 +0000877
878 // All the clip elements will be contained within these bounds.
879 static const SkRect kBounds = SkRect::MakeWH(100, 100);
880
881 enum {
882 kNumTests = 200,
883 kMinElemsPerTest = 1,
884 kMaxElemsPerTest = 50,
885 };
886
887 // min/max size of a clip element as a fraction of kBounds.
888 static const SkScalar kMinElemSizeFrac = SK_Scalar1 / 5;
889 static const SkScalar kMaxElemSizeFrac = SK_Scalar1;
890
891 static const SkRegion::Op kOps[] = {
892 SkRegion::kDifference_Op,
893 SkRegion::kIntersect_Op,
894 SkRegion::kUnion_Op,
895 SkRegion::kXOR_Op,
896 SkRegion::kReverseDifference_Op,
897 SkRegion::kReplace_Op,
898 };
899
900 // Replace operations short-circuit the optimizer. We want to make sure that we test this code
901 // path a little bit but we don't want it to prevent us from testing many longer traversals in
902 // the optimizer.
903 static const int kReplaceDiv = 4 * kMaxElemsPerTest;
904
bsalomon@google.com705e8402012-11-27 15:43:57 +0000905 // We want to test inverse fills. However, they are quite rare in practice so don't over do it.
906 static const SkScalar kFractionInverted = SK_Scalar1 / kMaxElemsPerTest;
907
bsalomon@google.com51a62862012-11-26 21:19:43 +0000908 static const AddElementFunc kElementFuncs[] = {
909 add_rect,
910 add_round_rect,
911 add_oval,
912 };
913
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000914 SkRandom r;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000915
916 for (int i = 0; i < kNumTests; ++i) {
917 // Randomly generate a clip stack.
918 SkClipStack stack;
919 int numElems = r.nextRangeU(kMinElemsPerTest, kMaxElemsPerTest);
920 for (int e = 0; e < numElems; ++e) {
921 SkRegion::Op op = kOps[r.nextULessThan(SK_ARRAY_COUNT(kOps))];
922 if (op == SkRegion::kReplace_Op) {
923 if (r.nextU() % kReplaceDiv) {
924 --e;
925 continue;
926 }
927 }
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000928
bsalomon@google.com51a62862012-11-26 21:19:43 +0000929 // saves can change the clip stack behavior when an element is added.
930 bool doSave = r.nextBool();
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000931
bsalomon@google.com51a62862012-11-26 21:19:43 +0000932 SkSize size = SkSize::Make(
933 SkScalarFloorToScalar(SkScalarMul(kBounds.width(), r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac))),
934 SkScalarFloorToScalar(SkScalarMul(kBounds.height(), r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac))));
935
936 SkPoint xy = {SkScalarFloorToScalar(r.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth)),
937 SkScalarFloorToScalar(r.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight))};
938
939 SkRect rect = SkRect::MakeXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight);
940
bsalomon@google.com705e8402012-11-27 15:43:57 +0000941 bool invert = r.nextBiasedBool(kFractionInverted);
942 kElementFuncs[r.nextULessThan(SK_ARRAY_COUNT(kElementFuncs))](rect, invert, op, &stack);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000943 if (doSave) {
944 stack.save();
945 }
946 }
947
bsalomon@google.coma4444302012-12-04 15:22:12 +0000948 SkRect inflatedBounds = kBounds;
949 inflatedBounds.outset(kBounds.width() / 2, kBounds.height() / 2);
950 SkIRect inflatedIBounds;
951 inflatedBounds.roundOut(&inflatedIBounds);
952
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000953 typedef GrReducedClip::ElementList ElementList;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000954 // Get the reduced version of the stack.
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000955 ElementList reducedClips;
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000956 int32_t reducedGenID;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000957 GrReducedClip::InitialState initial;
jvanverth@google.comc490f802013-03-04 13:56:38 +0000958 SkIRect tBounds(inflatedIBounds);
bsalomon@google.com34cd70a2012-12-06 14:23:20 +0000959 SkIRect* tightBounds = r.nextBool() ? &tBounds : NULL;
960 GrReducedClip::ReduceClipStack(stack,
961 inflatedIBounds,
962 &reducedClips,
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000963 &reducedGenID,
bsalomon@google.com34cd70a2012-12-06 14:23:20 +0000964 &initial,
965 tightBounds);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000966
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000967 REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reducedGenID);
968
bsalomon@google.com51a62862012-11-26 21:19:43 +0000969 // Build a new clip stack based on the reduced clip elements
970 SkClipStack reducedStack;
971 if (GrReducedClip::kAllOut_InitialState == initial) {
972 // whether the result is bounded or not, the whole plane should start outside the clip.
973 reducedStack.clipEmpty();
974 }
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000975 for (ElementList::Iter iter = reducedClips.headIter(); NULL != iter.get(); iter.next()) {
976 add_elem_to_stack(*iter.get(), &reducedStack);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000977 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000978
bsalomon@google.com34cd70a2012-12-06 14:23:20 +0000979 // GrReducedClipStack assumes that the final result is clipped to the returned bounds
980 if (NULL != tightBounds) {
981 reducedStack.clipDevRect(*tightBounds, SkRegion::kIntersect_Op);
982 }
983
bsalomon@google.com51a62862012-11-26 21:19:43 +0000984 // convert both the original stack and reduced stack to SkRegions and see if they're equal
bsalomon@google.com51a62862012-11-26 21:19:43 +0000985 SkRegion region;
986 SkRegion reducedRegion;
987
988 region.setRect(inflatedIBounds);
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000989 const SkClipStack::Element* element;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000990 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000991 while ((element = iter.next())) {
992 add_elem_to_region(*element, inflatedIBounds, &region);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000993 }
994
995 reducedRegion.setRect(inflatedIBounds);
996 iter.reset(reducedStack, SkClipStack::Iter::kBottom_IterStart);
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000997 while ((element = iter.next())) {
998 add_elem_to_region(*element, inflatedIBounds, &reducedRegion);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000999 }
1000
1001 REPORTER_ASSERT(reporter, region == reducedRegion);
1002 }
1003}
1004
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001005#if defined(WIN32)
1006 #define SUPPRESS_VISIBILITY_WARNING
1007#else
1008 #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden")))
1009#endif
1010
1011static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) {
1012 {
1013 SkClipStack stack;
1014 stack.clipDevRect(SkRect::MakeXYWH(0, 0, 100, 100), SkRegion::kReplace_Op, true);
1015 stack.clipDevRect(SkRect::MakeXYWH(0, 0, SkScalar(50.3), SkScalar(50.3)), SkRegion::kReplace_Op, true);
1016 SkIRect inflatedIBounds = SkIRect::MakeXYWH(0, 0, 100, 100);
1017
1018 GrReducedClip::ElementList reducedClips;
1019 int32_t reducedGenID;
1020 GrReducedClip::InitialState initial;
1021 SkIRect tightBounds;
1022
1023 GrReducedClip::ReduceClipStack(stack,
1024 inflatedIBounds,
1025 &reducedClips,
1026 &reducedGenID,
1027 &initial,
1028 &tightBounds);
1029
1030 REPORTER_ASSERT(reporter, reducedClips.count() == 1);
1031 // Clips will be cached based on the generation id. Make sure the gen id is valid.
1032 REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reducedGenID);
1033 }
1034 {
1035 SkClipStack stack;
1036
1037 // Create a clip with following 25.3, 25.3 boxes which are 25 apart:
1038 // A B
1039 // C D
1040
1041 stack.clipDevRect(SkRect::MakeXYWH(0, 0, SkScalar(25.3), SkScalar(25.3)), SkRegion::kReplace_Op, true);
1042 int32_t genIDA = stack.getTopmostGenID();
1043 stack.clipDevRect(SkRect::MakeXYWH(50, 0, SkScalar(25.3), SkScalar(25.3)), SkRegion::kUnion_Op, true);
1044 int32_t genIDB = stack.getTopmostGenID();
1045 stack.clipDevRect(SkRect::MakeXYWH(0, 50, SkScalar(25.3), SkScalar(25.3)), SkRegion::kUnion_Op, true);
1046 int32_t genIDC = stack.getTopmostGenID();
1047 stack.clipDevRect(SkRect::MakeXYWH(50, 50, SkScalar(25.3), SkScalar(25.3)), SkRegion::kUnion_Op, true);
1048 int32_t genIDD = stack.getTopmostGenID();
1049
1050
1051#define XYWH SkIRect::MakeXYWH
1052
1053 SkIRect unused;
1054 unused.setEmpty();
1055 SkIRect stackBounds = XYWH(0, 0, 76, 76);
1056
1057 // The base test is to test each rect in two ways:
1058 // 1) The box dimensions. (Should reduce to "all in", no elements).
1059 // 2) A bit over the box dimensions.
1060 // In the case 2, test that the generation id is what is expected.
1061 // The rects are of fractional size so that case 2 never gets optimized to an empty element
1062 // list.
1063
1064 // Not passing in tighter bounds is tested for consistency.
1065 static const struct SUPPRESS_VISIBILITY_WARNING {
1066 SkIRect testBounds;
1067 int reducedClipCount;
1068 int32_t reducedGenID;
1069 GrReducedClip::InitialState initialState;
1070 SkIRect tighterBounds; // If this is empty, the query will not pass tighter bounds
1071 // parameter.
1072 } testCases[] = {
1073 // Rect A.
1074 { XYWH(0, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, XYWH(0, 0, 25, 25) },
1075 { XYWH(0, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, unused },
1076 { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::kAllOut_InitialState, XYWH(0, 0, 27, 27)},
1077 { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::kAllOut_InitialState, unused },
1078
1079 // Rect B.
1080 { XYWH(50, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, XYWH(50, 0, 25, 25) },
1081 { XYWH(50, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, unused },
1082 { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::kAllOut_InitialState, XYWH(50, 0, 26, 27) },
1083 { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::kAllOut_InitialState, unused },
1084
1085 // Rect C.
1086 { XYWH(0, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, XYWH(0, 50, 25, 25) },
1087 { XYWH(0, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, unused },
1088 { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::kAllOut_InitialState, XYWH(0, 50, 27, 26) },
1089 { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::kAllOut_InitialState, unused },
1090
1091 // Rect D.
1092 { XYWH(50, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, unused },
1093 { XYWH(50, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, XYWH(50, 50, 25, 25)},
1094 { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::kAllOut_InitialState, unused },
1095 { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::kAllOut_InitialState, XYWH(50, 50, 26, 26)},
1096
1097 // Other tests:
1098 { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::kAllOut_InitialState, unused },
1099 { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::kAllOut_InitialState, stackBounds },
1100
1101 // Rect in the middle, touches none.
1102 { XYWH(26, 26, 24, 24), 0, SkClipStack::kEmptyGenID, GrReducedClip::kAllOut_InitialState, unused },
1103 { XYWH(26, 26, 24, 24), 0, SkClipStack::kEmptyGenID, GrReducedClip::kAllOut_InitialState, XYWH(26, 26, 24, 24) },
1104
1105 // Rect in the middle, touches all the rects. GenID is the last rect.
1106 { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::kAllOut_InitialState, unused },
1107 { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::kAllOut_InitialState, XYWH(24, 24, 27, 27) },
1108 };
1109
1110#undef XYWH
1111
1112 for (size_t i = 0; i < SK_ARRAY_COUNT(testCases); ++i) {
1113 GrReducedClip::ElementList reducedClips;
1114 int32_t reducedGenID;
1115 GrReducedClip::InitialState initial;
1116 SkIRect tightBounds;
1117
1118 GrReducedClip::ReduceClipStack(stack,
1119 testCases[i].testBounds,
1120 &reducedClips,
1121 &reducedGenID,
1122 &initial,
1123 testCases[i].tighterBounds.isEmpty() ? NULL : &tightBounds);
1124
1125 REPORTER_ASSERT(reporter, reducedClips.count() == testCases[i].reducedClipCount);
1126 SkASSERT(reducedClips.count() == testCases[i].reducedClipCount);
1127 REPORTER_ASSERT(reporter, reducedGenID == testCases[i].reducedGenID);
1128 SkASSERT(reducedGenID == testCases[i].reducedGenID);
1129 REPORTER_ASSERT(reporter, initial == testCases[i].initialState);
1130 SkASSERT(initial == testCases[i].initialState);
1131 if (!testCases[i].tighterBounds.isEmpty()) {
1132 REPORTER_ASSERT(reporter, tightBounds == testCases[i].tighterBounds);
1133 SkASSERT(tightBounds == testCases[i].tighterBounds);
1134 }
1135 }
1136 }
1137}
1138
1139static void test_reduced_clip_stack_no_aa_crash(skiatest::Reporter* reporter) {
1140 SkClipStack stack;
1141 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 100, 100), SkRegion::kReplace_Op);
1142 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 50, 50), SkRegion::kReplace_Op);
1143 SkIRect inflatedIBounds = SkIRect::MakeXYWH(0, 0, 100, 100);
1144
1145 GrReducedClip::ElementList reducedClips;
1146 int32_t reducedGenID;
1147 GrReducedClip::InitialState initial;
1148 SkIRect tightBounds;
1149
1150 // At the time, this would crash.
1151 GrReducedClip::ReduceClipStack(stack,
1152 inflatedIBounds,
1153 &reducedClips,
1154 &reducedGenID,
1155 &initial,
1156 &tightBounds);
1157
1158 REPORTER_ASSERT(reporter, 0 == reducedClips.count());
1159}
1160
bsalomon@google.coma4e13c82012-11-26 21:38:37 +00001161#endif
bsalomon@google.com51a62862012-11-26 21:19:43 +00001162
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00001163DEF_TEST(ClipStack, reporter) {
reed@google.combdee9fc2011-02-22 20:17:43 +00001164 SkClipStack stack;
1165
robertphillips@google.com80214e22012-07-20 15:33:18 +00001166 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001167 assert_count(reporter, stack, 0);
1168
1169 static const SkIRect gRects[] = {
1170 { 0, 0, 100, 100 },
1171 { 25, 25, 125, 125 },
1172 { 0, 0, 1000, 1000 },
1173 { 0, 0, 75, 75 }
1174 };
1175 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
reed@google.comd9f2dea2011-10-12 14:43:27 +00001176 stack.clipDevRect(gRects[i], SkRegion::kIntersect_Op);
reed@google.combdee9fc2011-02-22 20:17:43 +00001177 }
1178
1179 // all of the above rects should have been intersected, leaving only 1 rect
robertphillips@google.com80214e22012-07-20 15:33:18 +00001180 SkClipStack::B2TIter iter(stack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001181 const SkClipStack::Element* element = iter.next();
epoger@google.com2047f002011-05-17 17:36:59 +00001182 SkRect answer;
1183 answer.iset(25, 25, 75, 75);
reed@google.combdee9fc2011-02-22 20:17:43 +00001184
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001185 REPORTER_ASSERT(reporter, NULL != element);
1186 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
1187 REPORTER_ASSERT(reporter, SkRegion::kIntersect_Op == element->getOp());
1188 REPORTER_ASSERT(reporter, element->getRect() == answer);
reed@google.combdee9fc2011-02-22 20:17:43 +00001189 // now check that we only had one in our iterator
1190 REPORTER_ASSERT(reporter, !iter.next());
1191
1192 stack.reset();
robertphillips@google.com80214e22012-07-20 15:33:18 +00001193 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001194 assert_count(reporter, stack, 0);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +00001195
1196 test_assign_and_comparison(reporter);
robertphillips@google.com80214e22012-07-20 15:33:18 +00001197 test_iterators(reporter);
robertphillips@google.com08eacc12012-08-02 12:49:00 +00001198 test_bounds(reporter, true); // once with rects
1199 test_bounds(reporter, false); // once with paths
robertphillips@google.comcc6493b2012-07-26 18:39:13 +00001200 test_isWideOpen(reporter);
robertphillips@google.com08eacc12012-08-02 12:49:00 +00001201 test_rect_merging(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001202 test_rect_replace(reporter);
junov@chromium.orgedf32d52012-12-10 14:57:54 +00001203 test_rect_inverse_fill(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001204 test_path_replace(reporter);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +00001205 test_quickContains(reporter);
bsalomon@google.come7b3d292012-11-26 21:42:32 +00001206#if SK_SUPPORT_GPU
bsalomon@google.comedb26fd2012-11-28 14:42:41 +00001207 test_reduced_clip_stack(reporter);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001208 test_reduced_clip_stack_genid(reporter);
1209 test_reduced_clip_stack_no_aa_crash(reporter);
bsalomon@google.come7b3d292012-11-26 21:42:32 +00001210#endif
reed@google.combdee9fc2011-02-22 20:17:43 +00001211}