blob: cbed19364ffd88e06d189b79fa88d8756b94a984 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
reed@google.combdee9fc2011-02-22 20:17:43 +00008#include "Test.h"
bsalomon@google.coma4e13c82012-11-26 21:38:37 +00009#if SK_SUPPORT_GPU
bsalomon@google.com170bd792012-12-05 22:26:11 +000010 #include "GrReducedClip.h"
bsalomon@google.coma4e13c82012-11-26 21:38:37 +000011#endif
reed@google.combdee9fc2011-02-22 20:17:43 +000012#include "SkClipStack.h"
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000013#include "SkPath.h"
bsalomon@google.com51a62862012-11-26 21:19:43 +000014#include "SkRandom.h"
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000015#include "SkRect.h"
bsalomon@google.com51a62862012-11-26 21:19:43 +000016#include "SkRegion.h"
robertphillips@google.com80214e22012-07-20 15:33:18 +000017
18
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());
274
robertphillips@google.com7b112892012-07-31 15:18:21 +0000275 stack.getConservativeBounds(0, 0, 100, 100, &devClipBound,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000276 &isIntersectionOfRects);
277
278 if (useRects) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000279 REPORTER_ASSERT(reporter, isIntersectionOfRects ==
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000280 (gOps[op] == SkRegion::kIntersect_Op));
281 } else {
282 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
283 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000284
285 SkASSERT(testCase < gNumCases);
robertphillips@google.com7b112892012-07-31 15:18:21 +0000286 REPORTER_ASSERT(reporter, devClipBound == gAnswerRectsBW[testCase]);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000287 ++testCase;
288
289 stack.restore();
290 }
291 }
292}
293
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000294// Test out 'isWideOpen' entry point
295static void test_isWideOpen(skiatest::Reporter* reporter) {
296
297 SkRect rectA, rectB;
298
299 rectA.iset(10, 10, 40, 40);
300 rectB.iset(50, 50, 80, 80);
301
302 // Stack should initially be wide open
303 {
304 SkClipStack stack;
305
306 REPORTER_ASSERT(reporter, stack.isWideOpen());
307 }
308
309 // Test out case where the user specifies a union that includes everything
310 {
311 SkClipStack stack;
312
313 SkPath clipA, clipB;
314
315 clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
316 clipA.setFillType(SkPath::kInverseEvenOdd_FillType);
317
318 clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
319 clipB.setFillType(SkPath::kInverseEvenOdd_FillType);
320
321 stack.clipDevPath(clipA, SkRegion::kReplace_Op, false);
322 stack.clipDevPath(clipB, SkRegion::kUnion_Op, false);
323
324 REPORTER_ASSERT(reporter, stack.isWideOpen());
325 }
326
327 // Test out union w/ a wide open clip
328 {
329 SkClipStack stack;
330
331 stack.clipDevRect(rectA, SkRegion::kUnion_Op, false);
332
333 REPORTER_ASSERT(reporter, stack.isWideOpen());
334 }
335
336 // Test out empty difference from a wide open clip
337 {
338 SkClipStack stack;
339
340 SkRect emptyRect;
341 emptyRect.setEmpty();
342
343 stack.clipDevRect(emptyRect, SkRegion::kDifference_Op, false);
344
345 REPORTER_ASSERT(reporter, stack.isWideOpen());
346 }
347
348 // Test out return to wide open
349 {
350 SkClipStack stack;
351
352 stack.save();
353
354 stack.clipDevRect(rectA, SkRegion::kReplace_Op, false);
355
356 REPORTER_ASSERT(reporter, !stack.isWideOpen());
357
358 stack.restore();
359
360 REPORTER_ASSERT(reporter, stack.isWideOpen());
361 }
362}
363
bsalomon@google.com100abf42012-09-05 17:40:04 +0000364static int count(const SkClipStack& stack) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000365
366 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
367
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000368 const SkClipStack::Element* element = NULL;
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000369 int count = 0;
370
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000371 for (element = iter.prev(); element; element = iter.prev(), ++count) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000372 ;
373 }
374
375 return count;
376}
377
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000378static void test_rect_inverse_fill(skiatest::Reporter* reporter) {
379 // non-intersecting rectangles
380 SkRect rect = SkRect::MakeLTRB(0, 0, 10, 10);
381
382 SkPath path;
383 path.addRect(rect);
384 path.toggleInverseFillType();
385 SkClipStack stack;
386 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
387
388 SkRect bounds;
389 SkClipStack::BoundsType boundsType;
390 stack.getBounds(&bounds, &boundsType);
391 REPORTER_ASSERT(reporter, SkClipStack::kInsideOut_BoundsType == boundsType);
392 REPORTER_ASSERT(reporter, bounds == rect);
393}
394
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000395static void test_rect_replace(skiatest::Reporter* reporter) {
396 SkRect rect = SkRect::MakeWH(100, 100);
397 SkRect rect2 = SkRect::MakeXYWH(50, 50, 100, 100);
398
399 SkRect bound;
400 SkClipStack::BoundsType type;
401 bool isIntersectionOfRects;
402
403 // Adding a new rect with the replace operator should not increase
404 // the stack depth. BW replacing BW.
405 {
406 SkClipStack stack;
407 REPORTER_ASSERT(reporter, 0 == count(stack));
408 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
409 REPORTER_ASSERT(reporter, 1 == count(stack));
410 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
411 REPORTER_ASSERT(reporter, 1 == count(stack));
412 }
413
414 // Adding a new rect with the replace operator should not increase
415 // the stack depth. AA replacing AA.
416 {
417 SkClipStack stack;
418 REPORTER_ASSERT(reporter, 0 == count(stack));
419 stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
420 REPORTER_ASSERT(reporter, 1 == count(stack));
421 stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
422 REPORTER_ASSERT(reporter, 1 == count(stack));
423 }
424
425 // Adding a new rect with the replace operator should not increase
426 // the stack depth. BW replacing AA replacing BW.
427 {
428 SkClipStack stack;
429 REPORTER_ASSERT(reporter, 0 == count(stack));
430 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
431 REPORTER_ASSERT(reporter, 1 == count(stack));
432 stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
433 REPORTER_ASSERT(reporter, 1 == count(stack));
434 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
435 REPORTER_ASSERT(reporter, 1 == count(stack));
436 }
437
438 // Make sure replace clip rects don't collapse too much.
439 {
440 SkClipStack stack;
441 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
442 stack.clipDevRect(rect2, SkRegion::kIntersect_Op, false);
443 REPORTER_ASSERT(reporter, 1 == count(stack));
444
445 stack.save();
446 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
447 REPORTER_ASSERT(reporter, 2 == count(stack));
448 stack.getBounds(&bound, &type, &isIntersectionOfRects);
449 REPORTER_ASSERT(reporter, bound == rect);
450 stack.restore();
451 REPORTER_ASSERT(reporter, 1 == count(stack));
452
453 stack.save();
454 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
455 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
456 REPORTER_ASSERT(reporter, 2 == count(stack));
457 stack.restore();
458 REPORTER_ASSERT(reporter, 1 == count(stack));
459
460 stack.save();
461 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
462 stack.clipDevRect(rect2, SkRegion::kIntersect_Op, false);
463 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
464 REPORTER_ASSERT(reporter, 2 == count(stack));
465 stack.restore();
466 REPORTER_ASSERT(reporter, 1 == count(stack));
467 }
468}
469
470// Simplified path-based version of test_rect_replace.
471static void test_path_replace(skiatest::Reporter* reporter) {
472 SkRect rect = SkRect::MakeWH(100, 100);
473 SkPath path;
474 path.addCircle(50, 50, 50);
475
476 // Replace operation doesn't grow the stack.
477 {
478 SkClipStack stack;
479 REPORTER_ASSERT(reporter, 0 == count(stack));
480 stack.clipDevPath(path, SkRegion::kReplace_Op, false);
481 REPORTER_ASSERT(reporter, 1 == count(stack));
482 stack.clipDevPath(path, SkRegion::kReplace_Op, false);
483 REPORTER_ASSERT(reporter, 1 == count(stack));
484 }
485
486 // Replacing rect with path.
487 {
488 SkClipStack stack;
489 stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
490 REPORTER_ASSERT(reporter, 1 == count(stack));
491 stack.clipDevPath(path, SkRegion::kReplace_Op, true);
492 REPORTER_ASSERT(reporter, 1 == count(stack));
493 }
494}
495
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000496// Test out SkClipStack's merging of rect clips. In particular exercise
497// merging of aa vs. bw rects.
498static void test_rect_merging(skiatest::Reporter* reporter) {
499
500 SkRect overlapLeft = SkRect::MakeLTRB(10, 10, 50, 50);
501 SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80);
502
503 SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90);
504 SkRect nestedChild = SkRect::MakeLTRB(40, 40, 60, 60);
505
506 SkRect bound;
507 SkClipStack::BoundsType type;
508 bool isIntersectionOfRects;
509
510 // all bw overlapping - should merge
511 {
512 SkClipStack stack;
513
514 stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, false);
515
516 stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false);
517
518 REPORTER_ASSERT(reporter, 1 == count(stack));
519
520 stack.getBounds(&bound, &type, &isIntersectionOfRects);
521
522 REPORTER_ASSERT(reporter, isIntersectionOfRects);
523 }
524
525 // all aa overlapping - should merge
526 {
527 SkClipStack stack;
528
529 stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true);
530
531 stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, true);
532
533 REPORTER_ASSERT(reporter, 1 == count(stack));
534
535 stack.getBounds(&bound, &type, &isIntersectionOfRects);
536
537 REPORTER_ASSERT(reporter, isIntersectionOfRects);
538 }
539
540 // mixed overlapping - should _not_ merge
541 {
542 SkClipStack stack;
543
544 stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true);
545
546 stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false);
547
548 REPORTER_ASSERT(reporter, 2 == count(stack));
549
550 stack.getBounds(&bound, &type, &isIntersectionOfRects);
551
552 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
553 }
554
555 // mixed nested (bw inside aa) - should merge
556 {
557 SkClipStack stack;
558
559 stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, true);
560
561 stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, false);
562
563 REPORTER_ASSERT(reporter, 1 == count(stack));
564
565 stack.getBounds(&bound, &type, &isIntersectionOfRects);
566
567 REPORTER_ASSERT(reporter, isIntersectionOfRects);
568 }
569
570 // mixed nested (aa inside bw) - should merge
571 {
572 SkClipStack stack;
573
574 stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, false);
575
576 stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, true);
577
578 REPORTER_ASSERT(reporter, 1 == count(stack));
579
580 stack.getBounds(&bound, &type, &isIntersectionOfRects);
581
582 REPORTER_ASSERT(reporter, isIntersectionOfRects);
583 }
584
585 // reverse nested (aa inside bw) - should _not_ merge
586 {
587 SkClipStack stack;
588
589 stack.clipDevRect(nestedChild, SkRegion::kReplace_Op, false);
590
591 stack.clipDevRect(nestedParent, SkRegion::kIntersect_Op, true);
592
593 REPORTER_ASSERT(reporter, 2 == count(stack));
594
595 stack.getBounds(&bound, &type, &isIntersectionOfRects);
596
597 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
598 }
599}
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000600
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000601static void test_quickContains(skiatest::Reporter* reporter) {
602 SkRect testRect = SkRect::MakeLTRB(10, 10, 40, 40);
603 SkRect insideRect = SkRect::MakeLTRB(20, 20, 30, 30);
604 SkRect intersectingRect = SkRect::MakeLTRB(25, 25, 50, 50);
605 SkRect outsideRect = SkRect::MakeLTRB(0, 0, 50, 50);
606 SkRect nonIntersectingRect = SkRect::MakeLTRB(100, 100, 110, 110);
607
608 SkPath insideCircle;
609 insideCircle.addCircle(25, 25, 5);
610 SkPath intersectingCircle;
611 intersectingCircle.addCircle(25, 40, 10);
612 SkPath outsideCircle;
613 outsideCircle.addCircle(25, 25, 50);
614 SkPath nonIntersectingCircle;
615 nonIntersectingCircle.addCircle(100, 100, 5);
616
617 {
618 SkClipStack stack;
619 stack.clipDevRect(outsideRect, SkRegion::kDifference_Op, false);
620 // return false because quickContains currently does not care for kDifference_Op
621 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
622 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000623
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000624 // Replace Op tests
625 {
626 SkClipStack stack;
627 stack.clipDevRect(outsideRect, SkRegion::kReplace_Op, false);
628 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
629 }
630
631 {
632 SkClipStack stack;
633 stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
634 stack.save(); // To prevent in-place substitution by replace OP
635 stack.clipDevRect(outsideRect, SkRegion::kReplace_Op, false);
636 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
637 stack.restore();
638 }
639
640 {
641 SkClipStack stack;
642 stack.clipDevRect(outsideRect, SkRegion::kIntersect_Op, false);
643 stack.save(); // To prevent in-place substitution by replace OP
644 stack.clipDevRect(insideRect, SkRegion::kReplace_Op, false);
645 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
646 stack.restore();
647 }
648
649 // Verify proper traversal of multi-element clip
650 {
651 SkClipStack stack;
652 stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
653 // Use a path for second clip to prevent in-place intersection
654 stack.clipDevPath(outsideCircle, SkRegion::kIntersect_Op, false);
655 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
656 }
657
658 // Intersect Op tests with rectangles
659 {
660 SkClipStack stack;
661 stack.clipDevRect(outsideRect, SkRegion::kIntersect_Op, false);
662 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
663 }
664
665 {
666 SkClipStack stack;
667 stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
668 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
669 }
670
671 {
672 SkClipStack stack;
673 stack.clipDevRect(intersectingRect, SkRegion::kIntersect_Op, false);
674 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
675 }
676
677 {
678 SkClipStack stack;
679 stack.clipDevRect(nonIntersectingRect, SkRegion::kIntersect_Op, false);
680 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
681 }
682
683 // Intersect Op tests with circle paths
684 {
685 SkClipStack stack;
686 stack.clipDevPath(outsideCircle, SkRegion::kIntersect_Op, false);
687 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
688 }
689
690 {
691 SkClipStack stack;
692 stack.clipDevPath(insideCircle, SkRegion::kIntersect_Op, false);
693 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
694 }
695
696 {
697 SkClipStack stack;
698 stack.clipDevPath(intersectingCircle, SkRegion::kIntersect_Op, false);
699 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
700 }
701
702 {
703 SkClipStack stack;
704 stack.clipDevPath(nonIntersectingCircle, SkRegion::kIntersect_Op, false);
705 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
706 }
707
708 // Intersect Op tests with inverse filled rectangles
709 {
710 SkClipStack stack;
711 SkPath path;
712 path.addRect(outsideRect);
713 path.toggleInverseFillType();
714 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
715 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
716 }
717
718 {
719 SkClipStack stack;
720 SkPath path;
721 path.addRect(insideRect);
722 path.toggleInverseFillType();
723 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
724 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
725 }
726
727 {
728 SkClipStack stack;
729 SkPath path;
730 path.addRect(intersectingRect);
731 path.toggleInverseFillType();
732 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
733 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
734 }
735
736 {
737 SkClipStack stack;
738 SkPath path;
739 path.addRect(nonIntersectingRect);
740 path.toggleInverseFillType();
741 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
742 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
743 }
744
745 // Intersect Op tests with inverse filled circles
746 {
747 SkClipStack stack;
748 SkPath path = outsideCircle;
749 path.toggleInverseFillType();
750 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
751 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
752 }
753
754 {
755 SkClipStack stack;
756 SkPath path = insideCircle;
757 path.toggleInverseFillType();
758 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
759 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
760 }
761
762 {
763 SkClipStack stack;
764 SkPath path = intersectingCircle;
765 path.toggleInverseFillType();
766 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
767 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
768 }
769
770 {
771 SkClipStack stack;
772 SkPath path = nonIntersectingCircle;
773 path.toggleInverseFillType();
774 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
775 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
776 }
777}
778
bsalomon@google.com51a62862012-11-26 21:19:43 +0000779///////////////////////////////////////////////////////////////////////////////////////////////////
780
bsalomon@google.coma4e13c82012-11-26 21:38:37 +0000781#if SK_SUPPORT_GPU
bsalomon@google.com705e8402012-11-27 15:43:57 +0000782// Functions that add a shape to the clip stack. The shape is computed from a rectangle.
783// AA is always disabled since the clip stack reducer can cause changes in aa rasterization of the
784// stack. A fractional edge repeated in different elements may be rasterized fewer times using the
785// reduced stack.
786typedef void (*AddElementFunc) (const SkRect& rect,
787 bool invert,
788 SkRegion::Op op,
789 SkClipStack* stack);
bsalomon@google.coma4e13c82012-11-26 21:38:37 +0000790
bsalomon@google.com705e8402012-11-27 15:43:57 +0000791static void add_round_rect(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000792 SkPath path;
793 SkScalar rx = rect.width() / 10;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000794 SkScalar ry = rect.height() / 20;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000795 path.addRoundRect(rect, rx, ry);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000796 if (invert) {
797 path.setFillType(SkPath::kInverseWinding_FillType);
798 }
799 stack->clipDevPath(path, op, false);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000800};
801
bsalomon@google.com705e8402012-11-27 15:43:57 +0000802static void add_rect(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) {
803 if (invert) {
804 SkPath path;
805 path.addRect(rect);
806 path.setFillType(SkPath::kInverseWinding_FillType);
807 stack->clipDevPath(path, op, false);
808 } else {
809 stack->clipDevRect(rect, op, false);
810 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000811};
812
bsalomon@google.com705e8402012-11-27 15:43:57 +0000813static void add_oval(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000814 SkPath path;
815 path.addOval(rect);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000816 if (invert) {
817 path.setFillType(SkPath::kInverseWinding_FillType);
818 }
819 stack->clipDevPath(path, op, false);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000820};
821
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000822static void add_elem_to_stack(const SkClipStack::Element& element, SkClipStack* stack) {
823 switch (element.getType()) {
824 case SkClipStack::Element::kRect_Type:
825 stack->clipDevRect(element.getRect(), element.getOp(), element.isAA());
826 break;
827 case SkClipStack::Element::kPath_Type:
828 stack->clipDevPath(element.getPath(), element.getOp(), element.isAA());
829 break;
830 case SkClipStack::Element::kEmpty_Type:
831 SkDEBUGFAIL("Why did the reducer produce an explicit empty.");
832 stack->clipEmpty();
833 break;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000834 }
835}
836
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000837static void add_elem_to_region(const SkClipStack::Element& element,
bsalomon@google.com51a62862012-11-26 21:19:43 +0000838 const SkIRect& bounds,
839 SkRegion* region) {
840 SkRegion elemRegion;
841 SkRegion boundsRgn(bounds);
842
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000843 switch (element.getType()) {
844 case SkClipStack::Element::kRect_Type: {
845 SkPath path;
846 path.addRect(element.getRect());
847 elemRegion.setPath(path, boundsRgn);
848 break;
849 }
850 case SkClipStack::Element::kPath_Type:
851 elemRegion.setPath(element.getPath(), boundsRgn);
852 break;
853 case SkClipStack::Element::kEmpty_Type:
skia.committer@gmail.com73b140a2012-12-05 02:01:21 +0000854 //
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000855 region->setEmpty();
856 return;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000857 }
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000858 region->op(elemRegion, element.getOp());
bsalomon@google.com51a62862012-11-26 21:19:43 +0000859}
860
861// This can assist with debugging the clip stack reduction code when the test below fails.
humper@google.com05af1af2013-01-07 16:47:43 +0000862static inline void print_clip(const SkClipStack::Element& element) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000863 static const char* kOpStrs[] = {
864 "DF",
865 "IS",
866 "UN",
867 "XR",
868 "RD",
869 "RP",
870 };
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000871 if (SkClipStack::Element::kEmpty_Type != element.getType()) {
872 const SkRect& bounds = element.getBounds();
873 bool isRect = SkClipStack::Element::kRect_Type == element.getType();
bsalomon@google.com705e8402012-11-27 15:43:57 +0000874 SkDebugf("%s %s %s [%f %f] x [%f %f]\n",
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000875 kOpStrs[element.getOp()],
876 (isRect ? "R" : "P"),
877 (element.isInverseFilled() ? "I" : " "),
bsalomon@google.com51a62862012-11-26 21:19:43 +0000878 bounds.fLeft, bounds.fRight, bounds.fTop, bounds.fBottom);
879 } else {
880 SkDebugf("EM\n");
881 }
882}
883
884static void test_reduced_clip_stack(skiatest::Reporter* reporter) {
885 // We construct random clip stacks, reduce them, and then rasterize both versions to verify that
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000886 // they are equal.
bsalomon@google.com51a62862012-11-26 21:19:43 +0000887
888 // All the clip elements will be contained within these bounds.
889 static const SkRect kBounds = SkRect::MakeWH(100, 100);
890
891 enum {
892 kNumTests = 200,
893 kMinElemsPerTest = 1,
894 kMaxElemsPerTest = 50,
895 };
896
897 // min/max size of a clip element as a fraction of kBounds.
898 static const SkScalar kMinElemSizeFrac = SK_Scalar1 / 5;
899 static const SkScalar kMaxElemSizeFrac = SK_Scalar1;
900
901 static const SkRegion::Op kOps[] = {
902 SkRegion::kDifference_Op,
903 SkRegion::kIntersect_Op,
904 SkRegion::kUnion_Op,
905 SkRegion::kXOR_Op,
906 SkRegion::kReverseDifference_Op,
907 SkRegion::kReplace_Op,
908 };
909
910 // Replace operations short-circuit the optimizer. We want to make sure that we test this code
911 // path a little bit but we don't want it to prevent us from testing many longer traversals in
912 // the optimizer.
913 static const int kReplaceDiv = 4 * kMaxElemsPerTest;
914
bsalomon@google.com705e8402012-11-27 15:43:57 +0000915 // We want to test inverse fills. However, they are quite rare in practice so don't over do it.
916 static const SkScalar kFractionInverted = SK_Scalar1 / kMaxElemsPerTest;
917
bsalomon@google.com51a62862012-11-26 21:19:43 +0000918 static const AddElementFunc kElementFuncs[] = {
919 add_rect,
920 add_round_rect,
921 add_oval,
922 };
923
jvanverth@google.comc490f802013-03-04 13:56:38 +0000924 SkMWCRandom r;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000925
926 for (int i = 0; i < kNumTests; ++i) {
927 // Randomly generate a clip stack.
928 SkClipStack stack;
929 int numElems = r.nextRangeU(kMinElemsPerTest, kMaxElemsPerTest);
930 for (int e = 0; e < numElems; ++e) {
931 SkRegion::Op op = kOps[r.nextULessThan(SK_ARRAY_COUNT(kOps))];
932 if (op == SkRegion::kReplace_Op) {
933 if (r.nextU() % kReplaceDiv) {
934 --e;
935 continue;
936 }
937 }
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000938
bsalomon@google.com51a62862012-11-26 21:19:43 +0000939 // saves can change the clip stack behavior when an element is added.
940 bool doSave = r.nextBool();
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000941
bsalomon@google.com51a62862012-11-26 21:19:43 +0000942 SkSize size = SkSize::Make(
943 SkScalarFloorToScalar(SkScalarMul(kBounds.width(), r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac))),
944 SkScalarFloorToScalar(SkScalarMul(kBounds.height(), r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac))));
945
946 SkPoint xy = {SkScalarFloorToScalar(r.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth)),
947 SkScalarFloorToScalar(r.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight))};
948
949 SkRect rect = SkRect::MakeXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight);
950
bsalomon@google.com705e8402012-11-27 15:43:57 +0000951 bool invert = r.nextBiasedBool(kFractionInverted);
952 kElementFuncs[r.nextULessThan(SK_ARRAY_COUNT(kElementFuncs))](rect, invert, op, &stack);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000953 if (doSave) {
954 stack.save();
955 }
956 }
957
bsalomon@google.coma4444302012-12-04 15:22:12 +0000958 SkRect inflatedBounds = kBounds;
959 inflatedBounds.outset(kBounds.width() / 2, kBounds.height() / 2);
960 SkIRect inflatedIBounds;
961 inflatedBounds.roundOut(&inflatedIBounds);
962
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000963 typedef GrReducedClip::ElementList ElementList;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000964 // Get the reduced version of the stack.
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000965 ElementList reducedClips;
bsalomon@google.coma4444302012-12-04 15:22:12 +0000966
bsalomon@google.com51a62862012-11-26 21:19:43 +0000967 GrReducedClip::InitialState initial;
jvanverth@google.comc490f802013-03-04 13:56:38 +0000968 SkIRect tBounds(inflatedIBounds);
bsalomon@google.com34cd70a2012-12-06 14:23:20 +0000969 SkIRect* tightBounds = r.nextBool() ? &tBounds : NULL;
970 GrReducedClip::ReduceClipStack(stack,
971 inflatedIBounds,
972 &reducedClips,
973 &initial,
974 tightBounds);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000975
976 // Build a new clip stack based on the reduced clip elements
977 SkClipStack reducedStack;
978 if (GrReducedClip::kAllOut_InitialState == initial) {
979 // whether the result is bounded or not, the whole plane should start outside the clip.
980 reducedStack.clipEmpty();
981 }
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000982 for (ElementList::Iter iter = reducedClips.headIter(); NULL != iter.get(); iter.next()) {
983 add_elem_to_stack(*iter.get(), &reducedStack);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000984 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000985
bsalomon@google.com34cd70a2012-12-06 14:23:20 +0000986 // GrReducedClipStack assumes that the final result is clipped to the returned bounds
987 if (NULL != tightBounds) {
988 reducedStack.clipDevRect(*tightBounds, SkRegion::kIntersect_Op);
989 }
990
bsalomon@google.com51a62862012-11-26 21:19:43 +0000991 // convert both the original stack and reduced stack to SkRegions and see if they're equal
bsalomon@google.com51a62862012-11-26 21:19:43 +0000992 SkRegion region;
993 SkRegion reducedRegion;
994
995 region.setRect(inflatedIBounds);
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000996 const SkClipStack::Element* element;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000997 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000998 while ((element = iter.next())) {
999 add_elem_to_region(*element, inflatedIBounds, &region);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001000 }
1001
1002 reducedRegion.setRect(inflatedIBounds);
1003 iter.reset(reducedStack, SkClipStack::Iter::kBottom_IterStart);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001004 while ((element = iter.next())) {
1005 add_elem_to_region(*element, inflatedIBounds, &reducedRegion);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001006 }
1007
1008 REPORTER_ASSERT(reporter, region == reducedRegion);
1009 }
1010}
1011
bsalomon@google.coma4e13c82012-11-26 21:38:37 +00001012#endif
bsalomon@google.com51a62862012-11-26 21:19:43 +00001013///////////////////////////////////////////////////////////////////////////////////////////////////
1014
reed@google.combdee9fc2011-02-22 20:17:43 +00001015static void TestClipStack(skiatest::Reporter* reporter) {
1016 SkClipStack stack;
1017
robertphillips@google.com80214e22012-07-20 15:33:18 +00001018 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001019 assert_count(reporter, stack, 0);
1020
1021 static const SkIRect gRects[] = {
1022 { 0, 0, 100, 100 },
1023 { 25, 25, 125, 125 },
1024 { 0, 0, 1000, 1000 },
1025 { 0, 0, 75, 75 }
1026 };
1027 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
reed@google.comd9f2dea2011-10-12 14:43:27 +00001028 stack.clipDevRect(gRects[i], SkRegion::kIntersect_Op);
reed@google.combdee9fc2011-02-22 20:17:43 +00001029 }
1030
1031 // all of the above rects should have been intersected, leaving only 1 rect
robertphillips@google.com80214e22012-07-20 15:33:18 +00001032 SkClipStack::B2TIter iter(stack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001033 const SkClipStack::Element* element = iter.next();
epoger@google.com2047f002011-05-17 17:36:59 +00001034 SkRect answer;
1035 answer.iset(25, 25, 75, 75);
reed@google.combdee9fc2011-02-22 20:17:43 +00001036
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001037 REPORTER_ASSERT(reporter, NULL != element);
1038 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
1039 REPORTER_ASSERT(reporter, SkRegion::kIntersect_Op == element->getOp());
1040 REPORTER_ASSERT(reporter, element->getRect() == answer);
reed@google.combdee9fc2011-02-22 20:17:43 +00001041 // now check that we only had one in our iterator
1042 REPORTER_ASSERT(reporter, !iter.next());
1043
1044 stack.reset();
robertphillips@google.com80214e22012-07-20 15:33:18 +00001045 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001046 assert_count(reporter, stack, 0);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +00001047
1048 test_assign_and_comparison(reporter);
robertphillips@google.com80214e22012-07-20 15:33:18 +00001049 test_iterators(reporter);
robertphillips@google.com08eacc12012-08-02 12:49:00 +00001050 test_bounds(reporter, true); // once with rects
1051 test_bounds(reporter, false); // once with paths
robertphillips@google.comcc6493b2012-07-26 18:39:13 +00001052 test_isWideOpen(reporter);
robertphillips@google.com08eacc12012-08-02 12:49:00 +00001053 test_rect_merging(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001054 test_rect_replace(reporter);
junov@chromium.orgedf32d52012-12-10 14:57:54 +00001055 test_rect_inverse_fill(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001056 test_path_replace(reporter);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +00001057 test_quickContains(reporter);
bsalomon@google.come7b3d292012-11-26 21:42:32 +00001058#if SK_SUPPORT_GPU
bsalomon@google.comedb26fd2012-11-28 14:42:41 +00001059 test_reduced_clip_stack(reporter);
bsalomon@google.come7b3d292012-11-26 21:42:32 +00001060#endif
reed@google.combdee9fc2011-02-22 20:17:43 +00001061}
1062
1063#include "TestClassDef.h"
1064DEFINE_TESTCLASS("ClipStack", TestClipStackClass, TestClipStack)