blob: 8d49af078b6a410830b59a0592a40a7ec9b3d24e [file] [log] [blame]
bsalomon@google.com170bd792012-12-05 22:26:11 +00001/*
csmartdalton77f2fae2016-08-08 09:55:06 -07002 * Copyright 2016 Google Inc.
bsalomon@google.com170bd792012-12-05 22:26:11 +00003 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrReducedClip.h"
9
csmartdaltonbde96c62016-08-31 12:54:46 -070010#include "GrAppliedClip.h"
csmartdaltoncbecb082016-07-22 08:59:08 -070011#include "GrClip.h"
csmartdaltonbde96c62016-08-31 12:54:46 -070012#include "GrColor.h"
13#include "GrContextPriv.h"
Brian Osman11052242016-10-27 14:47:55 -040014#include "GrRenderTargetContext.h"
15#include "GrRenderTargetContextPriv.h"
csmartdaltonbde96c62016-08-31 12:54:46 -070016#include "GrDrawingManager.h"
17#include "GrFixedClip.h"
18#include "GrPathRenderer.h"
csmartdaltonc633abb2016-11-01 08:55:55 -070019#include "GrStencilSettings.h"
csmartdaltonbde96c62016-08-31 12:54:46 -070020#include "GrStyle.h"
21#include "GrUserStencilSettings.h"
Mike Reedebfce6d2016-12-12 10:02:12 -050022#include "SkClipOpPriv.h"
Chris Daltond29e0da2017-11-01 11:40:58 -060023#include "effects/GrConvexPolyEffect.h"
24#include "effects/GrRRectEffect.h"
csmartdaltoncbecb082016-07-22 08:59:08 -070025
csmartdalton5ecbbbe2016-08-23 13:26:40 -070026/**
27 * There are plenty of optimizations that could be added here. Maybe flips could be folded into
28 * earlier operations. Or would inserting flips and reversing earlier ops ever be a win? Perhaps
29 * for the case where the bounds are kInsideOut_BoundsType. We could restrict earlier operations
30 * based on later intersect operations, and perhaps remove intersect-rects. We could optionally
31 * take a rect in case the caller knows a bound on what is to be drawn through this clip.
32 */
csmartdaltonbf4a8f92016-09-06 10:01:06 -070033GrReducedClip::GrReducedClip(const SkClipStack& stack, const SkRect& queryBounds,
Chris Daltond29e0da2017-11-01 11:40:58 -060034 int maxWindowRectangles, int maxAnalyticFPs)
35 : fMaxWindowRectangles(maxWindowRectangles)
36 , fMaxAnalyticFPs(maxAnalyticFPs) {
csmartdaltoncbecb082016-07-22 08:59:08 -070037 SkASSERT(!queryBounds.isEmpty());
Chris Daltond29e0da2017-11-01 11:40:58 -060038 SkASSERT(fMaxWindowRectangles <= GrWindowRectangles::kMaxWindows);
Chris Dalton79471932017-10-27 01:50:57 -060039 fHasScissor = false;
40 fAAClipRectGenID = SK_InvalidGenID;
csmartdaltoncbecb082016-07-22 08:59:08 -070041
bsalomon@google.com170bd792012-12-05 22:26:11 +000042 if (stack.isWideOpen()) {
csmartdalton77f2fae2016-08-08 09:55:06 -070043 fInitialState = InitialState::kAllIn;
44 return;
bsalomon@google.com170bd792012-12-05 22:26:11 +000045 }
46
47 SkClipStack::BoundsType stackBoundsType;
48 SkRect stackBounds;
49 bool iior;
50 stack.getBounds(&stackBounds, &stackBoundsType, &iior);
51
Chris Dalton348060f2017-06-05 13:15:37 -060052 if (GrClip::IsOutsideClip(stackBounds, queryBounds)) {
csmartdaltoncbecb082016-07-22 08:59:08 -070053 bool insideOut = SkClipStack::kInsideOut_BoundsType == stackBoundsType;
csmartdalton77f2fae2016-08-08 09:55:06 -070054 fInitialState = insideOut ? InitialState::kAllIn : InitialState::kAllOut;
55 return;
bsalomon@google.com170bd792012-12-05 22:26:11 +000056 }
57
csmartdaltoncbecb082016-07-22 08:59:08 -070058 if (iior) {
59 // "Is intersection of rects" means the clip is a single rect indicated by the stack bounds.
60 // This should only be true if aa/non-aa status matches among all elements.
61 SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType);
62 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
63 if (!iter.prev()->isAA() || GrClip::IsPixelAligned(stackBounds)) {
Chris Dalton79471932017-10-27 01:50:57 -060064 // The clip is a non-aa rect. Here we just implement the entire thing using fScissor.
Mike Kleine26062a2017-10-31 20:56:54 +000065 stackBounds.round(&fScissor);
Chris Dalton79471932017-10-27 01:50:57 -060066 fHasScissor = true;
67 fInitialState = fScissor.isEmpty() ? InitialState::kAllOut : InitialState::kAllIn;
csmartdalton77f2fae2016-08-08 09:55:06 -070068 return;
csmartdaltoncbecb082016-07-22 08:59:08 -070069 }
70 if (GrClip::IsInsideClip(stackBounds, queryBounds)) {
csmartdalton77f2fae2016-08-08 09:55:06 -070071 fInitialState = InitialState::kAllIn;
72 return;
csmartdaltoncbecb082016-07-22 08:59:08 -070073 }
74
csmartdaltond211e782016-08-15 11:17:19 -070075 SkRect tightBounds;
76 SkAssertResult(tightBounds.intersect(stackBounds, queryBounds));
Chris Dalton79471932017-10-27 01:50:57 -060077 fScissor = GrClip::GetPixelIBounds(tightBounds);
78 if (fScissor.isEmpty()) {
Chris Dalton348060f2017-06-05 13:15:37 -060079 fInitialState = InitialState::kAllOut;
80 return;
81 }
Chris Dalton79471932017-10-27 01:50:57 -060082 fHasScissor = true;
csmartdaltoncbecb082016-07-22 08:59:08 -070083
Chris Dalton79471932017-10-27 01:50:57 -060084 fAAClipRect = stackBounds;
85 fAAClipRectGenID = stack.getTopmostGenID();
86 SkASSERT(SK_InvalidGenID != fAAClipRectGenID);
csmartdalton77f2fae2016-08-08 09:55:06 -070087
Chris Dalton79471932017-10-27 01:50:57 -060088 fInitialState = InitialState::kAllIn;
89 } else {
90 SkRect tighterQuery = queryBounds;
91 if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
92 // Tighten the query by introducing a new clip at the stack's pixel boundaries. (This
93 // new clip will be enforced by the scissor.)
94 SkAssertResult(tighterQuery.intersect(GrClip::GetPixelBounds(stackBounds)));
95 }
96
97 fScissor = GrClip::GetPixelIBounds(tighterQuery);
98 if (fScissor.isEmpty()) {
99 fInitialState = InitialState::kAllOut;
100 return;
101 }
102 fHasScissor = true;
103
Chris Daltond29e0da2017-11-01 11:40:58 -0600104 // Now that we have determined the bounds to use and filtered out the trivial cases, call
105 // the helper that actually walks the stack.
106 this->walkStack(stack, tighterQuery);
csmartdaltoncbecb082016-07-22 08:59:08 -0700107 }
108
Chris Daltond29e0da2017-11-01 11:40:58 -0600109 if (SK_InvalidGenID != fAAClipRectGenID && // Is there an AA clip rect?
110 ClipResult::kNotClipped == this->addAnalyticFP(fAAClipRect, Invert::kNo, true)) {
Chris Dalton79471932017-10-27 01:50:57 -0600111 if (fMaskElements.isEmpty()) {
112 // Use a replace since it is faster than intersect.
113 fMaskElements.addToHead(fAAClipRect, SkMatrix::I(), kReplace_SkClipOp, true /*doAA*/);
114 fInitialState = InitialState::kAllOut;
115 } else {
116 fMaskElements.addToTail(fAAClipRect, SkMatrix::I(), kIntersect_SkClipOp, true /*doAA*/);
117 }
118 fMaskRequiresAA = true;
119 fMaskGenID = fAAClipRectGenID;
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700120 }
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700121}
122
Chris Daltond29e0da2017-11-01 11:40:58 -0600123void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBounds) {
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700124 // walk backwards until we get to:
125 // a) the beginning
126 // b) an operation that is known to make the bounds all inside/outside
127 // c) a replace operation
128
129 enum class InitialTriState {
130 kUnknown = -1,
131 kAllIn = (int)GrReducedClip::InitialState::kAllIn,
132 kAllOut = (int)GrReducedClip::InitialState::kAllOut
133 } initialTriState = InitialTriState::kUnknown;
134
135 // During our backwards walk, track whether we've seen ops that either grow or shrink the clip.
136 // TODO: track these per saved clip so that we can consider them on the forward pass.
137 bool embiggens = false;
138 bool emsmallens = false;
139
140 // We use a slightly relaxed set of query bounds for element containment tests. This is to
141 // account for floating point rounding error that may have occurred during coord transforms.
142 SkRect relaxedQueryBounds = queryBounds.makeInset(GrClip::kBoundsTolerance,
143 GrClip::kBoundsTolerance);
Chris Dalton69824002017-10-31 00:37:52 -0600144 if (relaxedQueryBounds.isEmpty()) {
145 relaxedQueryBounds = queryBounds;
146 }
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700147
148 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
149 int numAAElements = 0;
150 while (InitialTriState::kUnknown == initialTriState) {
151 const Element* element = iter.prev();
152 if (nullptr == element) {
153 initialTriState = InitialTriState::kAllIn;
154 break;
155 }
156 if (SkClipStack::kEmptyGenID == element->getGenID()) {
157 initialTriState = InitialTriState::kAllOut;
158 break;
159 }
160 if (SkClipStack::kWideOpenGenID == element->getGenID()) {
161 initialTriState = InitialTriState::kAllIn;
162 break;
163 }
164
165 bool skippable = false;
166 bool isFlip = false; // does this op just flip the in/out state of every point in the bounds
167
168 switch (element->getOp()) {
Mike Reedc1f77742016-12-09 09:00:50 -0500169 case kDifference_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700170 // check if the shape subtracted either contains the entire bounds (and makes
171 // the clip empty) or is outside the bounds and therefore can be skipped.
172 if (element->isInverseFilled()) {
173 if (element->contains(relaxedQueryBounds)) {
174 skippable = true;
175 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
176 initialTriState = InitialTriState::kAllOut;
177 skippable = true;
178 }
179 } else {
180 if (element->contains(relaxedQueryBounds)) {
181 initialTriState = InitialTriState::kAllOut;
182 skippable = true;
183 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
184 skippable = true;
Chris Dalton79471932017-10-27 01:50:57 -0600185 } else if (!embiggens) {
Chris Daltond29e0da2017-11-01 11:40:58 -0600186 ClipResult result = this->clipOutsideElement(element);
Chris Dalton79471932017-10-27 01:50:57 -0600187 if (ClipResult::kMadeEmpty == result) {
188 return;
189 }
190 skippable = (ClipResult::kClipped == result);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700191 }
192 }
193 if (!skippable) {
194 emsmallens = true;
195 }
196 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500197 case kIntersect_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700198 // check if the shape intersected contains the entire bounds and therefore can
199 // be skipped or it is outside the entire bounds and therefore makes the clip
200 // empty.
201 if (element->isInverseFilled()) {
202 if (element->contains(relaxedQueryBounds)) {
203 initialTriState = InitialTriState::kAllOut;
204 skippable = true;
205 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
206 skippable = true;
207 }
208 } else {
209 if (element->contains(relaxedQueryBounds)) {
210 skippable = true;
211 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
212 initialTriState = InitialTriState::kAllOut;
213 skippable = true;
Chris Dalton79471932017-10-27 01:50:57 -0600214 } else if (!embiggens) {
215 ClipResult result = this->clipInsideElement(element);
216 if (ClipResult::kMadeEmpty == result) {
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700217 return;
218 }
Chris Dalton79471932017-10-27 01:50:57 -0600219 skippable = (ClipResult::kClipped == result);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700220 }
221 }
222 if (!skippable) {
223 emsmallens = true;
224 }
225 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500226 case kUnion_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700227 // If the union-ed shape contains the entire bounds then after this element
228 // the bounds is entirely inside the clip. If the union-ed shape is outside the
229 // bounds then this op can be skipped.
230 if (element->isInverseFilled()) {
231 if (element->contains(relaxedQueryBounds)) {
232 skippable = true;
233 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
234 initialTriState = InitialTriState::kAllIn;
235 skippable = true;
236 }
237 } else {
238 if (element->contains(relaxedQueryBounds)) {
239 initialTriState = InitialTriState::kAllIn;
240 skippable = true;
241 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
242 skippable = true;
243 }
244 }
245 if (!skippable) {
246 embiggens = true;
247 }
248 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500249 case kXOR_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700250 // If the bounds is entirely inside the shape being xor-ed then the effect is
251 // to flip the inside/outside state of every point in the bounds. We may be
252 // able to take advantage of this in the forward pass. If the xor-ed shape
253 // doesn't intersect the bounds then it can be skipped.
254 if (element->isInverseFilled()) {
255 if (element->contains(relaxedQueryBounds)) {
256 skippable = true;
257 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
258 isFlip = true;
259 }
260 } else {
261 if (element->contains(relaxedQueryBounds)) {
262 isFlip = true;
263 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
264 skippable = true;
265 }
266 }
267 if (!skippable) {
268 emsmallens = embiggens = true;
269 }
270 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500271 case kReverseDifference_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700272 // When the bounds is entirely within the rev-diff shape then this behaves like xor
273 // and reverses every point inside the bounds. If the shape is completely outside
274 // the bounds then we know after this element is applied that the bounds will be
275 // all outside the current clip.B
276 if (element->isInverseFilled()) {
277 if (element->contains(relaxedQueryBounds)) {
278 initialTriState = InitialTriState::kAllOut;
279 skippable = true;
280 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
281 isFlip = true;
282 }
283 } else {
284 if (element->contains(relaxedQueryBounds)) {
285 isFlip = true;
286 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
287 initialTriState = InitialTriState::kAllOut;
288 skippable = true;
289 }
290 }
291 if (!skippable) {
292 emsmallens = embiggens = true;
293 }
294 break;
295
Mike Reedc1f77742016-12-09 09:00:50 -0500296 case kReplace_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700297 // Replace will always terminate our walk. We will either begin the forward walk
298 // at the replace op or detect here than the shape is either completely inside
299 // or completely outside the bounds. In this latter case it can be skipped by
300 // setting the correct value for initialTriState.
301 if (element->isInverseFilled()) {
302 if (element->contains(relaxedQueryBounds)) {
303 initialTriState = InitialTriState::kAllOut;
304 skippable = true;
305 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
306 initialTriState = InitialTriState::kAllIn;
307 skippable = true;
308 }
309 } else {
310 if (element->contains(relaxedQueryBounds)) {
311 initialTriState = InitialTriState::kAllIn;
312 skippable = true;
313 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
314 initialTriState = InitialTriState::kAllOut;
315 skippable = true;
Chris Dalton79471932017-10-27 01:50:57 -0600316 } else if (!embiggens) {
317 ClipResult result = this->clipInsideElement(element);
318 if (ClipResult::kMadeEmpty == result) {
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700319 return;
320 }
Chris Dalton79471932017-10-27 01:50:57 -0600321 if (ClipResult::kClipped == result) {
322 initialTriState = InitialTriState::kAllIn;
323 skippable = true;
324 }
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700325 }
326 }
327 if (!skippable) {
328 initialTriState = InitialTriState::kAllOut;
329 embiggens = emsmallens = true;
330 }
331 break;
332 default:
333 SkDEBUGFAIL("Unexpected op.");
334 break;
335 }
336 if (!skippable) {
Chris Dalton79471932017-10-27 01:50:57 -0600337 if (fMaskElements.isEmpty()) {
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700338 // This will be the last element. Record the stricter genID.
Chris Dalton79471932017-10-27 01:50:57 -0600339 fMaskGenID = element->getGenID();
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700340 }
341
342 // if it is a flip, change it to a bounds-filling rect
343 if (isFlip) {
Mike Reedc1f77742016-12-09 09:00:50 -0500344 SkASSERT(kXOR_SkClipOp == element->getOp() ||
345 kReverseDifference_SkClipOp == element->getOp());
Chris Dalton79471932017-10-27 01:50:57 -0600346 fMaskElements.addToHead(SkRect::Make(fScissor), SkMatrix::I(),
347 kReverseDifference_SkClipOp, false);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700348 } else {
Chris Dalton79471932017-10-27 01:50:57 -0600349 Element* newElement = fMaskElements.addToHead(*element);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700350 if (newElement->isAA()) {
351 ++numAAElements;
352 }
353 // Intersecting an inverse shape is the same as differencing the non-inverse shape.
354 // Replacing with an inverse shape is the same as setting initialState=kAllIn and
355 // differencing the non-inverse shape.
Mike Reedc1f77742016-12-09 09:00:50 -0500356 bool isReplace = kReplace_SkClipOp == newElement->getOp();
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700357 if (newElement->isInverseFilled() &&
Mike Reedc1f77742016-12-09 09:00:50 -0500358 (kIntersect_SkClipOp == newElement->getOp() || isReplace)) {
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700359 newElement->invertShapeFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500360 newElement->setOp(kDifference_SkClipOp);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700361 if (isReplace) {
362 SkASSERT(InitialTriState::kAllOut == initialTriState);
363 initialTriState = InitialTriState::kAllIn;
364 }
365 }
366 }
367 }
368 }
369
370 if ((InitialTriState::kAllOut == initialTriState && !embiggens) ||
371 (InitialTriState::kAllIn == initialTriState && !emsmallens)) {
Chris Dalton79471932017-10-27 01:50:57 -0600372 fMaskElements.reset();
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700373 numAAElements = 0;
374 } else {
Chris Dalton79471932017-10-27 01:50:57 -0600375 Element* element = fMaskElements.headIter().get();
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700376 while (element) {
377 bool skippable = false;
378 switch (element->getOp()) {
Mike Reedc1f77742016-12-09 09:00:50 -0500379 case kDifference_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700380 // subtracting from the empty set yields the empty set.
381 skippable = InitialTriState::kAllOut == initialTriState;
382 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500383 case kIntersect_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700384 // intersecting with the empty set yields the empty set
385 if (InitialTriState::kAllOut == initialTriState) {
386 skippable = true;
387 } else {
388 // We can clear to zero and then simply draw the clip element.
389 initialTriState = InitialTriState::kAllOut;
Mike Reedc1f77742016-12-09 09:00:50 -0500390 element->setOp(kReplace_SkClipOp);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700391 }
392 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500393 case kUnion_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700394 if (InitialTriState::kAllIn == initialTriState) {
395 // unioning the infinite plane with anything is a no-op.
396 skippable = true;
397 } else {
398 // unioning the empty set with a shape is the shape.
Mike Reedc1f77742016-12-09 09:00:50 -0500399 element->setOp(kReplace_SkClipOp);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700400 }
401 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500402 case kXOR_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700403 if (InitialTriState::kAllOut == initialTriState) {
404 // xor could be changed to diff in the kAllIn case, not sure it's a win.
Mike Reedc1f77742016-12-09 09:00:50 -0500405 element->setOp(kReplace_SkClipOp);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700406 }
407 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500408 case kReverseDifference_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700409 if (InitialTriState::kAllIn == initialTriState) {
410 // subtracting the whole plane will yield the empty set.
411 skippable = true;
412 initialTriState = InitialTriState::kAllOut;
413 } else {
414 // this picks up flips inserted in the backwards pass.
415 skippable = element->isInverseFilled() ?
416 GrClip::IsOutsideClip(element->getBounds(), queryBounds) :
417 element->contains(relaxedQueryBounds);
418 if (skippable) {
419 initialTriState = InitialTriState::kAllIn;
420 } else {
Mike Reedc1f77742016-12-09 09:00:50 -0500421 element->setOp(kReplace_SkClipOp);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700422 }
423 }
424 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500425 case kReplace_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700426 skippable = false; // we would have skipped it in the backwards walk if we
427 // could've.
428 break;
429 default:
430 SkDEBUGFAIL("Unexpected op.");
431 break;
432 }
433 if (!skippable) {
434 break;
435 } else {
436 if (element->isAA()) {
437 --numAAElements;
438 }
Chris Dalton79471932017-10-27 01:50:57 -0600439 fMaskElements.popHead();
440 element = fMaskElements.headIter().get();
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700441 }
442 }
443 }
Chris Dalton79471932017-10-27 01:50:57 -0600444 fMaskRequiresAA = numAAElements > 0;
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700445
446 SkASSERT(InitialTriState::kUnknown != initialTriState);
447 fInitialState = static_cast<GrReducedClip::InitialState>(initialTriState);
448}
449
Chris Dalton79471932017-10-27 01:50:57 -0600450GrReducedClip::ClipResult GrReducedClip::clipInsideElement(const Element* element) {
451 SkIRect elementIBounds;
452 if (!element->isAA()) {
453 element->getBounds().round(&elementIBounds);
454 } else {
455 elementIBounds = GrClip::GetPixelIBounds(element->getBounds());
456 }
457 SkASSERT(fHasScissor);
458 if (!fScissor.intersect(elementIBounds)) {
459 this->makeEmpty();
460 return ClipResult::kMadeEmpty;
461 }
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700462
Chris Dalton79471932017-10-27 01:50:57 -0600463 switch (element->getDeviceSpaceType()) {
464 case Element::DeviceSpaceType::kEmpty:
465 return ClipResult::kMadeEmpty;
466
467 case Element::DeviceSpaceType::kRect:
468 SkASSERT(element->getBounds() == element->getDeviceSpaceRect());
469 if (element->isAA()) {
470 if (SK_InvalidGenID == fAAClipRectGenID) { // No AA clip rect yet?
471 fAAClipRect = element->getDeviceSpaceRect();
472 // fAAClipRectGenID is the value we should use for fMaskGenID if we end up
473 // moving the AA clip rect into the mask. The mask GenID is simply the topmost
474 // element's GenID. And since we walk the stack backwards, this means it's just
475 // the first element we don't skip during our walk.
476 fAAClipRectGenID = fMaskElements.isEmpty() ? element->getGenID() : fMaskGenID;
477 SkASSERT(SK_InvalidGenID != fAAClipRectGenID);
478 } else if (!fAAClipRect.intersect(element->getDeviceSpaceRect())) {
479 this->makeEmpty();
480 return ClipResult::kMadeEmpty;
481 }
482 }
483 return ClipResult::kClipped;
484
485 case Element::DeviceSpaceType::kRRect:
Chris Daltond29e0da2017-11-01 11:40:58 -0600486 return this->addAnalyticFP(element->getDeviceSpaceRRect(), Invert::kNo,
487 element->isAA());
488
Chris Dalton79471932017-10-27 01:50:57 -0600489 case Element::DeviceSpaceType::kPath:
Chris Daltond29e0da2017-11-01 11:40:58 -0600490 return this->addAnalyticFP(element->getDeviceSpacePath(), Invert::kNo, element->isAA());
Chris Dalton79471932017-10-27 01:50:57 -0600491 }
492
493 SK_ABORT("Unexpected DeviceSpaceType");
494 return ClipResult::kNotClipped;
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700495}
496
Chris Daltond29e0da2017-11-01 11:40:58 -0600497GrReducedClip::ClipResult GrReducedClip::clipOutsideElement(const Element* element) {
Chris Dalton79471932017-10-27 01:50:57 -0600498 switch (element->getDeviceSpaceType()) {
499 case Element::DeviceSpaceType::kEmpty:
500 return ClipResult::kMadeEmpty;
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700501
Chris Dalton79471932017-10-27 01:50:57 -0600502 case Element::DeviceSpaceType::kRect:
Chris Daltond29e0da2017-11-01 11:40:58 -0600503 if (fWindowRects.count() < fMaxWindowRectangles) {
504 // Clip out the inside of every rect. We won't be able to entirely skip the AA ones,
505 // but it saves processing time.
506 this->addWindowRectangle(element->getDeviceSpaceRect(), element->isAA());
507 if (!element->isAA()) {
508 return ClipResult::kClipped;
509 }
510 }
511 return this->addAnalyticFP(element->getDeviceSpaceRect(), Invert::kYes,
512 element->isAA());
Chris Dalton79471932017-10-27 01:50:57 -0600513
514 case Element::DeviceSpaceType::kRRect: {
Chris Daltona47b5752017-11-01 16:40:48 +0000515 const SkRRect& clipRRect = element->getDeviceSpaceRRect();
Chris Daltond29e0da2017-11-01 11:40:58 -0600516 ClipResult clipResult = this->addAnalyticFP(clipRRect, Invert::kYes, element->isAA());
517 if (fWindowRects.count() >= fMaxWindowRectangles) {
518 return clipResult;
519 }
520
521 // Clip out the interiors of round rects with two window rectangles in the shape of a
522 // "plus". This doesn't let us skip the clip element, but still saves processing time.
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700523 SkVector insetTL = clipRRect.radii(SkRRect::kUpperLeft_Corner);
524 SkVector insetBR = clipRRect.radii(SkRRect::kLowerRight_Corner);
525 if (SkRRect::kComplex_Type == clipRRect.getType()) {
526 const SkVector& insetTR = clipRRect.radii(SkRRect::kUpperRight_Corner);
527 const SkVector& insetBL = clipRRect.radii(SkRRect::kLowerLeft_Corner);
528 insetTL.fX = SkTMax(insetTL.x(), insetBL.x());
529 insetTL.fY = SkTMax(insetTL.y(), insetTR.y());
530 insetBR.fX = SkTMax(insetBR.x(), insetTR.x());
531 insetBR.fY = SkTMax(insetBR.y(), insetBL.y());
532 }
533 const SkRect& bounds = clipRRect.getBounds();
534 if (insetTL.x() + insetBR.x() >= bounds.width() ||
535 insetTL.y() + insetBR.y() >= bounds.height()) {
Chris Daltond29e0da2017-11-01 11:40:58 -0600536 return clipResult; // The interior "plus" is empty.
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700537 }
538
539 SkRect horzRect = SkRect::MakeLTRB(bounds.left(), bounds.top() + insetTL.y(),
540 bounds.right(), bounds.bottom() - insetBR.y());
541 this->addWindowRectangle(horzRect, element->isAA());
Chris Daltond29e0da2017-11-01 11:40:58 -0600542
543 if (fWindowRects.count() < fMaxWindowRectangles) {
544 SkRect vertRect = SkRect::MakeLTRB(bounds.left() + insetTL.x(), bounds.top(),
545 bounds.right() - insetBR.x(), bounds.bottom());
546 this->addWindowRectangle(vertRect, element->isAA());
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700547 }
548
Chris Daltond29e0da2017-11-01 11:40:58 -0600549 return clipResult;
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700550 }
Chris Dalton79471932017-10-27 01:50:57 -0600551
552 case Element::DeviceSpaceType::kPath:
Chris Daltond29e0da2017-11-01 11:40:58 -0600553 return this->addAnalyticFP(element->getDeviceSpacePath(), Invert::kYes,
554 element->isAA());
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700555 }
Chris Dalton79471932017-10-27 01:50:57 -0600556
557 SK_ABORT("Unexpected DeviceSpaceType");
558 return ClipResult::kNotClipped;
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700559}
560
561inline void GrReducedClip::addWindowRectangle(const SkRect& elementInteriorRect, bool elementIsAA) {
562 SkIRect window;
563 if (!elementIsAA) {
564 elementInteriorRect.round(&window);
565 } else {
566 elementInteriorRect.roundIn(&window);
567 }
568 if (!window.isEmpty()) { // Skip very thin windows that round to zero or negative dimensions.
569 fWindowRects.addWindow(window);
570 }
571}
572
Chris Daltond29e0da2017-11-01 11:40:58 -0600573std::unique_ptr<GrFragmentProcessor> make_analytic_clip_fp(GrPrimitiveEdgeType edgeType,
574 const SkRect& deviceSpaceRect) {
575 return GrConvexPolyEffect::Make(edgeType, deviceSpaceRect);
576}
577
578std::unique_ptr<GrFragmentProcessor> make_analytic_clip_fp(GrPrimitiveEdgeType edgeType,
579 const SkRRect& deviceSpaceRRect) {
580 return GrRRectEffect::Make(edgeType, deviceSpaceRRect);
581}
582
583std::unique_ptr<GrFragmentProcessor> make_analytic_clip_fp(GrPrimitiveEdgeType edgeType,
584 const SkPath& deviceSpacePath) {
585 return GrConvexPolyEffect::Make(edgeType, deviceSpacePath);
586}
587
588template<typename T>
589inline GrReducedClip::ClipResult GrReducedClip::addAnalyticFP(const T& deviceSpaceShape,
590 Invert invert, bool aa) {
591 if (fAnalyticFPs.count() >= fMaxAnalyticFPs) {
592 return ClipResult::kNotClipped;
593 }
594
595 GrPrimitiveEdgeType edgeType;
596 if (Invert::kNo == invert) {
597 edgeType = aa ? kFillAA_GrProcessorEdgeType : kFillBW_GrProcessorEdgeType;
598 } else {
599 edgeType = aa ? kInverseFillAA_GrProcessorEdgeType : kInverseFillBW_GrProcessorEdgeType;
600 }
601
602 if (auto fp = make_analytic_clip_fp(edgeType, deviceSpaceShape)) {
603 fAnalyticFPs.push_back(std::move(fp));
604 return ClipResult::kClipped;
605 }
606
607 return ClipResult::kNotClipped;
608}
609
Chris Dalton79471932017-10-27 01:50:57 -0600610void GrReducedClip::makeEmpty() {
611 fHasScissor = false;
612 fAAClipRectGenID = SK_InvalidGenID;
613 fWindowRects.reset();
614 fMaskElements.reset();
615 fInitialState = InitialState::kAllOut;
bsalomon@google.com34cd70a2012-12-06 14:23:20 +0000616}
csmartdaltonbde96c62016-08-31 12:54:46 -0700617
618////////////////////////////////////////////////////////////////////////////////
619// Create a 8-bit clip mask in alpha
620
Brian Osman11052242016-10-27 14:47:55 -0400621static bool stencil_element(GrRenderTargetContext* rtc,
csmartdaltonbde96c62016-08-31 12:54:46 -0700622 const GrFixedClip& clip,
623 const GrUserStencilSettings* ss,
624 const SkMatrix& viewMatrix,
625 const SkClipStack::Element* element) {
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500626 GrAA aa = GrBoolToAA(element->isAA());
Brian Salomonf3b46e52017-08-30 11:37:57 -0400627 switch (element->getDeviceSpaceType()) {
Chris Dalton79471932017-10-27 01:50:57 -0600628 case SkClipStack::Element::DeviceSpaceType::kEmpty:
csmartdaltonbde96c62016-08-31 12:54:46 -0700629 SkDEBUGFAIL("Should never get here with an empty element.");
630 break;
Chris Dalton79471932017-10-27 01:50:57 -0600631 case SkClipStack::Element::DeviceSpaceType::kRect:
Brian Salomonf3b46e52017-08-30 11:37:57 -0400632 return rtc->priv().drawAndStencilRect(clip, ss, (SkRegion::Op)element->getOp(),
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500633 element->isInverseFilled(), aa, viewMatrix,
Brian Salomonf3b46e52017-08-30 11:37:57 -0400634 element->getDeviceSpaceRect());
csmartdaltonbde96c62016-08-31 12:54:46 -0700635 break;
636 default: {
637 SkPath path;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400638 element->asDeviceSpacePath(&path);
csmartdaltonbde96c62016-08-31 12:54:46 -0700639 if (path.isInverseFillType()) {
640 path.toggleInverseFillType();
641 }
642
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500643 return rtc->priv().drawAndStencilPath(clip, ss, (SkRegion::Op)element->getOp(),
644 element->isInverseFilled(), aa, viewMatrix, path);
csmartdaltonbde96c62016-08-31 12:54:46 -0700645 break;
646 }
647 }
648
649 return false;
650}
651
Brian Osman11052242016-10-27 14:47:55 -0400652static void draw_element(GrRenderTargetContext* rtc,
Brian Salomon82f44312017-01-11 13:42:54 -0500653 const GrClip& clip, // TODO: can this just always be WideOpen?
654 GrPaint&& paint,
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500655 GrAA aa,
csmartdaltonbde96c62016-08-31 12:54:46 -0700656 const SkMatrix& viewMatrix,
657 const SkClipStack::Element* element) {
csmartdaltonbde96c62016-08-31 12:54:46 -0700658 // TODO: Draw rrects directly here.
Brian Salomonf3b46e52017-08-30 11:37:57 -0400659 switch (element->getDeviceSpaceType()) {
Chris Dalton79471932017-10-27 01:50:57 -0600660 case SkClipStack::Element::DeviceSpaceType::kEmpty:
csmartdaltonbde96c62016-08-31 12:54:46 -0700661 SkDEBUGFAIL("Should never get here with an empty element.");
662 break;
Chris Dalton79471932017-10-27 01:50:57 -0600663 case SkClipStack::Element::DeviceSpaceType::kRect:
Brian Salomonf3b46e52017-08-30 11:37:57 -0400664 rtc->drawRect(clip, std::move(paint), aa, viewMatrix, element->getDeviceSpaceRect());
csmartdaltonbde96c62016-08-31 12:54:46 -0700665 break;
666 default: {
667 SkPath path;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400668 element->asDeviceSpacePath(&path);
csmartdaltonbde96c62016-08-31 12:54:46 -0700669 if (path.isInverseFillType()) {
670 path.toggleInverseFillType();
671 }
672
Brian Salomon82f44312017-01-11 13:42:54 -0500673 rtc->drawPath(clip, std::move(paint), aa, viewMatrix, path, GrStyle::SimpleFill());
csmartdaltonbde96c62016-08-31 12:54:46 -0700674 break;
675 }
676 }
677}
678
Brian Osman11052242016-10-27 14:47:55 -0400679bool GrReducedClip::drawAlphaClipMask(GrRenderTargetContext* rtc) const {
csmartdaltonbde96c62016-08-31 12:54:46 -0700680 // The texture may be larger than necessary, this rect represents the part of the texture
681 // we populate with a rasterization of the clip.
Chris Dalton79471932017-10-27 01:50:57 -0600682 GrFixedClip clip(SkIRect::MakeWH(fScissor.width(), fScissor.height()));
csmartdaltonbde96c62016-08-31 12:54:46 -0700683
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700684 if (!fWindowRects.empty()) {
Chris Dalton79471932017-10-27 01:50:57 -0600685 clip.setWindowRectangles(fWindowRects.makeOffset(-fScissor.left(), -fScissor.top()),
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700686 GrWindowRectsState::Mode::kExclusive);
687 }
688
csmartdaltonbde96c62016-08-31 12:54:46 -0700689 // The scratch texture that we are drawing into can be substantially larger than the mask. Only
690 // clear the part that we care about.
691 GrColor initialCoverage = InitialState::kAllIn == this->initialState() ? -1 : 0;
Brian Osman693a5402016-10-27 15:13:22 -0400692 rtc->priv().clear(clip, initialCoverage, true);
csmartdaltonbde96c62016-08-31 12:54:46 -0700693
694 // Set the matrix so that rendered clip elements are transformed to mask space from clip space.
695 SkMatrix translate;
Chris Dalton79471932017-10-27 01:50:57 -0600696 translate.setTranslate(SkIntToScalar(-fScissor.left()), SkIntToScalar(-fScissor.top()));
csmartdaltonbde96c62016-08-31 12:54:46 -0700697
698 // walk through each clip element and perform its set op
Chris Dalton79471932017-10-27 01:50:57 -0600699 for (ElementList::Iter iter(fMaskElements); iter.get(); iter.next()) {
csmartdaltonbde96c62016-08-31 12:54:46 -0700700 const Element* element = iter.get();
reed73603f32016-09-20 08:42:38 -0700701 SkRegion::Op op = (SkRegion::Op)element->getOp();
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500702 GrAA aa = GrBoolToAA(element->isAA());
csmartdaltonbde96c62016-08-31 12:54:46 -0700703 bool invert = element->isInverseFilled();
704 if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) {
705 // draw directly into the result with the stencil set to make the pixels affected
706 // by the clip shape be non-zero.
707 static constexpr GrUserStencilSettings kStencilInElement(
708 GrUserStencilSettings::StaticInit<
709 0xffff,
710 GrUserStencilTest::kAlways,
711 0xffff,
712 GrUserStencilOp::kReplace,
713 GrUserStencilOp::kReplace,
714 0xffff>()
715 );
Brian Osman11052242016-10-27 14:47:55 -0400716 if (!stencil_element(rtc, clip, &kStencilInElement, translate, element)) {
csmartdaltonbde96c62016-08-31 12:54:46 -0700717 return false;
718 }
719
720 // Draw to the exterior pixels (those with a zero stencil value).
721 static constexpr GrUserStencilSettings kDrawOutsideElement(
722 GrUserStencilSettings::StaticInit<
723 0x0000,
724 GrUserStencilTest::kEqual,
725 0xffff,
726 GrUserStencilOp::kZero,
727 GrUserStencilOp::kZero,
728 0xffff>()
729 );
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500730 if (!rtc->priv().drawAndStencilRect(clip, &kDrawOutsideElement, op, !invert, GrAA::kNo,
Chris Dalton79471932017-10-27 01:50:57 -0600731 translate, SkRect::Make(fScissor))) {
csmartdaltonbde96c62016-08-31 12:54:46 -0700732 return false;
733 }
734 } else {
735 // all the remaining ops can just be directly draw into the accumulation buffer
736 GrPaint paint;
csmartdaltonbde96c62016-08-31 12:54:46 -0700737 paint.setCoverageSetOpXPFactory(op, false);
738
Brian Salomon82f44312017-01-11 13:42:54 -0500739 draw_element(rtc, clip, std::move(paint), aa, translate, element);
csmartdaltonbde96c62016-08-31 12:54:46 -0700740 }
741 }
742
743 return true;
744}
745
746////////////////////////////////////////////////////////////////////////////////
747// Create a 1-bit clip mask in the stencil buffer.
748
749class StencilClip final : public GrClip {
750public:
Robert Phillips806be2d2017-06-28 15:23:59 -0400751 StencilClip(const SkIRect& scissorRect, uint32_t clipStackID)
Robert Phillipsa4f792d2017-06-28 08:40:11 -0400752 : fFixedClip(scissorRect)
753 , fClipStackID(clipStackID) {
754 }
755
csmartdaltonbde96c62016-08-31 12:54:46 -0700756 const GrFixedClip& fixedClip() const { return fFixedClip; }
757
Brian Salomon9a767722017-03-13 17:57:28 -0400758 void setWindowRectangles(const GrWindowRectangles& windows, GrWindowRectsState::Mode mode) {
759 fFixedClip.setWindowRectangles(windows, mode);
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700760 }
761
csmartdaltonbde96c62016-08-31 12:54:46 -0700762private:
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700763 bool quickContains(const SkRect&) const override {
csmartdaltonbde96c62016-08-31 12:54:46 -0700764 return false;
765 }
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700766 void getConservativeBounds(int width, int height, SkIRect* bounds, bool* iior) const override {
767 fFixedClip.getConservativeBounds(width, height, bounds, iior);
csmartdaltonbde96c62016-08-31 12:54:46 -0700768 }
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500769 bool isRRect(const SkRect& rtBounds, SkRRect* rr, GrAA*) const override {
csmartdaltonbde96c62016-08-31 12:54:46 -0700770 return false;
771 }
Brian Osman11052242016-10-27 14:47:55 -0400772 bool apply(GrContext* context, GrRenderTargetContext* renderTargetContext, bool useHWAA,
Brian Salomon97180af2017-03-14 13:42:58 -0400773 bool hasUserStencilSettings, GrAppliedClip* out, SkRect* bounds) const override {
774 if (!fFixedClip.apply(context, renderTargetContext, useHWAA, hasUserStencilSettings, out,
775 bounds)) {
csmartdaltonbde96c62016-08-31 12:54:46 -0700776 return false;
777 }
Robert Phillipsa4f792d2017-06-28 08:40:11 -0400778 out->addStencilClip(fClipStackID);
csmartdaltonbde96c62016-08-31 12:54:46 -0700779 return true;
780 }
781
782 GrFixedClip fFixedClip;
Robert Phillips806be2d2017-06-28 15:23:59 -0400783 uint32_t fClipStackID;
csmartdaltonbde96c62016-08-31 12:54:46 -0700784
785 typedef GrClip INHERITED;
786};
787
788bool GrReducedClip::drawStencilClipMask(GrContext* context,
Brian Salomon9a767722017-03-13 17:57:28 -0400789 GrRenderTargetContext* renderTargetContext) const {
csmartdaltonbde96c62016-08-31 12:54:46 -0700790 // We set the current clip to the bounds so that our recursive draws are scissored to them.
Chris Dalton79471932017-10-27 01:50:57 -0600791 StencilClip stencilClip(fScissor, this->maskGenID());
csmartdaltonbde96c62016-08-31 12:54:46 -0700792
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700793 if (!fWindowRects.empty()) {
Brian Salomon9a767722017-03-13 17:57:28 -0400794 stencilClip.setWindowRectangles(fWindowRects, GrWindowRectsState::Mode::kExclusive);
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700795 }
796
csmartdaltonbde96c62016-08-31 12:54:46 -0700797 bool initialState = InitialState::kAllIn == this->initialState();
Brian Osman693a5402016-10-27 15:13:22 -0400798 renderTargetContext->priv().clearStencilClip(stencilClip.fixedClip(), initialState);
csmartdaltonbde96c62016-08-31 12:54:46 -0700799
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500800 // walk through each clip element and perform its set op with the existing clip.
Chris Dalton79471932017-10-27 01:50:57 -0600801 for (ElementList::Iter iter(fMaskElements); iter.get(); iter.next()) {
csmartdaltonbde96c62016-08-31 12:54:46 -0700802 const Element* element = iter.get();
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500803 GrAAType aaType = GrAAType::kNone;
Brian Salomon7c8460e2017-05-12 11:36:10 -0400804 if (element->isAA() && GrFSAAType::kNone != renderTargetContext->fsaaType()) {
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500805 aaType = GrAAType::kMSAA;
806 }
csmartdaltonbde96c62016-08-31 12:54:46 -0700807
808 bool fillInverted = false;
809
810 // This will be used to determine whether the clip shape can be rendered into the
811 // stencil with arbitrary stencil settings.
812 GrPathRenderer::StencilSupport stencilSupport;
813
reed73603f32016-09-20 08:42:38 -0700814 SkRegion::Op op = (SkRegion::Op)element->getOp();
csmartdaltonbde96c62016-08-31 12:54:46 -0700815
816 GrPathRenderer* pr = nullptr;
817 SkPath clipPath;
Brian Salomonf3b46e52017-08-30 11:37:57 -0400818 if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) {
csmartdaltonbde96c62016-08-31 12:54:46 -0700819 stencilSupport = GrPathRenderer::kNoRestriction_StencilSupport;
820 fillInverted = false;
821 } else {
Brian Salomonf3b46e52017-08-30 11:37:57 -0400822 element->asDeviceSpacePath(&clipPath);
csmartdaltonbde96c62016-08-31 12:54:46 -0700823 fillInverted = clipPath.isInverseFillType();
824 if (fillInverted) {
825 clipPath.toggleInverseFillType();
826 }
827
828 GrShape shape(clipPath, GrStyle::SimpleFill());
829 GrPathRenderer::CanDrawPathArgs canDrawArgs;
Eric Karl5c779752017-05-08 12:02:07 -0700830 canDrawArgs.fCaps = context->caps();
Chris Daltondb91c6e2017-09-08 16:25:08 -0600831 canDrawArgs.fClipConservativeBounds = &stencilClip.fixedClip().scissorRect();
Brian Salomon9a767722017-03-13 17:57:28 -0400832 canDrawArgs.fViewMatrix = &SkMatrix::I();
csmartdaltonbde96c62016-08-31 12:54:46 -0700833 canDrawArgs.fShape = &shape;
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500834 canDrawArgs.fAAType = aaType;
csmartdaltonbde96c62016-08-31 12:54:46 -0700835 canDrawArgs.fHasUserStencilSettings = false;
csmartdaltonbde96c62016-08-31 12:54:46 -0700836
837 GrDrawingManager* dm = context->contextPriv().drawingManager();
Brian Salomon82125e92016-12-10 09:35:48 -0500838 pr = dm->getPathRenderer(canDrawArgs, false, GrPathRendererChain::DrawType::kStencil,
csmartdaltonbde96c62016-08-31 12:54:46 -0700839 &stencilSupport);
840 if (!pr) {
841 return false;
842 }
843 }
844
845 bool canRenderDirectToStencil =
846 GrPathRenderer::kNoRestriction_StencilSupport == stencilSupport;
847 bool drawDirectToClip; // Given the renderer, the element,
848 // fill rule, and set operation should
849 // we render the element directly to
850 // stencil bit used for clipping.
851 GrUserStencilSettings const* const* stencilPasses =
852 GrStencilSettings::GetClipPasses(op, canRenderDirectToStencil, fillInverted,
853 &drawDirectToClip);
854
855 // draw the element to the client stencil bits if necessary
856 if (!drawDirectToClip) {
857 static constexpr GrUserStencilSettings kDrawToStencil(
858 GrUserStencilSettings::StaticInit<
859 0x0000,
860 GrUserStencilTest::kAlways,
861 0xffff,
862 GrUserStencilOp::kIncMaybeClamp,
863 GrUserStencilOp::kIncMaybeClamp,
864 0xffff>()
865 );
Brian Salomonf3b46e52017-08-30 11:37:57 -0400866 if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) {
Brian Osman693a5402016-10-27 15:13:22 -0400867 renderTargetContext->priv().stencilRect(stencilClip.fixedClip(), &kDrawToStencil,
Brian Salomonf3b46e52017-08-30 11:37:57 -0400868 aaType, SkMatrix::I(),
869 element->getDeviceSpaceRect());
csmartdaltonbde96c62016-08-31 12:54:46 -0700870 } else {
871 if (!clipPath.isEmpty()) {
872 GrShape shape(clipPath, GrStyle::SimpleFill());
873 if (canRenderDirectToStencil) {
874 GrPaint paint;
Brian Salomona1633922017-01-09 11:46:10 -0500875 paint.setXPFactory(GrDisableColorXPFactory::Get());
csmartdaltonbde96c62016-08-31 12:54:46 -0700876
Robert Phillips256c37b2017-03-01 14:32:46 -0500877 GrPathRenderer::DrawPathArgs args{context,
Brian Salomon82f44312017-01-11 13:42:54 -0500878 std::move(paint),
879 &kDrawToStencil,
880 renderTargetContext,
881 &stencilClip.fixedClip(),
Chris Daltondb91c6e2017-09-08 16:25:08 -0600882 &stencilClip.fixedClip().scissorRect(),
Brian Salomon9a767722017-03-13 17:57:28 -0400883 &SkMatrix::I(),
Brian Salomon82f44312017-01-11 13:42:54 -0500884 &shape,
885 aaType,
886 false};
csmartdaltonbde96c62016-08-31 12:54:46 -0700887 pr->drawPath(args);
888 } else {
889 GrPathRenderer::StencilPathArgs args;
Robert Phillips256c37b2017-03-01 14:32:46 -0500890 args.fContext = context;
Brian Osman11052242016-10-27 14:47:55 -0400891 args.fRenderTargetContext = renderTargetContext;
csmartdaltonbde96c62016-08-31 12:54:46 -0700892 args.fClip = &stencilClip.fixedClip();
Chris Daltondb91c6e2017-09-08 16:25:08 -0600893 args.fClipConservativeBounds = &stencilClip.fixedClip().scissorRect();
Brian Salomon9a767722017-03-13 17:57:28 -0400894 args.fViewMatrix = &SkMatrix::I();
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500895 args.fAAType = aaType;
csmartdaltonbde96c62016-08-31 12:54:46 -0700896 args.fShape = &shape;
897 pr->stencilPath(args);
898 }
899 }
900 }
901 }
902
903 // now we modify the clip bit by rendering either the clip
904 // element directly or a bounding rect of the entire clip.
905 for (GrUserStencilSettings const* const* pass = stencilPasses; *pass; ++pass) {
906 if (drawDirectToClip) {
Brian Salomonf3b46e52017-08-30 11:37:57 -0400907 if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) {
Brian Salomon9a767722017-03-13 17:57:28 -0400908 renderTargetContext->priv().stencilRect(stencilClip, *pass, aaType,
Brian Salomonf3b46e52017-08-30 11:37:57 -0400909 SkMatrix::I(),
910 element->getDeviceSpaceRect());
csmartdaltonbde96c62016-08-31 12:54:46 -0700911 } else {
912 GrShape shape(clipPath, GrStyle::SimpleFill());
913 GrPaint paint;
Brian Salomona1633922017-01-09 11:46:10 -0500914 paint.setXPFactory(GrDisableColorXPFactory::Get());
Robert Phillips256c37b2017-03-01 14:32:46 -0500915 GrPathRenderer::DrawPathArgs args{context,
Brian Salomon82f44312017-01-11 13:42:54 -0500916 std::move(paint),
917 *pass,
918 renderTargetContext,
919 &stencilClip,
Chris Daltondb91c6e2017-09-08 16:25:08 -0600920 &stencilClip.fixedClip().scissorRect(),
Brian Salomon9a767722017-03-13 17:57:28 -0400921 &SkMatrix::I(),
Brian Salomon82f44312017-01-11 13:42:54 -0500922 &shape,
923 aaType,
924 false};
csmartdaltonbde96c62016-08-31 12:54:46 -0700925 pr->drawPath(args);
926 }
927 } else {
928 // The view matrix is setup to do clip space -> stencil space translation, so
929 // draw rect in clip space.
Brian Salomon9a767722017-03-13 17:57:28 -0400930 renderTargetContext->priv().stencilRect(stencilClip, *pass, aaType, SkMatrix::I(),
Chris Dalton79471932017-10-27 01:50:57 -0600931 SkRect::Make(fScissor));
csmartdaltonbde96c62016-08-31 12:54:46 -0700932 }
933 }
934 }
935 return true;
936}