blob: c2ad21b24d9f26ebba6f3bae0a3f789accfe0ae8 [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());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000061 r = SkRect::MakeLTRB(14, 15, 16, 17);
reed@google.comd9f2dea2011-10-12 14:43:27 +000062 s.clipDevRect(r, SkRegion::kUnion_Op, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000063 REPORTER_ASSERT(reporter, s == copy);
64
65 // Test that a different op on one level triggers not equal.
66 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000067 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000068 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000069 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000070 r = SkRect::MakeLTRB(14, 15, 16, 17);
reed@google.comd9f2dea2011-10-12 14:43:27 +000071 s.clipDevRect(r, SkRegion::kIntersect_Op, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000072 REPORTER_ASSERT(reporter, s != copy);
73
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000074 // Test that version constructed with rect-path rather than a rect is still considered equal.
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000075 s.restore();
76 s.save();
77 SkPath rp;
78 rp.addRect(r);
reed@google.comd9f2dea2011-10-12 14:43:27 +000079 s.clipDevPath(rp, SkRegion::kUnion_Op, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000080 REPORTER_ASSERT(reporter, s == copy);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000081
82 // Test that different rects triggers not equal.
83 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000084 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000085 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000086 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
87
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000088 r = SkRect::MakeLTRB(24, 25, 26, 27);
reed@google.comd9f2dea2011-10-12 14:43:27 +000089 s.clipDevRect(r, SkRegion::kUnion_Op, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000090 REPORTER_ASSERT(reporter, s != copy);
91
92 // Sanity check
93 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000094 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
95
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000096 copy.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000097 REPORTER_ASSERT(reporter, 2 == copy.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000098 REPORTER_ASSERT(reporter, s == copy);
99 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000100 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000101 copy.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000102 REPORTER_ASSERT(reporter, 1 == copy.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000103 REPORTER_ASSERT(reporter, s == copy);
104
105 // Test that different paths triggers not equal.
106 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000107 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000108 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000109 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
110
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000111 p.addRect(r);
reed@google.comd9f2dea2011-10-12 14:43:27 +0000112 s.clipDevPath(p, SkRegion::kIntersect_Op, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000113 REPORTER_ASSERT(reporter, s != copy);
114}
reed@google.combdee9fc2011-02-22 20:17:43 +0000115
116static void assert_count(skiatest::Reporter* reporter, const SkClipStack& stack,
117 int count) {
robertphillips@google.com80214e22012-07-20 15:33:18 +0000118 SkClipStack::B2TIter iter(stack);
reed@google.combdee9fc2011-02-22 20:17:43 +0000119 int counter = 0;
120 while (iter.next()) {
121 counter += 1;
122 }
123 REPORTER_ASSERT(reporter, count == counter);
124}
125
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000126// Exercise the SkClipStack's bottom to top and bidirectional iterators
127// (including the skipToTopmost functionality)
robertphillips@google.com80214e22012-07-20 15:33:18 +0000128static void test_iterators(skiatest::Reporter* reporter) {
129 SkClipStack stack;
130
131 static const SkRect gRects[] = {
132 { 0, 0, 40, 40 },
133 { 60, 0, 100, 40 },
134 { 0, 60, 40, 100 },
135 { 60, 60, 100, 100 }
136 };
137
138 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
139 // the union op will prevent these from being fused together
140 stack.clipDevRect(gRects[i], SkRegion::kUnion_Op, false);
141 }
142
143 assert_count(reporter, stack, 4);
144
145 // bottom to top iteration
146 {
halcanary96fcdcc2015-08-27 07:41:13 -0700147 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000148
149 SkClipStack::B2TIter iter(stack);
150 int i;
151
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000152 for (i = 0, element = iter.next(); element; ++i, element = iter.next()) {
153 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
154 REPORTER_ASSERT(reporter, element->getRect() == gRects[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000155 }
156
157 SkASSERT(i == 4);
158 }
159
160 // top to bottom iteration
161 {
halcanary96fcdcc2015-08-27 07:41:13 -0700162 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000163
164 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
165 int i;
166
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000167 for (i = 3, element = iter.prev(); element; --i, element = iter.prev()) {
168 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
169 REPORTER_ASSERT(reporter, element->getRect() == gRects[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000170 }
171
172 SkASSERT(i == -1);
173 }
174
175 // skipToTopmost
176 {
halcanary96fcdcc2015-08-27 07:41:13 -0700177 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000178
179 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
180
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000181 element = iter.skipToTopmost(SkRegion::kUnion_Op);
182 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
183 REPORTER_ASSERT(reporter, element->getRect() == gRects[3]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000184 }
185}
186
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000187// Exercise the SkClipStack's getConservativeBounds computation
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000188static void test_bounds(skiatest::Reporter* reporter, SkClipStack::Element::Type primType) {
robertphillips@google.com607fe072012-07-24 13:54:00 +0000189 static const int gNumCases = 20;
190 static const SkRect gAnswerRectsBW[gNumCases] = {
191 // A op B
192 { 40, 40, 50, 50 },
193 { 10, 10, 50, 50 },
194 { 10, 10, 80, 80 },
195 { 10, 10, 80, 80 },
196 { 40, 40, 80, 80 },
197
198 // invA op B
199 { 40, 40, 80, 80 },
200 { 0, 0, 100, 100 },
201 { 0, 0, 100, 100 },
202 { 0, 0, 100, 100 },
203 { 40, 40, 50, 50 },
204
205 // A op invB
206 { 10, 10, 50, 50 },
207 { 40, 40, 50, 50 },
208 { 0, 0, 100, 100 },
209 { 0, 0, 100, 100 },
210 { 0, 0, 100, 100 },
211
212 // invA op invB
213 { 0, 0, 100, 100 },
214 { 40, 40, 80, 80 },
215 { 0, 0, 100, 100 },
216 { 10, 10, 80, 80 },
217 { 10, 10, 50, 50 },
218 };
219
220 static const SkRegion::Op gOps[] = {
221 SkRegion::kIntersect_Op,
222 SkRegion::kDifference_Op,
223 SkRegion::kUnion_Op,
224 SkRegion::kXOR_Op,
225 SkRegion::kReverseDifference_Op
226 };
227
228 SkRect rectA, rectB;
229
230 rectA.iset(10, 10, 50, 50);
231 rectB.iset(40, 40, 80, 80);
232
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000233 SkRRect rrectA, rrectB;
234 rrectA.setOval(rectA);
235 rrectB.setRectXY(rectB, SkIntToScalar(1), SkIntToScalar(2));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000236
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000237 SkPath pathA, pathB;
238
239 pathA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
240 pathB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000241
242 SkClipStack stack;
robertphillips@google.com7b112892012-07-31 15:18:21 +0000243 SkRect devClipBound;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000244 bool isIntersectionOfRects = false;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000245
246 int testCase = 0;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000247 int numBitTests = SkClipStack::Element::kPath_Type == primType ? 4 : 1;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000248 for (int invBits = 0; invBits < numBitTests; ++invBits) {
robertphillips@google.com607fe072012-07-24 13:54:00 +0000249 for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) {
250
251 stack.save();
252 bool doInvA = SkToBool(invBits & 1);
253 bool doInvB = SkToBool(invBits & 2);
254
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000255 pathA.setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType :
robertphillips@google.com607fe072012-07-24 13:54:00 +0000256 SkPath::kEvenOdd_FillType);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000257 pathB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType :
robertphillips@google.com607fe072012-07-24 13:54:00 +0000258 SkPath::kEvenOdd_FillType);
259
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000260 switch (primType) {
261 case SkClipStack::Element::kEmpty_Type:
262 SkDEBUGFAIL("Don't call this with kEmpty.");
263 break;
264 case SkClipStack::Element::kRect_Type:
265 stack.clipDevRect(rectA, SkRegion::kIntersect_Op, false);
266 stack.clipDevRect(rectB, gOps[op], false);
267 break;
268 case SkClipStack::Element::kRRect_Type:
269 stack.clipDevRRect(rrectA, SkRegion::kIntersect_Op, false);
270 stack.clipDevRRect(rrectB, gOps[op], false);
271 break;
272 case SkClipStack::Element::kPath_Type:
273 stack.clipDevPath(pathA, SkRegion::kIntersect_Op, false);
274 stack.clipDevPath(pathB, gOps[op], false);
275 break;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000276 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000277
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000278 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000279 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000280
robertphillips@google.com7b112892012-07-31 15:18:21 +0000281 stack.getConservativeBounds(0, 0, 100, 100, &devClipBound,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000282 &isIntersectionOfRects);
283
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000284 if (SkClipStack::Element::kRect_Type == primType) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000285 REPORTER_ASSERT(reporter, isIntersectionOfRects ==
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000286 (gOps[op] == SkRegion::kIntersect_Op));
287 } else {
288 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
289 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000290
291 SkASSERT(testCase < gNumCases);
robertphillips@google.com7b112892012-07-31 15:18:21 +0000292 REPORTER_ASSERT(reporter, devClipBound == gAnswerRectsBW[testCase]);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000293 ++testCase;
294
295 stack.restore();
296 }
297 }
298}
299
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000300// Test out 'isWideOpen' entry point
301static void test_isWideOpen(skiatest::Reporter* reporter) {
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000302 {
303 // Empty stack is wide open. Wide open stack means that gen id is wide open.
304 SkClipStack stack;
305 REPORTER_ASSERT(reporter, stack.isWideOpen());
306 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
307 }
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000308
309 SkRect rectA, rectB;
310
311 rectA.iset(10, 10, 40, 40);
312 rectB.iset(50, 50, 80, 80);
313
314 // Stack should initially be wide open
315 {
316 SkClipStack stack;
317
318 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000319 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000320 }
321
322 // Test out case where the user specifies a union that includes everything
323 {
324 SkClipStack stack;
325
326 SkPath clipA, clipB;
327
328 clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
329 clipA.setFillType(SkPath::kInverseEvenOdd_FillType);
330
331 clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
332 clipB.setFillType(SkPath::kInverseEvenOdd_FillType);
333
334 stack.clipDevPath(clipA, SkRegion::kReplace_Op, false);
335 stack.clipDevPath(clipB, SkRegion::kUnion_Op, false);
336
337 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000338 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000339 }
340
341 // Test out union w/ a wide open clip
342 {
343 SkClipStack stack;
344
345 stack.clipDevRect(rectA, SkRegion::kUnion_Op, false);
346
347 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000348 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000349 }
350
351 // Test out empty difference from a wide open clip
352 {
353 SkClipStack stack;
354
355 SkRect emptyRect;
356 emptyRect.setEmpty();
357
358 stack.clipDevRect(emptyRect, SkRegion::kDifference_Op, false);
359
360 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000361 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000362 }
363
364 // Test out return to wide open
365 {
366 SkClipStack stack;
367
368 stack.save();
369
370 stack.clipDevRect(rectA, SkRegion::kReplace_Op, false);
371
372 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000373 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000374
375 stack.restore();
376
377 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000378 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000379 }
380}
381
bsalomon@google.com100abf42012-09-05 17:40:04 +0000382static int count(const SkClipStack& stack) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000383
384 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
385
halcanary96fcdcc2015-08-27 07:41:13 -0700386 const SkClipStack::Element* element = nullptr;
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000387 int count = 0;
388
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000389 for (element = iter.prev(); element; element = iter.prev(), ++count) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000390 ;
391 }
392
393 return count;
394}
395
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000396static void test_rect_inverse_fill(skiatest::Reporter* reporter) {
397 // non-intersecting rectangles
398 SkRect rect = SkRect::MakeLTRB(0, 0, 10, 10);
399
400 SkPath path;
401 path.addRect(rect);
402 path.toggleInverseFillType();
403 SkClipStack stack;
404 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
405
406 SkRect bounds;
407 SkClipStack::BoundsType boundsType;
408 stack.getBounds(&bounds, &boundsType);
409 REPORTER_ASSERT(reporter, SkClipStack::kInsideOut_BoundsType == boundsType);
410 REPORTER_ASSERT(reporter, bounds == rect);
411}
412
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000413static void test_rect_replace(skiatest::Reporter* reporter) {
414 SkRect rect = SkRect::MakeWH(100, 100);
415 SkRect rect2 = SkRect::MakeXYWH(50, 50, 100, 100);
416
417 SkRect bound;
418 SkClipStack::BoundsType type;
419 bool isIntersectionOfRects;
420
421 // Adding a new rect with the replace operator should not increase
422 // the stack depth. BW replacing BW.
423 {
424 SkClipStack stack;
425 REPORTER_ASSERT(reporter, 0 == count(stack));
426 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
427 REPORTER_ASSERT(reporter, 1 == count(stack));
428 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
429 REPORTER_ASSERT(reporter, 1 == count(stack));
430 }
431
432 // Adding a new rect with the replace operator should not increase
433 // the stack depth. AA replacing AA.
434 {
435 SkClipStack stack;
436 REPORTER_ASSERT(reporter, 0 == count(stack));
437 stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
438 REPORTER_ASSERT(reporter, 1 == count(stack));
439 stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
440 REPORTER_ASSERT(reporter, 1 == count(stack));
441 }
442
443 // Adding a new rect with the replace operator should not increase
444 // the stack depth. BW replacing AA replacing BW.
445 {
446 SkClipStack stack;
447 REPORTER_ASSERT(reporter, 0 == count(stack));
448 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
449 REPORTER_ASSERT(reporter, 1 == count(stack));
450 stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
451 REPORTER_ASSERT(reporter, 1 == count(stack));
452 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
453 REPORTER_ASSERT(reporter, 1 == count(stack));
454 }
455
456 // Make sure replace clip rects don't collapse too much.
457 {
458 SkClipStack stack;
459 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
460 stack.clipDevRect(rect2, SkRegion::kIntersect_Op, false);
461 REPORTER_ASSERT(reporter, 1 == count(stack));
462
463 stack.save();
464 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
465 REPORTER_ASSERT(reporter, 2 == count(stack));
466 stack.getBounds(&bound, &type, &isIntersectionOfRects);
467 REPORTER_ASSERT(reporter, bound == rect);
468 stack.restore();
469 REPORTER_ASSERT(reporter, 1 == count(stack));
470
471 stack.save();
472 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
473 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
474 REPORTER_ASSERT(reporter, 2 == count(stack));
475 stack.restore();
476 REPORTER_ASSERT(reporter, 1 == count(stack));
477
478 stack.save();
479 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
480 stack.clipDevRect(rect2, SkRegion::kIntersect_Op, false);
481 stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
482 REPORTER_ASSERT(reporter, 2 == count(stack));
483 stack.restore();
484 REPORTER_ASSERT(reporter, 1 == count(stack));
485 }
486}
487
488// Simplified path-based version of test_rect_replace.
489static void test_path_replace(skiatest::Reporter* reporter) {
490 SkRect rect = SkRect::MakeWH(100, 100);
491 SkPath path;
492 path.addCircle(50, 50, 50);
493
494 // Replace operation doesn't grow the stack.
495 {
496 SkClipStack stack;
497 REPORTER_ASSERT(reporter, 0 == count(stack));
498 stack.clipDevPath(path, SkRegion::kReplace_Op, false);
499 REPORTER_ASSERT(reporter, 1 == count(stack));
500 stack.clipDevPath(path, SkRegion::kReplace_Op, false);
501 REPORTER_ASSERT(reporter, 1 == count(stack));
502 }
503
504 // Replacing rect with path.
505 {
506 SkClipStack stack;
507 stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
508 REPORTER_ASSERT(reporter, 1 == count(stack));
509 stack.clipDevPath(path, SkRegion::kReplace_Op, true);
510 REPORTER_ASSERT(reporter, 1 == count(stack));
511 }
512}
513
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000514// Test out SkClipStack's merging of rect clips. In particular exercise
515// merging of aa vs. bw rects.
516static void test_rect_merging(skiatest::Reporter* reporter) {
517
518 SkRect overlapLeft = SkRect::MakeLTRB(10, 10, 50, 50);
519 SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80);
520
521 SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90);
522 SkRect nestedChild = SkRect::MakeLTRB(40, 40, 60, 60);
523
524 SkRect bound;
525 SkClipStack::BoundsType type;
526 bool isIntersectionOfRects;
527
528 // all bw overlapping - should merge
529 {
530 SkClipStack stack;
531
532 stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, false);
533
534 stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false);
535
536 REPORTER_ASSERT(reporter, 1 == count(stack));
537
538 stack.getBounds(&bound, &type, &isIntersectionOfRects);
539
540 REPORTER_ASSERT(reporter, isIntersectionOfRects);
541 }
542
543 // all aa overlapping - should merge
544 {
545 SkClipStack stack;
546
547 stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true);
548
549 stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, true);
550
551 REPORTER_ASSERT(reporter, 1 == count(stack));
552
553 stack.getBounds(&bound, &type, &isIntersectionOfRects);
554
555 REPORTER_ASSERT(reporter, isIntersectionOfRects);
556 }
557
558 // mixed overlapping - should _not_ merge
559 {
560 SkClipStack stack;
561
562 stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true);
563
564 stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false);
565
566 REPORTER_ASSERT(reporter, 2 == count(stack));
567
568 stack.getBounds(&bound, &type, &isIntersectionOfRects);
569
570 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
571 }
572
573 // mixed nested (bw inside aa) - should merge
574 {
575 SkClipStack stack;
576
577 stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, true);
578
579 stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, false);
580
581 REPORTER_ASSERT(reporter, 1 == count(stack));
582
583 stack.getBounds(&bound, &type, &isIntersectionOfRects);
584
585 REPORTER_ASSERT(reporter, isIntersectionOfRects);
586 }
587
588 // mixed nested (aa inside bw) - should merge
589 {
590 SkClipStack stack;
591
592 stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, false);
593
594 stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, true);
595
596 REPORTER_ASSERT(reporter, 1 == count(stack));
597
598 stack.getBounds(&bound, &type, &isIntersectionOfRects);
599
600 REPORTER_ASSERT(reporter, isIntersectionOfRects);
601 }
602
603 // reverse nested (aa inside bw) - should _not_ merge
604 {
605 SkClipStack stack;
606
607 stack.clipDevRect(nestedChild, SkRegion::kReplace_Op, false);
608
609 stack.clipDevRect(nestedParent, SkRegion::kIntersect_Op, true);
610
611 REPORTER_ASSERT(reporter, 2 == count(stack));
612
613 stack.getBounds(&bound, &type, &isIntersectionOfRects);
614
615 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
616 }
617}
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000618
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000619static void test_quickContains(skiatest::Reporter* reporter) {
620 SkRect testRect = SkRect::MakeLTRB(10, 10, 40, 40);
621 SkRect insideRect = SkRect::MakeLTRB(20, 20, 30, 30);
622 SkRect intersectingRect = SkRect::MakeLTRB(25, 25, 50, 50);
623 SkRect outsideRect = SkRect::MakeLTRB(0, 0, 50, 50);
624 SkRect nonIntersectingRect = SkRect::MakeLTRB(100, 100, 110, 110);
625
626 SkPath insideCircle;
627 insideCircle.addCircle(25, 25, 5);
628 SkPath intersectingCircle;
629 intersectingCircle.addCircle(25, 40, 10);
630 SkPath outsideCircle;
631 outsideCircle.addCircle(25, 25, 50);
632 SkPath nonIntersectingCircle;
633 nonIntersectingCircle.addCircle(100, 100, 5);
634
635 {
636 SkClipStack stack;
637 stack.clipDevRect(outsideRect, SkRegion::kDifference_Op, false);
638 // return false because quickContains currently does not care for kDifference_Op
639 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
640 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000641
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000642 // Replace Op tests
643 {
644 SkClipStack stack;
645 stack.clipDevRect(outsideRect, SkRegion::kReplace_Op, false);
646 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
647 }
648
649 {
650 SkClipStack stack;
651 stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
652 stack.save(); // To prevent in-place substitution by replace OP
653 stack.clipDevRect(outsideRect, SkRegion::kReplace_Op, false);
654 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
655 stack.restore();
656 }
657
658 {
659 SkClipStack stack;
660 stack.clipDevRect(outsideRect, SkRegion::kIntersect_Op, false);
661 stack.save(); // To prevent in-place substitution by replace OP
662 stack.clipDevRect(insideRect, SkRegion::kReplace_Op, false);
663 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
664 stack.restore();
665 }
666
667 // Verify proper traversal of multi-element clip
668 {
669 SkClipStack stack;
670 stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
671 // Use a path for second clip to prevent in-place intersection
672 stack.clipDevPath(outsideCircle, SkRegion::kIntersect_Op, false);
673 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
674 }
675
676 // Intersect Op tests with rectangles
677 {
678 SkClipStack stack;
679 stack.clipDevRect(outsideRect, SkRegion::kIntersect_Op, false);
680 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
681 }
682
683 {
684 SkClipStack stack;
685 stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
686 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
687 }
688
689 {
690 SkClipStack stack;
691 stack.clipDevRect(intersectingRect, SkRegion::kIntersect_Op, false);
692 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
693 }
694
695 {
696 SkClipStack stack;
697 stack.clipDevRect(nonIntersectingRect, SkRegion::kIntersect_Op, false);
698 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
699 }
700
701 // Intersect Op tests with circle paths
702 {
703 SkClipStack stack;
704 stack.clipDevPath(outsideCircle, SkRegion::kIntersect_Op, false);
705 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
706 }
707
708 {
709 SkClipStack stack;
710 stack.clipDevPath(insideCircle, SkRegion::kIntersect_Op, false);
711 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
712 }
713
714 {
715 SkClipStack stack;
716 stack.clipDevPath(intersectingCircle, SkRegion::kIntersect_Op, false);
717 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
718 }
719
720 {
721 SkClipStack stack;
722 stack.clipDevPath(nonIntersectingCircle, SkRegion::kIntersect_Op, false);
723 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
724 }
725
726 // Intersect Op tests with inverse filled rectangles
727 {
728 SkClipStack stack;
729 SkPath path;
730 path.addRect(outsideRect);
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(insideRect);
740 path.toggleInverseFillType();
741 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
742 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
743 }
744
745 {
746 SkClipStack stack;
747 SkPath path;
748 path.addRect(intersectingRect);
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;
757 path.addRect(nonIntersectingRect);
758 path.toggleInverseFillType();
759 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
760 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
761 }
762
763 // Intersect Op tests with inverse filled circles
764 {
765 SkClipStack stack;
766 SkPath path = outsideCircle;
767 path.toggleInverseFillType();
768 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
769 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
770 }
771
772 {
773 SkClipStack stack;
774 SkPath path = insideCircle;
775 path.toggleInverseFillType();
776 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
777 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
778 }
779
780 {
781 SkClipStack stack;
782 SkPath path = intersectingCircle;
783 path.toggleInverseFillType();
784 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
785 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
786 }
787
788 {
789 SkClipStack stack;
790 SkPath path = nonIntersectingCircle;
791 path.toggleInverseFillType();
792 stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
793 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
794 }
795}
796
bsalomon@google.com51a62862012-11-26 21:19:43 +0000797///////////////////////////////////////////////////////////////////////////////////////////////////
798
bsalomon@google.coma4e13c82012-11-26 21:38:37 +0000799#if SK_SUPPORT_GPU
bsalomon@google.com705e8402012-11-27 15:43:57 +0000800// Functions that add a shape to the clip stack. The shape is computed from a rectangle.
801// AA is always disabled since the clip stack reducer can cause changes in aa rasterization of the
802// stack. A fractional edge repeated in different elements may be rasterized fewer times using the
803// reduced stack.
804typedef void (*AddElementFunc) (const SkRect& rect,
805 bool invert,
806 SkRegion::Op op,
807 SkClipStack* stack);
bsalomon@google.coma4e13c82012-11-26 21:38:37 +0000808
bsalomon@google.com705e8402012-11-27 15:43:57 +0000809static void add_round_rect(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000810 SkScalar rx = rect.width() / 10;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000811 SkScalar ry = rect.height() / 20;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000812 if (invert) {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000813 SkPath path;
814 path.addRoundRect(rect, rx, ry);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000815 path.setFillType(SkPath::kInverseWinding_FillType);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000816 stack->clipDevPath(path, op, false);
817 } else {
818 SkRRect rrect;
819 rrect.setRectXY(rect, rx, ry);
820 stack->clipDevRRect(rrect, op, false);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000821 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000822};
823
bsalomon@google.com705e8402012-11-27 15:43:57 +0000824static void add_rect(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) {
825 if (invert) {
826 SkPath path;
827 path.addRect(rect);
828 path.setFillType(SkPath::kInverseWinding_FillType);
829 stack->clipDevPath(path, op, false);
830 } else {
831 stack->clipDevRect(rect, op, false);
832 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000833};
834
bsalomon@google.com705e8402012-11-27 15:43:57 +0000835static void add_oval(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000836 SkPath path;
837 path.addOval(rect);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000838 if (invert) {
839 path.setFillType(SkPath::kInverseWinding_FillType);
840 }
841 stack->clipDevPath(path, op, false);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000842};
843
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000844static void add_elem_to_stack(const SkClipStack::Element& element, SkClipStack* stack) {
845 switch (element.getType()) {
846 case SkClipStack::Element::kRect_Type:
847 stack->clipDevRect(element.getRect(), element.getOp(), element.isAA());
848 break;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000849 case SkClipStack::Element::kRRect_Type:
850 stack->clipDevRRect(element.getRRect(), element.getOp(), element.isAA());
851 break;
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000852 case SkClipStack::Element::kPath_Type:
853 stack->clipDevPath(element.getPath(), element.getOp(), element.isAA());
854 break;
855 case SkClipStack::Element::kEmpty_Type:
856 SkDEBUGFAIL("Why did the reducer produce an explicit empty.");
857 stack->clipEmpty();
858 break;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000859 }
860}
861
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000862static void add_elem_to_region(const SkClipStack::Element& element,
bsalomon@google.com51a62862012-11-26 21:19:43 +0000863 const SkIRect& bounds,
864 SkRegion* region) {
865 SkRegion elemRegion;
866 SkRegion boundsRgn(bounds);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000867 SkPath path;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000868
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000869 switch (element.getType()) {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000870 case SkClipStack::Element::kEmpty_Type:
871 elemRegion.setEmpty();
872 break;
873 default:
874 element.asPath(&path);
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000875 elemRegion.setPath(path, boundsRgn);
876 break;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000877 }
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000878 region->op(elemRegion, element.getOp());
bsalomon@google.com51a62862012-11-26 21:19:43 +0000879}
880
bsalomon@google.com51a62862012-11-26 21:19:43 +0000881static void test_reduced_clip_stack(skiatest::Reporter* reporter) {
882 // We construct random clip stacks, reduce them, and then rasterize both versions to verify that
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000883 // they are equal.
bsalomon@google.com51a62862012-11-26 21:19:43 +0000884
885 // All the clip elements will be contained within these bounds.
886 static const SkRect kBounds = SkRect::MakeWH(100, 100);
887
888 enum {
889 kNumTests = 200,
890 kMinElemsPerTest = 1,
891 kMaxElemsPerTest = 50,
892 };
893
894 // min/max size of a clip element as a fraction of kBounds.
895 static const SkScalar kMinElemSizeFrac = SK_Scalar1 / 5;
896 static const SkScalar kMaxElemSizeFrac = SK_Scalar1;
897
898 static const SkRegion::Op kOps[] = {
899 SkRegion::kDifference_Op,
900 SkRegion::kIntersect_Op,
901 SkRegion::kUnion_Op,
902 SkRegion::kXOR_Op,
903 SkRegion::kReverseDifference_Op,
904 SkRegion::kReplace_Op,
905 };
906
907 // Replace operations short-circuit the optimizer. We want to make sure that we test this code
908 // path a little bit but we don't want it to prevent us from testing many longer traversals in
909 // the optimizer.
910 static const int kReplaceDiv = 4 * kMaxElemsPerTest;
911
bsalomon@google.com705e8402012-11-27 15:43:57 +0000912 // We want to test inverse fills. However, they are quite rare in practice so don't over do it.
913 static const SkScalar kFractionInverted = SK_Scalar1 / kMaxElemsPerTest;
914
bsalomon@google.com51a62862012-11-26 21:19:43 +0000915 static const AddElementFunc kElementFuncs[] = {
916 add_rect,
917 add_round_rect,
918 add_oval,
919 };
920
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000921 SkRandom r;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000922
923 for (int i = 0; i < kNumTests; ++i) {
924 // Randomly generate a clip stack.
925 SkClipStack stack;
926 int numElems = r.nextRangeU(kMinElemsPerTest, kMaxElemsPerTest);
927 for (int e = 0; e < numElems; ++e) {
928 SkRegion::Op op = kOps[r.nextULessThan(SK_ARRAY_COUNT(kOps))];
929 if (op == SkRegion::kReplace_Op) {
930 if (r.nextU() % kReplaceDiv) {
931 --e;
932 continue;
933 }
934 }
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000935
bsalomon@google.com51a62862012-11-26 21:19:43 +0000936 // saves can change the clip stack behavior when an element is added.
937 bool doSave = r.nextBool();
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000938
bsalomon@google.com51a62862012-11-26 21:19:43 +0000939 SkSize size = SkSize::Make(
940 SkScalarFloorToScalar(SkScalarMul(kBounds.width(), r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac))),
941 SkScalarFloorToScalar(SkScalarMul(kBounds.height(), r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac))));
942
943 SkPoint xy = {SkScalarFloorToScalar(r.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth)),
944 SkScalarFloorToScalar(r.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight))};
945
946 SkRect rect = SkRect::MakeXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight);
947
bsalomon@google.com705e8402012-11-27 15:43:57 +0000948 bool invert = r.nextBiasedBool(kFractionInverted);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000949
bsalomon@google.com705e8402012-11-27 15:43:57 +0000950 kElementFuncs[r.nextULessThan(SK_ARRAY_COUNT(kElementFuncs))](rect, invert, op, &stack);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000951 if (doSave) {
952 stack.save();
953 }
954 }
955
bsalomon@google.coma4444302012-12-04 15:22:12 +0000956 SkRect inflatedBounds = kBounds;
957 inflatedBounds.outset(kBounds.width() / 2, kBounds.height() / 2);
958 SkIRect inflatedIBounds;
959 inflatedBounds.roundOut(&inflatedIBounds);
960
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000961 typedef GrReducedClip::ElementList ElementList;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000962 // Get the reduced version of the stack.
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000963 ElementList reducedClips;
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000964 int32_t reducedGenID;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000965 GrReducedClip::InitialState initial;
jvanverth@google.comc490f802013-03-04 13:56:38 +0000966 SkIRect tBounds(inflatedIBounds);
halcanary96fcdcc2015-08-27 07:41:13 -0700967 SkIRect* tightBounds = r.nextBool() ? &tBounds : nullptr;
bsalomon@google.com34cd70a2012-12-06 14:23:20 +0000968 GrReducedClip::ReduceClipStack(stack,
969 inflatedIBounds,
970 &reducedClips,
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000971 &reducedGenID,
bsalomon@google.com34cd70a2012-12-06 14:23:20 +0000972 &initial,
973 tightBounds);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000974
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000975 REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reducedGenID);
976
bsalomon@google.com51a62862012-11-26 21:19:43 +0000977 // Build a new clip stack based on the reduced clip elements
978 SkClipStack reducedStack;
979 if (GrReducedClip::kAllOut_InitialState == initial) {
980 // whether the result is bounded or not, the whole plane should start outside the clip.
981 reducedStack.clipEmpty();
982 }
bsalomon49f085d2014-09-05 13:34:00 -0700983 for (ElementList::Iter iter = reducedClips.headIter(); iter.get(); iter.next()) {
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000984 add_elem_to_stack(*iter.get(), &reducedStack);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000985 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000986
bsalomon@google.com34cd70a2012-12-06 14:23:20 +0000987 // GrReducedClipStack assumes that the final result is clipped to the returned bounds
bsalomon49f085d2014-09-05 13:34:00 -0700988 if (tightBounds) {
bsalomon@google.com34cd70a2012-12-06 14:23:20 +0000989 reducedStack.clipDevRect(*tightBounds, SkRegion::kIntersect_Op);
990 }
991
bsalomon@google.com51a62862012-11-26 21:19:43 +0000992 // convert both the original stack and reduced stack to SkRegions and see if they're equal
bsalomon@google.com51a62862012-11-26 21:19:43 +0000993 SkRegion region;
994 SkRegion reducedRegion;
995
996 region.setRect(inflatedIBounds);
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000997 const SkClipStack::Element* element;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000998 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000999 while ((element = iter.next())) {
1000 add_elem_to_region(*element, inflatedIBounds, &region);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001001 }
1002
1003 reducedRegion.setRect(inflatedIBounds);
1004 iter.reset(reducedStack, SkClipStack::Iter::kBottom_IterStart);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001005 while ((element = iter.next())) {
1006 add_elem_to_region(*element, inflatedIBounds, &reducedRegion);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001007 }
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001008 SkString testCase;
1009 testCase.printf("Iteration %d", i);
1010 REPORTER_ASSERT_MESSAGE(reporter, region == reducedRegion, testCase.c_str());
bsalomon@google.com51a62862012-11-26 21:19:43 +00001011 }
1012}
1013
halcanary4dbbd042016-06-07 17:21:10 -07001014#ifdef SK_BUILD_FOR_WIN
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001015 #define SUPPRESS_VISIBILITY_WARNING
1016#else
1017 #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden")))
1018#endif
1019
1020static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) {
1021 {
1022 SkClipStack stack;
1023 stack.clipDevRect(SkRect::MakeXYWH(0, 0, 100, 100), SkRegion::kReplace_Op, true);
1024 stack.clipDevRect(SkRect::MakeXYWH(0, 0, SkScalar(50.3), SkScalar(50.3)), SkRegion::kReplace_Op, true);
1025 SkIRect inflatedIBounds = SkIRect::MakeXYWH(0, 0, 100, 100);
1026
1027 GrReducedClip::ElementList reducedClips;
1028 int32_t reducedGenID;
1029 GrReducedClip::InitialState initial;
1030 SkIRect tightBounds;
1031
1032 GrReducedClip::ReduceClipStack(stack,
1033 inflatedIBounds,
1034 &reducedClips,
1035 &reducedGenID,
1036 &initial,
1037 &tightBounds);
1038
1039 REPORTER_ASSERT(reporter, reducedClips.count() == 1);
1040 // Clips will be cached based on the generation id. Make sure the gen id is valid.
1041 REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reducedGenID);
1042 }
1043 {
1044 SkClipStack stack;
1045
1046 // Create a clip with following 25.3, 25.3 boxes which are 25 apart:
1047 // A B
1048 // C D
1049
1050 stack.clipDevRect(SkRect::MakeXYWH(0, 0, SkScalar(25.3), SkScalar(25.3)), SkRegion::kReplace_Op, true);
1051 int32_t genIDA = stack.getTopmostGenID();
1052 stack.clipDevRect(SkRect::MakeXYWH(50, 0, SkScalar(25.3), SkScalar(25.3)), SkRegion::kUnion_Op, true);
1053 int32_t genIDB = stack.getTopmostGenID();
1054 stack.clipDevRect(SkRect::MakeXYWH(0, 50, SkScalar(25.3), SkScalar(25.3)), SkRegion::kUnion_Op, true);
1055 int32_t genIDC = stack.getTopmostGenID();
1056 stack.clipDevRect(SkRect::MakeXYWH(50, 50, SkScalar(25.3), SkScalar(25.3)), SkRegion::kUnion_Op, true);
1057 int32_t genIDD = stack.getTopmostGenID();
1058
1059
1060#define XYWH SkIRect::MakeXYWH
1061
1062 SkIRect unused;
1063 unused.setEmpty();
1064 SkIRect stackBounds = XYWH(0, 0, 76, 76);
1065
1066 // The base test is to test each rect in two ways:
1067 // 1) The box dimensions. (Should reduce to "all in", no elements).
1068 // 2) A bit over the box dimensions.
1069 // In the case 2, test that the generation id is what is expected.
1070 // The rects are of fractional size so that case 2 never gets optimized to an empty element
1071 // list.
1072
1073 // Not passing in tighter bounds is tested for consistency.
1074 static const struct SUPPRESS_VISIBILITY_WARNING {
1075 SkIRect testBounds;
1076 int reducedClipCount;
1077 int32_t reducedGenID;
1078 GrReducedClip::InitialState initialState;
1079 SkIRect tighterBounds; // If this is empty, the query will not pass tighter bounds
1080 // parameter.
1081 } testCases[] = {
1082 // Rect A.
1083 { XYWH(0, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, XYWH(0, 0, 25, 25) },
1084 { XYWH(0, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, unused },
1085 { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::kAllOut_InitialState, XYWH(0, 0, 27, 27)},
1086 { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::kAllOut_InitialState, unused },
1087
1088 // Rect B.
1089 { XYWH(50, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, XYWH(50, 0, 25, 25) },
1090 { XYWH(50, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, unused },
1091 { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::kAllOut_InitialState, XYWH(50, 0, 26, 27) },
1092 { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::kAllOut_InitialState, unused },
1093
1094 // Rect C.
1095 { XYWH(0, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, XYWH(0, 50, 25, 25) },
1096 { XYWH(0, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, unused },
1097 { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::kAllOut_InitialState, XYWH(0, 50, 27, 26) },
1098 { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::kAllOut_InitialState, unused },
1099
1100 // Rect D.
1101 { XYWH(50, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, unused },
1102 { XYWH(50, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, XYWH(50, 50, 25, 25)},
1103 { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::kAllOut_InitialState, unused },
1104 { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::kAllOut_InitialState, XYWH(50, 50, 26, 26)},
1105
1106 // Other tests:
1107 { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::kAllOut_InitialState, unused },
1108 { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::kAllOut_InitialState, stackBounds },
1109
1110 // Rect in the middle, touches none.
1111 { XYWH(26, 26, 24, 24), 0, SkClipStack::kEmptyGenID, GrReducedClip::kAllOut_InitialState, unused },
1112 { XYWH(26, 26, 24, 24), 0, SkClipStack::kEmptyGenID, GrReducedClip::kAllOut_InitialState, XYWH(26, 26, 24, 24) },
1113
1114 // Rect in the middle, touches all the rects. GenID is the last rect.
1115 { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::kAllOut_InitialState, unused },
1116 { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::kAllOut_InitialState, XYWH(24, 24, 27, 27) },
1117 };
1118
1119#undef XYWH
1120
1121 for (size_t i = 0; i < SK_ARRAY_COUNT(testCases); ++i) {
1122 GrReducedClip::ElementList reducedClips;
1123 int32_t reducedGenID;
1124 GrReducedClip::InitialState initial;
1125 SkIRect tightBounds;
1126
1127 GrReducedClip::ReduceClipStack(stack,
1128 testCases[i].testBounds,
1129 &reducedClips,
1130 &reducedGenID,
1131 &initial,
halcanary96fcdcc2015-08-27 07:41:13 -07001132 testCases[i].tighterBounds.isEmpty() ? nullptr : &tightBounds);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001133
1134 REPORTER_ASSERT(reporter, reducedClips.count() == testCases[i].reducedClipCount);
1135 SkASSERT(reducedClips.count() == testCases[i].reducedClipCount);
1136 REPORTER_ASSERT(reporter, reducedGenID == testCases[i].reducedGenID);
1137 SkASSERT(reducedGenID == testCases[i].reducedGenID);
1138 REPORTER_ASSERT(reporter, initial == testCases[i].initialState);
1139 SkASSERT(initial == testCases[i].initialState);
1140 if (!testCases[i].tighterBounds.isEmpty()) {
1141 REPORTER_ASSERT(reporter, tightBounds == testCases[i].tighterBounds);
1142 SkASSERT(tightBounds == testCases[i].tighterBounds);
1143 }
1144 }
1145 }
1146}
1147
1148static void test_reduced_clip_stack_no_aa_crash(skiatest::Reporter* reporter) {
1149 SkClipStack stack;
1150 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 100, 100), SkRegion::kReplace_Op);
1151 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 50, 50), SkRegion::kReplace_Op);
1152 SkIRect inflatedIBounds = SkIRect::MakeXYWH(0, 0, 100, 100);
1153
1154 GrReducedClip::ElementList reducedClips;
1155 int32_t reducedGenID;
1156 GrReducedClip::InitialState initial;
1157 SkIRect tightBounds;
1158
1159 // At the time, this would crash.
1160 GrReducedClip::ReduceClipStack(stack,
1161 inflatedIBounds,
1162 &reducedClips,
1163 &reducedGenID,
1164 &initial,
1165 &tightBounds);
1166
1167 REPORTER_ASSERT(reporter, 0 == reducedClips.count());
1168}
1169
bsalomon@google.coma4e13c82012-11-26 21:38:37 +00001170#endif
bsalomon@google.com51a62862012-11-26 21:19:43 +00001171
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00001172DEF_TEST(ClipStack, reporter) {
reed@google.combdee9fc2011-02-22 20:17:43 +00001173 SkClipStack stack;
1174
robertphillips@google.com80214e22012-07-20 15:33:18 +00001175 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001176 assert_count(reporter, stack, 0);
1177
1178 static const SkIRect gRects[] = {
1179 { 0, 0, 100, 100 },
1180 { 25, 25, 125, 125 },
1181 { 0, 0, 1000, 1000 },
1182 { 0, 0, 75, 75 }
1183 };
1184 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
reed@google.comd9f2dea2011-10-12 14:43:27 +00001185 stack.clipDevRect(gRects[i], SkRegion::kIntersect_Op);
reed@google.combdee9fc2011-02-22 20:17:43 +00001186 }
1187
1188 // all of the above rects should have been intersected, leaving only 1 rect
robertphillips@google.com80214e22012-07-20 15:33:18 +00001189 SkClipStack::B2TIter iter(stack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001190 const SkClipStack::Element* element = iter.next();
epoger@google.com2047f002011-05-17 17:36:59 +00001191 SkRect answer;
1192 answer.iset(25, 25, 75, 75);
reed@google.combdee9fc2011-02-22 20:17:43 +00001193
bsalomon49f085d2014-09-05 13:34:00 -07001194 REPORTER_ASSERT(reporter, element);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001195 REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
1196 REPORTER_ASSERT(reporter, SkRegion::kIntersect_Op == element->getOp());
1197 REPORTER_ASSERT(reporter, element->getRect() == answer);
reed@google.combdee9fc2011-02-22 20:17:43 +00001198 // now check that we only had one in our iterator
1199 REPORTER_ASSERT(reporter, !iter.next());
1200
1201 stack.reset();
robertphillips@google.com80214e22012-07-20 15:33:18 +00001202 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001203 assert_count(reporter, stack, 0);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +00001204
1205 test_assign_and_comparison(reporter);
robertphillips@google.com80214e22012-07-20 15:33:18 +00001206 test_iterators(reporter);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001207 test_bounds(reporter, SkClipStack::Element::kRect_Type);
1208 test_bounds(reporter, SkClipStack::Element::kRRect_Type);
1209 test_bounds(reporter, SkClipStack::Element::kPath_Type);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +00001210 test_isWideOpen(reporter);
robertphillips@google.com08eacc12012-08-02 12:49:00 +00001211 test_rect_merging(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001212 test_rect_replace(reporter);
junov@chromium.orgedf32d52012-12-10 14:57:54 +00001213 test_rect_inverse_fill(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001214 test_path_replace(reporter);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +00001215 test_quickContains(reporter);
bsalomon@google.come7b3d292012-11-26 21:42:32 +00001216#if SK_SUPPORT_GPU
bsalomon@google.comedb26fd2012-11-28 14:42:41 +00001217 test_reduced_clip_stack(reporter);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001218 test_reduced_clip_stack_genid(reporter);
1219 test_reduced_clip_stack_no_aa_crash(reporter);
bsalomon@google.come7b3d292012-11-26 21:42:32 +00001220#endif
reed@google.combdee9fc2011-02-22 20:17:43 +00001221}