blob: 2cfeaae9f30b3a85545dfbbdb6e7c6927244775b [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
427 return count;
428}
429
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000430static void test_rect_inverse_fill(skiatest::Reporter* reporter) {
431 // non-intersecting rectangles
432 SkRect rect = SkRect::MakeLTRB(0, 0, 10, 10);
433
434 SkPath path;
435 path.addRect(rect);
436 path.toggleInverseFillType();
437 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500438 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.orgedf32d52012-12-10 14:57:54 +0000439
440 SkRect bounds;
441 SkClipStack::BoundsType boundsType;
442 stack.getBounds(&bounds, &boundsType);
443 REPORTER_ASSERT(reporter, SkClipStack::kInsideOut_BoundsType == boundsType);
444 REPORTER_ASSERT(reporter, bounds == rect);
445}
446
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000447static void test_rect_replace(skiatest::Reporter* reporter) {
448 SkRect rect = SkRect::MakeWH(100, 100);
449 SkRect rect2 = SkRect::MakeXYWH(50, 50, 100, 100);
450
451 SkRect bound;
452 SkClipStack::BoundsType type;
453 bool isIntersectionOfRects;
454
455 // Adding a new rect with the replace operator should not increase
456 // the stack depth. BW replacing BW.
457 {
458 SkClipStack stack;
459 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500460 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000461 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500462 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000463 REPORTER_ASSERT(reporter, 1 == count(stack));
464 }
465
466 // Adding a new rect with the replace operator should not increase
467 // the stack depth. AA replacing AA.
468 {
469 SkClipStack stack;
470 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500471 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000472 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500473 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000474 REPORTER_ASSERT(reporter, 1 == count(stack));
475 }
476
477 // Adding a new rect with the replace operator should not increase
478 // the stack depth. BW replacing AA replacing BW.
479 {
480 SkClipStack stack;
481 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500482 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000483 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500484 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000485 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500486 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000487 REPORTER_ASSERT(reporter, 1 == count(stack));
488 }
489
490 // Make sure replace clip rects don't collapse too much.
491 {
492 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500493 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
494 stack.clipRect(rect2, SkMatrix::I(), kIntersect_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000495 REPORTER_ASSERT(reporter, 1 == count(stack));
496
497 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500498 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000499 REPORTER_ASSERT(reporter, 2 == count(stack));
500 stack.getBounds(&bound, &type, &isIntersectionOfRects);
501 REPORTER_ASSERT(reporter, bound == rect);
502 stack.restore();
503 REPORTER_ASSERT(reporter, 1 == count(stack));
504
505 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500506 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
507 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000508 REPORTER_ASSERT(reporter, 2 == count(stack));
509 stack.restore();
510 REPORTER_ASSERT(reporter, 1 == count(stack));
511
512 stack.save();
Mike Reedc1f77742016-12-09 09:00:50 -0500513 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
514 stack.clipRect(rect2, SkMatrix::I(), kIntersect_SkClipOp, false);
515 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000516 REPORTER_ASSERT(reporter, 2 == count(stack));
517 stack.restore();
518 REPORTER_ASSERT(reporter, 1 == count(stack));
519 }
520}
521
522// Simplified path-based version of test_rect_replace.
523static void test_path_replace(skiatest::Reporter* reporter) {
524 SkRect rect = SkRect::MakeWH(100, 100);
525 SkPath path;
526 path.addCircle(50, 50, 50);
527
528 // Replace operation doesn't grow the stack.
529 {
530 SkClipStack stack;
531 REPORTER_ASSERT(reporter, 0 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500532 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000533 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500534 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, false);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000535 REPORTER_ASSERT(reporter, 1 == count(stack));
536 }
537
538 // Replacing rect with path.
539 {
540 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500541 stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000542 REPORTER_ASSERT(reporter, 1 == count(stack));
Mike Reedc1f77742016-12-09 09:00:50 -0500543 stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, true);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000544 REPORTER_ASSERT(reporter, 1 == count(stack));
545 }
546}
547
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000548// Test out SkClipStack's merging of rect clips. In particular exercise
549// merging of aa vs. bw rects.
550static void test_rect_merging(skiatest::Reporter* reporter) {
551
552 SkRect overlapLeft = SkRect::MakeLTRB(10, 10, 50, 50);
553 SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80);
554
555 SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90);
556 SkRect nestedChild = SkRect::MakeLTRB(40, 40, 60, 60);
557
558 SkRect bound;
559 SkClipStack::BoundsType type;
560 bool isIntersectionOfRects;
561
562 // all bw overlapping - should merge
563 {
564 SkClipStack stack;
565
Mike Reedc1f77742016-12-09 09:00:50 -0500566 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000567
Mike Reedc1f77742016-12-09 09:00:50 -0500568 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000569
570 REPORTER_ASSERT(reporter, 1 == count(stack));
571
572 stack.getBounds(&bound, &type, &isIntersectionOfRects);
573
574 REPORTER_ASSERT(reporter, isIntersectionOfRects);
575 }
576
577 // all aa overlapping - should merge
578 {
579 SkClipStack stack;
580
Mike Reedc1f77742016-12-09 09:00:50 -0500581 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000582
Mike Reedc1f77742016-12-09 09:00:50 -0500583 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000584
585 REPORTER_ASSERT(reporter, 1 == count(stack));
586
587 stack.getBounds(&bound, &type, &isIntersectionOfRects);
588
589 REPORTER_ASSERT(reporter, isIntersectionOfRects);
590 }
591
592 // mixed overlapping - should _not_ merge
593 {
594 SkClipStack stack;
595
Mike Reedc1f77742016-12-09 09:00:50 -0500596 stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000597
Mike Reedc1f77742016-12-09 09:00:50 -0500598 stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000599
600 REPORTER_ASSERT(reporter, 2 == count(stack));
601
602 stack.getBounds(&bound, &type, &isIntersectionOfRects);
603
604 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
605 }
606
607 // mixed nested (bw inside aa) - should merge
608 {
609 SkClipStack stack;
610
Mike Reedc1f77742016-12-09 09:00:50 -0500611 stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000612
Mike Reedc1f77742016-12-09 09:00:50 -0500613 stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000614
615 REPORTER_ASSERT(reporter, 1 == count(stack));
616
617 stack.getBounds(&bound, &type, &isIntersectionOfRects);
618
619 REPORTER_ASSERT(reporter, isIntersectionOfRects);
620 }
621
622 // mixed nested (aa inside bw) - should merge
623 {
624 SkClipStack stack;
625
Mike Reedc1f77742016-12-09 09:00:50 -0500626 stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000627
Mike Reedc1f77742016-12-09 09:00:50 -0500628 stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000629
630 REPORTER_ASSERT(reporter, 1 == count(stack));
631
632 stack.getBounds(&bound, &type, &isIntersectionOfRects);
633
634 REPORTER_ASSERT(reporter, isIntersectionOfRects);
635 }
636
637 // reverse nested (aa inside bw) - should _not_ merge
638 {
639 SkClipStack stack;
640
Mike Reedc1f77742016-12-09 09:00:50 -0500641 stack.clipRect(nestedChild, SkMatrix::I(), kReplace_SkClipOp, false);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000642
Mike Reedc1f77742016-12-09 09:00:50 -0500643 stack.clipRect(nestedParent, SkMatrix::I(), kIntersect_SkClipOp, true);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000644
645 REPORTER_ASSERT(reporter, 2 == count(stack));
646
647 stack.getBounds(&bound, &type, &isIntersectionOfRects);
648
649 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
650 }
651}
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000652
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000653static void test_quickContains(skiatest::Reporter* reporter) {
654 SkRect testRect = SkRect::MakeLTRB(10, 10, 40, 40);
655 SkRect insideRect = SkRect::MakeLTRB(20, 20, 30, 30);
656 SkRect intersectingRect = SkRect::MakeLTRB(25, 25, 50, 50);
657 SkRect outsideRect = SkRect::MakeLTRB(0, 0, 50, 50);
658 SkRect nonIntersectingRect = SkRect::MakeLTRB(100, 100, 110, 110);
659
660 SkPath insideCircle;
661 insideCircle.addCircle(25, 25, 5);
662 SkPath intersectingCircle;
663 intersectingCircle.addCircle(25, 40, 10);
664 SkPath outsideCircle;
665 outsideCircle.addCircle(25, 25, 50);
666 SkPath nonIntersectingCircle;
667 nonIntersectingCircle.addCircle(100, 100, 5);
668
669 {
670 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500671 stack.clipRect(outsideRect, SkMatrix::I(), kDifference_SkClipOp, false);
672 // return false because quickContains currently does not care for kDifference_SkClipOp
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000673 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
674 }
skia.committer@gmail.com306ab9d2012-12-13 02:01:33 +0000675
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000676 // Replace Op tests
677 {
678 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500679 stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000680 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
681 }
682
683 {
684 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500685 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000686 stack.save(); // To prevent in-place substitution by replace OP
Mike Reedc1f77742016-12-09 09:00:50 -0500687 stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000688 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
689 stack.restore();
690 }
691
692 {
693 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500694 stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000695 stack.save(); // To prevent in-place substitution by replace OP
Mike Reedc1f77742016-12-09 09:00:50 -0500696 stack.clipRect(insideRect, SkMatrix::I(), kReplace_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000697 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
698 stack.restore();
699 }
700
701 // Verify proper traversal of multi-element clip
702 {
703 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500704 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000705 // Use a path for second clip to prevent in-place intersection
Mike Reedc1f77742016-12-09 09:00:50 -0500706 stack.clipPath(outsideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000707 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
708 }
709
710 // Intersect Op tests with rectangles
711 {
712 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500713 stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000714 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
715 }
716
717 {
718 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500719 stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000720 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
721 }
722
723 {
724 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500725 stack.clipRect(intersectingRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000726 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
727 }
728
729 {
730 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500731 stack.clipRect(nonIntersectingRect, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000732 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
733 }
734
735 // Intersect Op tests with circle paths
736 {
737 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500738 stack.clipPath(outsideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000739 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
740 }
741
742 {
743 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500744 stack.clipPath(insideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000745 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
746 }
747
748 {
749 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500750 stack.clipPath(intersectingCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000751 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
752 }
753
754 {
755 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500756 stack.clipPath(nonIntersectingCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000757 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
758 }
759
760 // Intersect Op tests with inverse filled rectangles
761 {
762 SkClipStack stack;
763 SkPath path;
764 path.addRect(outsideRect);
765 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500766 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000767 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
768 }
769
770 {
771 SkClipStack stack;
772 SkPath path;
773 path.addRect(insideRect);
774 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500775 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000776 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
777 }
778
779 {
780 SkClipStack stack;
781 SkPath path;
782 path.addRect(intersectingRect);
783 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500784 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000785 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
786 }
787
788 {
789 SkClipStack stack;
790 SkPath path;
791 path.addRect(nonIntersectingRect);
792 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500793 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000794 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
795 }
796
797 // Intersect Op tests with inverse filled circles
798 {
799 SkClipStack stack;
800 SkPath path = outsideCircle;
801 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500802 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000803 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
804 }
805
806 {
807 SkClipStack stack;
808 SkPath path = insideCircle;
809 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500810 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000811 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
812 }
813
814 {
815 SkClipStack stack;
816 SkPath path = intersectingCircle;
817 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500818 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000819 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
820 }
821
822 {
823 SkClipStack stack;
824 SkPath path = nonIntersectingCircle;
825 path.toggleInverseFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500826 stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000827 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
828 }
829}
830
csmartdaltond50e2402016-07-22 08:39:06 -0700831static void set_region_to_stack(const SkClipStack& stack, const SkIRect& bounds, SkRegion* region) {
832 region->setRect(bounds);
833 SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
834 while (const SkClipStack::Element *element = iter.next()) {
835 SkRegion elemRegion;
836 SkRegion boundsRgn(bounds);
837 SkPath path;
838
Brian Salomonf3b46e52017-08-30 11:37:57 -0400839 switch (element->getDeviceSpaceType()) {
840 case SkClipStack::Element::DeviceSpaceType::kEmpty:
csmartdaltond50e2402016-07-22 08:39:06 -0700841 elemRegion.setEmpty();
842 break;
843 default:
Brian Salomonf3b46e52017-08-30 11:37:57 -0400844 element->asDeviceSpacePath(&path);
csmartdaltond50e2402016-07-22 08:39:06 -0700845 elemRegion.setPath(path, boundsRgn);
846 break;
847 }
reed73603f32016-09-20 08:42:38 -0700848 region->op(elemRegion, (SkRegion::Op)element->getOp());
csmartdaltond50e2402016-07-22 08:39:06 -0700849 }
850}
851
852static void test_invfill_diff_bug(skiatest::Reporter* reporter) {
853 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -0500854 stack.clipRect({10, 10, 20, 20}, SkMatrix::I(), kIntersect_SkClipOp, false);
csmartdaltond50e2402016-07-22 08:39:06 -0700855
856 SkPath path;
857 path.addRect({30, 10, 40, 20});
858 path.setFillType(SkPath::kInverseWinding_FillType);
Mike Reedc1f77742016-12-09 09:00:50 -0500859 stack.clipPath(path, SkMatrix::I(), kDifference_SkClipOp, false);
csmartdaltond50e2402016-07-22 08:39:06 -0700860
861 REPORTER_ASSERT(reporter, SkClipStack::kEmptyGenID == stack.getTopmostGenID());
862
863 SkRect stackBounds;
864 SkClipStack::BoundsType stackBoundsType;
865 stack.getBounds(&stackBounds, &stackBoundsType);
866
867 REPORTER_ASSERT(reporter, stackBounds.isEmpty());
868 REPORTER_ASSERT(reporter, SkClipStack::kNormal_BoundsType == stackBoundsType);
869
870 SkRegion region;
871 set_region_to_stack(stack, {0, 0, 50, 30}, &region);
872
873 REPORTER_ASSERT(reporter, region.isEmpty());
874}
875
bsalomon@google.com51a62862012-11-26 21:19:43 +0000876///////////////////////////////////////////////////////////////////////////////////////////////////
877
bsalomon@google.com705e8402012-11-27 15:43:57 +0000878// Functions that add a shape to the clip stack. The shape is computed from a rectangle.
879// AA is always disabled since the clip stack reducer can cause changes in aa rasterization of the
880// stack. A fractional edge repeated in different elements may be rasterized fewer times using the
881// reduced stack.
882typedef void (*AddElementFunc) (const SkRect& rect,
883 bool invert,
Mike Reedc1f77742016-12-09 09:00:50 -0500884 SkClipOp op,
csmartdaltoncbecb082016-07-22 08:59:08 -0700885 SkClipStack* stack,
886 bool doAA);
bsalomon@google.coma4e13c82012-11-26 21:38:37 +0000887
Mike Reedc1f77742016-12-09 09:00:50 -0500888static void add_round_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700889 bool doAA) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000890 SkScalar rx = rect.width() / 10;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000891 SkScalar ry = rect.height() / 20;
bsalomon@google.com705e8402012-11-27 15:43:57 +0000892 if (invert) {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000893 SkPath path;
894 path.addRoundRect(rect, rx, ry);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000895 path.setFillType(SkPath::kInverseWinding_FillType);
Brian Salomona3b45d42016-10-03 11:36:16 -0400896 stack->clipPath(path, SkMatrix::I(), op, doAA);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000897 } else {
898 SkRRect rrect;
899 rrect.setRectXY(rect, rx, ry);
Brian Salomona3b45d42016-10-03 11:36:16 -0400900 stack->clipRRect(rrect, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000901 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000902};
903
Mike Reedc1f77742016-12-09 09:00:50 -0500904static void add_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700905 bool doAA) {
bsalomon@google.com705e8402012-11-27 15:43:57 +0000906 if (invert) {
907 SkPath path;
908 path.addRect(rect);
909 path.setFillType(SkPath::kInverseWinding_FillType);
Brian Salomona3b45d42016-10-03 11:36:16 -0400910 stack->clipPath(path, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000911 } else {
Brian Salomona3b45d42016-10-03 11:36:16 -0400912 stack->clipRect(rect, SkMatrix::I(), op, doAA);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000913 }
bsalomon@google.com51a62862012-11-26 21:19:43 +0000914};
915
Mike Reedc1f77742016-12-09 09:00:50 -0500916static void add_oval(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
csmartdaltoncbecb082016-07-22 08:59:08 -0700917 bool doAA) {
bsalomon@google.com51a62862012-11-26 21:19:43 +0000918 SkPath path;
919 path.addOval(rect);
bsalomon@google.com705e8402012-11-27 15:43:57 +0000920 if (invert) {
921 path.setFillType(SkPath::kInverseWinding_FillType);
922 }
Brian Salomona3b45d42016-10-03 11:36:16 -0400923 stack->clipPath(path, SkMatrix::I(), op, doAA);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000924};
925
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000926static void add_elem_to_stack(const SkClipStack::Element& element, SkClipStack* stack) {
Brian Salomonf3b46e52017-08-30 11:37:57 -0400927 switch (element.getDeviceSpaceType()) {
928 case SkClipStack::Element::DeviceSpaceType::kRect:
929 stack->clipRect(element.getDeviceSpaceRect(), SkMatrix::I(), element.getOp(),
930 element.isAA());
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000931 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400932 case SkClipStack::Element::DeviceSpaceType::kRRect:
933 stack->clipRRect(element.getDeviceSpaceRRect(), SkMatrix::I(), element.getOp(),
934 element.isAA());
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000935 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400936 case SkClipStack::Element::DeviceSpaceType::kPath:
937 stack->clipPath(element.getDeviceSpacePath(), SkMatrix::I(), element.getOp(),
938 element.isAA());
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000939 break;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400940 case SkClipStack::Element::DeviceSpaceType::kEmpty:
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000941 SkDEBUGFAIL("Why did the reducer produce an explicit empty.");
942 stack->clipEmpty();
943 break;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000944 }
945}
946
bsalomon@google.com51a62862012-11-26 21:19:43 +0000947static void test_reduced_clip_stack(skiatest::Reporter* reporter) {
948 // We construct random clip stacks, reduce them, and then rasterize both versions to verify that
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +0000949 // they are equal.
bsalomon@google.com51a62862012-11-26 21:19:43 +0000950
951 // All the clip elements will be contained within these bounds.
csmartdaltond211e782016-08-15 11:17:19 -0700952 static const SkIRect kIBounds = SkIRect::MakeWH(100, 100);
953 static const SkRect kBounds = SkRect::Make(kIBounds);
bsalomon@google.com51a62862012-11-26 21:19:43 +0000954
955 enum {
csmartdaltoncbecb082016-07-22 08:59:08 -0700956 kNumTests = 250,
bsalomon@google.com51a62862012-11-26 21:19:43 +0000957 kMinElemsPerTest = 1,
958 kMaxElemsPerTest = 50,
959 };
960
961 // min/max size of a clip element as a fraction of kBounds.
962 static const SkScalar kMinElemSizeFrac = SK_Scalar1 / 5;
963 static const SkScalar kMaxElemSizeFrac = SK_Scalar1;
964
Mike Reedc1f77742016-12-09 09:00:50 -0500965 static const SkClipOp kOps[] = {
966 kDifference_SkClipOp,
967 kIntersect_SkClipOp,
968 kUnion_SkClipOp,
969 kXOR_SkClipOp,
970 kReverseDifference_SkClipOp,
971 kReplace_SkClipOp,
bsalomon@google.com51a62862012-11-26 21:19:43 +0000972 };
973
974 // Replace operations short-circuit the optimizer. We want to make sure that we test this code
975 // path a little bit but we don't want it to prevent us from testing many longer traversals in
976 // the optimizer.
977 static const int kReplaceDiv = 4 * kMaxElemsPerTest;
978
bsalomon@google.com705e8402012-11-27 15:43:57 +0000979 // We want to test inverse fills. However, they are quite rare in practice so don't over do it.
980 static const SkScalar kFractionInverted = SK_Scalar1 / kMaxElemsPerTest;
981
csmartdaltoncbecb082016-07-22 08:59:08 -0700982 static const SkScalar kFractionAntialiased = 0.25;
983
bsalomon@google.com51a62862012-11-26 21:19:43 +0000984 static const AddElementFunc kElementFuncs[] = {
985 add_rect,
986 add_round_rect,
987 add_oval,
988 };
989
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000990 SkRandom r;
bsalomon@google.com51a62862012-11-26 21:19:43 +0000991
992 for (int i = 0; i < kNumTests; ++i) {
csmartdaltoncbecb082016-07-22 08:59:08 -0700993 SkString testCase;
994 testCase.printf("Iteration %d", i);
995
bsalomon@google.com51a62862012-11-26 21:19:43 +0000996 // Randomly generate a clip stack.
997 SkClipStack stack;
998 int numElems = r.nextRangeU(kMinElemsPerTest, kMaxElemsPerTest);
csmartdaltoncbecb082016-07-22 08:59:08 -0700999 bool doAA = r.nextBiasedBool(kFractionAntialiased);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001000 for (int e = 0; e < numElems; ++e) {
Mike Reedc1f77742016-12-09 09:00:50 -05001001 SkClipOp op = kOps[r.nextULessThan(SK_ARRAY_COUNT(kOps))];
1002 if (op == kReplace_SkClipOp) {
bsalomon@google.com51a62862012-11-26 21:19:43 +00001003 if (r.nextU() % kReplaceDiv) {
1004 --e;
1005 continue;
1006 }
1007 }
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +00001008
bsalomon@google.com51a62862012-11-26 21:19:43 +00001009 // saves can change the clip stack behavior when an element is added.
1010 bool doSave = r.nextBool();
skia.committer@gmail.com8ccf5902012-11-27 02:01:19 +00001011
bsalomon@google.com51a62862012-11-26 21:19:43 +00001012 SkSize size = SkSize::Make(
Mike Reeddf85c382017-02-14 10:59:19 -05001013 kBounds.width() * r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac),
1014 kBounds.height() * r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac));
bsalomon@google.com51a62862012-11-26 21:19:43 +00001015
csmartdaltoncbecb082016-07-22 08:59:08 -07001016 SkPoint xy = {r.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth),
1017 r.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight)};
bsalomon@google.com51a62862012-11-26 21:19:43 +00001018
csmartdaltoncbecb082016-07-22 08:59:08 -07001019 SkRect rect;
1020 if (doAA) {
1021 rect.setXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight);
1022 if (GrClip::IsPixelAligned(rect)) {
1023 // Don't create an element that may accidentally become not antialiased.
1024 rect.outset(0.5f, 0.5f);
1025 }
1026 SkASSERT(!GrClip::IsPixelAligned(rect));
1027 } else {
1028 rect.setXYWH(SkScalarFloorToScalar(xy.fX),
1029 SkScalarFloorToScalar(xy.fY),
1030 SkScalarCeilToScalar(size.fWidth),
1031 SkScalarCeilToScalar(size.fHeight));
1032 }
bsalomon@google.com51a62862012-11-26 21:19:43 +00001033
bsalomon@google.com705e8402012-11-27 15:43:57 +00001034 bool invert = r.nextBiasedBool(kFractionInverted);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +00001035
csmartdaltoncbecb082016-07-22 08:59:08 -07001036 kElementFuncs[r.nextULessThan(SK_ARRAY_COUNT(kElementFuncs))](rect, invert, op, &stack,
1037 doAA);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001038 if (doSave) {
1039 stack.save();
1040 }
1041 }
1042
Brian Salomon14471772017-12-05 10:35:15 -05001043 auto context = GrContext::MakeMock(nullptr);
Chris Dalton4c458b12018-06-16 17:22:59 -06001044 const GrCaps* caps = context->contextPriv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001045
Brian Salomonc3833b42018-07-09 18:23:58 +00001046 // Zero the memory we will new the GrReducedClip into. This ensures the elements gen ID
1047 // will be kInvalidGenID if left uninitialized.
1048 SkAlignedSTStorage<1, GrReducedClip> storage;
1049 memset(storage.get(), 0, sizeof(GrReducedClip));
1050 GR_STATIC_ASSERT(0 == SkClipStack::kInvalidGenID);
1051
csmartdalton77f2fae2016-08-08 09:55:06 -07001052 // Get the reduced version of the stack.
csmartdaltoncbecb082016-07-22 08:59:08 -07001053 SkRect queryBounds = kBounds;
1054 queryBounds.outset(kBounds.width() / 2, kBounds.height() / 2);
Brian Salomonc3833b42018-07-09 18:23:58 +00001055 const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, queryBounds, caps);
bsalomon@google.coma4444302012-12-04 15:22:12 +00001056
Brian Salomonc3833b42018-07-09 18:23:58 +00001057 REPORTER_ASSERT(reporter,
1058 reduced->maskElements().isEmpty() ||
1059 SkClipStack::kInvalidGenID != reduced->maskGenID(),
1060 testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001061
Brian Salomonc3833b42018-07-09 18:23:58 +00001062 if (!reduced->maskElements().isEmpty()) {
1063 REPORTER_ASSERT(reporter, reduced->hasScissor(), testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001064 SkRect stackBounds;
1065 SkClipStack::BoundsType stackBoundsType;
1066 stack.getBounds(&stackBounds, &stackBoundsType);
Brian Salomonc3833b42018-07-09 18:23:58 +00001067 REPORTER_ASSERT(reporter, reduced->maskRequiresAA() == doAA, testCase.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001068 }
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001069
bsalomon@google.com51a62862012-11-26 21:19:43 +00001070 // Build a new clip stack based on the reduced clip elements
1071 SkClipStack reducedStack;
Brian Salomonc3833b42018-07-09 18:23:58 +00001072 if (GrReducedClip::InitialState::kAllOut == reduced->initialState()) {
bsalomon@google.com51a62862012-11-26 21:19:43 +00001073 // whether the result is bounded or not, the whole plane should start outside the clip.
1074 reducedStack.clipEmpty();
1075 }
Brian Salomonc3833b42018-07-09 18:23:58 +00001076 for (ElementList::Iter iter(reduced->maskElements()); iter.get(); iter.next()) {
1077 add_elem_to_stack(*iter.get(), &reducedStack);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001078 }
bsalomon@google.com51a62862012-11-26 21:19:43 +00001079
Brian Salomonc3833b42018-07-09 18:23:58 +00001080 SkIRect scissor = reduced->hasScissor() ? reduced->scissor() : kIBounds;
csmartdaltond211e782016-08-15 11:17:19 -07001081
bsalomon@google.com34cd70a2012-12-06 14:23:20 +00001082 // GrReducedClipStack assumes that the final result is clipped to the returned bounds
Chris Dalton79471932017-10-27 01:50:57 -06001083 reducedStack.clipDevRect(scissor, kIntersect_SkClipOp);
1084 stack.clipDevRect(scissor, kIntersect_SkClipOp);
bsalomon@google.com34cd70a2012-12-06 14:23:20 +00001085
bsalomon@google.com51a62862012-11-26 21:19:43 +00001086 // convert both the original stack and reduced stack to SkRegions and see if they're equal
bsalomon@google.com51a62862012-11-26 21:19:43 +00001087 SkRegion region;
Chris Dalton79471932017-10-27 01:50:57 -06001088 set_region_to_stack(stack, scissor, &region);
csmartdaltond50e2402016-07-22 08:39:06 -07001089
bsalomon@google.com51a62862012-11-26 21:19:43 +00001090 SkRegion reducedRegion;
Chris Dalton79471932017-10-27 01:50:57 -06001091 set_region_to_stack(reducedStack, scissor, &reducedRegion);
bsalomon@google.com51a62862012-11-26 21:19:43 +00001092
Brian Salomon1c80e992018-01-29 09:50:47 -05001093 REPORTER_ASSERT(reporter, region == reducedRegion, testCase.c_str());
Brian Salomonc3833b42018-07-09 18:23:58 +00001094
1095 reduced->~GrReducedClip();
bsalomon@google.com51a62862012-11-26 21:19:43 +00001096 }
1097}
1098
halcanary4dbbd042016-06-07 17:21:10 -07001099#ifdef SK_BUILD_FOR_WIN
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001100 #define SUPPRESS_VISIBILITY_WARNING
1101#else
1102 #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden")))
1103#endif
1104
Brian Salomonc3833b42018-07-09 18:23:58 +00001105static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) {
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001106 {
1107 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001108 stack.clipRect(SkRect::MakeXYWH(0, 0, 100, 100), SkMatrix::I(), kReplace_SkClipOp,
Brian Salomona3b45d42016-10-03 11:36:16 -04001109 true);
1110 stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(50.3), SkScalar(50.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001111 kReplace_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001112 SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001113
Brian Salomon14471772017-12-05 10:35:15 -05001114 auto context = GrContext::MakeMock(nullptr);
Chris Dalton4c458b12018-06-16 17:22:59 -06001115 const GrCaps* caps = context->contextPriv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001116
Brian Salomonc3833b42018-07-09 18:23:58 +00001117 SkAlignedSTStorage<1, GrReducedClip> storage;
1118 memset(storage.get(), 0, sizeof(GrReducedClip));
1119 GR_STATIC_ASSERT(0 == SkClipStack::kInvalidGenID);
1120 const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, bounds, caps);
1121
1122 REPORTER_ASSERT(reporter, reduced->maskElements().count() == 1);
1123 // Clips will be cached based on the generation id. Make sure the gen id is valid.
1124 REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reduced->maskGenID());
1125
1126 reduced->~GrReducedClip();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001127 }
1128 {
1129 SkClipStack stack;
1130
1131 // Create a clip with following 25.3, 25.3 boxes which are 25 apart:
1132 // A B
1133 // C D
1134
Brian Salomona3b45d42016-10-03 11:36:16 -04001135 stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001136 kReplace_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001137 uint32_t genIDA = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001138 stack.clipRect(SkRect::MakeXYWH(50, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001139 kUnion_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001140 uint32_t genIDB = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001141 stack.clipRect(SkRect::MakeXYWH(0, 50, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001142 kUnion_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001143 uint32_t genIDC = stack.getTopmostGenID();
Brian Salomona3b45d42016-10-03 11:36:16 -04001144 stack.clipRect(SkRect::MakeXYWH(50, 50, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
Mike Reedc1f77742016-12-09 09:00:50 -05001145 kUnion_SkClipOp, true);
Robert Phillips806be2d2017-06-28 15:23:59 -04001146 uint32_t genIDD = stack.getTopmostGenID();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001147
1148
csmartdaltoncbecb082016-07-22 08:59:08 -07001149#define IXYWH SkIRect::MakeXYWH
1150#define XYWH SkRect::MakeXYWH
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001151
csmartdaltoncbecb082016-07-22 08:59:08 -07001152 SkIRect stackBounds = IXYWH(0, 0, 76, 76);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001153
1154 // The base test is to test each rect in two ways:
1155 // 1) The box dimensions. (Should reduce to "all in", no elements).
1156 // 2) A bit over the box dimensions.
1157 // In the case 2, test that the generation id is what is expected.
1158 // The rects are of fractional size so that case 2 never gets optimized to an empty element
1159 // list.
1160
1161 // Not passing in tighter bounds is tested for consistency.
1162 static const struct SUPPRESS_VISIBILITY_WARNING {
csmartdaltoncbecb082016-07-22 08:59:08 -07001163 SkRect testBounds;
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001164 int reducedClipCount;
Brian Salomonc3833b42018-07-09 18:23:58 +00001165 uint32_t reducedGenID;
csmartdaltoncbecb082016-07-22 08:59:08 -07001166 InitialState initialState;
1167 SkIRect clipIRect;
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001168 // parameter.
1169 } testCases[] = {
csmartdalton77f2fae2016-08-08 09:55:06 -07001170
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001171 // Rect A.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001172 { XYWH(0, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 0, 25, 25) },
1173 { 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 -06001174 { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::InitialState::kAllOut, IXYWH(0, 0, 26, 26)},
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001175
1176 // Rect B.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001177 { XYWH(50, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 25, 25) },
1178 { XYWH(50, 0, 25.3f, 25.3f), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 26, 26) },
csmartdalton77f2fae2016-08-08 09:55:06 -07001179 { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::InitialState::kAllOut, IXYWH(50, 0, 26, 27) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001180
1181 // Rect C.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001182 { XYWH(0, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 50, 25, 25) },
1183 { 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 -07001184 { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::InitialState::kAllOut, IXYWH(0, 50, 27, 26) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001185
1186 // Rect D.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001187 { XYWH(50, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 25, 25)},
1188 { XYWH(50.3f, 50.3f, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 26, 26)},
csmartdalton77f2fae2016-08-08 09:55:06 -07001189 { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(50, 50, 26, 26)},
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001190
1191 // Other tests:
csmartdalton77f2fae2016-08-08 09:55:06 -07001192 { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::InitialState::kAllOut, stackBounds },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001193
1194 // Rect in the middle, touches none.
csmartdalton8d3f92a2016-08-17 09:39:38 -07001195 { 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 +00001196
1197 // Rect in the middle, touches all the rects. GenID is the last rect.
csmartdalton77f2fae2016-08-08 09:55:06 -07001198 { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(24, 24, 27, 27) },
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001199 };
1200
1201#undef XYWH
csmartdaltoncbecb082016-07-22 08:59:08 -07001202#undef IXYWH
Brian Salomon14471772017-12-05 10:35:15 -05001203 auto context = GrContext::MakeMock(nullptr);
Chris Dalton4c458b12018-06-16 17:22:59 -06001204 const GrCaps* caps = context->contextPriv().caps();
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001205
1206 for (size_t i = 0; i < SK_ARRAY_COUNT(testCases); ++i) {
Brian Salomon14471772017-12-05 10:35:15 -05001207 const GrReducedClip reduced(stack, testCases[i].testBounds, caps);
Chris Dalton79471932017-10-27 01:50:57 -06001208 REPORTER_ASSERT(reporter, reduced.maskElements().count() ==
1209 testCases[i].reducedClipCount);
Brian Salomonc3833b42018-07-09 18:23:58 +00001210 SkASSERT(reduced.maskElements().count() == testCases[i].reducedClipCount);
1211 if (reduced.maskElements().count()) {
1212 REPORTER_ASSERT(reporter, reduced.maskGenID() == testCases[i].reducedGenID);
1213 SkASSERT(reduced.maskGenID() == testCases[i].reducedGenID);
csmartdalton8d3f92a2016-08-17 09:39:38 -07001214 }
csmartdalton77f2fae2016-08-08 09:55:06 -07001215 REPORTER_ASSERT(reporter, reduced.initialState() == testCases[i].initialState);
Brian Salomonc3833b42018-07-09 18:23:58 +00001216 SkASSERT(reduced.initialState() == testCases[i].initialState);
Chris Dalton79471932017-10-27 01:50:57 -06001217 REPORTER_ASSERT(reporter, reduced.hasScissor());
Brian Salomonc3833b42018-07-09 18:23:58 +00001218 SkASSERT(reduced.hasScissor());
Chris Dalton79471932017-10-27 01:50:57 -06001219 REPORTER_ASSERT(reporter, reduced.scissor() == testCases[i].clipIRect);
Brian Salomonc3833b42018-07-09 18:23:58 +00001220 SkASSERT(reduced.scissor() == testCases[i].clipIRect);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001221 }
1222 }
1223}
1224
1225static void test_reduced_clip_stack_no_aa_crash(skiatest::Reporter* reporter) {
1226 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001227 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 100, 100), kReplace_SkClipOp);
1228 stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 50, 50), kReplace_SkClipOp);
csmartdaltoncbecb082016-07-22 08:59:08 -07001229 SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001230
Brian Salomon14471772017-12-05 10:35:15 -05001231 auto context = GrContext::MakeMock(nullptr);
Chris Dalton4c458b12018-06-16 17:22:59 -06001232 const GrCaps* caps = context->contextPriv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001233
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001234 // At the time, this would crash.
Brian Salomon14471772017-12-05 10:35:15 -05001235 const GrReducedClip reduced(stack, bounds, caps);
Chris Dalton79471932017-10-27 01:50:57 -06001236 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty());
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001237}
1238
csmartdaltoncbecb082016-07-22 08:59:08 -07001239enum class ClipMethod {
1240 kSkipDraw,
1241 kIgnoreClip,
1242 kScissor,
1243 kAAElements
1244};
1245
1246static void test_aa_query(skiatest::Reporter* reporter, const SkString& testName,
1247 const SkClipStack& stack, const SkMatrix& queryXform,
1248 const SkRect& preXformQuery, ClipMethod expectedMethod,
1249 int numExpectedElems = 0) {
Brian Salomon14471772017-12-05 10:35:15 -05001250 auto context = GrContext::MakeMock(nullptr);
Chris Dalton4c458b12018-06-16 17:22:59 -06001251 const GrCaps* caps = context->contextPriv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001252
csmartdaltoncbecb082016-07-22 08:59:08 -07001253 SkRect queryBounds;
1254 queryXform.mapRect(&queryBounds, preXformQuery);
Brian Salomon14471772017-12-05 10:35:15 -05001255 const GrReducedClip reduced(stack, queryBounds, caps);
csmartdaltoncbecb082016-07-22 08:59:08 -07001256
1257 SkClipStack::BoundsType stackBoundsType;
1258 SkRect stackBounds;
1259 stack.getBounds(&stackBounds, &stackBoundsType);
1260
1261 switch (expectedMethod) {
1262 case ClipMethod::kSkipDraw:
1263 SkASSERT(0 == numExpectedElems);
Brian Salomon1c80e992018-01-29 09:50:47 -05001264 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty(), testName.c_str());
1265 REPORTER_ASSERT(reporter,
1266 GrReducedClip::InitialState::kAllOut == reduced.initialState(),
1267 testName.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001268 return;
1269 case ClipMethod::kIgnoreClip:
1270 SkASSERT(0 == numExpectedElems);
Brian Salomon1c80e992018-01-29 09:50:47 -05001271 REPORTER_ASSERT(
1272 reporter,
1273 !reduced.hasScissor() || GrClip::IsInsideClip(reduced.scissor(), queryBounds),
1274 testName.c_str());
1275 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty(), testName.c_str());
1276 REPORTER_ASSERT(reporter,
1277 GrReducedClip::InitialState::kAllIn == reduced.initialState(),
1278 testName.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001279 return;
1280 case ClipMethod::kScissor: {
1281 SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType);
1282 SkASSERT(0 == numExpectedElems);
1283 SkIRect expectedScissor;
1284 stackBounds.round(&expectedScissor);
Brian Salomon1c80e992018-01-29 09:50:47 -05001285 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty(), testName.c_str());
1286 REPORTER_ASSERT(reporter, reduced.hasScissor(), testName.c_str());
1287 REPORTER_ASSERT(reporter, expectedScissor == reduced.scissor(), testName.c_str());
1288 REPORTER_ASSERT(reporter,
1289 GrReducedClip::InitialState::kAllIn == reduced.initialState(),
1290 testName.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001291 return;
1292 }
1293 case ClipMethod::kAAElements: {
1294 SkIRect expectedClipIBounds = GrClip::GetPixelIBounds(queryBounds);
1295 if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
1296 SkAssertResult(expectedClipIBounds.intersect(GrClip::GetPixelIBounds(stackBounds)));
1297 }
Brian Salomon1c80e992018-01-29 09:50:47 -05001298 REPORTER_ASSERT(reporter, numExpectedElems == reduced.maskElements().count(),
1299 testName.c_str());
1300 REPORTER_ASSERT(reporter, reduced.hasScissor(), testName.c_str());
1301 REPORTER_ASSERT(reporter, expectedClipIBounds == reduced.scissor(), testName.c_str());
1302 REPORTER_ASSERT(reporter,
1303 reduced.maskElements().isEmpty() || reduced.maskRequiresAA(),
1304 testName.c_str());
csmartdaltoncbecb082016-07-22 08:59:08 -07001305 break;
1306 }
1307 }
1308}
1309
1310static void test_reduced_clip_stack_aa(skiatest::Reporter* reporter) {
1311 constexpr SkScalar IL = 2, IT = 1, IR = 6, IB = 7; // Pixel aligned rect.
1312 constexpr SkScalar L = 2.2f, T = 1.7f, R = 5.8f, B = 7.3f; // Generic rect.
1313 constexpr SkScalar l = 3.3f, t = 2.8f, r = 4.7f, b = 6.2f; // Small rect contained in R.
1314
1315 SkRect alignedRect = {IL, IT, IR, IB};
1316 SkRect rect = {L, T, R, B};
1317 SkRect innerRect = {l, t, r, b};
1318
1319 SkMatrix m;
1320 m.setIdentity();
1321
1322 constexpr SkScalar kMinScale = 2.0001f;
1323 constexpr SkScalar kMaxScale = 3;
1324 constexpr int kNumIters = 8;
1325
1326 SkString name;
1327 SkRandom rand;
1328
1329 for (int i = 0; i < kNumIters; ++i) {
1330 // Pixel-aligned rect (iior=true).
1331 name.printf("Pixel-aligned rect test, iter %i", i);
1332 SkClipStack stack;
Mike Reedc1f77742016-12-09 09:00:50 -05001333 stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001334 test_aa_query(reporter, name, stack, m, {IL, IT, IR, IB}, ClipMethod::kIgnoreClip);
1335 test_aa_query(reporter, name, stack, m, {IL, IT-1, IR, IT}, ClipMethod::kSkipDraw);
csmartdalton77f2fae2016-08-08 09:55:06 -07001336 test_aa_query(reporter, name, stack, m, {IL, IT-2, IR, IB}, ClipMethod::kScissor);
csmartdaltoncbecb082016-07-22 08:59:08 -07001337
1338 // Rect (iior=true).
1339 name.printf("Rect test, iter %i", i);
1340 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001341 stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001342 test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kIgnoreClip);
1343 test_aa_query(reporter, name, stack, m, {L-.1f, T, L, B}, ClipMethod::kSkipDraw);
1344 test_aa_query(reporter, name, stack, m, {L-.1f, T, L+.1f, B}, ClipMethod::kAAElements, 1);
1345
1346 // Difference rect (iior=false, inside-out bounds).
1347 name.printf("Difference rect test, iter %i", i);
1348 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001349 stack.clipRect(rect, SkMatrix::I(), kDifference_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001350 test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kSkipDraw);
1351 test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T}, ClipMethod::kIgnoreClip);
1352 test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T+.1f}, ClipMethod::kAAElements, 1);
1353
1354 // Complex clip (iior=false, normal bounds).
1355 name.printf("Complex clip test, iter %i", i);
1356 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001357 stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true);
1358 stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001359 test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
1360 test_aa_query(reporter, name, stack, m, {r-.1f, t, R, b}, ClipMethod::kAAElements, 1);
1361 test_aa_query(reporter, name, stack, m, {r-.1f, t, R+.1f, b}, ClipMethod::kAAElements, 2);
1362 test_aa_query(reporter, name, stack, m, {r, t, R+.1f, b}, ClipMethod::kAAElements, 1);
1363 test_aa_query(reporter, name, stack, m, {r, t, R, b}, ClipMethod::kIgnoreClip);
1364 test_aa_query(reporter, name, stack, m, {R, T, R+.1f, B}, ClipMethod::kSkipDraw);
1365
1366 // Complex clip where outer rect is pixel aligned (iior=false, normal bounds).
1367 name.printf("Aligned Complex clip test, iter %i", i);
1368 stack.reset();
Mike Reedc1f77742016-12-09 09:00:50 -05001369 stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true);
1370 stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, true);
csmartdaltoncbecb082016-07-22 08:59:08 -07001371 test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
1372 test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB}, ClipMethod::kAAElements, 1);
1373 test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB+.1f}, ClipMethod::kAAElements, 1);
1374 test_aa_query(reporter, name, stack, m, {l, b, r, IB+.1f}, ClipMethod::kAAElements, 0);
1375 test_aa_query(reporter, name, stack, m, {l, b, r, IB}, ClipMethod::kIgnoreClip);
1376 test_aa_query(reporter, name, stack, m, {IL, IB, IR, IB+.1f}, ClipMethod::kSkipDraw);
1377
1378 // Apply random transforms and try again. This ensures the clip stack reduction is hardened
1379 // against FP rounding error.
1380 SkScalar sx = rand.nextRangeScalar(kMinScale, kMaxScale);
1381 sx = SkScalarFloorToScalar(sx * alignedRect.width()) / alignedRect.width();
1382 SkScalar sy = rand.nextRangeScalar(kMinScale, kMaxScale);
1383 sy = SkScalarFloorToScalar(sy * alignedRect.height()) / alignedRect.height();
1384 SkScalar tx = SkScalarRoundToScalar(sx * alignedRect.x()) - sx * alignedRect.x();
1385 SkScalar ty = SkScalarRoundToScalar(sy * alignedRect.y()) - sy * alignedRect.y();
1386
1387 SkMatrix xform = SkMatrix::MakeScale(sx, sy);
1388 xform.postTranslate(tx, ty);
1389 xform.mapRect(&alignedRect);
1390 xform.mapRect(&rect);
1391 xform.mapRect(&innerRect);
1392 m.postConcat(xform);
1393 }
1394}
1395
Chris Dalton348060f2017-06-05 13:15:37 -06001396static void test_tiny_query_bounds_assertion_bug(skiatest::Reporter* reporter) {
1397 // https://bugs.chromium.org/p/skia/issues/detail?id=5990
1398 const SkRect clipBounds = SkRect::MakeXYWH(1.5f, 100, 1000, 1000);
1399
1400 SkClipStack rectStack;
1401 rectStack.clipRect(clipBounds, SkMatrix::I(), kIntersect_SkClipOp, true);
1402
1403 SkPath clipPath;
1404 clipPath.moveTo(clipBounds.left(), clipBounds.top());
1405 clipPath.quadTo(clipBounds.right(), clipBounds.top(),
1406 clipBounds.right(), clipBounds.bottom());
1407 clipPath.quadTo(clipBounds.left(), clipBounds.bottom(),
1408 clipBounds.left(), clipBounds.top());
1409 SkClipStack pathStack;
1410 pathStack.clipPath(clipPath, SkMatrix::I(), kIntersect_SkClipOp, true);
1411
Brian Salomon14471772017-12-05 10:35:15 -05001412 auto context = GrContext::MakeMock(nullptr);
Chris Dalton4c458b12018-06-16 17:22:59 -06001413 const GrCaps* caps = context->contextPriv().caps();
Brian Salomon14471772017-12-05 10:35:15 -05001414
Chris Dalton348060f2017-06-05 13:15:37 -06001415 for (const SkClipStack& stack : {rectStack, pathStack}) {
1416 for (SkRect queryBounds : {SkRect::MakeXYWH(53, 60, GrClip::kBoundsTolerance, 1000),
1417 SkRect::MakeXYWH(53, 60, GrClip::kBoundsTolerance/2, 1000),
1418 SkRect::MakeXYWH(53, 160, 1000, GrClip::kBoundsTolerance),
1419 SkRect::MakeXYWH(53, 160, 1000, GrClip::kBoundsTolerance/2)}) {
Brian Salomon14471772017-12-05 10:35:15 -05001420 const GrReducedClip reduced(stack, queryBounds, caps);
Chris Dalton79471932017-10-27 01:50:57 -06001421 REPORTER_ASSERT(reporter, !reduced.hasScissor());
1422 REPORTER_ASSERT(reporter, reduced.maskElements().isEmpty());
Chris Dalton348060f2017-06-05 13:15:37 -06001423 REPORTER_ASSERT(reporter,
1424 GrReducedClip::InitialState::kAllOut == reduced.initialState());
1425 }
1426 }
1427}
1428
tfarina@chromium.orge4fafb12013-12-12 21:11:12 +00001429DEF_TEST(ClipStack, reporter) {
reed@google.combdee9fc2011-02-22 20:17:43 +00001430 SkClipStack stack;
1431
robertphillips@google.com80214e22012-07-20 15:33:18 +00001432 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001433 assert_count(reporter, stack, 0);
1434
1435 static const SkIRect gRects[] = {
1436 { 0, 0, 100, 100 },
1437 { 25, 25, 125, 125 },
1438 { 0, 0, 1000, 1000 },
1439 { 0, 0, 75, 75 }
1440 };
1441 for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
Mike Reedc1f77742016-12-09 09:00:50 -05001442 stack.clipDevRect(gRects[i], kIntersect_SkClipOp);
reed@google.combdee9fc2011-02-22 20:17:43 +00001443 }
1444
1445 // all of the above rects should have been intersected, leaving only 1 rect
robertphillips@google.com80214e22012-07-20 15:33:18 +00001446 SkClipStack::B2TIter iter(stack);
bsalomon@google.com8182fa02012-12-04 14:06:06 +00001447 const SkClipStack::Element* element = iter.next();
epoger@google.com2047f002011-05-17 17:36:59 +00001448 SkRect answer;
1449 answer.iset(25, 25, 75, 75);
reed@google.combdee9fc2011-02-22 20:17:43 +00001450
bsalomon49f085d2014-09-05 13:34:00 -07001451 REPORTER_ASSERT(reporter, element);
Brian Salomonf3b46e52017-08-30 11:37:57 -04001452 REPORTER_ASSERT(reporter,
1453 SkClipStack::Element::DeviceSpaceType::kRect == element->getDeviceSpaceType());
Mike Reedc1f77742016-12-09 09:00:50 -05001454 REPORTER_ASSERT(reporter, kIntersect_SkClipOp == element->getOp());
Brian Salomonf3b46e52017-08-30 11:37:57 -04001455 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == answer);
reed@google.combdee9fc2011-02-22 20:17:43 +00001456 // now check that we only had one in our iterator
1457 REPORTER_ASSERT(reporter, !iter.next());
1458
1459 stack.reset();
robertphillips@google.com80214e22012-07-20 15:33:18 +00001460 REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
reed@google.combdee9fc2011-02-22 20:17:43 +00001461 assert_count(reporter, stack, 0);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +00001462
1463 test_assign_and_comparison(reporter);
robertphillips@google.com80214e22012-07-20 15:33:18 +00001464 test_iterators(reporter);
Brian Salomonf3b46e52017-08-30 11:37:57 -04001465 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kRect);
1466 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kRRect);
1467 test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kPath);
robertphillips@google.comcc6493b2012-07-26 18:39:13 +00001468 test_isWideOpen(reporter);
robertphillips@google.com08eacc12012-08-02 12:49:00 +00001469 test_rect_merging(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001470 test_rect_replace(reporter);
junov@chromium.orgedf32d52012-12-10 14:57:54 +00001471 test_rect_inverse_fill(reporter);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +00001472 test_path_replace(reporter);
junov@chromium.org8cdf0f52012-12-12 17:58:15 +00001473 test_quickContains(reporter);
csmartdaltond50e2402016-07-22 08:39:06 -07001474 test_invfill_diff_bug(reporter);
Brian Osmanc7ad40f2018-05-31 14:27:17 -04001475
bsalomon@google.comedb26fd2012-11-28 14:42:41 +00001476 test_reduced_clip_stack(reporter);
Brian Salomonc3833b42018-07-09 18:23:58 +00001477 test_reduced_clip_stack_genid(reporter);
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +00001478 test_reduced_clip_stack_no_aa_crash(reporter);
csmartdaltoncbecb082016-07-22 08:59:08 -07001479 test_reduced_clip_stack_aa(reporter);
Chris Dalton348060f2017-06-05 13:15:37 -06001480 test_tiny_query_bounds_assertion_bug(reporter);
reed@google.combdee9fc2011-02-22 20:17:43 +00001481}
Brian Salomon19f0ed52017-01-06 13:54:58 -05001482
1483//////////////////////////////////////////////////////////////////////////////
1484
Robert Phillips875218e2017-02-24 08:37:13 -05001485sk_sp<GrTextureProxy> GrClipStackClip::testingOnly_createClipMask(GrContext* context) const {
Brian Salomon19f0ed52017-01-06 13:54:58 -05001486 const GrReducedClip reducedClip(*fStack, SkRect::MakeWH(512, 512), 0);
Brian Osman5d034742017-09-11 13:38:55 -04001487 return this->createSoftwareClipMask(context, reducedClip, nullptr);
Brian Salomon19f0ed52017-01-06 13:54:58 -05001488}
1489
1490// Verify that clip masks are freed up when the clip state that generated them goes away.
1491DEF_GPUTEST_FOR_ALL_CONTEXTS(ClipMaskCache, reporter, ctxInfo) {
1492 // This test uses resource key tags which only function in debug builds.
1493#ifdef SK_DEBUG
1494 GrContext* context = ctxInfo.grContext();
1495 SkClipStack stack;
1496
1497 SkPath path;
1498 path.addCircle(10, 10, 8);
1499 path.addCircle(15, 15, 8);
1500 path.setFillType(SkPath::kEvenOdd_FillType);
1501
Brian Salomonc3833b42018-07-09 18:23:58 +00001502 static const char* kTag = GrClipStackClip::kMaskTestTag;
Robert Phillips6be756b2018-01-16 15:07:54 -05001503 GrResourceCache* cache = context->contextPriv().getResourceCache();
Brian Salomon19f0ed52017-01-06 13:54:58 -05001504
1505 static constexpr int kN = 5;
1506
1507 for (int i = 0; i < kN; ++i) {
1508 SkMatrix m;
1509 m.setTranslate(0.5, 0.5);
1510 stack.save();
1511 stack.clipPath(path, m, SkClipOp::kIntersect, true);
Robert Phillips875218e2017-02-24 08:37:13 -05001512 sk_sp<GrTextureProxy> mask = GrClipStackClip(&stack).testingOnly_createClipMask(context);
Robert Phillips6be756b2018-01-16 15:07:54 -05001513 mask->instantiate(context->contextPriv().resourceProvider());
Brian Salomonfd98c2c2018-07-31 17:25:29 -04001514 GrTexture* tex = mask->peekTexture();
Robert Phillips875218e2017-02-24 08:37:13 -05001515 REPORTER_ASSERT(reporter, 0 == strcmp(tex->getUniqueKey().tag(), kTag));
Brian Salomon19f0ed52017-01-06 13:54:58 -05001516 // Make sure mask isn't pinned in cache.
1517 mask.reset(nullptr);
1518 context->flush();
1519 REPORTER_ASSERT(reporter, i + 1 == cache->countUniqueKeysWithTag(kTag));
1520 }
1521
1522 for (int i = 0; i < kN; ++i) {
1523 stack.restore();
1524 cache->purgeAsNeeded();
1525 REPORTER_ASSERT(reporter, kN - (i + 1) == cache->countUniqueKeysWithTag(kTag));
1526 }
1527#endif
1528}
1529
Mike Reed3726a4a2017-01-19 11:36:41 -05001530DEF_GPUTEST_FOR_ALL_CONTEXTS(canvas_private_clipRgn, reporter, ctxInfo) {
1531 GrContext* context = ctxInfo.grContext();
1532
1533 const int w = 10;
1534 const int h = 10;
Brian Salomon8996e182017-07-05 17:01:48 -04001535 SkImageInfo info = SkImageInfo::Make(w, h, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
Mike Reed3726a4a2017-01-19 11:36:41 -05001536 sk_sp<SkSurface> surf = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info);
1537 SkCanvas* canvas = surf->getCanvas();
1538 SkRegion rgn;
1539
1540 canvas->temporary_internal_getRgnClip(&rgn);
1541 REPORTER_ASSERT(reporter, rgn.isRect());
1542 REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeWH(w, h));
1543
1544 canvas->save();
1545 canvas->clipRect(SkRect::MakeWH(5, 5), kDifference_SkClipOp);
1546 canvas->temporary_internal_getRgnClip(&rgn);
1547 REPORTER_ASSERT(reporter, rgn.isComplex());
1548 REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeWH(w, h));
1549 canvas->restore();
1550
1551 canvas->save();
1552 canvas->clipRRect(SkRRect::MakeOval(SkRect::MakeLTRB(3, 3, 7, 7)));
1553 canvas->temporary_internal_getRgnClip(&rgn);
1554 REPORTER_ASSERT(reporter, rgn.isComplex());
1555 REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeLTRB(3, 3, 7, 7));
1556 canvas->restore();
1557}