blob: 11504315c808a3014588194fcac6fbabfa65a2ba [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"
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
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000018static void test_assign_and_comparison(skiatest::Reporter* reporter) {
19 SkClipStack s;
reed@google.comd9f2dea2011-10-12 14:43:27 +000020 bool doAA = false;
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000021
robertphillips@google.com80214e22012-07-20 15:33:18 +000022 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
23
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000024 // Build up a clip stack with a path, an empty clip, and a rect.
25 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000026 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
27
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000028 SkPath p;
29 p.moveTo(5, 6);
30 p.lineTo(7, 8);
31 p.lineTo(5, 9);
32 p.close();
reed@google.comd9f2dea2011-10-12 14:43:27 +000033 s.clipDevPath(p, SkRegion::kIntersect_Op, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000034
35 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000036 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
37
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000038 SkRect r = SkRect::MakeLTRB(1, 2, 3, 4);
reed@google.comd9f2dea2011-10-12 14:43:27 +000039 s.clipDevRect(r, SkRegion::kIntersect_Op, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000040 r = SkRect::MakeLTRB(10, 11, 12, 13);
reed@google.comd9f2dea2011-10-12 14:43:27 +000041 s.clipDevRect(r, SkRegion::kIntersect_Op, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000042
43 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000044 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
45
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000046 r = SkRect::MakeLTRB(14, 15, 16, 17);
reed@google.comd9f2dea2011-10-12 14:43:27 +000047 s.clipDevRect(r, SkRegion::kUnion_Op, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000048
49 // Test that assignment works.
50 SkClipStack copy = s;
51 REPORTER_ASSERT(reporter, s == copy);
52
53 // Test that different save levels triggers not equal.
54 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000055 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000056 REPORTER_ASSERT(reporter, s != copy);
57
58 // Test that an equal, but not copied version is equal.
59 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000060 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
61
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000062 r = SkRect::MakeLTRB(14, 15, 16, 17);
reed@google.comd9f2dea2011-10-12 14:43:27 +000063 s.clipDevRect(r, SkRegion::kUnion_Op, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000064 REPORTER_ASSERT(reporter, s == copy);
65
66 // Test that a different op on one level triggers not equal.
67 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000068 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000069 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000070 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
71
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000072 r = SkRect::MakeLTRB(14, 15, 16, 17);
reed@google.comd9f2dea2011-10-12 14:43:27 +000073 s.clipDevRect(r, SkRegion::kIntersect_Op, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000074 REPORTER_ASSERT(reporter, s != copy);
75
76 // Test that different state (clip type) triggers not equal.
tomhudson@google.com4c433722012-03-09 16:48:20 +000077 // NO LONGER VALID: if a path contains only a rect, we turn
78 // it into a bare rect for performance reasons (working
79 // around Chromium/JavaScript bad pattern).
80/*
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000081 s.restore();
82 s.save();
83 SkPath rp;
84 rp.addRect(r);
reed@google.comd9f2dea2011-10-12 14:43:27 +000085 s.clipDevPath(rp, SkRegion::kUnion_Op, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000086 REPORTER_ASSERT(reporter, s != copy);
tomhudson@google.com4c433722012-03-09 16:48:20 +000087*/
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000088
89 // Test that different rects triggers not equal.
90 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000091 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000092 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000093 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
94
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000095 r = SkRect::MakeLTRB(24, 25, 26, 27);
reed@google.comd9f2dea2011-10-12 14:43:27 +000096 s.clipDevRect(r, SkRegion::kUnion_Op, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000097 REPORTER_ASSERT(reporter, s != copy);
98
99 // Sanity check
100 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000101 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
102
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000103 copy.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000104 REPORTER_ASSERT(reporter, 2 == copy.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000105 REPORTER_ASSERT(reporter, s == copy);
106 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000107 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000108 copy.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000109 REPORTER_ASSERT(reporter, 1 == copy.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000110 REPORTER_ASSERT(reporter, s == copy);
111
112 // Test that different paths triggers not equal.
113 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000114 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000115 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000116 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
117
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000118 p.addRect(r);
reed@google.comd9f2dea2011-10-12 14:43:27 +0000119 s.clipDevPath(p, SkRegion::kIntersect_Op, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000120 REPORTER_ASSERT(reporter, s != copy);
121}
reed@google.combdee9fc2011-02-22 20:17:43 +0000122
123static void assert_count(skiatest::Reporter* reporter, const SkClipStack& stack,
124 int count) {
robertphillips@google.com80214e22012-07-20 15:33:18 +0000125 SkClipStack::B2TIter iter(stack);
reed@google.combdee9fc2011-02-22 20:17:43 +0000126 int counter = 0;
127 while (iter.next()) {
128 counter += 1;
129 }
130 REPORTER_ASSERT(reporter, count == counter);
131}
132
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000133// Exercise the SkClipStack's bottom to top and bidirectional iterators
134// (including the skipToTopmost functionality)
robertphillips@google.com80214e22012-07-20 15:33:18 +0000135static void test_iterators(skiatest::Reporter* reporter) {
136 SkClipStack stack;
137
138 static const SkRect gRects[] = {
139 { 0, 0, 40, 40 },
140 { 60, 0, 100, 40 },
141 { 0, 60, 40, 100 },
142 { 60, 60, 100, 100 }
143 };
144
145 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
146 // the union op will prevent these from being fused together
147 stack.clipDevRect(gRects[i], SkRegion::kUnion_Op, false);
148 }
149
150 assert_count(reporter, stack, 4);
151
152 // bottom to top iteration
153 {
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000154 const SkClipStack::Element* element = NULL;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000155
156 SkClipStack::B2TIter iter(stack);
157 int i;
158
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000159 for (i = 0, element = iter.next(); element; ++i, element = iter.next()) {
160 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
161 REPORTER_ASSERT(reporter, element->getRect() == gRects[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000162 }
163
164 SkASSERT(i == 4);
165 }
166
167 // top to bottom iteration
168 {
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000169 const SkClipStack::Element* element = NULL;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000170
171 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
172 int i;
173
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000174 for (i = 3, element = iter.prev(); element; --i, element = iter.prev()) {
175 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
176 REPORTER_ASSERT(reporter, element->getRect() == gRects[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000177 }
178
179 SkASSERT(i == -1);
180 }
181
182 // skipToTopmost
183 {
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000184 const SkClipStack::Element* element = NULL;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000185
186 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
187
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000188 element = iter.skipToTopmost(SkRegion::kUnion_Op);
189 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
190 REPORTER_ASSERT(reporter, element->getRect() == gRects[3]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000191 }
192}
193
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000194// Exercise the SkClipStack's getConservativeBounds computation
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000195static void test_bounds(skiatest::Reporter* reporter, bool useRects) {
robertphillips@google.com607fe072012-07-24 13:54:00 +0000196
197 static const int gNumCases = 20;
198 static const SkRect gAnswerRectsBW[gNumCases] = {
199 // A op B
200 { 40, 40, 50, 50 },
201 { 10, 10, 50, 50 },
202 { 10, 10, 80, 80 },
203 { 10, 10, 80, 80 },
204 { 40, 40, 80, 80 },
205
206 // invA op B
207 { 40, 40, 80, 80 },
208 { 0, 0, 100, 100 },
209 { 0, 0, 100, 100 },
210 { 0, 0, 100, 100 },
211 { 40, 40, 50, 50 },
212
213 // A op invB
214 { 10, 10, 50, 50 },
215 { 40, 40, 50, 50 },
216 { 0, 0, 100, 100 },
217 { 0, 0, 100, 100 },
218 { 0, 0, 100, 100 },
219
220 // invA op invB
221 { 0, 0, 100, 100 },
222 { 40, 40, 80, 80 },
223 { 0, 0, 100, 100 },
224 { 10, 10, 80, 80 },
225 { 10, 10, 50, 50 },
226 };
227
228 static const SkRegion::Op gOps[] = {
229 SkRegion::kIntersect_Op,
230 SkRegion::kDifference_Op,
231 SkRegion::kUnion_Op,
232 SkRegion::kXOR_Op,
233 SkRegion::kReverseDifference_Op
234 };
235
236 SkRect rectA, rectB;
237
238 rectA.iset(10, 10, 50, 50);
239 rectB.iset(40, 40, 80, 80);
240
241 SkPath clipA, clipB;
242
243 clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
244 clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
245
246 SkClipStack stack;
robertphillips@google.com7b112892012-07-31 15:18:21 +0000247 SkRect devClipBound;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000248 bool isIntersectionOfRects = false;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000249
250 int testCase = 0;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000251 int numBitTests = useRects ? 1 : 4;
252 for (int invBits = 0; invBits < numBitTests; ++invBits) {
robertphillips@google.com607fe072012-07-24 13:54:00 +0000253 for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) {
254
255 stack.save();
256 bool doInvA = SkToBool(invBits & 1);
257 bool doInvB = SkToBool(invBits & 2);
258
259 clipA.setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType :
260 SkPath::kEvenOdd_FillType);
261 clipB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType :
262 SkPath::kEvenOdd_FillType);
263
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000264 if (useRects) {
265 stack.clipDevRect(rectA, SkRegion::kIntersect_Op, false);
266 stack.clipDevRect(rectB, gOps[op], false);
267 } else {
268 stack.clipDevPath(clipA, SkRegion::kIntersect_Op, false);
269 stack.clipDevPath(clipB, gOps[op], false);
270 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000271
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000272 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000273 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000274
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) {
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000296 {
297 // Empty stack is wide open. Wide open stack means that gen id is wide open.
298 SkClipStack stack;
299 REPORTER_ASSERT(reporter, stack.isWideOpen());
300 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
301 }
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000302
303 SkRect rectA, rectB;
304
305 rectA.iset(10, 10, 40, 40);
306 rectB.iset(50, 50, 80, 80);
307
308 // Stack should initially be wide open
309 {
310 SkClipStack stack;
311
312 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000313 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000314 }
315
316 // Test out case where the user specifies a union that includes everything
317 {
318 SkClipStack stack;
319
320 SkPath clipA, clipB;
321
322 clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
323 clipA.setFillType(SkPath::kInverseEvenOdd_FillType);
324
325 clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
326 clipB.setFillType(SkPath::kInverseEvenOdd_FillType);
327
328 stack.clipDevPath(clipA, SkRegion::kReplace_Op, false);
329 stack.clipDevPath(clipB, SkRegion::kUnion_Op, false);
330
331 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000332 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000333 }
334
335 // Test out union w/ a wide open clip
336 {
337 SkClipStack stack;
338
339 stack.clipDevRect(rectA, SkRegion::kUnion_Op, false);
340
341 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000342 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000343 }
344
345 // Test out empty difference from a wide open clip
346 {
347 SkClipStack stack;
348
349 SkRect emptyRect;
350 emptyRect.setEmpty();
351
352 stack.clipDevRect(emptyRect, SkRegion::kDifference_Op, false);
353
354 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000355 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000356 }
357
358 // Test out return to wide open
359 {
360 SkClipStack stack;
361
362 stack.save();
363
364 stack.clipDevRect(rectA, SkRegion::kReplace_Op, false);
365
366 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000367 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000368
369 stack.restore();
370
371 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000372 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000373 }
374}
375
bsalomon@google.com100abf42012-09-05 17:40:04 +0000376static int count(const SkClipStack& stack) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000377
378 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
379
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000380 const SkClipStack::Element* element = NULL;
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000381 int count = 0;
382
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000383 for (element = iter.prev(); element; element = iter.prev(), ++count) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000384 ;
385 }
386
387 return count;
388}
389
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000390static void test_rect_inverse_fill(skiatest::Reporter* reporter) {
391 // non-intersecting rectangles
392 SkRect rect = SkRect::MakeLTRB(0, 0, 10, 10);
393
394 SkPath path;
395 path.addRect(rect);
396 path.toggleInverseFillType();
397 SkClipStack stack;
398 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
399
400 SkRect bounds;
401 SkClipStack::BoundsType boundsType;
402 stack.getBounds(&bounds, &boundsType);
403 REPORTER_ASSERT(reporter, SkClipStack::kInsideOut_BoundsType == boundsType);
404 REPORTER_ASSERT(reporter, bounds == rect);
405}
406
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000407static void test_rect_replace(skiatest::Reporter* reporter) {
408 SkRect rect = SkRect::MakeWH(100, 100);
409 SkRect rect2 = SkRect::MakeXYWH(50, 50, 100, 100);
410
411 SkRect bound;
412 SkClipStack::BoundsType type;
413 bool isIntersectionOfRects;
414
415 // Adding a new rect with the replace operator should not increase
416 // the stack depth. BW replacing BW.
417 {
418 SkClipStack stack;
419 REPORTER_ASSERT(reporter, 0 == count(stack));
420 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
421 REPORTER_ASSERT(reporter, 1 == count(stack));
422 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
423 REPORTER_ASSERT(reporter, 1 == count(stack));
424 }
425
426 // Adding a new rect with the replace operator should not increase
427 // the stack depth. AA replacing AA.
428 {
429 SkClipStack stack;
430 REPORTER_ASSERT(reporter, 0 == count(stack));
431 stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
432 REPORTER_ASSERT(reporter, 1 == count(stack));
433 stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
434 REPORTER_ASSERT(reporter, 1 == count(stack));
435 }
436
437 // Adding a new rect with the replace operator should not increase
438 // the stack depth. BW replacing AA replacing BW.
439 {
440 SkClipStack stack;
441 REPORTER_ASSERT(reporter, 0 == count(stack));
442 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
443 REPORTER_ASSERT(reporter, 1 == count(stack));
444 stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
445 REPORTER_ASSERT(reporter, 1 == count(stack));
446 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
447 REPORTER_ASSERT(reporter, 1 == count(stack));
448 }
449
450 // Make sure replace clip rects don't collapse too much.
451 {
452 SkClipStack stack;
453 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
454 stack.clipDevRect(rect2, SkRegion::kIntersect_Op, false);
455 REPORTER_ASSERT(reporter, 1 == count(stack));
456
457 stack.save();
458 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
459 REPORTER_ASSERT(reporter, 2 == count(stack));
460 stack.getBounds(&bound, &type, &isIntersectionOfRects);
461 REPORTER_ASSERT(reporter, bound == rect);
462 stack.restore();
463 REPORTER_ASSERT(reporter, 1 == count(stack));
464
465 stack.save();
466 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
467 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
468 REPORTER_ASSERT(reporter, 2 == count(stack));
469 stack.restore();
470 REPORTER_ASSERT(reporter, 1 == count(stack));
471
472 stack.save();
473 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
474 stack.clipDevRect(rect2, SkRegion::kIntersect_Op, false);
475 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
476 REPORTER_ASSERT(reporter, 2 == count(stack));
477 stack.restore();
478 REPORTER_ASSERT(reporter, 1 == count(stack));
479 }
480}
481
482// Simplified path-based version of test_rect_replace.
483static void test_path_replace(skiatest::Reporter* reporter) {
484 SkRect rect = SkRect::MakeWH(100, 100);
485 SkPath path;
486 path.addCircle(50, 50, 50);
487
488 // Replace operation doesn't grow the stack.
489 {
490 SkClipStack stack;
491 REPORTER_ASSERT(reporter, 0 == count(stack));
492 stack.clipDevPath(path, SkRegion::kReplace_Op, false);
493 REPORTER_ASSERT(reporter, 1 == count(stack));
494 stack.clipDevPath(path, SkRegion::kReplace_Op, false);
495 REPORTER_ASSERT(reporter, 1 == count(stack));
496 }
497
498 // Replacing rect with path.
499 {
500 SkClipStack stack;
501 stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
502 REPORTER_ASSERT(reporter, 1 == count(stack));
503 stack.clipDevPath(path, SkRegion::kReplace_Op, true);
504 REPORTER_ASSERT(reporter, 1 == count(stack));
505 }
506}
507
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000508// Test out SkClipStack's merging of rect clips. In particular exercise
509// merging of aa vs. bw rects.
510static void test_rect_merging(skiatest::Reporter* reporter) {
511
512 SkRect overlapLeft = SkRect::MakeLTRB(10, 10, 50, 50);
513 SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80);
514
515 SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90);
516 SkRect nestedChild = SkRect::MakeLTRB(40, 40, 60, 60);
517
518 SkRect bound;
519 SkClipStack::BoundsType type;
520 bool isIntersectionOfRects;
521
522 // all bw overlapping - should merge
523 {
524 SkClipStack stack;
525
526 stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, false);
527
528 stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false);
529
530 REPORTER_ASSERT(reporter, 1 == count(stack));
531
532 stack.getBounds(&bound, &type, &isIntersectionOfRects);
533
534 REPORTER_ASSERT(reporter, isIntersectionOfRects);
535 }
536
537 // all aa overlapping - should merge
538 {
539 SkClipStack stack;
540
541 stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true);
542
543 stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, true);
544
545 REPORTER_ASSERT(reporter, 1 == count(stack));
546
547 stack.getBounds(&bound, &type, &isIntersectionOfRects);
548
549 REPORTER_ASSERT(reporter, isIntersectionOfRects);
550 }
551
552 // mixed overlapping - should _not_ merge
553 {
554 SkClipStack stack;
555
556 stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true);
557
558 stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false);
559
560 REPORTER_ASSERT(reporter, 2 == count(stack));
561
562 stack.getBounds(&bound, &type, &isIntersectionOfRects);
563
564 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
565 }
566
567 // mixed nested (bw inside aa) - should merge
568 {
569 SkClipStack stack;
570
571 stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, true);
572
573 stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, false);
574
575 REPORTER_ASSERT(reporter, 1 == count(stack));
576
577 stack.getBounds(&bound, &type, &isIntersectionOfRects);
578
579 REPORTER_ASSERT(reporter, isIntersectionOfRects);
580 }
581
582 // mixed nested (aa inside bw) - should merge
583 {
584 SkClipStack stack;
585
586 stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, false);
587
588 stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, true);
589
590 REPORTER_ASSERT(reporter, 1 == count(stack));
591
592 stack.getBounds(&bound, &type, &isIntersectionOfRects);
593
594 REPORTER_ASSERT(reporter, isIntersectionOfRects);
595 }
596
597 // reverse nested (aa inside bw) - should _not_ merge
598 {
599 SkClipStack stack;
600
601 stack.clipDevRect(nestedChild, SkRegion::kReplace_Op, false);
602
603 stack.clipDevRect(nestedParent, SkRegion::kIntersect_Op, true);
604
605 REPORTER_ASSERT(reporter, 2 == count(stack));
606
607 stack.getBounds(&bound, &type, &isIntersectionOfRects);
608
609 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
610 }
611}
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000612
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000613static void test_quickContains(skiatest::Reporter* reporter) {
614 SkRect testRect = SkRect::MakeLTRB(10, 10, 40, 40);
615 SkRect insideRect = SkRect::MakeLTRB(20, 20, 30, 30);
616 SkRect intersectingRect = SkRect::MakeLTRB(25, 25, 50, 50);
617 SkRect outsideRect = SkRect::MakeLTRB(0, 0, 50, 50);
618 SkRect nonIntersectingRect = SkRect::MakeLTRB(100, 100, 110, 110);
619
620 SkPath insideCircle;
621 insideCircle.addCircle(25, 25, 5);
622 SkPath intersectingCircle;
623 intersectingCircle.addCircle(25, 40, 10);
624 SkPath outsideCircle;
625 outsideCircle.addCircle(25, 25, 50);
626 SkPath nonIntersectingCircle;
627 nonIntersectingCircle.addCircle(100, 100, 5);
628
629 {
630 SkClipStack stack;
631 stack.clipDevRect(outsideRect, SkRegion::kDifference_Op, false);
632 // return false because quickContains currently does not care for kDifference_Op
633 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
634 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000635
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000636 // Replace Op tests
637 {
638 SkClipStack stack;
639 stack.clipDevRect(outsideRect, SkRegion::kReplace_Op, false);
640 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
641 }
642
643 {
644 SkClipStack stack;
645 stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
646 stack.save(); // To prevent in-place substitution by replace OP
647 stack.clipDevRect(outsideRect, SkRegion::kReplace_Op, false);
648 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
649 stack.restore();
650 }
651
652 {
653 SkClipStack stack;
654 stack.clipDevRect(outsideRect, SkRegion::kIntersect_Op, false);
655 stack.save(); // To prevent in-place substitution by replace OP
656 stack.clipDevRect(insideRect, SkRegion::kReplace_Op, false);
657 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
658 stack.restore();
659 }
660
661 // Verify proper traversal of multi-element clip
662 {
663 SkClipStack stack;
664 stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
665 // Use a path for second clip to prevent in-place intersection
666 stack.clipDevPath(outsideCircle, SkRegion::kIntersect_Op, false);
667 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
668 }
669
670 // Intersect Op tests with rectangles
671 {
672 SkClipStack stack;
673 stack.clipDevRect(outsideRect, SkRegion::kIntersect_Op, false);
674 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
675 }
676
677 {
678 SkClipStack stack;
679 stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
680 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
681 }
682
683 {
684 SkClipStack stack;
685 stack.clipDevRect(intersectingRect, SkRegion::kIntersect_Op, false);
686 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
687 }
688
689 {
690 SkClipStack stack;
691 stack.clipDevRect(nonIntersectingRect, SkRegion::kIntersect_Op, false);
692 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
693 }
694
695 // Intersect Op tests with circle paths
696 {
697 SkClipStack stack;
698 stack.clipDevPath(outsideCircle, SkRegion::kIntersect_Op, false);
699 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
700 }
701
702 {
703 SkClipStack stack;
704 stack.clipDevPath(insideCircle, SkRegion::kIntersect_Op, false);
705 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
706 }
707
708 {
709 SkClipStack stack;
710 stack.clipDevPath(intersectingCircle, SkRegion::kIntersect_Op, false);
711 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
712 }
713
714 {
715 SkClipStack stack;
716 stack.clipDevPath(nonIntersectingCircle, SkRegion::kIntersect_Op, false);
717 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
718 }
719
720 // Intersect Op tests with inverse filled rectangles
721 {
722 SkClipStack stack;
723 SkPath path;
724 path.addRect(outsideRect);
725 path.toggleInverseFillType();
726 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
727 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
728 }
729
730 {
731 SkClipStack stack;
732 SkPath path;
733 path.addRect(insideRect);
734 path.toggleInverseFillType();
735 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
736 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
737 }
738
739 {
740 SkClipStack stack;
741 SkPath path;
742 path.addRect(intersectingRect);
743 path.toggleInverseFillType();
744 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
745 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
746 }
747
748 {
749 SkClipStack stack;
750 SkPath path;
751 path.addRect(nonIntersectingRect);
752 path.toggleInverseFillType();
753 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
754 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
755 }
756
757 // Intersect Op tests with inverse filled circles
758 {
759 SkClipStack stack;
760 SkPath path = outsideCircle;
761 path.toggleInverseFillType();
762 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
763 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
764 }
765
766 {
767 SkClipStack stack;
768 SkPath path = insideCircle;
769 path.toggleInverseFillType();
770 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
771 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
772 }
773
774 {
775 SkClipStack stack;
776 SkPath path = intersectingCircle;
777 path.toggleInverseFillType();
778 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
779 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
780 }
781
782 {
783 SkClipStack stack;
784 SkPath path = nonIntersectingCircle;
785 path.toggleInverseFillType();
786 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
787 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
788 }
789}
790
bsalomon@google.com51a62862012-11-26 21:19:43 +0000791///////////////////////////////////////////////////////////////////////////////////////////////////
792
bsalomon@google.coma4e13c82012-11-26 21:38:37 +0000793#if SK_SUPPORT_GPU
bsalomon@google.com705e8402012-11-27 15:43:57 +0000794// Functions that add a shape to the clip stack. The shape is computed from a rectangle.
795// AA is always disabled since the clip stack reducer can cause changes in aa rasterization of the
796// stack. A fractional edge repeated in different elements may be rasterized fewer times using the
797// reduced stack.
798typedef void (*AddElementFunc) (const SkRect& rect,
799 bool invert,
800 SkRegion::Op op,
801 SkClipStack* stack);
bsalomon@google.coma4e13c82012-11-26 21:38:37 +0000802
bsalomon@google.com705e8402012-11-27 15:43:57 +0000803static void add_round_rect(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000804 SkPath path;
805 SkScalar rx = rect.width() / 10;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000806 SkScalar ry = rect.height() / 20;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000807 path.addRoundRect(rect, rx, ry);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000808 if (invert) {
809 path.setFillType(SkPath::kInverseWinding_FillType);
810 }
811 stack->clipDevPath(path, op, false);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000812};
813
bsalomon@google.com705e8402012-11-27 15:43:57 +0000814static void add_rect(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) {
815 if (invert) {
816 SkPath path;
817 path.addRect(rect);
818 path.setFillType(SkPath::kInverseWinding_FillType);
819 stack->clipDevPath(path, op, false);
820 } else {
821 stack->clipDevRect(rect, op, false);
822 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000823};
824
bsalomon@google.com705e8402012-11-27 15:43:57 +0000825static void add_oval(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000826 SkPath path;
827 path.addOval(rect);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000828 if (invert) {
829 path.setFillType(SkPath::kInverseWinding_FillType);
830 }
831 stack->clipDevPath(path, op, false);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000832};
833
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000834static void add_elem_to_stack(const SkClipStack::Element& element, SkClipStack* stack) {
835 switch (element.getType()) {
836 case SkClipStack::Element::kRect_Type:
837 stack->clipDevRect(element.getRect(), element.getOp(), element.isAA());
838 break;
839 case SkClipStack::Element::kPath_Type:
840 stack->clipDevPath(element.getPath(), element.getOp(), element.isAA());
841 break;
842 case SkClipStack::Element::kEmpty_Type:
843 SkDEBUGFAIL("Why did the reducer produce an explicit empty.");
844 stack->clipEmpty();
845 break;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000846 }
847}
848
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000849static void add_elem_to_region(const SkClipStack::Element& element,
bsalomon@google.com51a62862012-11-26 21:19:43 +0000850 const SkIRect& bounds,
851 SkRegion* region) {
852 SkRegion elemRegion;
853 SkRegion boundsRgn(bounds);
854
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000855 switch (element.getType()) {
856 case SkClipStack::Element::kRect_Type: {
857 SkPath path;
858 path.addRect(element.getRect());
859 elemRegion.setPath(path, boundsRgn);
860 break;
861 }
862 case SkClipStack::Element::kPath_Type:
863 elemRegion.setPath(element.getPath(), boundsRgn);
864 break;
865 case SkClipStack::Element::kEmpty_Type:
skia.committer@gmail.com73b140a2012-12-05 02:01:21 +0000866 //
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000867 region->setEmpty();
868 return;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000869 }
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000870 region->op(elemRegion, element.getOp());
bsalomon@google.com51a62862012-11-26 21:19:43 +0000871}
872
bsalomon@google.com51a62862012-11-26 21:19:43 +0000873static void test_reduced_clip_stack(skiatest::Reporter* reporter) {
874 // We construct random clip stacks, reduce them, and then rasterize both versions to verify that
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000875 // they are equal.
bsalomon@google.com51a62862012-11-26 21:19:43 +0000876
877 // All the clip elements will be contained within these bounds.
878 static const SkRect kBounds = SkRect::MakeWH(100, 100);
879
880 enum {
881 kNumTests = 200,
882 kMinElemsPerTest = 1,
883 kMaxElemsPerTest = 50,
884 };
885
886 // min/max size of a clip element as a fraction of kBounds.
887 static const SkScalar kMinElemSizeFrac = SK_Scalar1 / 5;
888 static const SkScalar kMaxElemSizeFrac = SK_Scalar1;
889
890 static const SkRegion::Op kOps[] = {
891 SkRegion::kDifference_Op,
892 SkRegion::kIntersect_Op,
893 SkRegion::kUnion_Op,
894 SkRegion::kXOR_Op,
895 SkRegion::kReverseDifference_Op,
896 SkRegion::kReplace_Op,
897 };
898
899 // Replace operations short-circuit the optimizer. We want to make sure that we test this code
900 // path a little bit but we don't want it to prevent us from testing many longer traversals in
901 // the optimizer.
902 static const int kReplaceDiv = 4 * kMaxElemsPerTest;
903
bsalomon@google.com705e8402012-11-27 15:43:57 +0000904 // We want to test inverse fills. However, they are quite rare in practice so don't over do it.
905 static const SkScalar kFractionInverted = SK_Scalar1 / kMaxElemsPerTest;
906
bsalomon@google.com51a62862012-11-26 21:19:43 +0000907 static const AddElementFunc kElementFuncs[] = {
908 add_rect,
909 add_round_rect,
910 add_oval,
911 };
912
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000913 SkRandom r;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000914
915 for (int i = 0; i < kNumTests; ++i) {
916 // Randomly generate a clip stack.
917 SkClipStack stack;
918 int numElems = r.nextRangeU(kMinElemsPerTest, kMaxElemsPerTest);
919 for (int e = 0; e < numElems; ++e) {
920 SkRegion::Op op = kOps[r.nextULessThan(SK_ARRAY_COUNT(kOps))];
921 if (op == SkRegion::kReplace_Op) {
922 if (r.nextU() % kReplaceDiv) {
923 --e;
924 continue;
925 }
926 }
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000927
bsalomon@google.com51a62862012-11-26 21:19:43 +0000928 // saves can change the clip stack behavior when an element is added.
929 bool doSave = r.nextBool();
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000930
bsalomon@google.com51a62862012-11-26 21:19:43 +0000931 SkSize size = SkSize::Make(
932 SkScalarFloorToScalar(SkScalarMul(kBounds.width(), r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac))),
933 SkScalarFloorToScalar(SkScalarMul(kBounds.height(), r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac))));
934
935 SkPoint xy = {SkScalarFloorToScalar(r.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth)),
936 SkScalarFloorToScalar(r.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight))};
937
938 SkRect rect = SkRect::MakeXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight);
939
bsalomon@google.com705e8402012-11-27 15:43:57 +0000940 bool invert = r.nextBiasedBool(kFractionInverted);
941 kElementFuncs[r.nextULessThan(SK_ARRAY_COUNT(kElementFuncs))](rect, invert, op, &stack);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000942 if (doSave) {
943 stack.save();
944 }
945 }
946
bsalomon@google.coma4444302012-12-04 15:22:12 +0000947 SkRect inflatedBounds = kBounds;
948 inflatedBounds.outset(kBounds.width() / 2, kBounds.height() / 2);
949 SkIRect inflatedIBounds;
950 inflatedBounds.roundOut(&inflatedIBounds);
951
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000952 typedef GrReducedClip::ElementList ElementList;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000953 // Get the reduced version of the stack.
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000954 ElementList reducedClips;
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000955 int32_t reducedGenID;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000956 GrReducedClip::InitialState initial;
jvanverth@google.comc490f802013-03-04 13:56:38 +0000957 SkIRect tBounds(inflatedIBounds);
bsalomon@google.com34cd70a2012-12-06 14:23:20 +0000958 SkIRect* tightBounds = r.nextBool() ? &tBounds : NULL;
959 GrReducedClip::ReduceClipStack(stack,
960 inflatedIBounds,
961 &reducedClips,
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000962 &reducedGenID,
bsalomon@google.com34cd70a2012-12-06 14:23:20 +0000963 &initial,
964 tightBounds);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000965
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000966 REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reducedGenID);
967
bsalomon@google.com51a62862012-11-26 21:19:43 +0000968 // Build a new clip stack based on the reduced clip elements
969 SkClipStack reducedStack;
970 if (GrReducedClip::kAllOut_InitialState == initial) {
971 // whether the result is bounded or not, the whole plane should start outside the clip.
972 reducedStack.clipEmpty();
973 }
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000974 for (ElementList::Iter iter = reducedClips.headIter(); NULL != iter.get(); iter.next()) {
975 add_elem_to_stack(*iter.get(), &reducedStack);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000976 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000977
bsalomon@google.com34cd70a2012-12-06 14:23:20 +0000978 // GrReducedClipStack assumes that the final result is clipped to the returned bounds
979 if (NULL != tightBounds) {
980 reducedStack.clipDevRect(*tightBounds, SkRegion::kIntersect_Op);
981 }
982
bsalomon@google.com51a62862012-11-26 21:19:43 +0000983 // convert both the original stack and reduced stack to SkRegions and see if they're equal
bsalomon@google.com51a62862012-11-26 21:19:43 +0000984 SkRegion region;
985 SkRegion reducedRegion;
986
987 region.setRect(inflatedIBounds);
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000988 const SkClipStack::Element* element;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000989 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000990 while ((element = iter.next())) {
991 add_elem_to_region(*element, inflatedIBounds, &region);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000992 }
993
994 reducedRegion.setRect(inflatedIBounds);
995 iter.reset(reducedStack, SkClipStack::Iter::kBottom_IterStart);
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000996 while ((element = iter.next())) {
997 add_elem_to_region(*element, inflatedIBounds, &reducedRegion);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000998 }
999
1000 REPORTER_ASSERT(reporter, region == reducedRegion);
1001 }
1002}
1003
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001004#if defined(WIN32)
1005 #define SUPPRESS_VISIBILITY_WARNING
1006#else
1007 #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden")))
1008#endif
1009
1010static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) {
1011 {
1012 SkClipStack stack;
1013 stack.clipDevRect(SkRect::MakeXYWH(0, 0, 100, 100), SkRegion::kReplace_Op, true);
1014 stack.clipDevRect(SkRect::MakeXYWH(0, 0, SkScalar(50.3), SkScalar(50.3)), SkRegion::kReplace_Op, true);
1015 SkIRect inflatedIBounds = SkIRect::MakeXYWH(0, 0, 100, 100);
1016
1017 GrReducedClip::ElementList reducedClips;
1018 int32_t reducedGenID;
1019 GrReducedClip::InitialState initial;
1020 SkIRect tightBounds;
1021
1022 GrReducedClip::ReduceClipStack(stack,
1023 inflatedIBounds,
1024 &reducedClips,
1025 &reducedGenID,
1026 &initial,
1027 &tightBounds);
1028
1029 REPORTER_ASSERT(reporter, reducedClips.count() == 1);
1030 // Clips will be cached based on the generation id. Make sure the gen id is valid.
1031 REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reducedGenID);
1032 }
1033 {
1034 SkClipStack stack;
1035
1036 // Create a clip with following 25.3, 25.3 boxes which are 25 apart:
1037 // A B
1038 // C D
1039
1040 stack.clipDevRect(SkRect::MakeXYWH(0, 0, SkScalar(25.3), SkScalar(25.3)), SkRegion::kReplace_Op, true);
1041 int32_t genIDA = stack.getTopmostGenID();
1042 stack.clipDevRect(SkRect::MakeXYWH(50, 0, SkScalar(25.3), SkScalar(25.3)), SkRegion::kUnion_Op, true);
1043 int32_t genIDB = stack.getTopmostGenID();
1044 stack.clipDevRect(SkRect::MakeXYWH(0, 50, SkScalar(25.3), SkScalar(25.3)), SkRegion::kUnion_Op, true);
1045 int32_t genIDC = stack.getTopmostGenID();
1046 stack.clipDevRect(SkRect::MakeXYWH(50, 50, SkScalar(25.3), SkScalar(25.3)), SkRegion::kUnion_Op, true);
1047 int32_t genIDD = stack.getTopmostGenID();
1048
1049
1050#define XYWH SkIRect::MakeXYWH
1051
1052 SkIRect unused;
1053 unused.setEmpty();
1054 SkIRect stackBounds = XYWH(0, 0, 76, 76);
1055
1056 // The base test is to test each rect in two ways:
1057 // 1) The box dimensions. (Should reduce to "all in", no elements).
1058 // 2) A bit over the box dimensions.
1059 // In the case 2, test that the generation id is what is expected.
1060 // The rects are of fractional size so that case 2 never gets optimized to an empty element
1061 // list.
1062
1063 // Not passing in tighter bounds is tested for consistency.
1064 static const struct SUPPRESS_VISIBILITY_WARNING {
1065 SkIRect testBounds;
1066 int reducedClipCount;
1067 int32_t reducedGenID;
1068 GrReducedClip::InitialState initialState;
1069 SkIRect tighterBounds; // If this is empty, the query will not pass tighter bounds
1070 // parameter.
1071 } testCases[] = {
1072 // Rect A.
1073 { XYWH(0, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, XYWH(0, 0, 25, 25) },
1074 { XYWH(0, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, unused },
1075 { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::kAllOut_InitialState, XYWH(0, 0, 27, 27)},
1076 { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::kAllOut_InitialState, unused },
1077
1078 // Rect B.
1079 { XYWH(50, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, XYWH(50, 0, 25, 25) },
1080 { XYWH(50, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, unused },
1081 { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::kAllOut_InitialState, XYWH(50, 0, 26, 27) },
1082 { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::kAllOut_InitialState, unused },
1083
1084 // Rect C.
1085 { XYWH(0, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, XYWH(0, 50, 25, 25) },
1086 { XYWH(0, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, unused },
1087 { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::kAllOut_InitialState, XYWH(0, 50, 27, 26) },
1088 { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::kAllOut_InitialState, unused },
1089
1090 // Rect D.
1091 { XYWH(50, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, unused },
1092 { XYWH(50, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, XYWH(50, 50, 25, 25)},
1093 { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::kAllOut_InitialState, unused },
1094 { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::kAllOut_InitialState, XYWH(50, 50, 26, 26)},
1095
1096 // Other tests:
1097 { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::kAllOut_InitialState, unused },
1098 { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::kAllOut_InitialState, stackBounds },
1099
1100 // Rect in the middle, touches none.
1101 { XYWH(26, 26, 24, 24), 0, SkClipStack::kEmptyGenID, GrReducedClip::kAllOut_InitialState, unused },
1102 { XYWH(26, 26, 24, 24), 0, SkClipStack::kEmptyGenID, GrReducedClip::kAllOut_InitialState, XYWH(26, 26, 24, 24) },
1103
1104 // Rect in the middle, touches all the rects. GenID is the last rect.
1105 { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::kAllOut_InitialState, unused },
1106 { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::kAllOut_InitialState, XYWH(24, 24, 27, 27) },
1107 };
1108
1109#undef XYWH
1110
1111 for (size_t i = 0; i < SK_ARRAY_COUNT(testCases); ++i) {
1112 GrReducedClip::ElementList reducedClips;
1113 int32_t reducedGenID;
1114 GrReducedClip::InitialState initial;
1115 SkIRect tightBounds;
1116
1117 GrReducedClip::ReduceClipStack(stack,
1118 testCases[i].testBounds,
1119 &reducedClips,
1120 &reducedGenID,
1121 &initial,
1122 testCases[i].tighterBounds.isEmpty() ? NULL : &tightBounds);
1123
1124 REPORTER_ASSERT(reporter, reducedClips.count() == testCases[i].reducedClipCount);
1125 SkASSERT(reducedClips.count() == testCases[i].reducedClipCount);
1126 REPORTER_ASSERT(reporter, reducedGenID == testCases[i].reducedGenID);
1127 SkASSERT(reducedGenID == testCases[i].reducedGenID);
1128 REPORTER_ASSERT(reporter, initial == testCases[i].initialState);
1129 SkASSERT(initial == testCases[i].initialState);
1130 if (!testCases[i].tighterBounds.isEmpty()) {
1131 REPORTER_ASSERT(reporter, tightBounds == testCases[i].tighterBounds);
1132 SkASSERT(tightBounds == testCases[i].tighterBounds);
1133 }
1134 }
1135 }
1136}
1137
1138static void test_reduced_clip_stack_no_aa_crash(skiatest::Reporter* reporter) {
1139 SkClipStack stack;
1140 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 100, 100), SkRegion::kReplace_Op);
1141 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 50, 50), SkRegion::kReplace_Op);
1142 SkIRect inflatedIBounds = SkIRect::MakeXYWH(0, 0, 100, 100);
1143
1144 GrReducedClip::ElementList reducedClips;
1145 int32_t reducedGenID;
1146 GrReducedClip::InitialState initial;
1147 SkIRect tightBounds;
1148
1149 // At the time, this would crash.
1150 GrReducedClip::ReduceClipStack(stack,
1151 inflatedIBounds,
1152 &reducedClips,
1153 &reducedGenID,
1154 &initial,
1155 &tightBounds);
1156
1157 REPORTER_ASSERT(reporter, 0 == reducedClips.count());
1158}
1159
bsalomon@google.coma4e13c82012-11-26 21:38:37 +00001160#endif
bsalomon@google.com51a62862012-11-26 21:19:43 +00001161
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00001162DEF_TEST(ClipStack, reporter) {
reed@google.combdee9fc2011-02-22 20:17:43 +00001163 SkClipStack stack;
1164
robertphillips@google.com80214e22012-07-20 15:33:18 +00001165 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001166 assert_count(reporter, stack, 0);
1167
1168 static const SkIRect gRects[] = {
1169 { 0, 0, 100, 100 },
1170 { 25, 25, 125, 125 },
1171 { 0, 0, 1000, 1000 },
1172 { 0, 0, 75, 75 }
1173 };
1174 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
reed@google.comd9f2dea2011-10-12 14:43:27 +00001175 stack.clipDevRect(gRects[i], SkRegion::kIntersect_Op);
reed@google.combdee9fc2011-02-22 20:17:43 +00001176 }
1177
1178 // all of the above rects should have been intersected, leaving only 1 rect
robertphillips@google.com80214e22012-07-20 15:33:18 +00001179 SkClipStack::B2TIter iter(stack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001180 const SkClipStack::Element* element = iter.next();
epoger@google.com2047f002011-05-17 17:36:59 +00001181 SkRect answer;
1182 answer.iset(25, 25, 75, 75);
reed@google.combdee9fc2011-02-22 20:17:43 +00001183
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001184 REPORTER_ASSERT(reporter, NULL != element);
1185 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
1186 REPORTER_ASSERT(reporter, SkRegion::kIntersect_Op == element->getOp());
1187 REPORTER_ASSERT(reporter, element->getRect() == answer);
reed@google.combdee9fc2011-02-22 20:17:43 +00001188 // now check that we only had one in our iterator
1189 REPORTER_ASSERT(reporter, !iter.next());
1190
1191 stack.reset();
robertphillips@google.com80214e22012-07-20 15:33:18 +00001192 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001193 assert_count(reporter, stack, 0);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +00001194
1195 test_assign_and_comparison(reporter);
robertphillips@google.com80214e22012-07-20 15:33:18 +00001196 test_iterators(reporter);
robertphillips@google.com08eacc12012-08-02 12:49:00 +00001197 test_bounds(reporter, true); // once with rects
1198 test_bounds(reporter, false); // once with paths
robertphillips@google.comcc6493b2012-07-26 18:39:13 +00001199 test_isWideOpen(reporter);
robertphillips@google.com08eacc12012-08-02 12:49:00 +00001200 test_rect_merging(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001201 test_rect_replace(reporter);
junov@chromium.orgedf32d52012-12-10 14:57:54 +00001202 test_rect_inverse_fill(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001203 test_path_replace(reporter);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +00001204 test_quickContains(reporter);
bsalomon@google.come7b3d292012-11-26 21:42:32 +00001205#if SK_SUPPORT_GPU
bsalomon@google.comedb26fd2012-11-28 14:42:41 +00001206 test_reduced_clip_stack(reporter);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001207 test_reduced_clip_stack_genid(reporter);
1208 test_reduced_clip_stack_no_aa_crash(reporter);
bsalomon@google.come7b3d292012-11-26 21:42:32 +00001209#endif
reed@google.combdee9fc2011-02-22 20:17:43 +00001210}