blob: 794c18d041714bfc91e578e8e2bb172cca037a29 [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkCanvas.h"
9#include "include/core/SkClipOp.h"
10#include "include/core/SkImageInfo.h"
11#include "include/core/SkMatrix.h"
12#include "include/core/SkPath.h"
13#include "include/core/SkPoint.h"
14#include "include/core/SkRRect.h"
15#include "include/core/SkRect.h"
16#include "include/core/SkRefCnt.h"
17#include "include/core/SkRegion.h"
18#include "include/core/SkScalar.h"
19#include "include/core/SkSize.h"
20#include "include/core/SkString.h"
21#include "include/core/SkSurface.h"
22#include "include/core/SkTypes.h"
Michael Ludwig4e221bd2020-06-05 11:29:36 -040023#include "include/effects/SkGradientShader.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050024#include "include/private/GrResourceKey.h"
Ben Wagner9707a7e2019-05-06 17:17:19 -040025#include "include/private/SkTemplates.h"
26#include "include/utils/SkRandom.h"
Ben Wagner9707a7e2019-05-06 17:17:19 -040027#include "src/core/SkClipStack.h"
Ben Wagner9707a7e2019-05-06 17:17:19 -040028#include "tests/Test.h"
csmartdaltoncbecb082016-07-22 08:59:08 -070029
Ben Wagnerb607a8f2018-03-12 13:46:21 -040030#include <cstring>
Ben Wagner9707a7e2019-05-06 17:17:19 -040031#include <initializer_list>
Ben Wagnerb607a8f2018-03-12 13:46:21 -040032#include <new>
33
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000034static void test_assign_and_comparison(skiatest::Reporter* reporter) {
35 SkClipStack s;
reed@google.comd9f2dea2011-10-12 14:43:27 +000036 bool doAA = false;
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000037
robertphillips@google.com80214e22012-07-20 15:33:18 +000038 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
39
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000040 // Build up a clip stack with a path, an empty clip, and a rect.
41 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000042 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
43
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000044 SkPath p;
45 p.moveTo(5, 6);
46 p.lineTo(7, 8);
47 p.lineTo(5, 9);
48 p.close();
Michael Ludwigd59d3e12021-08-05 16:39:18 -040049 s.clipPath(p, SkMatrix::I(), SkClipOp::kIntersect, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000050
51 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000052 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
53
Michael Ludwiga3a67ae2021-08-06 10:42:01 -040054 SkRect r = SkRect::MakeLTRB(1, 2, 103, 104);
Michael Ludwigd59d3e12021-08-05 16:39:18 -040055 s.clipRect(r, SkMatrix::I(), SkClipOp::kIntersect, doAA);
Michael Ludwiga3a67ae2021-08-06 10:42:01 -040056 r = SkRect::MakeLTRB(4, 5, 56, 57);
Michael Ludwigd59d3e12021-08-05 16:39:18 -040057 s.clipRect(r, SkMatrix::I(), SkClipOp::kIntersect, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000058
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);
Michael Ludwiga3a67ae2021-08-06 10:42:01 -040063 s.clipRect(r, SkMatrix::I(), SkClipOp::kDifference, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000064
65 // Test that assignment works.
66 SkClipStack copy = s;
67 REPORTER_ASSERT(reporter, s == copy);
68
69 // Test that different save levels triggers not equal.
70 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000071 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000072 REPORTER_ASSERT(reporter, s != copy);
73
74 // Test that an equal, but not copied version is equal.
75 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000076 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000077 r = SkRect::MakeLTRB(14, 15, 16, 17);
Michael Ludwiga3a67ae2021-08-06 10:42:01 -040078 s.clipRect(r, SkMatrix::I(), SkClipOp::kDifference, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000079 REPORTER_ASSERT(reporter, s == copy);
80
81 // Test that a different op on one level triggers not equal.
82 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000083 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000084 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000085 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000086 r = SkRect::MakeLTRB(14, 15, 16, 17);
Michael Ludwigd59d3e12021-08-05 16:39:18 -040087 s.clipRect(r, SkMatrix::I(), SkClipOp::kIntersect, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000088 REPORTER_ASSERT(reporter, s != copy);
89
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000090 // Test that version constructed with rect-path rather than a rect is still considered equal.
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000091 s.restore();
92 s.save();
93 SkPath rp;
94 rp.addRect(r);
Michael Ludwiga3a67ae2021-08-06 10:42:01 -040095 s.clipPath(rp, SkMatrix::I(), SkClipOp::kDifference, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000096 REPORTER_ASSERT(reporter, s == copy);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000097
98 // Test that different rects triggers not equal.
99 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000100 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000101 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000102 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
103
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000104 r = SkRect::MakeLTRB(24, 25, 26, 27);
Michael Ludwiga3a67ae2021-08-06 10:42:01 -0400105 s.clipRect(r, SkMatrix::I(), SkClipOp::kDifference, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000106 REPORTER_ASSERT(reporter, s != copy);
107
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000108 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000109 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
110
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000111 copy.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000112 REPORTER_ASSERT(reporter, 2 == copy.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000113 REPORTER_ASSERT(reporter, s == copy);
114 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000115 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000116 copy.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000117 REPORTER_ASSERT(reporter, 1 == copy.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000118 REPORTER_ASSERT(reporter, s == copy);
119
120 // Test that different paths triggers not equal.
121 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000122 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000123 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000124 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
125
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000126 p.addRect(r);
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400127 s.clipPath(p, SkMatrix::I(), SkClipOp::kIntersect, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000128 REPORTER_ASSERT(reporter, s != copy);
129}
reed@google.combdee9fc2011-02-22 20:17:43 +0000130
131static void assert_count(skiatest::Reporter* reporter, const SkClipStack& stack,
132 int count) {
robertphillips@google.com80214e22012-07-20 15:33:18 +0000133 SkClipStack::B2TIter iter(stack);
reed@google.combdee9fc2011-02-22 20:17:43 +0000134 int counter = 0;
135 while (iter.next()) {
136 counter += 1;
137 }
138 REPORTER_ASSERT(reporter, count == counter);
139}
140
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000141// Exercise the SkClipStack's bottom to top and bidirectional iterators
142// (including the skipToTopmost functionality)
robertphillips@google.com80214e22012-07-20 15:33:18 +0000143static void test_iterators(skiatest::Reporter* reporter) {
144 SkClipStack stack;
145
146 static const SkRect gRects[] = {
147 { 0, 0, 40, 40 },
148 { 60, 0, 100, 40 },
149 { 0, 60, 40, 100 },
150 { 60, 60, 100, 100 }
151 };
152
153 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
Michael Ludwiga3a67ae2021-08-06 10:42:01 -0400154 // the difference op will prevent these from being fused together
155 stack.clipRect(gRects[i], SkMatrix::I(), SkClipOp::kDifference, false);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000156 }
157
158 assert_count(reporter, stack, 4);
159
160 // bottom to top 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::B2TIter iter(stack);
165 int i;
166
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000167 for (i = 0, element = iter.next(); element; ++i, element = iter.next()) {
Brian Salomonf3b46e52017-08-30 11:37:57 -0400168 REPORTER_ASSERT(reporter, SkClipStack::Element::DeviceSpaceType::kRect ==
169 element->getDeviceSpaceType());
170 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == gRects[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000171 }
172
173 SkASSERT(i == 4);
174 }
175
176 // top to bottom iteration
177 {
halcanary96fcdcc2015-08-27 07:41:13 -0700178 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000179
180 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
181 int i;
182
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000183 for (i = 3, element = iter.prev(); element; --i, element = iter.prev()) {
Brian Salomonf3b46e52017-08-30 11:37:57 -0400184 REPORTER_ASSERT(reporter, SkClipStack::Element::DeviceSpaceType::kRect ==
185 element->getDeviceSpaceType());
186 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == gRects[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000187 }
188
189 SkASSERT(i == -1);
190 }
191
192 // skipToTopmost
193 {
halcanary96fcdcc2015-08-27 07:41:13 -0700194 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000195
196 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
197
Michael Ludwiga3a67ae2021-08-06 10:42:01 -0400198 element = iter.skipToTopmost(SkClipOp::kDifference);
Brian Salomonf3b46e52017-08-30 11:37:57 -0400199 REPORTER_ASSERT(reporter, SkClipStack::Element::DeviceSpaceType::kRect ==
200 element->getDeviceSpaceType());
201 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == gRects[3]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000202 }
203}
204
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000205// Exercise the SkClipStack's getConservativeBounds computation
Brian Salomonf3b46e52017-08-30 11:37:57 -0400206static void test_bounds(skiatest::Reporter* reporter,
207 SkClipStack::Element::DeviceSpaceType primType) {
Michael Ludwiga0438e62021-08-06 11:19:26 -0400208 static const int gNumCases = 8;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000209 static const SkRect gAnswerRectsBW[gNumCases] = {
210 // A op B
211 { 40, 40, 50, 50 },
212 { 10, 10, 50, 50 },
robertphillips@google.com607fe072012-07-24 13:54:00 +0000213
214 // invA op B
215 { 40, 40, 80, 80 },
216 { 0, 0, 100, 100 },
robertphillips@google.com607fe072012-07-24 13:54:00 +0000217
218 // A op invB
219 { 10, 10, 50, 50 },
220 { 40, 40, 50, 50 },
robertphillips@google.com607fe072012-07-24 13:54:00 +0000221
222 // invA op invB
223 { 0, 0, 100, 100 },
224 { 40, 40, 80, 80 },
robertphillips@google.com607fe072012-07-24 13:54:00 +0000225 };
226
Mike Reedc1f77742016-12-09 09:00:50 -0500227 static const SkClipOp gOps[] = {
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400228 SkClipOp::kIntersect,
Michael Ludwiga0438e62021-08-06 11:19:26 -0400229 SkClipOp::kDifference
robertphillips@google.com607fe072012-07-24 13:54:00 +0000230 };
231
232 SkRect rectA, rectB;
233
Mike Reed92b33352019-08-24 19:39:13 -0400234 rectA.setLTRB(10, 10, 50, 50);
235 rectB.setLTRB(40, 40, 80, 80);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000236
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000237 SkRRect rrectA, rrectB;
238 rrectA.setOval(rectA);
239 rrectB.setRectXY(rectB, SkIntToScalar(1), SkIntToScalar(2));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000240
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000241 SkPath pathA, pathB;
242
243 pathA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
244 pathB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000245
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;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400251 int numBitTests = SkClipStack::Element::DeviceSpaceType::kPath == primType ? 4 : 1;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000252 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
Mike Reed7d34dc72019-11-26 12:17:17 -0500259 pathA.setFillType(doInvA ? SkPathFillType::kInverseEvenOdd :
260 SkPathFillType::kEvenOdd);
261 pathB.setFillType(doInvB ? SkPathFillType::kInverseEvenOdd :
262 SkPathFillType::kEvenOdd);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000263
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000264 switch (primType) {
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400265 case SkClipStack::Element::DeviceSpaceType::kShader:
Brian Salomonf3b46e52017-08-30 11:37:57 -0400266 case SkClipStack::Element::DeviceSpaceType::kEmpty:
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400267 SkDEBUGFAIL("Don't call this with kEmpty or kShader.");
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000268 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400269 case SkClipStack::Element::DeviceSpaceType::kRect:
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400270 stack.clipRect(rectA, SkMatrix::I(), SkClipOp::kIntersect, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400271 stack.clipRect(rectB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000272 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400273 case SkClipStack::Element::DeviceSpaceType::kRRect:
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400274 stack.clipRRect(rrectA, SkMatrix::I(), SkClipOp::kIntersect, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400275 stack.clipRRect(rrectB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000276 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400277 case SkClipStack::Element::DeviceSpaceType::kPath:
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400278 stack.clipPath(pathA, SkMatrix::I(), SkClipOp::kIntersect, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400279 stack.clipPath(pathB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000280 break;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000281 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000282
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000283 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000284 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000285
robertphillips@google.com7b112892012-07-31 15:18:21 +0000286 stack.getConservativeBounds(0, 0, 100, 100, &devClipBound,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000287 &isIntersectionOfRects);
288
Brian Salomonf3b46e52017-08-30 11:37:57 -0400289 if (SkClipStack::Element::DeviceSpaceType::kRect == primType) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000290 REPORTER_ASSERT(reporter, isIntersectionOfRects ==
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400291 (gOps[op] == SkClipOp::kIntersect));
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000292 } else {
293 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
294 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000295
296 SkASSERT(testCase < gNumCases);
robertphillips@google.com7b112892012-07-31 15:18:21 +0000297 REPORTER_ASSERT(reporter, devClipBound == gAnswerRectsBW[testCase]);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000298 ++testCase;
299
300 stack.restore();
301 }
302 }
303}
304
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000305// Test out 'isWideOpen' entry point
306static void test_isWideOpen(skiatest::Reporter* reporter) {
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000307 {
308 // Empty stack is wide open. Wide open stack means that gen id is wide open.
309 SkClipStack stack;
310 REPORTER_ASSERT(reporter, stack.isWideOpen());
311 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
312 }
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000313
314 SkRect rectA, rectB;
315
Mike Reed92b33352019-08-24 19:39:13 -0400316 rectA.setLTRB(10, 10, 40, 40);
317 rectB.setLTRB(50, 50, 80, 80);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000318
319 // Stack should initially be wide open
320 {
321 SkClipStack stack;
322
323 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000324 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000325 }
326
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000327 // Test out empty difference from a wide open clip
328 {
329 SkClipStack stack;
330
331 SkRect emptyRect;
332 emptyRect.setEmpty();
333
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400334 stack.clipRect(emptyRect, SkMatrix::I(), SkClipOp::kDifference, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000335
336 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000337 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000338 }
339
340 // Test out return to wide open
341 {
342 SkClipStack stack;
343
344 stack.save();
345
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400346 stack.clipRect(rectA, SkMatrix::I(), SkClipOp::kIntersect, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000347
348 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000349 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000350
351 stack.restore();
352
353 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000354 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000355 }
356}
357
bsalomon@google.com100abf42012-09-05 17:40:04 +0000358static int count(const SkClipStack& stack) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000359
360 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
361
halcanary96fcdcc2015-08-27 07:41:13 -0700362 const SkClipStack::Element* element = nullptr;
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000363 int count = 0;
364
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000365 for (element = iter.prev(); element; element = iter.prev(), ++count) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000366 }
367
368 return count;
369}
370
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000371static void test_rect_inverse_fill(skiatest::Reporter* reporter) {
372 // non-intersecting rectangles
373 SkRect rect = SkRect::MakeLTRB(0, 0, 10, 10);
374
375 SkPath path;
376 path.addRect(rect);
377 path.toggleInverseFillType();
378 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400379 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000380
381 SkRect bounds;
382 SkClipStack::BoundsType boundsType;
383 stack.getBounds(&bounds, &boundsType);
384 REPORTER_ASSERT(reporter, SkClipStack::kInsideOut_BoundsType == boundsType);
385 REPORTER_ASSERT(reporter, bounds == rect);
386}
387
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000388static void test_rect_replace(skiatest::Reporter* reporter) {
389 SkRect rect = SkRect::MakeWH(100, 100);
390 SkRect rect2 = SkRect::MakeXYWH(50, 50, 100, 100);
391
392 SkRect bound;
393 SkClipStack::BoundsType type;
394 bool isIntersectionOfRects;
395
396 // Adding a new rect with the replace operator should not increase
397 // the stack depth. BW replacing BW.
398 {
399 SkClipStack stack;
400 REPORTER_ASSERT(reporter, 0 == count(stack));
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400401 stack.replaceClip(rect, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000402 REPORTER_ASSERT(reporter, 1 == count(stack));
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400403 stack.replaceClip(rect, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000404 REPORTER_ASSERT(reporter, 1 == count(stack));
405 }
406
407 // Adding a new rect with the replace operator should not increase
408 // the stack depth. AA replacing AA.
409 {
410 SkClipStack stack;
411 REPORTER_ASSERT(reporter, 0 == count(stack));
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400412 stack.replaceClip(rect, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000413 REPORTER_ASSERT(reporter, 1 == count(stack));
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400414 stack.replaceClip(rect, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000415 REPORTER_ASSERT(reporter, 1 == count(stack));
416 }
417
418 // Adding a new rect with the replace operator should not increase
419 // the stack depth. BW replacing AA replacing BW.
420 {
421 SkClipStack stack;
422 REPORTER_ASSERT(reporter, 0 == count(stack));
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400423 stack.replaceClip(rect, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000424 REPORTER_ASSERT(reporter, 1 == count(stack));
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400425 stack.replaceClip(rect, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000426 REPORTER_ASSERT(reporter, 1 == count(stack));
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400427 stack.replaceClip(rect, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000428 REPORTER_ASSERT(reporter, 1 == count(stack));
429 }
430
431 // Make sure replace clip rects don't collapse too much.
432 {
433 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400434 stack.replaceClip(rect, false);
435 stack.clipRect(rect2, SkMatrix::I(), SkClipOp::kIntersect, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000436 REPORTER_ASSERT(reporter, 1 == count(stack));
437
438 stack.save();
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400439 stack.replaceClip(rect, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000440 REPORTER_ASSERT(reporter, 2 == count(stack));
441 stack.getBounds(&bound, &type, &isIntersectionOfRects);
442 REPORTER_ASSERT(reporter, bound == rect);
443 stack.restore();
444 REPORTER_ASSERT(reporter, 1 == count(stack));
445
446 stack.save();
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400447 stack.replaceClip(rect, false);
448 stack.replaceClip(rect, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000449 REPORTER_ASSERT(reporter, 2 == count(stack));
450 stack.restore();
451 REPORTER_ASSERT(reporter, 1 == count(stack));
452
453 stack.save();
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400454 stack.replaceClip(rect, false);
455 stack.clipRect(rect2, SkMatrix::I(), SkClipOp::kIntersect, false);
456 stack.replaceClip(rect, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000457 REPORTER_ASSERT(reporter, 2 == count(stack));
458 stack.restore();
459 REPORTER_ASSERT(reporter, 1 == count(stack));
460 }
461}
462
463// Simplified path-based version of test_rect_replace.
464static void test_path_replace(skiatest::Reporter* reporter) {
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400465 auto replacePath = [](SkClipStack* stack, const SkPath& path, bool doAA) {
466 const SkRect wideOpen = SkRect::MakeLTRB(-1000, -1000, 1000, 1000);
467 stack->replaceClip(wideOpen, false);
468 stack->clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, doAA);
469 };
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000470 SkRect rect = SkRect::MakeWH(100, 100);
471 SkPath path;
472 path.addCircle(50, 50, 50);
473
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400474 // Emulating replace operations with more complex geometry is not atomic, it's a replace
475 // with a wide-open rect and then an intersection with the complex geometry. The replace can
476 // combine with prior elements, but the subsequent intersect cannot be combined so the stack
477 // continues to grow.
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000478 {
479 SkClipStack stack;
480 REPORTER_ASSERT(reporter, 0 == count(stack));
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400481 replacePath(&stack, path, false);
482 REPORTER_ASSERT(reporter, 2 == count(stack));
483 replacePath(&stack, path, false);
484 REPORTER_ASSERT(reporter, 2 == count(stack));
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000485 }
486
487 // Replacing rect with path.
488 {
489 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400490 stack.replaceClip(rect, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000491 REPORTER_ASSERT(reporter, 1 == count(stack));
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400492 replacePath(&stack, path, true);
493 REPORTER_ASSERT(reporter, 2 == count(stack));
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000494 }
495}
496
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000497// Test out SkClipStack's merging of rect clips. In particular exercise
498// merging of aa vs. bw rects.
499static void test_rect_merging(skiatest::Reporter* reporter) {
500
501 SkRect overlapLeft = SkRect::MakeLTRB(10, 10, 50, 50);
502 SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80);
503
504 SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90);
505 SkRect nestedChild = SkRect::MakeLTRB(40, 40, 60, 60);
506
507 SkRect bound;
508 SkClipStack::BoundsType type;
509 bool isIntersectionOfRects;
510
511 // all bw overlapping - should merge
512 {
513 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400514 stack.clipRect(overlapLeft, SkMatrix::I(), SkClipOp::kIntersect, false);
515 stack.clipRect(overlapRight, SkMatrix::I(), SkClipOp::kIntersect, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000516
517 REPORTER_ASSERT(reporter, 1 == count(stack));
518
519 stack.getBounds(&bound, &type, &isIntersectionOfRects);
520
521 REPORTER_ASSERT(reporter, isIntersectionOfRects);
522 }
523
524 // all aa overlapping - should merge
525 {
526 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400527 stack.clipRect(overlapLeft, SkMatrix::I(), SkClipOp::kIntersect, true);
528 stack.clipRect(overlapRight, SkMatrix::I(), SkClipOp::kIntersect, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000529
530 REPORTER_ASSERT(reporter, 1 == count(stack));
531
532 stack.getBounds(&bound, &type, &isIntersectionOfRects);
533
534 REPORTER_ASSERT(reporter, isIntersectionOfRects);
535 }
536
537 // mixed overlapping - should _not_ merge
538 {
539 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400540 stack.clipRect(overlapLeft, SkMatrix::I(), SkClipOp::kIntersect, true);
541 stack.clipRect(overlapRight, SkMatrix::I(), SkClipOp::kIntersect, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000542
543 REPORTER_ASSERT(reporter, 2 == count(stack));
544
545 stack.getBounds(&bound, &type, &isIntersectionOfRects);
546
547 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
548 }
549
550 // mixed nested (bw inside aa) - should merge
551 {
552 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400553 stack.clipRect(nestedParent, SkMatrix::I(), SkClipOp::kIntersect, true);
554 stack.clipRect(nestedChild, SkMatrix::I(), SkClipOp::kIntersect, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000555
556 REPORTER_ASSERT(reporter, 1 == count(stack));
557
558 stack.getBounds(&bound, &type, &isIntersectionOfRects);
559
560 REPORTER_ASSERT(reporter, isIntersectionOfRects);
561 }
562
563 // mixed nested (aa inside bw) - should merge
564 {
565 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400566 stack.clipRect(nestedParent, SkMatrix::I(), SkClipOp::kIntersect, false);
567 stack.clipRect(nestedChild, SkMatrix::I(), SkClipOp::kIntersect, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000568
569 REPORTER_ASSERT(reporter, 1 == count(stack));
570
571 stack.getBounds(&bound, &type, &isIntersectionOfRects);
572
573 REPORTER_ASSERT(reporter, isIntersectionOfRects);
574 }
575
576 // reverse nested (aa inside bw) - should _not_ merge
577 {
578 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400579 stack.clipRect(nestedChild, SkMatrix::I(), SkClipOp::kIntersect, false);
580 stack.clipRect(nestedParent, SkMatrix::I(), SkClipOp::kIntersect, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000581
582 REPORTER_ASSERT(reporter, 2 == count(stack));
583
584 stack.getBounds(&bound, &type, &isIntersectionOfRects);
585
586 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
587 }
588}
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000589
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000590static void test_quickContains(skiatest::Reporter* reporter) {
591 SkRect testRect = SkRect::MakeLTRB(10, 10, 40, 40);
592 SkRect insideRect = SkRect::MakeLTRB(20, 20, 30, 30);
593 SkRect intersectingRect = SkRect::MakeLTRB(25, 25, 50, 50);
594 SkRect outsideRect = SkRect::MakeLTRB(0, 0, 50, 50);
595 SkRect nonIntersectingRect = SkRect::MakeLTRB(100, 100, 110, 110);
596
597 SkPath insideCircle;
598 insideCircle.addCircle(25, 25, 5);
599 SkPath intersectingCircle;
600 intersectingCircle.addCircle(25, 40, 10);
601 SkPath outsideCircle;
602 outsideCircle.addCircle(25, 25, 50);
603 SkPath nonIntersectingCircle;
604 nonIntersectingCircle.addCircle(100, 100, 5);
605
606 {
607 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400608 stack.clipRect(outsideRect, SkMatrix::I(), SkClipOp::kDifference, false);
609 // return false because quickContains currently does not care for kDifference
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000610 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
611 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000612
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000613 // Replace Op tests
614 {
615 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400616 stack.replaceClip(outsideRect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000617 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
618 }
619
620 {
621 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400622 stack.clipRect(insideRect, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000623 stack.save(); // To prevent in-place substitution by replace OP
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400624 stack.replaceClip(outsideRect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000625 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
626 stack.restore();
627 }
628
629 {
630 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400631 stack.clipRect(outsideRect, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000632 stack.save(); // To prevent in-place substitution by replace OP
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400633 stack.replaceClip(insideRect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000634 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
635 stack.restore();
636 }
637
638 // Verify proper traversal of multi-element clip
639 {
640 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400641 stack.clipRect(insideRect, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000642 // Use a path for second clip to prevent in-place intersection
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400643 stack.clipPath(outsideCircle, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000644 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
645 }
646
647 // Intersect Op tests with rectangles
648 {
649 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400650 stack.clipRect(outsideRect, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000651 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
652 }
653
654 {
655 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400656 stack.clipRect(insideRect, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000657 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
658 }
659
660 {
661 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400662 stack.clipRect(intersectingRect, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000663 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
664 }
665
666 {
667 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400668 stack.clipRect(nonIntersectingRect, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000669 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
670 }
671
672 // Intersect Op tests with circle paths
673 {
674 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400675 stack.clipPath(outsideCircle, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000676 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
677 }
678
679 {
680 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400681 stack.clipPath(insideCircle, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000682 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
683 }
684
685 {
686 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400687 stack.clipPath(intersectingCircle, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000688 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
689 }
690
691 {
692 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400693 stack.clipPath(nonIntersectingCircle, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000694 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
695 }
696
697 // Intersect Op tests with inverse filled rectangles
698 {
699 SkClipStack stack;
700 SkPath path;
701 path.addRect(outsideRect);
702 path.toggleInverseFillType();
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400703 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000704 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
705 }
706
707 {
708 SkClipStack stack;
709 SkPath path;
710 path.addRect(insideRect);
711 path.toggleInverseFillType();
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400712 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000713 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
714 }
715
716 {
717 SkClipStack stack;
718 SkPath path;
719 path.addRect(intersectingRect);
720 path.toggleInverseFillType();
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400721 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000722 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
723 }
724
725 {
726 SkClipStack stack;
727 SkPath path;
728 path.addRect(nonIntersectingRect);
729 path.toggleInverseFillType();
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400730 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000731 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
732 }
733
734 // Intersect Op tests with inverse filled circles
735 {
736 SkClipStack stack;
737 SkPath path = outsideCircle;
738 path.toggleInverseFillType();
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400739 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000740 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
741 }
742
743 {
744 SkClipStack stack;
745 SkPath path = insideCircle;
746 path.toggleInverseFillType();
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400747 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000748 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
749 }
750
751 {
752 SkClipStack stack;
753 SkPath path = intersectingCircle;
754 path.toggleInverseFillType();
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400755 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000756 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
757 }
758
759 {
760 SkClipStack stack;
761 SkPath path = nonIntersectingCircle;
762 path.toggleInverseFillType();
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400763 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000764 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
765 }
766}
767
csmartdaltond50e2402016-07-22 08:39:06 -0700768static void set_region_to_stack(const SkClipStack& stack, const SkIRect& bounds, SkRegion* region) {
769 region->setRect(bounds);
770 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
771 while (const SkClipStack::Element *element = iter.next()) {
772 SkRegion elemRegion;
773 SkRegion boundsRgn(bounds);
774 SkPath path;
775
Brian Salomonf3b46e52017-08-30 11:37:57 -0400776 switch (element->getDeviceSpaceType()) {
777 case SkClipStack::Element::DeviceSpaceType::kEmpty:
csmartdaltond50e2402016-07-22 08:39:06 -0700778 elemRegion.setEmpty();
779 break;
780 default:
Brian Salomonf3b46e52017-08-30 11:37:57 -0400781 element->asDeviceSpacePath(&path);
csmartdaltond50e2402016-07-22 08:39:06 -0700782 elemRegion.setPath(path, boundsRgn);
783 break;
784 }
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400785
Michael Ludwige1d00402021-08-09 12:21:52 -0400786 region->op(elemRegion, element->isReplaceOp() ? SkRegion::kReplace_Op
787 : (SkRegion::Op) element->getOp());
csmartdaltond50e2402016-07-22 08:39:06 -0700788 }
789}
790
791static void test_invfill_diff_bug(skiatest::Reporter* reporter) {
792 SkClipStack stack;
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400793 stack.clipRect({10, 10, 20, 20}, SkMatrix::I(), SkClipOp::kIntersect, false);
csmartdaltond50e2402016-07-22 08:39:06 -0700794
795 SkPath path;
796 path.addRect({30, 10, 40, 20});
Mike Reed7d34dc72019-11-26 12:17:17 -0500797 path.setFillType(SkPathFillType::kInverseWinding);
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400798 stack.clipPath(path, SkMatrix::I(), SkClipOp::kDifference, false);
csmartdaltond50e2402016-07-22 08:39:06 -0700799
800 REPORTER_ASSERT(reporter, SkClipStack::kEmptyGenID == stack.getTopmostGenID());
801
802 SkRect stackBounds;
803 SkClipStack::BoundsType stackBoundsType;
804 stack.getBounds(&stackBounds, &stackBoundsType);
805
806 REPORTER_ASSERT(reporter, stackBounds.isEmpty());
807 REPORTER_ASSERT(reporter, SkClipStack::kNormal_BoundsType == stackBoundsType);
808
809 SkRegion region;
810 set_region_to_stack(stack, {0, 0, 50, 30}, &region);
811
812 REPORTER_ASSERT(reporter, region.isEmpty());
813}
814
bsalomon@google.com51a62862012-11-26 21:19:43 +0000815///////////////////////////////////////////////////////////////////////////////////////////////////
816
Brian Salomon1c0b05a2019-04-19 15:37:28 -0400817static void test_is_rrect_deep_rect_stack(skiatest::Reporter* reporter) {
818 static constexpr SkRect kTargetBounds = SkRect::MakeWH(1000, 500);
819 // All antialiased or all not antialiased.
820 for (bool aa : {false, true}) {
821 SkClipStack stack;
822 for (int i = 0; i <= 100; ++i) {
823 stack.save();
824 stack.clipRect(SkRect::MakeLTRB(i, 0.5, kTargetBounds.width(), kTargetBounds.height()),
825 SkMatrix::I(), SkClipOp::kIntersect, aa);
826 }
827 SkRRect rrect;
828 bool isAA;
829 SkRRect expected = SkRRect::MakeRect(
830 SkRect::MakeLTRB(100, 0.5, kTargetBounds.width(), kTargetBounds.height()));
831 if (stack.isRRect(kTargetBounds, &rrect, &isAA)) {
832 REPORTER_ASSERT(reporter, rrect == expected);
833 REPORTER_ASSERT(reporter, aa == isAA);
834 } else {
835 ERRORF(reporter, "Expected to be an rrect.");
836 }
837 }
838 // Mixed AA and non-AA without simple containment.
839 SkClipStack stack;
840 for (int i = 0; i <= 100; ++i) {
841 bool aa = i & 0b1;
842 int j = 100 - i;
843 stack.save();
844 stack.clipRect(SkRect::MakeLTRB(i, j + 0.5, kTargetBounds.width(), kTargetBounds.height()),
845 SkMatrix::I(), SkClipOp::kIntersect, aa);
846 }
847 SkRRect rrect;
848 bool isAA;
849 REPORTER_ASSERT(reporter, !stack.isRRect(kTargetBounds, &rrect, &isAA));
850}
851
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +0000852DEF_TEST(ClipStack, reporter) {
reed@google.combdee9fc2011-02-22 20:17:43 +0000853 SkClipStack stack;
854
robertphillips@google.com80214e22012-07-20 15:33:18 +0000855 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +0000856 assert_count(reporter, stack, 0);
857
858 static const SkIRect gRects[] = {
859 { 0, 0, 100, 100 },
860 { 25, 25, 125, 125 },
861 { 0, 0, 1000, 1000 },
862 { 0, 0, 75, 75 }
863 };
864 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400865 stack.clipDevRect(gRects[i], SkClipOp::kIntersect);
reed@google.combdee9fc2011-02-22 20:17:43 +0000866 }
867
868 // all of the above rects should have been intersected, leaving only 1 rect
robertphillips@google.com80214e22012-07-20 15:33:18 +0000869 SkClipStack::B2TIter iter(stack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000870 const SkClipStack::Element* element = iter.next();
epoger@google.com2047f002011-05-17 17:36:59 +0000871 SkRect answer;
Mike Reed92b33352019-08-24 19:39:13 -0400872 answer.setLTRB(25, 25, 75, 75);
reed@google.combdee9fc2011-02-22 20:17:43 +0000873
bsalomon49f085d2014-09-05 13:34:00 -0700874 REPORTER_ASSERT(reporter, element);
Brian Salomonf3b46e52017-08-30 11:37:57 -0400875 REPORTER_ASSERT(reporter,
876 SkClipStack::Element::DeviceSpaceType::kRect == element->getDeviceSpaceType());
Michael Ludwigd59d3e12021-08-05 16:39:18 -0400877 REPORTER_ASSERT(reporter, SkClipOp::kIntersect == element->getOp());
Brian Salomonf3b46e52017-08-30 11:37:57 -0400878 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == answer);
reed@google.combdee9fc2011-02-22 20:17:43 +0000879 // now check that we only had one in our iterator
880 REPORTER_ASSERT(reporter, !iter.next());
881
882 stack.reset();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000883 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +0000884 assert_count(reporter, stack, 0);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000885
886 test_assign_and_comparison(reporter);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000887 test_iterators(reporter);
Brian Salomonf3b46e52017-08-30 11:37:57 -0400888 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kRect);
889 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kRRect);
890 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kPath);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000891 test_isWideOpen(reporter);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000892 test_rect_merging(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000893 test_rect_replace(reporter);
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000894 test_rect_inverse_fill(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000895 test_path_replace(reporter);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000896 test_quickContains(reporter);
csmartdaltond50e2402016-07-22 08:39:06 -0700897 test_invfill_diff_bug(reporter);
Brian Salomon1c0b05a2019-04-19 15:37:28 -0400898 test_is_rrect_deep_rect_stack(reporter);
reed@google.combdee9fc2011-02-22 20:17:43 +0000899}