blob: 93c01b64ad17dd12e52405e96e85cd704d4df0c2 [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "src/core/SkClipOpPriv.h"
9#include "src/gpu/GrAppliedClip.h"
10#include "src/gpu/GrClip.h"
Greg Danielf91aeb22019-06-18 09:58:02 -040011#include "src/gpu/GrColor.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050012#include "src/gpu/GrDrawingManager.h"
13#include "src/gpu/GrFixedClip.h"
14#include "src/gpu/GrPathRenderer.h"
15#include "src/gpu/GrRecordingContextPriv.h"
16#include "src/gpu/GrReducedClip.h"
17#include "src/gpu/GrRenderTargetContext.h"
18#include "src/gpu/GrRenderTargetContextPriv.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050019#include "src/gpu/GrStencilClip.h"
Michael Ludwig828d3412020-05-12 13:15:35 -040020#include "src/gpu/GrStencilMaskHelper.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050021#include "src/gpu/GrStencilSettings.h"
22#include "src/gpu/GrStyle.h"
23#include "src/gpu/GrUserStencilSettings.h"
24#include "src/gpu/ccpr/GrCoverageCountingPathRenderer.h"
25#include "src/gpu/effects/GrConvexPolyEffect.h"
26#include "src/gpu/effects/GrRRectEffect.h"
27#include "src/gpu/effects/generated/GrAARectEffect.h"
Michael Ludwig2686d692020-04-17 20:21:37 +000028#include "src/gpu/geometry/GrStyledShape.h"
csmartdaltoncbecb082016-07-22 08:59:08 -070029
csmartdalton5ecbbbe2016-08-23 13:26:40 -070030/**
31 * There are plenty of optimizations that could be added here. Maybe flips could be folded into
32 * earlier operations. Or would inserting flips and reversing earlier ops ever be a win? Perhaps
33 * for the case where the bounds are kInsideOut_BoundsType. We could restrict earlier operations
34 * based on later intersect operations, and perhaps remove intersect-rects. We could optionally
35 * take a rect in case the caller knows a bound on what is to be drawn through this clip.
36 */
csmartdaltonbf4a8f92016-09-06 10:01:06 -070037GrReducedClip::GrReducedClip(const SkClipStack& stack, const SkRect& queryBounds,
Ethan Nicholaseace9352018-10-15 20:09:54 +000038 const GrCaps* caps, int maxWindowRectangles, int maxAnalyticFPs,
39 int maxCCPRClipPaths)
40 : fCaps(caps)
Chris Daltona32a3c32017-12-05 10:05:21 -070041 , fMaxWindowRectangles(maxWindowRectangles)
42 , fMaxAnalyticFPs(maxAnalyticFPs)
Chris Dalton1dec19a2018-04-27 13:05:19 -060043 , fMaxCCPRClipPaths(maxCCPRClipPaths) {
csmartdaltoncbecb082016-07-22 08:59:08 -070044 SkASSERT(!queryBounds.isEmpty());
Chris Dalton584a79a2017-11-15 13:14:01 -070045 SkASSERT(fMaxWindowRectangles <= GrWindowRectangles::kMaxWindows);
Chris Dalton1dec19a2018-04-27 13:05:19 -060046 SkASSERT(fMaxCCPRClipPaths <= fMaxAnalyticFPs);
Chris Dalton79471932017-10-27 01:50:57 -060047 fHasScissor = false;
48 fAAClipRectGenID = SK_InvalidGenID;
csmartdaltoncbecb082016-07-22 08:59:08 -070049
bsalomon@google.com170bd792012-12-05 22:26:11 +000050 if (stack.isWideOpen()) {
csmartdalton77f2fae2016-08-08 09:55:06 -070051 fInitialState = InitialState::kAllIn;
52 return;
bsalomon@google.com170bd792012-12-05 22:26:11 +000053 }
54
55 SkClipStack::BoundsType stackBoundsType;
56 SkRect stackBounds;
57 bool iior;
58 stack.getBounds(&stackBounds, &stackBoundsType, &iior);
59
Chris Dalton348060f2017-06-05 13:15:37 -060060 if (GrClip::IsOutsideClip(stackBounds, queryBounds)) {
csmartdaltoncbecb082016-07-22 08:59:08 -070061 bool insideOut = SkClipStack::kInsideOut_BoundsType == stackBoundsType;
csmartdalton77f2fae2016-08-08 09:55:06 -070062 fInitialState = insideOut ? InitialState::kAllIn : InitialState::kAllOut;
63 return;
bsalomon@google.com170bd792012-12-05 22:26:11 +000064 }
65
csmartdaltoncbecb082016-07-22 08:59:08 -070066 if (iior) {
67 // "Is intersection of rects" means the clip is a single rect indicated by the stack bounds.
68 // This should only be true if aa/non-aa status matches among all elements.
69 SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType);
Robert Phillips9548c3b422019-01-08 12:35:43 -050070
71 if (GrClip::IsInsideClip(stackBounds, queryBounds)) {
72 fInitialState = InitialState::kAllIn;
73 return;
74 }
75
csmartdaltoncbecb082016-07-22 08:59:08 -070076 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
Robert Phillips9548c3b422019-01-08 12:35:43 -050077
csmartdaltoncbecb082016-07-22 08:59:08 -070078 if (!iter.prev()->isAA() || GrClip::IsPixelAligned(stackBounds)) {
Chris Dalton79471932017-10-27 01:50:57 -060079 // The clip is a non-aa rect. Here we just implement the entire thing using fScissor.
Mike Kleine26062a2017-10-31 20:56:54 +000080 stackBounds.round(&fScissor);
Chris Dalton79471932017-10-27 01:50:57 -060081 fHasScissor = true;
82 fInitialState = fScissor.isEmpty() ? InitialState::kAllOut : InitialState::kAllIn;
csmartdalton77f2fae2016-08-08 09:55:06 -070083 return;
csmartdaltoncbecb082016-07-22 08:59:08 -070084 }
csmartdaltoncbecb082016-07-22 08:59:08 -070085
csmartdaltond211e782016-08-15 11:17:19 -070086 SkRect tightBounds;
87 SkAssertResult(tightBounds.intersect(stackBounds, queryBounds));
Chris Dalton79471932017-10-27 01:50:57 -060088 fScissor = GrClip::GetPixelIBounds(tightBounds);
89 if (fScissor.isEmpty()) {
Chris Dalton348060f2017-06-05 13:15:37 -060090 fInitialState = InitialState::kAllOut;
91 return;
92 }
Chris Dalton79471932017-10-27 01:50:57 -060093 fHasScissor = true;
csmartdaltoncbecb082016-07-22 08:59:08 -070094
Chris Dalton79471932017-10-27 01:50:57 -060095 fAAClipRect = stackBounds;
Brian Salomonc3833b42018-07-09 18:23:58 +000096 fAAClipRectGenID = stack.getTopmostGenID();
97 SkASSERT(SK_InvalidGenID != fAAClipRectGenID);
csmartdalton77f2fae2016-08-08 09:55:06 -070098
Chris Dalton79471932017-10-27 01:50:57 -060099 fInitialState = InitialState::kAllIn;
100 } else {
101 SkRect tighterQuery = queryBounds;
102 if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
103 // Tighten the query by introducing a new clip at the stack's pixel boundaries. (This
104 // new clip will be enforced by the scissor.)
105 SkAssertResult(tighterQuery.intersect(GrClip::GetPixelBounds(stackBounds)));
106 }
107
108 fScissor = GrClip::GetPixelIBounds(tighterQuery);
109 if (fScissor.isEmpty()) {
110 fInitialState = InitialState::kAllOut;
111 return;
112 }
113 fHasScissor = true;
114
Chris Dalton584a79a2017-11-15 13:14:01 -0700115 // Now that we have determined the bounds to use and filtered out the trivial cases, call
116 // the helper that actually walks the stack.
117 this->walkStack(stack, tighterQuery);
Michael Ludwigde00dc92020-06-05 10:56:32 -0400118
119 if (fInitialState == InitialState::kAllOut && fMaskElements.isEmpty()) {
120 // The clip starts with no coverage and there are no elements to add coverage with
121 // expanding ops. We ignore the AAClipRectGenID since it is an implied intersection.
122 this->makeEmpty();
123 return;
124 }
csmartdaltoncbecb082016-07-22 08:59:08 -0700125 }
126
Brian Salomonc3833b42018-07-09 18:23:58 +0000127 if (SK_InvalidGenID != fAAClipRectGenID && // Is there an AA clip rect?
128 ClipResult::kNotClipped == this->addAnalyticFP(fAAClipRect, Invert::kNo, GrAA::kYes)) {
129 if (fMaskElements.isEmpty()) {
130 // Use a replace since it is faster than intersect.
131 fMaskElements.addToHead(fAAClipRect, SkMatrix::I(), kReplace_SkClipOp, true /*doAA*/);
132 fInitialState = InitialState::kAllOut;
Chris Dalton79471932017-10-27 01:50:57 -0600133 } else {
Brian Salomonc3833b42018-07-09 18:23:58 +0000134 fMaskElements.addToTail(fAAClipRect, SkMatrix::I(), kIntersect_SkClipOp, true /*doAA*/);
Chris Dalton79471932017-10-27 01:50:57 -0600135 }
Brian Salomonc3833b42018-07-09 18:23:58 +0000136 fMaskRequiresAA = true;
137 fMaskGenID = fAAClipRectGenID;
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700138 }
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700139}
140
Chris Dalton584a79a2017-11-15 13:14:01 -0700141void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBounds) {
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700142 // walk backwards until we get to:
143 // a) the beginning
144 // b) an operation that is known to make the bounds all inside/outside
145 // c) a replace operation
146
147 enum class InitialTriState {
148 kUnknown = -1,
149 kAllIn = (int)GrReducedClip::InitialState::kAllIn,
150 kAllOut = (int)GrReducedClip::InitialState::kAllOut
151 } initialTriState = InitialTriState::kUnknown;
152
153 // During our backwards walk, track whether we've seen ops that either grow or shrink the clip.
154 // TODO: track these per saved clip so that we can consider them on the forward pass.
155 bool embiggens = false;
156 bool emsmallens = false;
157
158 // We use a slightly relaxed set of query bounds for element containment tests. This is to
159 // account for floating point rounding error that may have occurred during coord transforms.
160 SkRect relaxedQueryBounds = queryBounds.makeInset(GrClip::kBoundsTolerance,
161 GrClip::kBoundsTolerance);
Chris Dalton69824002017-10-31 00:37:52 -0600162 if (relaxedQueryBounds.isEmpty()) {
163 relaxedQueryBounds = queryBounds;
164 }
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700165
166 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
167 int numAAElements = 0;
168 while (InitialTriState::kUnknown == initialTriState) {
Brian Salomonc3833b42018-07-09 18:23:58 +0000169 const Element* element = iter.prev();
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700170 if (nullptr == element) {
171 initialTriState = InitialTriState::kAllIn;
172 break;
173 }
174 if (SkClipStack::kEmptyGenID == element->getGenID()) {
175 initialTriState = InitialTriState::kAllOut;
176 break;
177 }
178 if (SkClipStack::kWideOpenGenID == element->getGenID()) {
179 initialTriState = InitialTriState::kAllIn;
180 break;
181 }
182
183 bool skippable = false;
184 bool isFlip = false; // does this op just flip the in/out state of every point in the bounds
185
186 switch (element->getOp()) {
Mike Reedc1f77742016-12-09 09:00:50 -0500187 case kDifference_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700188 // check if the shape subtracted either contains the entire bounds (and makes
189 // the clip empty) or is outside the bounds and therefore can be skipped.
190 if (element->isInverseFilled()) {
191 if (element->contains(relaxedQueryBounds)) {
192 skippable = true;
193 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
194 initialTriState = InitialTriState::kAllOut;
195 skippable = true;
Chris Daltona32a3c32017-12-05 10:05:21 -0700196 } else if (!embiggens) {
197 ClipResult result = this->clipInsideElement(element);
198 if (ClipResult::kMadeEmpty == result) {
199 return;
200 }
201 skippable = (ClipResult::kClipped == result);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700202 }
203 } else {
204 if (element->contains(relaxedQueryBounds)) {
205 initialTriState = InitialTriState::kAllOut;
206 skippable = true;
207 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
208 skippable = true;
Chris Dalton79471932017-10-27 01:50:57 -0600209 } else if (!embiggens) {
Chris Dalton584a79a2017-11-15 13:14:01 -0700210 ClipResult result = this->clipOutsideElement(element);
Chris Dalton79471932017-10-27 01:50:57 -0600211 if (ClipResult::kMadeEmpty == result) {
212 return;
213 }
214 skippable = (ClipResult::kClipped == result);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700215 }
216 }
217 if (!skippable) {
218 emsmallens = true;
219 }
220 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500221 case kIntersect_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700222 // check if the shape intersected contains the entire bounds and therefore can
223 // be skipped or it is outside the entire bounds and therefore makes the clip
224 // empty.
225 if (element->isInverseFilled()) {
226 if (element->contains(relaxedQueryBounds)) {
227 initialTriState = InitialTriState::kAllOut;
228 skippable = true;
229 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
230 skippable = true;
Chris Daltona32a3c32017-12-05 10:05:21 -0700231 } else if (!embiggens) {
232 ClipResult result = this->clipOutsideElement(element);
233 if (ClipResult::kMadeEmpty == result) {
234 return;
235 }
236 skippable = (ClipResult::kClipped == result);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700237 }
238 } else {
239 if (element->contains(relaxedQueryBounds)) {
240 skippable = true;
241 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
242 initialTriState = InitialTriState::kAllOut;
243 skippable = true;
Chris Dalton79471932017-10-27 01:50:57 -0600244 } else if (!embiggens) {
245 ClipResult result = this->clipInsideElement(element);
246 if (ClipResult::kMadeEmpty == result) {
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700247 return;
248 }
Chris Dalton79471932017-10-27 01:50:57 -0600249 skippable = (ClipResult::kClipped == result);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700250 }
251 }
252 if (!skippable) {
253 emsmallens = true;
254 }
255 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500256 case kUnion_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700257 // If the union-ed shape contains the entire bounds then after this element
258 // the bounds is entirely inside the clip. If the union-ed shape is outside the
259 // bounds then this op can be skipped.
260 if (element->isInverseFilled()) {
261 if (element->contains(relaxedQueryBounds)) {
262 skippable = true;
263 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
264 initialTriState = InitialTriState::kAllIn;
265 skippable = true;
266 }
267 } else {
268 if (element->contains(relaxedQueryBounds)) {
269 initialTriState = InitialTriState::kAllIn;
270 skippable = true;
271 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
272 skippable = true;
273 }
274 }
275 if (!skippable) {
276 embiggens = true;
277 }
278 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500279 case kXOR_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700280 // If the bounds is entirely inside the shape being xor-ed then the effect is
281 // to flip the inside/outside state of every point in the bounds. We may be
282 // able to take advantage of this in the forward pass. If the xor-ed shape
283 // doesn't intersect the bounds then it can be skipped.
284 if (element->isInverseFilled()) {
285 if (element->contains(relaxedQueryBounds)) {
286 skippable = true;
287 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
288 isFlip = true;
289 }
290 } else {
291 if (element->contains(relaxedQueryBounds)) {
292 isFlip = true;
293 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
294 skippable = true;
295 }
296 }
297 if (!skippable) {
298 emsmallens = embiggens = true;
299 }
300 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500301 case kReverseDifference_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700302 // When the bounds is entirely within the rev-diff shape then this behaves like xor
303 // and reverses every point inside the bounds. If the shape is completely outside
304 // the bounds then we know after this element is applied that the bounds will be
305 // all outside the current clip.B
306 if (element->isInverseFilled()) {
307 if (element->contains(relaxedQueryBounds)) {
308 initialTriState = InitialTriState::kAllOut;
309 skippable = true;
310 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
311 isFlip = true;
312 }
313 } else {
314 if (element->contains(relaxedQueryBounds)) {
315 isFlip = true;
316 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
317 initialTriState = InitialTriState::kAllOut;
318 skippable = true;
319 }
320 }
321 if (!skippable) {
322 emsmallens = embiggens = true;
323 }
324 break;
325
Mike Reedc1f77742016-12-09 09:00:50 -0500326 case kReplace_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700327 // Replace will always terminate our walk. We will either begin the forward walk
328 // at the replace op or detect here than the shape is either completely inside
329 // or completely outside the bounds. In this latter case it can be skipped by
330 // setting the correct value for initialTriState.
331 if (element->isInverseFilled()) {
332 if (element->contains(relaxedQueryBounds)) {
333 initialTriState = InitialTriState::kAllOut;
334 skippable = true;
335 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
336 initialTriState = InitialTriState::kAllIn;
337 skippable = true;
Chris Daltona32a3c32017-12-05 10:05:21 -0700338 } else if (!embiggens) {
339 ClipResult result = this->clipOutsideElement(element);
340 if (ClipResult::kMadeEmpty == result) {
341 return;
342 }
343 if (ClipResult::kClipped == result) {
344 initialTriState = InitialTriState::kAllIn;
345 skippable = true;
346 }
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700347 }
348 } else {
349 if (element->contains(relaxedQueryBounds)) {
350 initialTriState = InitialTriState::kAllIn;
351 skippable = true;
352 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
353 initialTriState = InitialTriState::kAllOut;
354 skippable = true;
Chris Dalton79471932017-10-27 01:50:57 -0600355 } else if (!embiggens) {
356 ClipResult result = this->clipInsideElement(element);
357 if (ClipResult::kMadeEmpty == result) {
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700358 return;
359 }
Chris Dalton79471932017-10-27 01:50:57 -0600360 if (ClipResult::kClipped == result) {
361 initialTriState = InitialTriState::kAllIn;
362 skippable = true;
363 }
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700364 }
365 }
366 if (!skippable) {
367 initialTriState = InitialTriState::kAllOut;
368 embiggens = emsmallens = true;
369 }
370 break;
371 default:
372 SkDEBUGFAIL("Unexpected op.");
373 break;
374 }
375 if (!skippable) {
Brian Salomonc3833b42018-07-09 18:23:58 +0000376 if (fMaskElements.isEmpty()) {
377 // This will be the last element. Record the stricter genID.
378 fMaskGenID = element->getGenID();
379 }
380
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700381 // if it is a flip, change it to a bounds-filling rect
382 if (isFlip) {
Mike Reedc1f77742016-12-09 09:00:50 -0500383 SkASSERT(kXOR_SkClipOp == element->getOp() ||
384 kReverseDifference_SkClipOp == element->getOp());
Brian Salomonc3833b42018-07-09 18:23:58 +0000385 fMaskElements.addToHead(SkRect::Make(fScissor), SkMatrix::I(),
386 kReverseDifference_SkClipOp, false);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700387 } else {
Brian Salomonc3833b42018-07-09 18:23:58 +0000388 Element* newElement = fMaskElements.addToHead(*element);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700389 if (newElement->isAA()) {
390 ++numAAElements;
391 }
392 // Intersecting an inverse shape is the same as differencing the non-inverse shape.
393 // Replacing with an inverse shape is the same as setting initialState=kAllIn and
394 // differencing the non-inverse shape.
Mike Reedc1f77742016-12-09 09:00:50 -0500395 bool isReplace = kReplace_SkClipOp == newElement->getOp();
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700396 if (newElement->isInverseFilled() &&
Mike Reedc1f77742016-12-09 09:00:50 -0500397 (kIntersect_SkClipOp == newElement->getOp() || isReplace)) {
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700398 newElement->invertShapeFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500399 newElement->setOp(kDifference_SkClipOp);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700400 if (isReplace) {
401 SkASSERT(InitialTriState::kAllOut == initialTriState);
402 initialTriState = InitialTriState::kAllIn;
403 }
404 }
405 }
406 }
407 }
408
409 if ((InitialTriState::kAllOut == initialTriState && !embiggens) ||
410 (InitialTriState::kAllIn == initialTriState && !emsmallens)) {
Chris Dalton79471932017-10-27 01:50:57 -0600411 fMaskElements.reset();
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700412 numAAElements = 0;
413 } else {
Brian Salomonc3833b42018-07-09 18:23:58 +0000414 Element* element = fMaskElements.headIter().get();
415 while (element) {
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700416 bool skippable = false;
417 switch (element->getOp()) {
Mike Reedc1f77742016-12-09 09:00:50 -0500418 case kDifference_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700419 // subtracting from the empty set yields the empty set.
420 skippable = InitialTriState::kAllOut == initialTriState;
421 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500422 case kIntersect_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700423 // intersecting with the empty set yields the empty set
424 if (InitialTriState::kAllOut == initialTriState) {
425 skippable = true;
426 } else {
427 // We can clear to zero and then simply draw the clip element.
428 initialTriState = InitialTriState::kAllOut;
Mike Reedc1f77742016-12-09 09:00:50 -0500429 element->setOp(kReplace_SkClipOp);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700430 }
431 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500432 case kUnion_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700433 if (InitialTriState::kAllIn == initialTriState) {
434 // unioning the infinite plane with anything is a no-op.
435 skippable = true;
436 } else {
437 // unioning the empty set with a shape is the shape.
Mike Reedc1f77742016-12-09 09:00:50 -0500438 element->setOp(kReplace_SkClipOp);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700439 }
440 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500441 case kXOR_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700442 if (InitialTriState::kAllOut == initialTriState) {
443 // xor could be changed to diff in the kAllIn case, not sure it's a win.
Mike Reedc1f77742016-12-09 09:00:50 -0500444 element->setOp(kReplace_SkClipOp);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700445 }
446 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500447 case kReverseDifference_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700448 if (InitialTriState::kAllIn == initialTriState) {
449 // subtracting the whole plane will yield the empty set.
450 skippable = true;
451 initialTriState = InitialTriState::kAllOut;
452 } else {
453 // this picks up flips inserted in the backwards pass.
454 skippable = element->isInverseFilled() ?
455 GrClip::IsOutsideClip(element->getBounds(), queryBounds) :
456 element->contains(relaxedQueryBounds);
457 if (skippable) {
458 initialTriState = InitialTriState::kAllIn;
459 } else {
Mike Reedc1f77742016-12-09 09:00:50 -0500460 element->setOp(kReplace_SkClipOp);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700461 }
462 }
463 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500464 case kReplace_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700465 skippable = false; // we would have skipped it in the backwards walk if we
466 // could've.
467 break;
468 default:
469 SkDEBUGFAIL("Unexpected op.");
470 break;
471 }
Brian Salomonc3833b42018-07-09 18:23:58 +0000472 if (!skippable) {
473 break;
474 } else {
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700475 if (element->isAA()) {
476 --numAAElements;
477 }
Chris Dalton79471932017-10-27 01:50:57 -0600478 fMaskElements.popHead();
Brian Salomonc3833b42018-07-09 18:23:58 +0000479 element = fMaskElements.headIter().get();
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700480 }
481 }
482 }
Chris Dalton79471932017-10-27 01:50:57 -0600483 fMaskRequiresAA = numAAElements > 0;
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700484
485 SkASSERT(InitialTriState::kUnknown != initialTriState);
486 fInitialState = static_cast<GrReducedClip::InitialState>(initialTriState);
487}
488
Brian Salomonc3833b42018-07-09 18:23:58 +0000489GrReducedClip::ClipResult GrReducedClip::clipInsideElement(const Element* element) {
Chris Dalton79471932017-10-27 01:50:57 -0600490 SkIRect elementIBounds;
491 if (!element->isAA()) {
492 element->getBounds().round(&elementIBounds);
493 } else {
494 elementIBounds = GrClip::GetPixelIBounds(element->getBounds());
495 }
496 SkASSERT(fHasScissor);
497 if (!fScissor.intersect(elementIBounds)) {
498 this->makeEmpty();
499 return ClipResult::kMadeEmpty;
500 }
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700501
Chris Dalton79471932017-10-27 01:50:57 -0600502 switch (element->getDeviceSpaceType()) {
Brian Salomonc3833b42018-07-09 18:23:58 +0000503 case Element::DeviceSpaceType::kEmpty:
Chris Dalton79471932017-10-27 01:50:57 -0600504 return ClipResult::kMadeEmpty;
505
Brian Salomonc3833b42018-07-09 18:23:58 +0000506 case Element::DeviceSpaceType::kRect:
Chris Dalton79471932017-10-27 01:50:57 -0600507 SkASSERT(element->getBounds() == element->getDeviceSpaceRect());
Chris Daltona32a3c32017-12-05 10:05:21 -0700508 SkASSERT(!element->isInverseFilled());
Chris Dalton79471932017-10-27 01:50:57 -0600509 if (element->isAA()) {
Brian Salomonc3833b42018-07-09 18:23:58 +0000510 if (SK_InvalidGenID == fAAClipRectGenID) { // No AA clip rect yet?
Chris Dalton79471932017-10-27 01:50:57 -0600511 fAAClipRect = element->getDeviceSpaceRect();
Brian Salomonc3833b42018-07-09 18:23:58 +0000512 // fAAClipRectGenID is the value we should use for fMaskGenID if we end up
513 // moving the AA clip rect into the mask. The mask GenID is simply the topmost
514 // element's GenID. And since we walk the stack backwards, this means it's just
515 // the first element we don't skip during our walk.
516 fAAClipRectGenID = fMaskElements.isEmpty() ? element->getGenID() : fMaskGenID;
517 SkASSERT(SK_InvalidGenID != fAAClipRectGenID);
Chris Dalton79471932017-10-27 01:50:57 -0600518 } else if (!fAAClipRect.intersect(element->getDeviceSpaceRect())) {
519 this->makeEmpty();
520 return ClipResult::kMadeEmpty;
521 }
522 }
523 return ClipResult::kClipped;
524
Brian Salomonc3833b42018-07-09 18:23:58 +0000525 case Element::DeviceSpaceType::kRRect:
Chris Daltona32a3c32017-12-05 10:05:21 -0700526 SkASSERT(!element->isInverseFilled());
Chris Dalton584a79a2017-11-15 13:14:01 -0700527 return this->addAnalyticFP(element->getDeviceSpaceRRect(), Invert::kNo,
Chris Dalton3b51df12017-11-27 14:33:06 -0700528 GrAA(element->isAA()));
Chris Dalton584a79a2017-11-15 13:14:01 -0700529
Brian Salomonc3833b42018-07-09 18:23:58 +0000530 case Element::DeviceSpaceType::kPath:
Chris Daltona32a3c32017-12-05 10:05:21 -0700531 return this->addAnalyticFP(element->getDeviceSpacePath(),
532 Invert(element->isInverseFilled()), GrAA(element->isAA()));
Chris Dalton79471932017-10-27 01:50:57 -0600533 }
534
535 SK_ABORT("Unexpected DeviceSpaceType");
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700536}
537
Brian Salomonc3833b42018-07-09 18:23:58 +0000538GrReducedClip::ClipResult GrReducedClip::clipOutsideElement(const Element* element) {
Chris Dalton79471932017-10-27 01:50:57 -0600539 switch (element->getDeviceSpaceType()) {
Brian Salomonc3833b42018-07-09 18:23:58 +0000540 case Element::DeviceSpaceType::kEmpty:
Chris Dalton79471932017-10-27 01:50:57 -0600541 return ClipResult::kMadeEmpty;
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700542
Brian Salomonc3833b42018-07-09 18:23:58 +0000543 case Element::DeviceSpaceType::kRect:
Chris Daltona32a3c32017-12-05 10:05:21 -0700544 SkASSERT(!element->isInverseFilled());
Chris Dalton584a79a2017-11-15 13:14:01 -0700545 if (fWindowRects.count() < fMaxWindowRectangles) {
546 // Clip out the inside of every rect. We won't be able to entirely skip the AA ones,
547 // but it saves processing time.
548 this->addWindowRectangle(element->getDeviceSpaceRect(), element->isAA());
549 if (!element->isAA()) {
550 return ClipResult::kClipped;
551 }
552 }
553 return this->addAnalyticFP(element->getDeviceSpaceRect(), Invert::kYes,
Chris Dalton3b51df12017-11-27 14:33:06 -0700554 GrAA(element->isAA()));
Chris Dalton79471932017-10-27 01:50:57 -0600555
Brian Salomonc3833b42018-07-09 18:23:58 +0000556 case Element::DeviceSpaceType::kRRect: {
Chris Daltona32a3c32017-12-05 10:05:21 -0700557 SkASSERT(!element->isInverseFilled());
Brian Osman554c1f02017-11-16 13:56:47 +0000558 const SkRRect& clipRRect = element->getDeviceSpaceRRect();
Chris Dalton3b51df12017-11-27 14:33:06 -0700559 ClipResult clipResult = this->addAnalyticFP(clipRRect, Invert::kYes,
560 GrAA(element->isAA()));
Chris Dalton584a79a2017-11-15 13:14:01 -0700561 if (fWindowRects.count() >= fMaxWindowRectangles) {
562 return clipResult;
563 }
564
565 // Clip out the interiors of round rects with two window rectangles in the shape of a
566 // "plus". This doesn't let us skip the clip element, but still saves processing time.
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700567 SkVector insetTL = clipRRect.radii(SkRRect::kUpperLeft_Corner);
568 SkVector insetBR = clipRRect.radii(SkRRect::kLowerRight_Corner);
569 if (SkRRect::kComplex_Type == clipRRect.getType()) {
570 const SkVector& insetTR = clipRRect.radii(SkRRect::kUpperRight_Corner);
571 const SkVector& insetBL = clipRRect.radii(SkRRect::kLowerLeft_Corner);
Brian Osman788b9162020-02-07 10:36:46 -0500572 insetTL.fX = std::max(insetTL.x(), insetBL.x());
573 insetTL.fY = std::max(insetTL.y(), insetTR.y());
574 insetBR.fX = std::max(insetBR.x(), insetTR.x());
575 insetBR.fY = std::max(insetBR.y(), insetBL.y());
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700576 }
577 const SkRect& bounds = clipRRect.getBounds();
578 if (insetTL.x() + insetBR.x() >= bounds.width() ||
579 insetTL.y() + insetBR.y() >= bounds.height()) {
Chris Dalton584a79a2017-11-15 13:14:01 -0700580 return clipResult; // The interior "plus" is empty.
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700581 }
582
583 SkRect horzRect = SkRect::MakeLTRB(bounds.left(), bounds.top() + insetTL.y(),
584 bounds.right(), bounds.bottom() - insetBR.y());
585 this->addWindowRectangle(horzRect, element->isAA());
Chris Dalton584a79a2017-11-15 13:14:01 -0700586
587 if (fWindowRects.count() < fMaxWindowRectangles) {
588 SkRect vertRect = SkRect::MakeLTRB(bounds.left() + insetTL.x(), bounds.top(),
589 bounds.right() - insetBR.x(), bounds.bottom());
590 this->addWindowRectangle(vertRect, element->isAA());
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700591 }
592
Chris Dalton584a79a2017-11-15 13:14:01 -0700593 return clipResult;
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700594 }
Chris Dalton79471932017-10-27 01:50:57 -0600595
Brian Salomonc3833b42018-07-09 18:23:58 +0000596 case Element::DeviceSpaceType::kPath:
Chris Daltona32a3c32017-12-05 10:05:21 -0700597 return this->addAnalyticFP(element->getDeviceSpacePath(),
598 Invert(!element->isInverseFilled()), GrAA(element->isAA()));
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700599 }
Chris Dalton79471932017-10-27 01:50:57 -0600600
601 SK_ABORT("Unexpected DeviceSpaceType");
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700602}
603
604inline void GrReducedClip::addWindowRectangle(const SkRect& elementInteriorRect, bool elementIsAA) {
605 SkIRect window;
606 if (!elementIsAA) {
607 elementInteriorRect.round(&window);
608 } else {
609 elementInteriorRect.roundIn(&window);
610 }
611 if (!window.isEmpty()) { // Skip very thin windows that round to zero or negative dimensions.
612 fWindowRects.addWindow(window);
613 }
614}
615
Chris Daltona32a3c32017-12-05 10:05:21 -0700616GrClipEdgeType GrReducedClip::GetClipEdgeType(Invert invert, GrAA aa) {
617 if (Invert::kNo == invert) {
618 return (GrAA::kYes == aa) ? GrClipEdgeType::kFillAA : GrClipEdgeType::kFillBW;
619 } else {
620 return (GrAA::kYes == aa) ? GrClipEdgeType::kInverseFillAA : GrClipEdgeType::kInverseFillBW;
621 }
622}
623
Ethan Nicholaseace9352018-10-15 20:09:54 +0000624GrReducedClip::ClipResult GrReducedClip::addAnalyticFP(const SkRect& deviceSpaceRect,
625 Invert invert, GrAA aa) {
Chris Daltona32a3c32017-12-05 10:05:21 -0700626 if (this->numAnalyticFPs() >= fMaxAnalyticFPs) {
Chris Dalton584a79a2017-11-15 13:14:01 -0700627 return ClipResult::kNotClipped;
628 }
629
Ethan Nicholaseace9352018-10-15 20:09:54 +0000630 fAnalyticFPs.push_back(GrAARectEffect::Make(GetClipEdgeType(invert, aa), deviceSpaceRect));
Chris Daltona32a3c32017-12-05 10:05:21 -0700631 SkASSERT(fAnalyticFPs.back());
632
633 return ClipResult::kClipped;
634}
635
636GrReducedClip::ClipResult GrReducedClip::addAnalyticFP(const SkRRect& deviceSpaceRRect,
637 Invert invert, GrAA aa) {
638 if (this->numAnalyticFPs() >= fMaxAnalyticFPs) {
639 return ClipResult::kNotClipped;
Chris Dalton584a79a2017-11-15 13:14:01 -0700640 }
641
Ethan Nicholaseace9352018-10-15 20:09:54 +0000642 if (auto fp = GrRRectEffect::Make(GetClipEdgeType(invert, aa), deviceSpaceRRect,
643 *fCaps->shaderCaps())) {
Chris Dalton584a79a2017-11-15 13:14:01 -0700644 fAnalyticFPs.push_back(std::move(fp));
645 return ClipResult::kClipped;
646 }
647
Chris Daltona32a3c32017-12-05 10:05:21 -0700648 SkPath deviceSpacePath;
649 deviceSpacePath.setIsVolatile(true);
650 deviceSpacePath.addRRect(deviceSpaceRRect);
651 return this->addAnalyticFP(deviceSpacePath, invert, aa);
652}
653
654GrReducedClip::ClipResult GrReducedClip::addAnalyticFP(const SkPath& deviceSpacePath,
655 Invert invert, GrAA aa) {
656 if (this->numAnalyticFPs() >= fMaxAnalyticFPs) {
657 return ClipResult::kNotClipped;
658 }
659
Ethan Nicholaseace9352018-10-15 20:09:54 +0000660 if (auto fp = GrConvexPolyEffect::Make(GetClipEdgeType(invert, aa), deviceSpacePath)) {
Chris Daltona32a3c32017-12-05 10:05:21 -0700661 fAnalyticFPs.push_back(std::move(fp));
662 return ClipResult::kClipped;
663 }
664
Chris Dalton1dec19a2018-04-27 13:05:19 -0600665 if (fCCPRClipPaths.count() < fMaxCCPRClipPaths && GrAA::kYes == aa) {
Chris Daltona32a3c32017-12-05 10:05:21 -0700666 // Set aside CCPR paths for later. We will create their clip FPs once we know the ID of the
Greg Danielf41b2bd2019-08-22 16:19:24 -0400667 // opsTask they will operate in.
Chris Daltona32a3c32017-12-05 10:05:21 -0700668 SkPath& ccprClipPath = fCCPRClipPaths.push_back(deviceSpacePath);
669 if (Invert::kYes == invert) {
670 ccprClipPath.toggleInverseFillType();
671 }
672 return ClipResult::kClipped;
673 }
674
Chris Dalton584a79a2017-11-15 13:14:01 -0700675 return ClipResult::kNotClipped;
676}
677
Chris Dalton79471932017-10-27 01:50:57 -0600678void GrReducedClip::makeEmpty() {
679 fHasScissor = false;
680 fAAClipRectGenID = SK_InvalidGenID;
681 fWindowRects.reset();
682 fMaskElements.reset();
683 fInitialState = InitialState::kAllOut;
bsalomon@google.com34cd70a2012-12-06 14:23:20 +0000684}
csmartdaltonbde96c62016-08-31 12:54:46 -0700685
686////////////////////////////////////////////////////////////////////////////////
Chris Daltonc5348082018-03-30 15:59:38 +0000687// Create a 8-bit clip mask in alpha
688
689static bool stencil_element(GrRenderTargetContext* rtc,
690 const GrFixedClip& clip,
691 const GrUserStencilSettings* ss,
692 const SkMatrix& viewMatrix,
Brian Salomonc3833b42018-07-09 18:23:58 +0000693 const SkClipStack::Element* element) {
694 GrAA aa = GrAA(element->isAA());
695 switch (element->getDeviceSpaceType()) {
Chris Daltonc5348082018-03-30 15:59:38 +0000696 case SkClipStack::Element::DeviceSpaceType::kEmpty:
697 SkDEBUGFAIL("Should never get here with an empty element.");
698 break;
Michael Ludwigaa1b6b32019-05-29 14:43:13 -0400699 case SkClipStack::Element::DeviceSpaceType::kRect: {
700 GrPaint paint;
701 paint.setCoverageSetOpXPFactory((SkRegion::Op)element->getOp(),
702 element->isInverseFilled());
Michael Ludwig7c12e282020-05-29 09:54:07 -0400703 rtc->priv().stencilRect(&clip, ss, std::move(paint), aa, viewMatrix,
Michael Ludwigaa1b6b32019-05-29 14:43:13 -0400704 element->getDeviceSpaceRect());
705 return true;
706 }
Chris Daltonc5348082018-03-30 15:59:38 +0000707 default: {
708 SkPath path;
Brian Salomonc3833b42018-07-09 18:23:58 +0000709 element->asDeviceSpacePath(&path);
Chris Daltonc5348082018-03-30 15:59:38 +0000710 if (path.isInverseFillType()) {
711 path.toggleInverseFillType();
712 }
713
Michael Ludwig7c12e282020-05-29 09:54:07 -0400714 return rtc->priv().drawAndStencilPath(&clip, ss, (SkRegion::Op)element->getOp(),
Brian Salomonc3833b42018-07-09 18:23:58 +0000715 element->isInverseFilled(), aa, viewMatrix, path);
Chris Daltonc5348082018-03-30 15:59:38 +0000716 }
717 }
718
719 return false;
720}
721
722static void draw_element(GrRenderTargetContext* rtc,
723 const GrClip& clip, // TODO: can this just always be WideOpen?
724 GrPaint&& paint,
725 GrAA aa,
726 const SkMatrix& viewMatrix,
Brian Salomonc3833b42018-07-09 18:23:58 +0000727 const SkClipStack::Element* element) {
Chris Daltonc5348082018-03-30 15:59:38 +0000728 // TODO: Draw rrects directly here.
Brian Salomonc3833b42018-07-09 18:23:58 +0000729 switch (element->getDeviceSpaceType()) {
Chris Daltonc5348082018-03-30 15:59:38 +0000730 case SkClipStack::Element::DeviceSpaceType::kEmpty:
731 SkDEBUGFAIL("Should never get here with an empty element.");
732 break;
733 case SkClipStack::Element::DeviceSpaceType::kRect:
Michael Ludwig7c12e282020-05-29 09:54:07 -0400734 rtc->drawRect(&clip, std::move(paint), aa, viewMatrix, element->getDeviceSpaceRect());
Chris Daltonc5348082018-03-30 15:59:38 +0000735 break;
736 default: {
737 SkPath path;
Brian Salomonc3833b42018-07-09 18:23:58 +0000738 element->asDeviceSpacePath(&path);
Chris Daltonc5348082018-03-30 15:59:38 +0000739 if (path.isInverseFillType()) {
740 path.toggleInverseFillType();
741 }
742
Michael Ludwig7c12e282020-05-29 09:54:07 -0400743 rtc->drawPath(&clip, std::move(paint), aa, viewMatrix, path, GrStyle::SimpleFill());
Chris Daltonc5348082018-03-30 15:59:38 +0000744 break;
745 }
746 }
747}
748
749bool GrReducedClip::drawAlphaClipMask(GrRenderTargetContext* rtc) const {
750 // The texture may be larger than necessary, this rect represents the part of the texture
751 // we populate with a rasterization of the clip.
Michael Ludwigd1d997e2020-06-04 15:52:44 -0400752 GrFixedClip clip(rtc->dimensions(), SkIRect::MakeWH(fScissor.width(), fScissor.height()));
Chris Daltonc5348082018-03-30 15:59:38 +0000753
754 if (!fWindowRects.empty()) {
755 clip.setWindowRectangles(fWindowRects.makeOffset(-fScissor.left(), -fScissor.top()),
756 GrWindowRectsState::Mode::kExclusive);
757 }
758
759 // The scratch texture that we are drawing into can be substantially larger than the mask. Only
760 // clear the part that we care about.
Brian Osman9a9baae2018-11-05 15:06:26 -0500761 SkPMColor4f initialCoverage =
762 InitialState::kAllIn == this->initialState() ? SK_PMColor4fWHITE : SK_PMColor4fTRANSPARENT;
Michael Ludwig81d41722020-05-26 16:57:38 -0400763 if (clip.hasWindowRectangles()) {
764 GrPaint paint;
765 paint.setColor4f(initialCoverage);
766 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
Michael Ludwig7c12e282020-05-29 09:54:07 -0400767 rtc->drawRect(&clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
Michael Ludwig81d41722020-05-26 16:57:38 -0400768 SkRect::Make(clip.scissorRect()));
769 } else {
770 rtc->priv().clearAtLeast(clip.scissorRect(), initialCoverage);
771 }
Chris Daltonc5348082018-03-30 15:59:38 +0000772
773 // Set the matrix so that rendered clip elements are transformed to mask space from clip space.
774 SkMatrix translate;
775 translate.setTranslate(SkIntToScalar(-fScissor.left()), SkIntToScalar(-fScissor.top()));
776
777 // walk through each clip element and perform its set op
778 for (ElementList::Iter iter(fMaskElements); iter.get(); iter.next()) {
Brian Salomonc3833b42018-07-09 18:23:58 +0000779 const Element* element = iter.get();
780 SkRegion::Op op = (SkRegion::Op)element->getOp();
781 GrAA aa = GrAA(element->isAA());
782 bool invert = element->isInverseFilled();
Chris Daltonc5348082018-03-30 15:59:38 +0000783 if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) {
784 // draw directly into the result with the stencil set to make the pixels affected
785 // by the clip shape be non-zero.
786 static constexpr GrUserStencilSettings kStencilInElement(
787 GrUserStencilSettings::StaticInit<
788 0xffff,
789 GrUserStencilTest::kAlways,
790 0xffff,
791 GrUserStencilOp::kReplace,
792 GrUserStencilOp::kReplace,
793 0xffff>()
794 );
795 if (!stencil_element(rtc, clip, &kStencilInElement, translate, element)) {
796 return false;
797 }
798
799 // Draw to the exterior pixels (those with a zero stencil value).
800 static constexpr GrUserStencilSettings kDrawOutsideElement(
801 GrUserStencilSettings::StaticInit<
802 0x0000,
803 GrUserStencilTest::kEqual,
804 0xffff,
805 GrUserStencilOp::kZero,
806 GrUserStencilOp::kZero,
807 0xffff>()
808 );
Michael Ludwigaa1b6b32019-05-29 14:43:13 -0400809
810 GrPaint paint;
811 paint.setCoverageSetOpXPFactory(op, !invert);
Michael Ludwig7c12e282020-05-29 09:54:07 -0400812 rtc->priv().stencilRect(&clip, &kDrawOutsideElement, std::move(paint), GrAA::kNo,
Michael Ludwigaa1b6b32019-05-29 14:43:13 -0400813 translate, SkRect::Make(fScissor));
Chris Daltonc5348082018-03-30 15:59:38 +0000814 } else {
815 // all the remaining ops can just be directly draw into the accumulation buffer
816 GrPaint paint;
817 paint.setCoverageSetOpXPFactory(op, false);
818
819 draw_element(rtc, clip, std::move(paint), aa, translate, element);
820 }
821 }
822
823 return true;
824}
825
826////////////////////////////////////////////////////////////////////////////////
csmartdaltonbde96c62016-08-31 12:54:46 -0700827// Create a 1-bit clip mask in the stencil buffer.
828
Robert Phillips6f0e02f2019-02-13 11:02:28 -0500829bool GrReducedClip::drawStencilClipMask(GrRecordingContext* context,
Ethan Nicholaseace9352018-10-15 20:09:54 +0000830 GrRenderTargetContext* renderTargetContext) const {
Michael Ludwig828d3412020-05-12 13:15:35 -0400831 GrStencilMaskHelper helper(context, renderTargetContext);
832 if (!helper.init(fScissor, this->maskGenID(), fWindowRects, this->numAnalyticFPs())) {
833 // The stencil mask doesn't need updating
834 return true;
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700835 }
836
Michael Ludwig828d3412020-05-12 13:15:35 -0400837 helper.clear(InitialState::kAllIn == this->initialState());
csmartdaltonbde96c62016-08-31 12:54:46 -0700838
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500839 // walk through each clip element and perform its set op with the existing clip.
Chris Dalton79471932017-10-27 01:50:57 -0600840 for (ElementList::Iter iter(fMaskElements); iter.get(); iter.next()) {
Brian Salomonc3833b42018-07-09 18:23:58 +0000841 const Element* element = iter.get();
Michael Ludwigde228e52020-05-12 16:17:20 +0000842 SkRegion::Op op = (SkRegion::Op)element->getOp();
Michael Ludwig828d3412020-05-12 13:15:35 -0400843 GrAA aa = element->isAA() ? GrAA::kYes : GrAA::kNo;
Michael Ludwigde228e52020-05-12 16:17:20 +0000844
Brian Salomonc3833b42018-07-09 18:23:58 +0000845 if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) {
Michael Ludwig828d3412020-05-12 13:15:35 -0400846 helper.drawRect(element->getDeviceSpaceRect(), SkMatrix::I(), op, aa);
csmartdaltonbde96c62016-08-31 12:54:46 -0700847 } else {
Michael Ludwig828d3412020-05-12 13:15:35 -0400848 SkPath path;
849 element->asDeviceSpacePath(&path);
850 if (!helper.drawPath(path, SkMatrix::I(), op, aa)) {
csmartdaltonbde96c62016-08-31 12:54:46 -0700851 return false;
852 }
853 }
Michael Ludwigde228e52020-05-12 16:17:20 +0000854 }
Michael Ludwig828d3412020-05-12 13:15:35 -0400855
856 helper.finish();
csmartdaltonbde96c62016-08-31 12:54:46 -0700857 return true;
858}
Chris Daltona32a3c32017-12-05 10:05:21 -0700859
Robert Phillips777707b2018-01-17 11:40:14 -0500860std::unique_ptr<GrFragmentProcessor> GrReducedClip::finishAndDetachAnalyticFPs(
Greg Danielf41b2bd2019-08-22 16:19:24 -0400861 GrCoverageCountingPathRenderer* ccpr, uint32_t opsTaskID) {
Chris Daltona32a3c32017-12-05 10:05:21 -0700862 // Make sure finishAndDetachAnalyticFPs hasn't been called already.
863 SkDEBUGCODE(for (const auto& fp : fAnalyticFPs) { SkASSERT(fp); })
864
865 if (!fCCPRClipPaths.empty()) {
866 fAnalyticFPs.reserve(fAnalyticFPs.count() + fCCPRClipPaths.count());
867 for (const SkPath& ccprClipPath : fCCPRClipPaths) {
Chris Dalton1dec19a2018-04-27 13:05:19 -0600868 SkASSERT(ccpr);
Chris Daltona32a3c32017-12-05 10:05:21 -0700869 SkASSERT(fHasScissor);
Greg Danielf41b2bd2019-08-22 16:19:24 -0400870 auto fp = ccpr->makeClipProcessor(opsTaskID, ccprClipPath, fScissor, *fCaps);
Chris Daltona32a3c32017-12-05 10:05:21 -0700871 fAnalyticFPs.push_back(std::move(fp));
872 }
873 fCCPRClipPaths.reset();
874 }
875
876 return GrFragmentProcessor::RunInSeries(fAnalyticFPs.begin(), fAnalyticFPs.count());
877}