blob: 0c3f3b2d5efb29f1e19320afa8cc300dc44c7fd1 [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
Ben Wagnerb607a8f2018-03-12 13:46:21 -04008#include "SkCanvas.h"
9#include "SkClipOp.h"
10#include "SkClipOpPriv.h"
reed@google.combdee9fc2011-02-22 20:17:43 +000011#include "SkClipStack.h"
Ben Wagnerb607a8f2018-03-12 13:46:21 -040012#include "SkImageInfo.h"
13#include "SkMatrix.h"
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000014#include "SkPath.h"
Ben Wagnerb607a8f2018-03-12 13:46:21 -040015#include "SkPoint.h"
16#include "SkRRect.h"
bsalomon@google.com51a62862012-11-26 21:19:43 +000017#include "SkRandom.h"
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000018#include "SkRect.h"
Ben Wagnerb607a8f2018-03-12 13:46:21 -040019#include "SkRefCnt.h"
bsalomon@google.com51a62862012-11-26 21:19:43 +000020#include "SkRegion.h"
Ben Wagnerb607a8f2018-03-12 13:46:21 -040021#include "SkScalar.h"
22#include "SkSize.h"
23#include "SkString.h"
24#include "SkSurface.h"
Ben Wagnerb607a8f2018-03-12 13:46:21 -040025#include "SkTemplates.h"
26#include "SkTypes.h"
27#include "Test.h"
robertphillips@google.com80214e22012-07-20 15:33:18 +000028
Ben Wagnerb607a8f2018-03-12 13:46:21 -040029#include "GrCaps.h"
30#include "GrClip.h"
Brian Salomon19f0ed52017-01-06 13:54:58 -050031#include "GrClipStackClip.h"
Ben Wagnerb607a8f2018-03-12 13:46:21 -040032#include "GrConfig.h"
33#include "GrContext.h"
34#include "GrContextFactory.h"
35#include "GrContextPriv.h"
csmartdaltoncbecb082016-07-22 08:59:08 -070036#include "GrReducedClip.h"
Brian Salomon19f0ed52017-01-06 13:54:58 -050037#include "GrResourceCache.h"
Ben Wagnerb607a8f2018-03-12 13:46:21 -040038#include "GrResourceKey.h"
Robert Phillipseee4d6e2017-06-05 09:26:07 -040039#include "GrSurfaceProxyPriv.h"
Robert Phillipsd9d84852017-06-09 10:48:29 -040040#include "GrTexture.h"
Mike Reed84dd8572017-03-08 22:21:00 -050041#include "GrTextureProxy.h"
csmartdaltoncbecb082016-07-22 08:59:08 -070042typedef GrReducedClip::ElementList ElementList;
43typedef GrReducedClip::InitialState InitialState;
csmartdaltoncbecb082016-07-22 08:59:08 -070044
Ben Wagnerb607a8f2018-03-12 13:46:21 -040045#include <cstring>
46#include <new>
47
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000048static void test_assign_and_comparison(skiatest::Reporter* reporter) {
49 SkClipStack s;
reed@google.comd9f2dea2011-10-12 14:43:27 +000050 bool doAA = false;
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000051
robertphillips@google.com80214e22012-07-20 15:33:18 +000052 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
53
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000054 // Build up a clip stack with a path, an empty clip, and a rect.
55 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000056 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
57
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000058 SkPath p;
59 p.moveTo(5, 6);
60 p.lineTo(7, 8);
61 p.lineTo(5, 9);
62 p.close();
Mike Reedc1f77742016-12-09 09:00:50 -050063 s.clipPath(p, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000064
65 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000066 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
67
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000068 SkRect r = SkRect::MakeLTRB(1, 2, 3, 4);
Mike Reedc1f77742016-12-09 09:00:50 -050069 s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000070 r = SkRect::MakeLTRB(10, 11, 12, 13);
Mike Reedc1f77742016-12-09 09:00:50 -050071 s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000072
73 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000074 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
75
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000076 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -050077 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000078
79 // Test that assignment works.
80 SkClipStack copy = s;
81 REPORTER_ASSERT(reporter, s == copy);
82
83 // Test that different save levels triggers not equal.
84 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000085 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000086 REPORTER_ASSERT(reporter, s != copy);
87
88 // Test that an equal, but not copied version is equal.
89 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000090 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000091 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -050092 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000093 REPORTER_ASSERT(reporter, s == copy);
94
95 // Test that a different op on one level triggers not equal.
96 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +000097 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000098 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +000099 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000100 r = SkRect::MakeLTRB(14, 15, 16, 17);
Mike Reedc1f77742016-12-09 09:00:50 -0500101 s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000102 REPORTER_ASSERT(reporter, s != copy);
103
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000104 // Test that version constructed with rect-path rather than a rect is still considered equal.
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000105 s.restore();
106 s.save();
107 SkPath rp;
108 rp.addRect(r);
Mike Reedc1f77742016-12-09 09:00:50 -0500109 s.clipPath(rp, SkMatrix::I(), kUnion_SkClipOp, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000110 REPORTER_ASSERT(reporter, s == copy);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000111
112 // Test that different rects triggers not equal.
113 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000114 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000115 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000116 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
117
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000118 r = SkRect::MakeLTRB(24, 25, 26, 27);
Mike Reedc1f77742016-12-09 09:00:50 -0500119 s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000120 REPORTER_ASSERT(reporter, s != copy);
121
122 // Sanity check
123 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000124 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
125
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000126 copy.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000127 REPORTER_ASSERT(reporter, 2 == copy.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000128 REPORTER_ASSERT(reporter, s == copy);
129 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000130 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000131 copy.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000132 REPORTER_ASSERT(reporter, 1 == copy.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000133 REPORTER_ASSERT(reporter, s == copy);
134
135 // Test that different paths triggers not equal.
136 s.restore();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000137 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000138 s.save();
robertphillips@google.com80214e22012-07-20 15:33:18 +0000139 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
140
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000141 p.addRect(r);
Mike Reedc1f77742016-12-09 09:00:50 -0500142 s.clipPath(p, SkMatrix::I(), kIntersect_SkClipOp, doAA);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000143 REPORTER_ASSERT(reporter, s != copy);
144}
reed@google.combdee9fc2011-02-22 20:17:43 +0000145
146static void assert_count(skiatest::Reporter* reporter, const SkClipStack& stack,
147 int count) {
robertphillips@google.com80214e22012-07-20 15:33:18 +0000148 SkClipStack::B2TIter iter(stack);
reed@google.combdee9fc2011-02-22 20:17:43 +0000149 int counter = 0;
150 while (iter.next()) {
151 counter += 1;
152 }
153 REPORTER_ASSERT(reporter, count == counter);
154}
155
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000156// Exercise the SkClipStack's bottom to top and bidirectional iterators
157// (including the skipToTopmost functionality)
robertphillips@google.com80214e22012-07-20 15:33:18 +0000158static void test_iterators(skiatest::Reporter* reporter) {
159 SkClipStack stack;
160
161 static const SkRect gRects[] = {
162 { 0, 0, 40, 40 },
163 { 60, 0, 100, 40 },
164 { 0, 60, 40, 100 },
165 { 60, 60, 100, 100 }
166 };
167
168 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
169 // the union op will prevent these from being fused together
Mike Reedc1f77742016-12-09 09:00:50 -0500170 stack.clipRect(gRects[i], SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000171 }
172
173 assert_count(reporter, stack, 4);
174
175 // bottom to top iteration
176 {
halcanary96fcdcc2015-08-27 07:41:13 -0700177 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000178
179 SkClipStack::B2TIter iter(stack);
180 int i;
181
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000182 for (i = 0, element = iter.next(); element; ++i, element = iter.next()) {
Brian Salomonf3b46e52017-08-30 11:37:57 -0400183 REPORTER_ASSERT(reporter, SkClipStack::Element::DeviceSpaceType::kRect ==
184 element->getDeviceSpaceType());
185 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == gRects[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000186 }
187
188 SkASSERT(i == 4);
189 }
190
191 // top to bottom iteration
192 {
halcanary96fcdcc2015-08-27 07:41:13 -0700193 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000194
195 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
196 int i;
197
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000198 for (i = 3, element = iter.prev(); element; --i, element = iter.prev()) {
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[i]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000202 }
203
204 SkASSERT(i == -1);
205 }
206
207 // skipToTopmost
208 {
halcanary96fcdcc2015-08-27 07:41:13 -0700209 const SkClipStack::Element* element = nullptr;
robertphillips@google.com80214e22012-07-20 15:33:18 +0000210
211 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
212
Mike Reedc1f77742016-12-09 09:00:50 -0500213 element = iter.skipToTopmost(kUnion_SkClipOp);
Brian Salomonf3b46e52017-08-30 11:37:57 -0400214 REPORTER_ASSERT(reporter, SkClipStack::Element::DeviceSpaceType::kRect ==
215 element->getDeviceSpaceType());
216 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == gRects[3]);
robertphillips@google.com80214e22012-07-20 15:33:18 +0000217 }
218}
219
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000220// Exercise the SkClipStack's getConservativeBounds computation
Brian Salomonf3b46e52017-08-30 11:37:57 -0400221static void test_bounds(skiatest::Reporter* reporter,
222 SkClipStack::Element::DeviceSpaceType primType) {
robertphillips@google.com607fe072012-07-24 13:54:00 +0000223 static const int gNumCases = 20;
224 static const SkRect gAnswerRectsBW[gNumCases] = {
225 // A op B
226 { 40, 40, 50, 50 },
227 { 10, 10, 50, 50 },
228 { 10, 10, 80, 80 },
229 { 10, 10, 80, 80 },
230 { 40, 40, 80, 80 },
231
232 // invA op B
233 { 40, 40, 80, 80 },
234 { 0, 0, 100, 100 },
235 { 0, 0, 100, 100 },
236 { 0, 0, 100, 100 },
237 { 40, 40, 50, 50 },
238
239 // A op invB
240 { 10, 10, 50, 50 },
241 { 40, 40, 50, 50 },
242 { 0, 0, 100, 100 },
243 { 0, 0, 100, 100 },
244 { 0, 0, 100, 100 },
245
246 // invA op invB
247 { 0, 0, 100, 100 },
248 { 40, 40, 80, 80 },
249 { 0, 0, 100, 100 },
250 { 10, 10, 80, 80 },
251 { 10, 10, 50, 50 },
252 };
253
Mike Reedc1f77742016-12-09 09:00:50 -0500254 static const SkClipOp gOps[] = {
255 kIntersect_SkClipOp,
256 kDifference_SkClipOp,
257 kUnion_SkClipOp,
258 kXOR_SkClipOp,
259 kReverseDifference_SkClipOp
robertphillips@google.com607fe072012-07-24 13:54:00 +0000260 };
261
262 SkRect rectA, rectB;
263
264 rectA.iset(10, 10, 50, 50);
265 rectB.iset(40, 40, 80, 80);
266
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000267 SkRRect rrectA, rrectB;
268 rrectA.setOval(rectA);
269 rrectB.setRectXY(rectB, SkIntToScalar(1), SkIntToScalar(2));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000270
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000271 SkPath pathA, pathB;
272
273 pathA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
274 pathB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000275
276 SkClipStack stack;
robertphillips@google.com7b112892012-07-31 15:18:21 +0000277 SkRect devClipBound;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000278 bool isIntersectionOfRects = false;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000279
280 int testCase = 0;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400281 int numBitTests = SkClipStack::Element::DeviceSpaceType::kPath == primType ? 4 : 1;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000282 for (int invBits = 0; invBits < numBitTests; ++invBits) {
robertphillips@google.com607fe072012-07-24 13:54:00 +0000283 for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) {
284
285 stack.save();
286 bool doInvA = SkToBool(invBits & 1);
287 bool doInvB = SkToBool(invBits & 2);
288
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000289 pathA.setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType :
robertphillips@google.com607fe072012-07-24 13:54:00 +0000290 SkPath::kEvenOdd_FillType);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000291 pathB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType :
robertphillips@google.com607fe072012-07-24 13:54:00 +0000292 SkPath::kEvenOdd_FillType);
293
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000294 switch (primType) {
Brian Salomonf3b46e52017-08-30 11:37:57 -0400295 case SkClipStack::Element::DeviceSpaceType::kEmpty:
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000296 SkDEBUGFAIL("Don't call this with kEmpty.");
297 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400298 case SkClipStack::Element::DeviceSpaceType::kRect:
Mike Reedc1f77742016-12-09 09:00:50 -0500299 stack.clipRect(rectA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400300 stack.clipRect(rectB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000301 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400302 case SkClipStack::Element::DeviceSpaceType::kRRect:
Mike Reedc1f77742016-12-09 09:00:50 -0500303 stack.clipRRect(rrectA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400304 stack.clipRRect(rrectB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000305 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400306 case SkClipStack::Element::DeviceSpaceType::kPath:
Mike Reedc1f77742016-12-09 09:00:50 -0500307 stack.clipPath(pathA, SkMatrix::I(), kIntersect_SkClipOp, false);
Brian Salomona3b45d42016-10-03 11:36:16 -0400308 stack.clipPath(pathB, SkMatrix::I(), gOps[op], false);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000309 break;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000310 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000311
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000312 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000313 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000314
robertphillips@google.com7b112892012-07-31 15:18:21 +0000315 stack.getConservativeBounds(0, 0, 100, 100, &devClipBound,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000316 &isIntersectionOfRects);
317
Brian Salomonf3b46e52017-08-30 11:37:57 -0400318 if (SkClipStack::Element::DeviceSpaceType::kRect == primType) {
rmistry@google.comd6176b02012-08-23 18:14:13 +0000319 REPORTER_ASSERT(reporter, isIntersectionOfRects ==
Mike Reedc1f77742016-12-09 09:00:50 -0500320 (gOps[op] == kIntersect_SkClipOp));
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000321 } else {
322 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
323 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000324
325 SkASSERT(testCase < gNumCases);
robertphillips@google.com7b112892012-07-31 15:18:21 +0000326 REPORTER_ASSERT(reporter, devClipBound == gAnswerRectsBW[testCase]);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000327 ++testCase;
328
329 stack.restore();
330 }
331 }
332}
333
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000334// Test out 'isWideOpen' entry point
335static void test_isWideOpen(skiatest::Reporter* reporter) {
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000336 {
337 // Empty stack is wide open. Wide open stack means that gen id is wide open.
338 SkClipStack stack;
339 REPORTER_ASSERT(reporter, stack.isWideOpen());
340 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
341 }
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000342
343 SkRect rectA, rectB;
344
345 rectA.iset(10, 10, 40, 40);
346 rectB.iset(50, 50, 80, 80);
347
348 // Stack should initially be wide open
349 {
350 SkClipStack stack;
351
352 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000353 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000354 }
355
356 // Test out case where the user specifies a union that includes everything
357 {
358 SkClipStack stack;
359
360 SkPath clipA, clipB;
361
362 clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
363 clipA.setFillType(SkPath::kInverseEvenOdd_FillType);
364
365 clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
366 clipB.setFillType(SkPath::kInverseEvenOdd_FillType);
367
Mike Reedc1f77742016-12-09 09:00:50 -0500368 stack.clipPath(clipA, SkMatrix::I(), kReplace_SkClipOp, false);
369 stack.clipPath(clipB, SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000370
371 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000372 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000373 }
374
375 // Test out union w/ a wide open clip
376 {
377 SkClipStack stack;
378
Mike Reedc1f77742016-12-09 09:00:50 -0500379 stack.clipRect(rectA, SkMatrix::I(), kUnion_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000380
381 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000382 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000383 }
384
385 // Test out empty difference from a wide open clip
386 {
387 SkClipStack stack;
388
389 SkRect emptyRect;
390 emptyRect.setEmpty();
391
Mike Reedc1f77742016-12-09 09:00:50 -0500392 stack.clipRect(emptyRect, SkMatrix::I(), kDifference_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000393
394 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000395 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000396 }
397
398 // Test out return to wide open
399 {
400 SkClipStack stack;
401
402 stack.save();
403
Mike Reedc1f77742016-12-09 09:00:50 -0500404 stack.clipRect(rectA, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000405
406 REPORTER_ASSERT(reporter, !stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000407 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000408
409 stack.restore();
410
411 REPORTER_ASSERT(reporter, stack.isWideOpen());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000412 REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000413 }
414}
415
bsalomon@google.com100abf42012-09-05 17:40:04 +0000416static int count(const SkClipStack& stack) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000417
418 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
419
halcanary96fcdcc2015-08-27 07:41:13 -0700420 const SkClipStack::Element* element = nullptr;
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000421 int count = 0;
422
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000423 for (element = iter.prev(); element; element = iter.prev(), ++count) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000424 }
425
426 return count;
427}
428
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000429static void test_rect_inverse_fill(skiatest::Reporter* reporter) {
430 // non-intersecting rectangles
431 SkRect rect = SkRect::MakeLTRB(0, 0, 10, 10);
432
433 SkPath path;
434 path.addRect(rect);
435 path.toggleInverseFillType();
436 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500437 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000438
439 SkRect bounds;
440 SkClipStack::BoundsType boundsType;
441 stack.getBounds(&bounds, &boundsType);
442 REPORTER_ASSERT(reporter, SkClipStack::kInsideOut_BoundsType == boundsType);
443 REPORTER_ASSERT(reporter, bounds == rect);
444}
445
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000446static void test_rect_replace(skiatest::Reporter* reporter) {
447 SkRect rect = SkRect::MakeWH(100, 100);
448 SkRect rect2 = SkRect::MakeXYWH(50, 50, 100, 100);
449
450 SkRect bound;
451 SkClipStack::BoundsType type;
452 bool isIntersectionOfRects;
453
454 // Adding a new rect with the replace operator should not increase
455 // the stack depth. BW replacing BW.
456 {
457 SkClipStack stack;
458 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500459 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000460 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500461 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000462 REPORTER_ASSERT(reporter, 1 == count(stack));
463 }
464
465 // Adding a new rect with the replace operator should not increase
466 // the stack depth. AA replacing AA.
467 {
468 SkClipStack stack;
469 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500470 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000471 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500472 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000473 REPORTER_ASSERT(reporter, 1 == count(stack));
474 }
475
476 // Adding a new rect with the replace operator should not increase
477 // the stack depth. BW replacing AA replacing BW.
478 {
479 SkClipStack stack;
480 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500481 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000482 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500483 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000484 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500485 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000486 REPORTER_ASSERT(reporter, 1 == count(stack));
487 }
488
489 // Make sure replace clip rects don't collapse too much.
490 {
491 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500492 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
493 stack.clipRect(rect2, SkMatrix::I(), kIntersect_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000494 REPORTER_ASSERT(reporter, 1 == count(stack));
495
496 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500497 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000498 REPORTER_ASSERT(reporter, 2 == count(stack));
499 stack.getBounds(&bound, &type, &isIntersectionOfRects);
500 REPORTER_ASSERT(reporter, bound == rect);
501 stack.restore();
502 REPORTER_ASSERT(reporter, 1 == count(stack));
503
504 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500505 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
506 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000507 REPORTER_ASSERT(reporter, 2 == count(stack));
508 stack.restore();
509 REPORTER_ASSERT(reporter, 1 == count(stack));
510
511 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500512 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
513 stack.clipRect(rect2, SkMatrix::I(), kIntersect_SkClipOp, false);
514 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000515 REPORTER_ASSERT(reporter, 2 == count(stack));
516 stack.restore();
517 REPORTER_ASSERT(reporter, 1 == count(stack));
518 }
519}
520
521// Simplified path-based version of test_rect_replace.
522static void test_path_replace(skiatest::Reporter* reporter) {
523 SkRect rect = SkRect::MakeWH(100, 100);
524 SkPath path;
525 path.addCircle(50, 50, 50);
526
527 // Replace operation doesn't grow the stack.
528 {
529 SkClipStack stack;
530 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500531 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000532 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500533 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000534 REPORTER_ASSERT(reporter, 1 == count(stack));
535 }
536
537 // Replacing rect with path.
538 {
539 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500540 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000541 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500542 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000543 REPORTER_ASSERT(reporter, 1 == count(stack));
544 }
545}
546
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000547// Test out SkClipStack's merging of rect clips. In particular exercise
548// merging of aa vs. bw rects.
549static void test_rect_merging(skiatest::Reporter* reporter) {
550
551 SkRect overlapLeft = SkRect::MakeLTRB(10, 10, 50, 50);
552 SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80);
553
554 SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90);
555 SkRect nestedChild = SkRect::MakeLTRB(40, 40, 60, 60);
556
557 SkRect bound;
558 SkClipStack::BoundsType type;
559 bool isIntersectionOfRects;
560
561 // all bw overlapping - should merge
562 {
563 SkClipStack stack;
564
Mike Reedc1f77742016-12-09 09:00:50 -0500565 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000566
Mike Reedc1f77742016-12-09 09:00:50 -0500567 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false);
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 // all aa overlapping - should merge
577 {
578 SkClipStack stack;
579
Mike Reedc1f77742016-12-09 09:00:50 -0500580 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000581
Mike Reedc1f77742016-12-09 09:00:50 -0500582 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000583
584 REPORTER_ASSERT(reporter, 1 == count(stack));
585
586 stack.getBounds(&bound, &type, &isIntersectionOfRects);
587
588 REPORTER_ASSERT(reporter, isIntersectionOfRects);
589 }
590
591 // mixed overlapping - should _not_ merge
592 {
593 SkClipStack stack;
594
Mike Reedc1f77742016-12-09 09:00:50 -0500595 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000596
Mike Reedc1f77742016-12-09 09:00:50 -0500597 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000598
599 REPORTER_ASSERT(reporter, 2 == count(stack));
600
601 stack.getBounds(&bound, &type, &isIntersectionOfRects);
602
603 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
604 }
605
606 // mixed nested (bw inside aa) - should merge
607 {
608 SkClipStack stack;
609
Mike Reedc1f77742016-12-09 09:00:50 -0500610 stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000611
Mike Reedc1f77742016-12-09 09:00:50 -0500612 stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000613
614 REPORTER_ASSERT(reporter, 1 == count(stack));
615
616 stack.getBounds(&bound, &type, &isIntersectionOfRects);
617
618 REPORTER_ASSERT(reporter, isIntersectionOfRects);
619 }
620
621 // mixed nested (aa inside bw) - should merge
622 {
623 SkClipStack stack;
624
Mike Reedc1f77742016-12-09 09:00:50 -0500625 stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000626
Mike Reedc1f77742016-12-09 09:00:50 -0500627 stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000628
629 REPORTER_ASSERT(reporter, 1 == count(stack));
630
631 stack.getBounds(&bound, &type, &isIntersectionOfRects);
632
633 REPORTER_ASSERT(reporter, isIntersectionOfRects);
634 }
635
636 // reverse nested (aa inside bw) - should _not_ merge
637 {
638 SkClipStack stack;
639
Mike Reedc1f77742016-12-09 09:00:50 -0500640 stack.clipRect(nestedChild, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000641
Mike Reedc1f77742016-12-09 09:00:50 -0500642 stack.clipRect(nestedParent, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000643
644 REPORTER_ASSERT(reporter, 2 == count(stack));
645
646 stack.getBounds(&bound, &type, &isIntersectionOfRects);
647
648 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
649 }
650}
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000651
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000652static void test_quickContains(skiatest::Reporter* reporter) {
653 SkRect testRect = SkRect::MakeLTRB(10, 10, 40, 40);
654 SkRect insideRect = SkRect::MakeLTRB(20, 20, 30, 30);
655 SkRect intersectingRect = SkRect::MakeLTRB(25, 25, 50, 50);
656 SkRect outsideRect = SkRect::MakeLTRB(0, 0, 50, 50);
657 SkRect nonIntersectingRect = SkRect::MakeLTRB(100, 100, 110, 110);
658
659 SkPath insideCircle;
660 insideCircle.addCircle(25, 25, 5);
661 SkPath intersectingCircle;
662 intersectingCircle.addCircle(25, 40, 10);
663 SkPath outsideCircle;
664 outsideCircle.addCircle(25, 25, 50);
665 SkPath nonIntersectingCircle;
666 nonIntersectingCircle.addCircle(100, 100, 5);
667
668 {
669 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500670 stack.clipRect(outsideRect, SkMatrix::I(), kDifference_SkClipOp, false);
671 // return false because quickContains currently does not care for kDifference_SkClipOp
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000672 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
673 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000674
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000675 // Replace Op tests
676 {
677 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500678 stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000679 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
680 }
681
682 {
683 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500684 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000685 stack.save(); // To prevent in-place substitution by replace OP
Mike Reedc1f77742016-12-09 09:00:50 -0500686 stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000687 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
688 stack.restore();
689 }
690
691 {
692 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500693 stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000694 stack.save(); // To prevent in-place substitution by replace OP
Mike Reedc1f77742016-12-09 09:00:50 -0500695 stack.clipRect(insideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000696 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
697 stack.restore();
698 }
699
700 // Verify proper traversal of multi-element clip
701 {
702 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500703 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000704 // Use a path for second clip to prevent in-place intersection
Mike Reedc1f77742016-12-09 09:00:50 -0500705 stack.clipPath(outsideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000706 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
707 }
708
709 // Intersect Op tests with rectangles
710 {
711 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500712 stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000713 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
714 }
715
716 {
717 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500718 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000719 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
720 }
721
722 {
723 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500724 stack.clipRect(intersectingRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000725 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
726 }
727
728 {
729 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500730 stack.clipRect(nonIntersectingRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000731 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
732 }
733
734 // Intersect Op tests with circle paths
735 {
736 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500737 stack.clipPath(outsideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000738 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
739 }
740
741 {
742 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500743 stack.clipPath(insideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000744 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
745 }
746
747 {
748 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500749 stack.clipPath(intersectingCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000750 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
751 }
752
753 {
754 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500755 stack.clipPath(nonIntersectingCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000756 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
757 }
758
759 // Intersect Op tests with inverse filled rectangles
760 {
761 SkClipStack stack;
762 SkPath path;
763 path.addRect(outsideRect);
764 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500765 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000766 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
767 }
768
769 {
770 SkClipStack stack;
771 SkPath path;
772 path.addRect(insideRect);
773 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500774 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000775 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
776 }
777
778 {
779 SkClipStack stack;
780 SkPath path;
781 path.addRect(intersectingRect);
782 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500783 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000784 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
785 }
786
787 {
788 SkClipStack stack;
789 SkPath path;
790 path.addRect(nonIntersectingRect);
791 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500792 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000793 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
794 }
795
796 // Intersect Op tests with inverse filled circles
797 {
798 SkClipStack stack;
799 SkPath path = outsideCircle;
800 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500801 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000802 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
803 }
804
805 {
806 SkClipStack stack;
807 SkPath path = insideCircle;
808 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500809 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000810 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
811 }
812
813 {
814 SkClipStack stack;
815 SkPath path = intersectingCircle;
816 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500817 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000818 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
819 }
820
821 {
822 SkClipStack stack;
823 SkPath path = nonIntersectingCircle;
824 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500825 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000826 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
827 }
828}
829
csmartdaltond50e2402016-07-22 08:39:06 -0700830static void set_region_to_stack(const SkClipStack& stack, const SkIRect& bounds, SkRegion* region) {
831 region->setRect(bounds);
832 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
833 while (const SkClipStack::Element *element = iter.next()) {
834 SkRegion elemRegion;
835 SkRegion boundsRgn(bounds);
836 SkPath path;
837
Brian Salomonf3b46e52017-08-30 11:37:57 -0400838 switch (element->getDeviceSpaceType()) {
839 case SkClipStack::Element::DeviceSpaceType::kEmpty:
csmartdaltond50e2402016-07-22 08:39:06 -0700840 elemRegion.setEmpty();
841 break;
842 default:
Brian Salomonf3b46e52017-08-30 11:37:57 -0400843 element->asDeviceSpacePath(&path);
csmartdaltond50e2402016-07-22 08:39:06 -0700844 elemRegion.setPath(path, boundsRgn);
845 break;
846 }
reed73603f32016-09-20 08:42:38 -0700847 region->op(elemRegion, (SkRegion::Op)element->getOp());
csmartdaltond50e2402016-07-22 08:39:06 -0700848 }
849}
850
851static void test_invfill_diff_bug(skiatest::Reporter* reporter) {
852 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500853 stack.clipRect({10, 10, 20, 20}, SkMatrix::I(), kIntersect_SkClipOp, false);
csmartdaltond50e2402016-07-22 08:39:06 -0700854
855 SkPath path;
856 path.addRect({30, 10, 40, 20});
857 path.setFillType(SkPath::kInverseWinding_FillType);
Mike Reedc1f77742016-12-09 09:00:50 -0500858 stack.clipPath(path, SkMatrix::I(), kDifference_SkClipOp, false);
csmartdaltond50e2402016-07-22 08:39:06 -0700859
860 REPORTER_ASSERT(reporter, SkClipStack::kEmptyGenID == stack.getTopmostGenID());
861
862 SkRect stackBounds;
863 SkClipStack::BoundsType stackBoundsType;
864 stack.getBounds(&stackBounds, &stackBoundsType);
865
866 REPORTER_ASSERT(reporter, stackBounds.isEmpty());
867 REPORTER_ASSERT(reporter, SkClipStack::kNormal_BoundsType == stackBoundsType);
868
869 SkRegion region;
870 set_region_to_stack(stack, {0, 0, 50, 30}, &region);
871
872 REPORTER_ASSERT(reporter, region.isEmpty());
873}
874
bsalomon@google.com51a62862012-11-26 21:19:43 +0000875///////////////////////////////////////////////////////////////////////////////////////////////////
876
bsalomon@google.com705e8402012-11-27 15:43:57 +0000877// Functions that add a shape to the clip stack. The shape is computed from a rectangle.
878// AA is always disabled since the clip stack reducer can cause changes in aa rasterization of the
879// stack. A fractional edge repeated in different elements may be rasterized fewer times using the
880// reduced stack.
881typedef void (*AddElementFunc) (const SkRect& rect,
882 bool invert,
Mike Reedc1f77742016-12-09 09:00:50 -0500883 SkClipOp op,
csmartdaltoncbecb082016-07-22 08:59:08 -0700884 SkClipStack* stack,
885 bool doAA);
bsalomon@google.coma4e13c82012-11-26 21:38:37 +0000886
Mike Reedc1f77742016-12-09 09:00:50 -0500887static void add_round_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700888 bool doAA) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000889 SkScalar rx = rect.width() / 10;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000890 SkScalar ry = rect.height() / 20;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000891 if (invert) {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000892 SkPath path;
893 path.addRoundRect(rect, rx, ry);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000894 path.setFillType(SkPath::kInverseWinding_FillType);
Brian Salomona3b45d42016-10-03 11:36:16 -0400895 stack->clipPath(path, SkMatrix::I(), op, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000896 } else {
897 SkRRect rrect;
898 rrect.setRectXY(rect, rx, ry);
Brian Salomona3b45d42016-10-03 11:36:16 -0400899 stack->clipRRect(rrect, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000900 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000901};
902
Mike Reedc1f77742016-12-09 09:00:50 -0500903static void add_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700904 bool doAA) {
bsalomon@google.com705e8402012-11-27 15:43:57 +0000905 if (invert) {
906 SkPath path;
907 path.addRect(rect);
908 path.setFillType(SkPath::kInverseWinding_FillType);
Brian Salomona3b45d42016-10-03 11:36:16 -0400909 stack->clipPath(path, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000910 } else {
Brian Salomona3b45d42016-10-03 11:36:16 -0400911 stack->clipRect(rect, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000912 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000913};
914
Mike Reedc1f77742016-12-09 09:00:50 -0500915static void add_oval(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700916 bool doAA) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000917 SkPath path;
918 path.addOval(rect);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000919 if (invert) {
920 path.setFillType(SkPath::kInverseWinding_FillType);
921 }
Brian Salomona3b45d42016-10-03 11:36:16 -0400922 stack->clipPath(path, SkMatrix::I(), op, doAA);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000923};
924
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000925static void add_elem_to_stack(const SkClipStack::Element& element, SkClipStack* stack) {
Brian Salomonf3b46e52017-08-30 11:37:57 -0400926 switch (element.getDeviceSpaceType()) {
927 case SkClipStack::Element::DeviceSpaceType::kRect:
928 stack->clipRect(element.getDeviceSpaceRect(), SkMatrix::I(), element.getOp(),
929 element.isAA());
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000930 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400931 case SkClipStack::Element::DeviceSpaceType::kRRect:
932 stack->clipRRect(element.getDeviceSpaceRRect(), SkMatrix::I(), element.getOp(),
933 element.isAA());
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000934 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400935 case SkClipStack::Element::DeviceSpaceType::kPath:
936 stack->clipPath(element.getDeviceSpacePath(), SkMatrix::I(), element.getOp(),
937 element.isAA());
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000938 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400939 case SkClipStack::Element::DeviceSpaceType::kEmpty:
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000940 SkDEBUGFAIL("Why did the reducer produce an explicit empty.");
941 stack->clipEmpty();
942 break;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000943 }
944}
945
bsalomon@google.com51a62862012-11-26 21:19:43 +0000946static void test_reduced_clip_stack(skiatest::Reporter* reporter) {
947 // We construct random clip stacks, reduce them, and then rasterize both versions to verify that
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000948 // they are equal.
bsalomon@google.com51a62862012-11-26 21:19:43 +0000949
950 // All the clip elements will be contained within these bounds.
csmartdaltond211e782016-08-15 11:17:19 -0700951 static const SkIRect kIBounds = SkIRect::MakeWH(100, 100);
952 static const SkRect kBounds = SkRect::Make(kIBounds);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000953
954 enum {
csmartdaltoncbecb082016-07-22 08:59:08 -0700955 kNumTests = 250,
bsalomon@google.com51a62862012-11-26 21:19:43 +0000956 kMinElemsPerTest = 1,
957 kMaxElemsPerTest = 50,
958 };
959
960 // min/max size of a clip element as a fraction of kBounds.
961 static const SkScalar kMinElemSizeFrac = SK_Scalar1 / 5;
962 static const SkScalar kMaxElemSizeFrac = SK_Scalar1;
963
Mike Reedc1f77742016-12-09 09:00:50 -0500964 static const SkClipOp kOps[] = {
965 kDifference_SkClipOp,
966 kIntersect_SkClipOp,
967 kUnion_SkClipOp,
968 kXOR_SkClipOp,
969 kReverseDifference_SkClipOp,
970 kReplace_SkClipOp,
bsalomon@google.com51a62862012-11-26 21:19:43 +0000971 };
972
973 // Replace operations short-circuit the optimizer. We want to make sure that we test this code
974 // path a little bit but we don't want it to prevent us from testing many longer traversals in
975 // the optimizer.
976 static const int kReplaceDiv = 4 * kMaxElemsPerTest;
977
bsalomon@google.com705e8402012-11-27 15:43:57 +0000978 // We want to test inverse fills. However, they are quite rare in practice so don't over do it.
979 static const SkScalar kFractionInverted = SK_Scalar1 / kMaxElemsPerTest;
980
csmartdaltoncbecb082016-07-22 08:59:08 -0700981 static const SkScalar kFractionAntialiased = 0.25;
982
bsalomon@google.com51a62862012-11-26 21:19:43 +0000983 static const AddElementFunc kElementFuncs[] = {
984 add_rect,
985 add_round_rect,
986 add_oval,
987 };
988
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000989 SkRandom r;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000990
991 for (int i = 0; i < kNumTests; ++i) {
csmartdaltoncbecb082016-07-22 08:59:08 -0700992 SkString testCase;
993 testCase.printf("Iteration %d", i);
994
bsalomon@google.com51a62862012-11-26 21:19:43 +0000995 // Randomly generate a clip stack.
996 SkClipStack stack;
997 int numElems = r.nextRangeU(kMinElemsPerTest, kMaxElemsPerTest);
csmartdaltoncbecb082016-07-22 08:59:08 -0700998 bool doAA = r.nextBiasedBool(kFractionAntialiased);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000999 for (int e = 0; e < numElems; ++e) {
Mike Reedc1f77742016-12-09 09:00:50 -05001000 SkClipOp op = kOps[r.nextULessThan(SK_ARRAY_COUNT(kOps))];
1001 if (op == kReplace_SkClipOp) {
bsalomon@google.com51a62862012-11-26 21:19:43 +00001002 if (r.nextU() % kReplaceDiv) {
1003 --e;
1004 continue;
1005 }
1006 }
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +00001007
bsalomon@google.com51a62862012-11-26 21:19:43 +00001008 // saves can change the clip stack behavior when an element is added.
1009 bool doSave = r.nextBool();
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +00001010
bsalomon@google.com51a62862012-11-26 21:19:43 +00001011 SkSize size = SkSize::Make(
Mike Reeddf85c382017-02-14 10:59:19 -05001012 kBounds.width() * r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac),
1013 kBounds.height() * r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac));
bsalomon@google.com51a62862012-11-26 21:19:43 +00001014
csmartdaltoncbecb082016-07-22 08:59:08 -07001015 SkPoint xy = {r.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth),
1016 r.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight)};
bsalomon@google.com51a62862012-11-26 21:19:43 +00001017
csmartdaltoncbecb082016-07-22 08:59:08 -07001018 SkRect rect;
1019 if (doAA) {
1020 rect.setXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight);
1021 if (GrClip::IsPixelAligned(rect)) {
1022 // Don't create an element that may accidentally become not antialiased.
1023 rect.outset(0.5f, 0.5f);
1024 }
1025 SkASSERT(!GrClip::IsPixelAligned(rect));
1026 } else {
1027 rect.setXYWH(SkScalarFloorToScalar(xy.fX),
1028 SkScalarFloorToScalar(xy.fY),
1029 SkScalarCeilToScalar(size.fWidth),
1030 SkScalarCeilToScalar(size.fHeight));
1031 }
bsalomon@google.com51a62862012-11-26 21:19:43 +00001032
bsalomon@google.com705e8402012-11-27 15:43:57 +00001033 bool invert = r.nextBiasedBool(kFractionInverted);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001034
csmartdaltoncbecb082016-07-22 08:59:08 -07001035 kElementFuncs[r.nextULessThan(SK_ARRAY_COUNT(kElementFuncs))](rect, invert, op, &stack,
1036 doAA);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001037 if (doSave) {
1038 stack.save();
1039 }
1040 }
1041
Brian Salomon14471772017-12-05 10:35:15 -05001042 auto context = GrContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001043 const GrCaps* caps = context->priv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001044
Brian Salomonc3833b42018-07-09 18:23:58 +00001045 // Zero the memory we will new the GrReducedClip into. This ensures the elements gen ID
1046 // will be kInvalidGenID if left uninitialized.
1047 SkAlignedSTStorage<1, GrReducedClip> storage;
1048 memset(storage.get(), 0, sizeof(GrReducedClip));
1049 GR_STATIC_ASSERT(0 == SkClipStack::kInvalidGenID);
1050
csmartdalton77f2fae2016-08-08 09:55:06 -07001051 // Get the reduced version of the stack.
csmartdaltoncbecb082016-07-22 08:59:08 -07001052 SkRect queryBounds = kBounds;
1053 queryBounds.outset(kBounds.width() / 2, kBounds.height() / 2);
Ethan Nicholaseace9352018-10-15 20:09:54 +00001054 const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, queryBounds, caps);
bsalomon@google.coma4444302012-12-04 15:22:12 +00001055
Brian Salomonc3833b42018-07-09 18:23:58 +00001056 REPORTER_ASSERT(reporter,
1057 reduced->maskElements().isEmpty() ||
1058 SkClipStack::kInvalidGenID != reduced->maskGenID(),
1059 testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001060
Brian Salomonc3833b42018-07-09 18:23:58 +00001061 if (!reduced->maskElements().isEmpty()) {
1062 REPORTER_ASSERT(reporter, reduced->hasScissor(), testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001063 SkRect stackBounds;
1064 SkClipStack::BoundsType stackBoundsType;
1065 stack.getBounds(&stackBounds, &stackBoundsType);
Brian Salomonc3833b42018-07-09 18:23:58 +00001066 REPORTER_ASSERT(reporter, reduced->maskRequiresAA() == doAA, testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001067 }
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001068
bsalomon@google.com51a62862012-11-26 21:19:43 +00001069 // Build a new clip stack based on the reduced clip elements
1070 SkClipStack reducedStack;
Brian Salomonc3833b42018-07-09 18:23:58 +00001071 if (GrReducedClip::InitialState::kAllOut == reduced->initialState()) {
bsalomon@google.com51a62862012-11-26 21:19:43 +00001072 // whether the result is bounded or not, the whole plane should start outside the clip.
1073 reducedStack.clipEmpty();
1074 }
Brian Salomonc3833b42018-07-09 18:23:58 +00001075 for (ElementList::Iter iter(reduced->maskElements()); iter.get(); iter.next()) {
1076 add_elem_to_stack(*iter.get(), &reducedStack);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001077 }
bsalomon@google.com51a62862012-11-26 21:19:43 +00001078
Brian Salomonc3833b42018-07-09 18:23:58 +00001079 SkIRect scissor = reduced->hasScissor() ? reduced->scissor() : kIBounds;
csmartdaltond211e782016-08-15 11:17:19 -07001080
bsalomon@google.com34cd70a2012-12-06 14:23:20 +00001081 // GrReducedClipStack assumes that the final result is clipped to the returned bounds
Chris Dalton79471932017-10-27 01:50:57 -06001082 reducedStack.clipDevRect(scissor, kIntersect_SkClipOp);
1083 stack.clipDevRect(scissor, kIntersect_SkClipOp);
bsalomon@google.com34cd70a2012-12-06 14:23:20 +00001084
bsalomon@google.com51a62862012-11-26 21:19:43 +00001085 // convert both the original stack and reduced stack to SkRegions and see if they're equal
bsalomon@google.com51a62862012-11-26 21:19:43 +00001086 SkRegion region;
Chris Dalton79471932017-10-27 01:50:57 -06001087 set_region_to_stack(stack, scissor, &region);
csmartdaltond50e2402016-07-22 08:39:06 -07001088
bsalomon@google.com51a62862012-11-26 21:19:43 +00001089 SkRegion reducedRegion;
Chris Dalton79471932017-10-27 01:50:57 -06001090 set_region_to_stack(reducedStack, scissor, &reducedRegion);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001091
Brian Salomon1c80e992018-01-29 09:50:47 -05001092 REPORTER_ASSERT(reporter, region == reducedRegion, testCase.c_str());
Brian Salomonc3833b42018-07-09 18:23:58 +00001093
1094 reduced->~GrReducedClip();
bsalomon@google.com51a62862012-11-26 21:19:43 +00001095 }
1096}
1097
halcanary4dbbd042016-06-07 17:21:10 -07001098#ifdef SK_BUILD_FOR_WIN
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001099 #define SUPPRESS_VISIBILITY_WARNING
1100#else
1101 #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden")))
1102#endif
1103
Brian Salomonc3833b42018-07-09 18:23:58 +00001104static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) {
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001105 {
1106 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001107 stack.clipRect(SkRect::MakeXYWH(0, 0, 100, 100), SkMatrix::I(), kReplace_SkClipOp,
Brian Salomona3b45d42016-10-03 11:36:16 -04001108 true);
1109 stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(50.3), SkScalar(50.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001110 kReplace_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001111 SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001112
Brian Salomon14471772017-12-05 10:35:15 -05001113 auto context = GrContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001114 const GrCaps* caps = context->priv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001115
Brian Salomonc3833b42018-07-09 18:23:58 +00001116 SkAlignedSTStorage<1, GrReducedClip> storage;
1117 memset(storage.get(), 0, sizeof(GrReducedClip));
1118 GR_STATIC_ASSERT(0 == SkClipStack::kInvalidGenID);
Ethan Nicholaseace9352018-10-15 20:09:54 +00001119 const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, bounds, caps);
Brian Salomonc3833b42018-07-09 18:23:58 +00001120
1121 REPORTER_ASSERT(reporter, reduced->maskElements().count() == 1);
1122 // Clips will be cached based on the generation id. Make sure the gen id is valid.
1123 REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reduced->maskGenID());
1124
1125 reduced->~GrReducedClip();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001126 }
1127 {
1128 SkClipStack stack;
1129
1130 // Create a clip with following 25.3, 25.3 boxes which are 25 apart:
1131 // A B
1132 // C D
1133
Brian Salomona3b45d42016-10-03 11:36:16 -04001134 stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001135 kReplace_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001136 uint32_t genIDA = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001137 stack.clipRect(SkRect::MakeXYWH(50, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001138 kUnion_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001139 uint32_t genIDB = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001140 stack.clipRect(SkRect::MakeXYWH(0, 50, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001141 kUnion_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001142 uint32_t genIDC = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001143 stack.clipRect(SkRect::MakeXYWH(50, 50, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001144 kUnion_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001145 uint32_t genIDD = stack.getTopmostGenID();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001146
1147
csmartdaltoncbecb082016-07-22 08:59:08 -07001148#define IXYWH SkIRect::MakeXYWH
1149#define XYWH SkRect::MakeXYWH
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001150
csmartdaltoncbecb082016-07-22 08:59:08 -07001151 SkIRect stackBounds = IXYWH(0, 0, 76, 76);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001152
1153 // The base test is to test each rect in two ways:
1154 // 1) The box dimensions. (Should reduce to "all in", no elements).
1155 // 2) A bit over the box dimensions.
1156 // In the case 2, test that the generation id is what is expected.
1157 // The rects are of fractional size so that case 2 never gets optimized to an empty element
1158 // list.
1159
1160 // Not passing in tighter bounds is tested for consistency.
1161 static const struct SUPPRESS_VISIBILITY_WARNING {
csmartdaltoncbecb082016-07-22 08:59:08 -07001162 SkRect testBounds;
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001163 int reducedClipCount;
Brian Salomonc3833b42018-07-09 18:23:58 +00001164 uint32_t reducedGenID;
csmartdaltoncbecb082016-07-22 08:59:08 -07001165 InitialState initialState;
1166 SkIRect clipIRect;
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001167 // parameter.
1168 } testCases[] = {
csmartdalton77f2fae2016-08-08 09:55:06 -07001169
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001170 // Rect A.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001171 { XYWH(0, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 0, 25, 25) },
1172 { XYWH(0.1f, 0.1f, 25.1f, 25.1f), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 0, 26, 26) },
Chris Dalton79471932017-10-27 01:50:57 -06001173 { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::InitialState::kAllOut, IXYWH(0, 0, 26, 26)},
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001174
1175 // Rect B.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001176 { XYWH(50, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 25, 25) },
1177 { XYWH(50, 0, 25.3f, 25.3f), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 26, 26) },
csmartdalton77f2fae2016-08-08 09:55:06 -07001178 { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::InitialState::kAllOut, IXYWH(50, 0, 26, 27) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001179
1180 // Rect C.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001181 { XYWH(0, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 50, 25, 25) },
1182 { XYWH(0.2f, 50.1f, 25.1f, 25.2f), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 50, 26, 26) },
csmartdalton77f2fae2016-08-08 09:55:06 -07001183 { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::InitialState::kAllOut, IXYWH(0, 50, 27, 26) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001184
1185 // Rect D.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001186 { XYWH(50, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 25, 25)},
1187 { XYWH(50.3f, 50.3f, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 26, 26)},
csmartdalton77f2fae2016-08-08 09:55:06 -07001188 { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(50, 50, 26, 26)},
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001189
1190 // Other tests:
csmartdalton77f2fae2016-08-08 09:55:06 -07001191 { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::InitialState::kAllOut, stackBounds },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001192
1193 // Rect in the middle, touches none.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001194 { XYWH(26, 26, 24, 24), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllOut, IXYWH(26, 26, 24, 24) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001195
1196 // Rect in the middle, touches all the rects. GenID is the last rect.
csmartdalton77f2fae2016-08-08 09:55:06 -07001197 { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(24, 24, 27, 27) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001198 };
1199
1200#undef XYWH
csmartdaltoncbecb082016-07-22 08:59:08 -07001201#undef IXYWH
Brian Salomon14471772017-12-05 10:35:15 -05001202 auto context = GrContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001203 const GrCaps* caps = context->priv().caps();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001204
1205 for (size_t i = 0; i < SK_ARRAY_COUNT(testCases); ++i) {
Ethan Nicholaseace9352018-10-15 20:09:54 +00001206 const GrReducedClip reduced(stack, testCases[i].testBounds, caps);
Chris Dalton79471932017-10-27 01:50:57 -06001207 REPORTER_ASSERT(reporter, reduced.maskElements().count() ==
1208 testCases[i].reducedClipCount);
Brian Salomonc3833b42018-07-09 18:23:58 +00001209 SkASSERT(reduced.maskElements().count() == testCases[i].reducedClipCount);
1210 if (reduced.maskElements().count()) {
1211 REPORTER_ASSERT(reporter, reduced.maskGenID() == testCases[i].reducedGenID);
1212 SkASSERT(reduced.maskGenID() == testCases[i].reducedGenID);
csmartdalton8d3f92a2016-08-17 09:39:38 -07001213 }
csmartdalton77f2fae2016-08-08 09:55:06 -07001214 REPORTER_ASSERT(reporter, reduced.initialState() == testCases[i].initialState);
Brian Salomonc3833b42018-07-09 18:23:58 +00001215 SkASSERT(reduced.initialState() == testCases[i].initialState);
Chris Dalton79471932017-10-27 01:50:57 -06001216 REPORTER_ASSERT(reporter, reduced.hasScissor());
Brian Salomonc3833b42018-07-09 18:23:58 +00001217 SkASSERT(reduced.hasScissor());
Chris Dalton79471932017-10-27 01:50:57 -06001218 REPORTER_ASSERT(reporter, reduced.scissor() == testCases[i].clipIRect);
Brian Salomonc3833b42018-07-09 18:23:58 +00001219 SkASSERT(reduced.scissor() == testCases[i].clipIRect);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001220 }
1221 }
1222}
1223
1224static void test_reduced_clip_stack_no_aa_crash(skiatest::Reporter* reporter) {
1225 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001226 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 100, 100), kReplace_SkClipOp);
1227 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 50, 50), kReplace_SkClipOp);
csmartdaltoncbecb082016-07-22 08:59:08 -07001228 SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001229
Brian Salomon14471772017-12-05 10:35:15 -05001230 auto context = GrContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001231 const GrCaps* caps = context->priv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001232
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001233 // At the time, this would crash.
Ethan Nicholaseace9352018-10-15 20:09:54 +00001234 const GrReducedClip reduced(stack, bounds, caps);
Chris Dalton79471932017-10-27 01:50:57 -06001235 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001236}
1237
csmartdaltoncbecb082016-07-22 08:59:08 -07001238enum class ClipMethod {
1239 kSkipDraw,
1240 kIgnoreClip,
1241 kScissor,
1242 kAAElements
1243};
1244
1245static void test_aa_query(skiatest::Reporter* reporter, const SkString& testName,
1246 const SkClipStack& stack, const SkMatrix& queryXform,
1247 const SkRect& preXformQuery, ClipMethod expectedMethod,
1248 int numExpectedElems = 0) {
Brian Salomon14471772017-12-05 10:35:15 -05001249 auto context = GrContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001250 const GrCaps* caps = context->priv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001251
csmartdaltoncbecb082016-07-22 08:59:08 -07001252 SkRect queryBounds;
1253 queryXform.mapRect(&queryBounds, preXformQuery);
Ethan Nicholaseace9352018-10-15 20:09:54 +00001254 const GrReducedClip reduced(stack, queryBounds, caps);
csmartdaltoncbecb082016-07-22 08:59:08 -07001255
1256 SkClipStack::BoundsType stackBoundsType;
1257 SkRect stackBounds;
1258 stack.getBounds(&stackBounds, &stackBoundsType);
1259
1260 switch (expectedMethod) {
1261 case ClipMethod::kSkipDraw:
1262 SkASSERT(0 == numExpectedElems);
Brian Salomon1c80e992018-01-29 09:50:47 -05001263 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty(), testName.c_str());
1264 REPORTER_ASSERT(reporter,
1265 GrReducedClip::InitialState::kAllOut == reduced.initialState(),
1266 testName.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001267 return;
1268 case ClipMethod::kIgnoreClip:
1269 SkASSERT(0 == numExpectedElems);
Brian Salomon1c80e992018-01-29 09:50:47 -05001270 REPORTER_ASSERT(
1271 reporter,
1272 !reduced.hasScissor() || GrClip::IsInsideClip(reduced.scissor(), queryBounds),
1273 testName.c_str());
1274 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty(), testName.c_str());
1275 REPORTER_ASSERT(reporter,
1276 GrReducedClip::InitialState::kAllIn == reduced.initialState(),
1277 testName.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001278 return;
1279 case ClipMethod::kScissor: {
1280 SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType);
1281 SkASSERT(0 == numExpectedElems);
1282 SkIRect expectedScissor;
1283 stackBounds.round(&expectedScissor);
Brian Salomon1c80e992018-01-29 09:50:47 -05001284 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty(), testName.c_str());
1285 REPORTER_ASSERT(reporter, reduced.hasScissor(), testName.c_str());
1286 REPORTER_ASSERT(reporter, expectedScissor == reduced.scissor(), testName.c_str());
1287 REPORTER_ASSERT(reporter,
1288 GrReducedClip::InitialState::kAllIn == reduced.initialState(),
1289 testName.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001290 return;
1291 }
1292 case ClipMethod::kAAElements: {
1293 SkIRect expectedClipIBounds = GrClip::GetPixelIBounds(queryBounds);
1294 if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
1295 SkAssertResult(expectedClipIBounds.intersect(GrClip::GetPixelIBounds(stackBounds)));
1296 }
Brian Salomon1c80e992018-01-29 09:50:47 -05001297 REPORTER_ASSERT(reporter, numExpectedElems == reduced.maskElements().count(),
1298 testName.c_str());
1299 REPORTER_ASSERT(reporter, reduced.hasScissor(), testName.c_str());
1300 REPORTER_ASSERT(reporter, expectedClipIBounds == reduced.scissor(), testName.c_str());
1301 REPORTER_ASSERT(reporter,
1302 reduced.maskElements().isEmpty() || reduced.maskRequiresAA(),
1303 testName.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001304 break;
1305 }
1306 }
1307}
1308
1309static void test_reduced_clip_stack_aa(skiatest::Reporter* reporter) {
1310 constexpr SkScalar IL = 2, IT = 1, IR = 6, IB = 7; // Pixel aligned rect.
1311 constexpr SkScalar L = 2.2f, T = 1.7f, R = 5.8f, B = 7.3f; // Generic rect.
1312 constexpr SkScalar l = 3.3f, t = 2.8f, r = 4.7f, b = 6.2f; // Small rect contained in R.
1313
1314 SkRect alignedRect = {IL, IT, IR, IB};
1315 SkRect rect = {L, T, R, B};
1316 SkRect innerRect = {l, t, r, b};
1317
1318 SkMatrix m;
1319 m.setIdentity();
1320
1321 constexpr SkScalar kMinScale = 2.0001f;
1322 constexpr SkScalar kMaxScale = 3;
1323 constexpr int kNumIters = 8;
1324
1325 SkString name;
1326 SkRandom rand;
1327
1328 for (int i = 0; i < kNumIters; ++i) {
1329 // Pixel-aligned rect (iior=true).
1330 name.printf("Pixel-aligned rect test, iter %i", i);
1331 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001332 stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001333 test_aa_query(reporter, name, stack, m, {IL, IT, IR, IB}, ClipMethod::kIgnoreClip);
1334 test_aa_query(reporter, name, stack, m, {IL, IT-1, IR, IT}, ClipMethod::kSkipDraw);
csmartdalton77f2fae2016-08-08 09:55:06 -07001335 test_aa_query(reporter, name, stack, m, {IL, IT-2, IR, IB}, ClipMethod::kScissor);
csmartdaltoncbecb082016-07-22 08:59:08 -07001336
1337 // Rect (iior=true).
1338 name.printf("Rect test, iter %i", i);
1339 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001340 stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001341 test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kIgnoreClip);
1342 test_aa_query(reporter, name, stack, m, {L-.1f, T, L, B}, ClipMethod::kSkipDraw);
1343 test_aa_query(reporter, name, stack, m, {L-.1f, T, L+.1f, B}, ClipMethod::kAAElements, 1);
1344
1345 // Difference rect (iior=false, inside-out bounds).
1346 name.printf("Difference rect test, iter %i", i);
1347 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001348 stack.clipRect(rect, SkMatrix::I(), kDifference_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001349 test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kSkipDraw);
1350 test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T}, ClipMethod::kIgnoreClip);
1351 test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T+.1f}, ClipMethod::kAAElements, 1);
1352
1353 // Complex clip (iior=false, normal bounds).
1354 name.printf("Complex clip test, iter %i", i);
1355 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001356 stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true);
1357 stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001358 test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
1359 test_aa_query(reporter, name, stack, m, {r-.1f, t, R, b}, ClipMethod::kAAElements, 1);
1360 test_aa_query(reporter, name, stack, m, {r-.1f, t, R+.1f, b}, ClipMethod::kAAElements, 2);
1361 test_aa_query(reporter, name, stack, m, {r, t, R+.1f, b}, ClipMethod::kAAElements, 1);
1362 test_aa_query(reporter, name, stack, m, {r, t, R, b}, ClipMethod::kIgnoreClip);
1363 test_aa_query(reporter, name, stack, m, {R, T, R+.1f, B}, ClipMethod::kSkipDraw);
1364
1365 // Complex clip where outer rect is pixel aligned (iior=false, normal bounds).
1366 name.printf("Aligned Complex clip test, iter %i", i);
1367 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001368 stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true);
1369 stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001370 test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
1371 test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB}, ClipMethod::kAAElements, 1);
1372 test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB+.1f}, ClipMethod::kAAElements, 1);
1373 test_aa_query(reporter, name, stack, m, {l, b, r, IB+.1f}, ClipMethod::kAAElements, 0);
1374 test_aa_query(reporter, name, stack, m, {l, b, r, IB}, ClipMethod::kIgnoreClip);
1375 test_aa_query(reporter, name, stack, m, {IL, IB, IR, IB+.1f}, ClipMethod::kSkipDraw);
1376
1377 // Apply random transforms and try again. This ensures the clip stack reduction is hardened
1378 // against FP rounding error.
1379 SkScalar sx = rand.nextRangeScalar(kMinScale, kMaxScale);
1380 sx = SkScalarFloorToScalar(sx * alignedRect.width()) / alignedRect.width();
1381 SkScalar sy = rand.nextRangeScalar(kMinScale, kMaxScale);
1382 sy = SkScalarFloorToScalar(sy * alignedRect.height()) / alignedRect.height();
1383 SkScalar tx = SkScalarRoundToScalar(sx * alignedRect.x()) - sx * alignedRect.x();
1384 SkScalar ty = SkScalarRoundToScalar(sy * alignedRect.y()) - sy * alignedRect.y();
1385
1386 SkMatrix xform = SkMatrix::MakeScale(sx, sy);
1387 xform.postTranslate(tx, ty);
1388 xform.mapRect(&alignedRect);
1389 xform.mapRect(&rect);
1390 xform.mapRect(&innerRect);
1391 m.postConcat(xform);
1392 }
1393}
1394
Chris Dalton348060f2017-06-05 13:15:37 -06001395static void test_tiny_query_bounds_assertion_bug(skiatest::Reporter* reporter) {
1396 // https://bugs.chromium.org/p/skia/issues/detail?id=5990
1397 const SkRect clipBounds = SkRect::MakeXYWH(1.5f, 100, 1000, 1000);
1398
1399 SkClipStack rectStack;
1400 rectStack.clipRect(clipBounds, SkMatrix::I(), kIntersect_SkClipOp, true);
1401
1402 SkPath clipPath;
1403 clipPath.moveTo(clipBounds.left(), clipBounds.top());
1404 clipPath.quadTo(clipBounds.right(), clipBounds.top(),
1405 clipBounds.right(), clipBounds.bottom());
1406 clipPath.quadTo(clipBounds.left(), clipBounds.bottom(),
1407 clipBounds.left(), clipBounds.top());
1408 SkClipStack pathStack;
1409 pathStack.clipPath(clipPath, SkMatrix::I(), kIntersect_SkClipOp, true);
1410
Brian Salomon14471772017-12-05 10:35:15 -05001411 auto context = GrContext::MakeMock(nullptr);
Robert Phillips9da87e02019-02-04 13:26:26 -05001412 const GrCaps* caps = context->priv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001413
Chris Dalton348060f2017-06-05 13:15:37 -06001414 for (const SkClipStack& stack : {rectStack, pathStack}) {
1415 for (SkRect queryBounds : {SkRect::MakeXYWH(53, 60, GrClip::kBoundsTolerance, 1000),
1416 SkRect::MakeXYWH(53, 60, GrClip::kBoundsTolerance/2, 1000),
1417 SkRect::MakeXYWH(53, 160, 1000, GrClip::kBoundsTolerance),
1418 SkRect::MakeXYWH(53, 160, 1000, GrClip::kBoundsTolerance/2)}) {
Ethan Nicholaseace9352018-10-15 20:09:54 +00001419 const GrReducedClip reduced(stack, queryBounds, caps);
Chris Dalton79471932017-10-27 01:50:57 -06001420 REPORTER_ASSERT(reporter, !reduced.hasScissor());
1421 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty());
Chris Dalton348060f2017-06-05 13:15:37 -06001422 REPORTER_ASSERT(reporter,
1423 GrReducedClip::InitialState::kAllOut == reduced.initialState());
1424 }
1425 }
1426}
1427
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00001428DEF_TEST(ClipStack, reporter) {
reed@google.combdee9fc2011-02-22 20:17:43 +00001429 SkClipStack stack;
1430
robertphillips@google.com80214e22012-07-20 15:33:18 +00001431 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001432 assert_count(reporter, stack, 0);
1433
1434 static const SkIRect gRects[] = {
1435 { 0, 0, 100, 100 },
1436 { 25, 25, 125, 125 },
1437 { 0, 0, 1000, 1000 },
1438 { 0, 0, 75, 75 }
1439 };
1440 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
Mike Reedc1f77742016-12-09 09:00:50 -05001441 stack.clipDevRect(gRects[i], kIntersect_SkClipOp);
reed@google.combdee9fc2011-02-22 20:17:43 +00001442 }
1443
1444 // all of the above rects should have been intersected, leaving only 1 rect
robertphillips@google.com80214e22012-07-20 15:33:18 +00001445 SkClipStack::B2TIter iter(stack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001446 const SkClipStack::Element* element = iter.next();
epoger@google.com2047f002011-05-17 17:36:59 +00001447 SkRect answer;
1448 answer.iset(25, 25, 75, 75);
reed@google.combdee9fc2011-02-22 20:17:43 +00001449
bsalomon49f085d2014-09-05 13:34:00 -07001450 REPORTER_ASSERT(reporter, element);
Brian Salomonf3b46e52017-08-30 11:37:57 -04001451 REPORTER_ASSERT(reporter,
1452 SkClipStack::Element::DeviceSpaceType::kRect == element->getDeviceSpaceType());
Mike Reedc1f77742016-12-09 09:00:50 -05001453 REPORTER_ASSERT(reporter, kIntersect_SkClipOp == element->getOp());
Brian Salomonf3b46e52017-08-30 11:37:57 -04001454 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == answer);
reed@google.combdee9fc2011-02-22 20:17:43 +00001455 // now check that we only had one in our iterator
1456 REPORTER_ASSERT(reporter, !iter.next());
1457
1458 stack.reset();
robertphillips@google.com80214e22012-07-20 15:33:18 +00001459 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001460 assert_count(reporter, stack, 0);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +00001461
1462 test_assign_and_comparison(reporter);
robertphillips@google.com80214e22012-07-20 15:33:18 +00001463 test_iterators(reporter);
Brian Salomonf3b46e52017-08-30 11:37:57 -04001464 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kRect);
1465 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kRRect);
1466 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kPath);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +00001467 test_isWideOpen(reporter);
robertphillips@google.com08eacc12012-08-02 12:49:00 +00001468 test_rect_merging(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001469 test_rect_replace(reporter);
junov@chromium.orgedf32d52012-12-10 14:57:54 +00001470 test_rect_inverse_fill(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001471 test_path_replace(reporter);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +00001472 test_quickContains(reporter);
csmartdaltond50e2402016-07-22 08:39:06 -07001473 test_invfill_diff_bug(reporter);
Brian Osmanc7ad40f2018-05-31 14:27:17 -04001474
bsalomon@google.comedb26fd2012-11-28 14:42:41 +00001475 test_reduced_clip_stack(reporter);
Brian Salomonc3833b42018-07-09 18:23:58 +00001476 test_reduced_clip_stack_genid(reporter);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001477 test_reduced_clip_stack_no_aa_crash(reporter);
csmartdaltoncbecb082016-07-22 08:59:08 -07001478 test_reduced_clip_stack_aa(reporter);
Chris Dalton348060f2017-06-05 13:15:37 -06001479 test_tiny_query_bounds_assertion_bug(reporter);
reed@google.combdee9fc2011-02-22 20:17:43 +00001480}
Brian Salomon19f0ed52017-01-06 13:54:58 -05001481
1482//////////////////////////////////////////////////////////////////////////////
1483
Robert Phillips875218e2017-02-24 08:37:13 -05001484sk_sp<GrTextureProxy> GrClipStackClip::testingOnly_createClipMask(GrContext* context) const {
Brian Salomon19f0ed52017-01-06 13:54:58 -05001485 const GrReducedClip reducedClip(*fStack, SkRect::MakeWH(512, 512), 0);
Brian Osman5d034742017-09-11 13:38:55 -04001486 return this->createSoftwareClipMask(context, reducedClip, nullptr);
Brian Salomon19f0ed52017-01-06 13:54:58 -05001487}
1488
1489// Verify that clip masks are freed up when the clip state that generated them goes away.
1490DEF_GPUTEST_FOR_ALL_CONTEXTS(ClipMaskCache, reporter, ctxInfo) {
1491 // This test uses resource key tags which only function in debug builds.
1492#ifdef SK_DEBUG
1493 GrContext* context = ctxInfo.grContext();
1494 SkClipStack stack;
1495
1496 SkPath path;
1497 path.addCircle(10, 10, 8);
1498 path.addCircle(15, 15, 8);
1499 path.setFillType(SkPath::kEvenOdd_FillType);
1500
Brian Salomonc3833b42018-07-09 18:23:58 +00001501 static const char* kTag = GrClipStackClip::kMaskTestTag;
Robert Phillips9da87e02019-02-04 13:26:26 -05001502 GrResourceCache* cache = context->priv().getResourceCache();
Brian Salomon19f0ed52017-01-06 13:54:58 -05001503
1504 static constexpr int kN = 5;
1505
1506 for (int i = 0; i < kN; ++i) {
1507 SkMatrix m;
1508 m.setTranslate(0.5, 0.5);
1509 stack.save();
1510 stack.clipPath(path, m, SkClipOp::kIntersect, true);
Robert Phillips875218e2017-02-24 08:37:13 -05001511 sk_sp<GrTextureProxy> mask = GrClipStackClip(&stack).testingOnly_createClipMask(context);
Robert Phillips9da87e02019-02-04 13:26:26 -05001512 mask->instantiate(context->priv().resourceProvider());
Brian Salomonfd98c2c2018-07-31 17:25:29 -04001513 GrTexture* tex = mask->peekTexture();
Robert Phillips875218e2017-02-24 08:37:13 -05001514 REPORTER_ASSERT(reporter, 0 == strcmp(tex->getUniqueKey().tag(), kTag));
Brian Salomon19f0ed52017-01-06 13:54:58 -05001515 // Make sure mask isn't pinned in cache.
1516 mask.reset(nullptr);
1517 context->flush();
1518 REPORTER_ASSERT(reporter, i + 1 == cache->countUniqueKeysWithTag(kTag));
1519 }
1520
1521 for (int i = 0; i < kN; ++i) {
1522 stack.restore();
1523 cache->purgeAsNeeded();
1524 REPORTER_ASSERT(reporter, kN - (i + 1) == cache->countUniqueKeysWithTag(kTag));
1525 }
1526#endif
1527}
1528
Mike Reed3726a4a2017-01-19 11:36:41 -05001529DEF_GPUTEST_FOR_ALL_CONTEXTS(canvas_private_clipRgn, reporter, ctxInfo) {
1530 GrContext* context = ctxInfo.grContext();
1531
1532 const int w = 10;
1533 const int h = 10;
Brian Salomon8996e182017-07-05 17:01:48 -04001534 SkImageInfo info = SkImageInfo::Make(w, h, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
Mike Reed3726a4a2017-01-19 11:36:41 -05001535 sk_sp<SkSurface> surf = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info);
1536 SkCanvas* canvas = surf->getCanvas();
1537 SkRegion rgn;
1538
1539 canvas->temporary_internal_getRgnClip(&rgn);
1540 REPORTER_ASSERT(reporter, rgn.isRect());
1541 REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeWH(w, h));
1542
1543 canvas->save();
1544 canvas->clipRect(SkRect::MakeWH(5, 5), kDifference_SkClipOp);
1545 canvas->temporary_internal_getRgnClip(&rgn);
1546 REPORTER_ASSERT(reporter, rgn.isComplex());
1547 REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeWH(w, h));
1548 canvas->restore();
1549
1550 canvas->save();
1551 canvas->clipRRect(SkRRect::MakeOval(SkRect::MakeLTRB(3, 3, 7, 7)));
1552 canvas->temporary_internal_getRgnClip(&rgn);
1553 REPORTER_ASSERT(reporter, rgn.isComplex());
1554 REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeLTRB(3, 3, 7, 7));
1555 canvas->restore();
1556}