blob: d1b10418b64c231294eb8275df7308948ad9006c [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 Ludwig4e221bd2020-06-05 11:29:36 -040028#include "src/gpu/effects/generated/GrDeviceSpaceEffect.h"
Michael Ludwig2686d692020-04-17 20:21:37 +000029#include "src/gpu/geometry/GrStyledShape.h"
Michael Ludwig4e221bd2020-06-05 11:29:36 -040030#include "src/shaders/SkShaderBase.h"
csmartdaltoncbecb082016-07-22 08:59:08 -070031
csmartdalton5ecbbbe2016-08-23 13:26:40 -070032/**
33 * There are plenty of optimizations that could be added here. Maybe flips could be folded into
34 * earlier operations. Or would inserting flips and reversing earlier ops ever be a win? Perhaps
35 * for the case where the bounds are kInsideOut_BoundsType. We could restrict earlier operations
36 * based on later intersect operations, and perhaps remove intersect-rects. We could optionally
37 * take a rect in case the caller knows a bound on what is to be drawn through this clip.
38 */
csmartdaltonbf4a8f92016-09-06 10:01:06 -070039GrReducedClip::GrReducedClip(const SkClipStack& stack, const SkRect& queryBounds,
Ethan Nicholaseace9352018-10-15 20:09:54 +000040 const GrCaps* caps, int maxWindowRectangles, int maxAnalyticFPs,
41 int maxCCPRClipPaths)
42 : fCaps(caps)
Chris Daltona32a3c32017-12-05 10:05:21 -070043 , fMaxWindowRectangles(maxWindowRectangles)
44 , fMaxAnalyticFPs(maxAnalyticFPs)
Chris Dalton1dec19a2018-04-27 13:05:19 -060045 , fMaxCCPRClipPaths(maxCCPRClipPaths) {
csmartdaltoncbecb082016-07-22 08:59:08 -070046 SkASSERT(!queryBounds.isEmpty());
Chris Dalton584a79a2017-11-15 13:14:01 -070047 SkASSERT(fMaxWindowRectangles <= GrWindowRectangles::kMaxWindows);
Chris Dalton1dec19a2018-04-27 13:05:19 -060048 SkASSERT(fMaxCCPRClipPaths <= fMaxAnalyticFPs);
Chris Dalton79471932017-10-27 01:50:57 -060049 fHasScissor = false;
50 fAAClipRectGenID = SK_InvalidGenID;
csmartdaltoncbecb082016-07-22 08:59:08 -070051
bsalomon@google.com170bd792012-12-05 22:26:11 +000052 if (stack.isWideOpen()) {
csmartdalton77f2fae2016-08-08 09:55:06 -070053 fInitialState = InitialState::kAllIn;
54 return;
bsalomon@google.com170bd792012-12-05 22:26:11 +000055 }
56
57 SkClipStack::BoundsType stackBoundsType;
58 SkRect stackBounds;
59 bool iior;
60 stack.getBounds(&stackBounds, &stackBoundsType, &iior);
61
Chris Dalton348060f2017-06-05 13:15:37 -060062 if (GrClip::IsOutsideClip(stackBounds, queryBounds)) {
csmartdaltoncbecb082016-07-22 08:59:08 -070063 bool insideOut = SkClipStack::kInsideOut_BoundsType == stackBoundsType;
csmartdalton77f2fae2016-08-08 09:55:06 -070064 fInitialState = insideOut ? InitialState::kAllIn : InitialState::kAllOut;
65 return;
bsalomon@google.com170bd792012-12-05 22:26:11 +000066 }
67
csmartdaltoncbecb082016-07-22 08:59:08 -070068 if (iior) {
69 // "Is intersection of rects" means the clip is a single rect indicated by the stack bounds.
70 // This should only be true if aa/non-aa status matches among all elements.
71 SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType);
Robert Phillips9548c3b422019-01-08 12:35:43 -050072
73 if (GrClip::IsInsideClip(stackBounds, queryBounds)) {
74 fInitialState = InitialState::kAllIn;
75 return;
76 }
77
csmartdaltoncbecb082016-07-22 08:59:08 -070078 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
Robert Phillips9548c3b422019-01-08 12:35:43 -050079
csmartdaltoncbecb082016-07-22 08:59:08 -070080 if (!iter.prev()->isAA() || GrClip::IsPixelAligned(stackBounds)) {
Chris Dalton79471932017-10-27 01:50:57 -060081 // The clip is a non-aa rect. Here we just implement the entire thing using fScissor.
Mike Kleine26062a2017-10-31 20:56:54 +000082 stackBounds.round(&fScissor);
Chris Dalton79471932017-10-27 01:50:57 -060083 fHasScissor = true;
84 fInitialState = fScissor.isEmpty() ? InitialState::kAllOut : InitialState::kAllIn;
csmartdalton77f2fae2016-08-08 09:55:06 -070085 return;
csmartdaltoncbecb082016-07-22 08:59:08 -070086 }
csmartdaltoncbecb082016-07-22 08:59:08 -070087
csmartdaltond211e782016-08-15 11:17:19 -070088 SkRect tightBounds;
89 SkAssertResult(tightBounds.intersect(stackBounds, queryBounds));
Chris Dalton79471932017-10-27 01:50:57 -060090 fScissor = GrClip::GetPixelIBounds(tightBounds);
91 if (fScissor.isEmpty()) {
Chris Dalton348060f2017-06-05 13:15:37 -060092 fInitialState = InitialState::kAllOut;
93 return;
94 }
Chris Dalton79471932017-10-27 01:50:57 -060095 fHasScissor = true;
csmartdaltoncbecb082016-07-22 08:59:08 -070096
Chris Dalton79471932017-10-27 01:50:57 -060097 fAAClipRect = stackBounds;
Brian Salomonc3833b42018-07-09 18:23:58 +000098 fAAClipRectGenID = stack.getTopmostGenID();
99 SkASSERT(SK_InvalidGenID != fAAClipRectGenID);
csmartdalton77f2fae2016-08-08 09:55:06 -0700100
Chris Dalton79471932017-10-27 01:50:57 -0600101 fInitialState = InitialState::kAllIn;
102 } else {
103 SkRect tighterQuery = queryBounds;
104 if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
105 // Tighten the query by introducing a new clip at the stack's pixel boundaries. (This
106 // new clip will be enforced by the scissor.)
107 SkAssertResult(tighterQuery.intersect(GrClip::GetPixelBounds(stackBounds)));
108 }
109
110 fScissor = GrClip::GetPixelIBounds(tighterQuery);
111 if (fScissor.isEmpty()) {
112 fInitialState = InitialState::kAllOut;
113 return;
114 }
115 fHasScissor = true;
116
Chris Dalton584a79a2017-11-15 13:14:01 -0700117 // Now that we have determined the bounds to use and filtered out the trivial cases, call
118 // the helper that actually walks the stack.
119 this->walkStack(stack, tighterQuery);
Michael Ludwigde00dc92020-06-05 10:56:32 -0400120
121 if (fInitialState == InitialState::kAllOut && fMaskElements.isEmpty()) {
122 // The clip starts with no coverage and there are no elements to add coverage with
123 // expanding ops. We ignore the AAClipRectGenID since it is an implied intersection.
124 this->makeEmpty();
125 return;
126 }
csmartdaltoncbecb082016-07-22 08:59:08 -0700127 }
128
Brian Salomonc3833b42018-07-09 18:23:58 +0000129 if (SK_InvalidGenID != fAAClipRectGenID && // Is there an AA clip rect?
130 ClipResult::kNotClipped == this->addAnalyticFP(fAAClipRect, Invert::kNo, GrAA::kYes)) {
131 if (fMaskElements.isEmpty()) {
132 // Use a replace since it is faster than intersect.
133 fMaskElements.addToHead(fAAClipRect, SkMatrix::I(), kReplace_SkClipOp, true /*doAA*/);
134 fInitialState = InitialState::kAllOut;
Chris Dalton79471932017-10-27 01:50:57 -0600135 } else {
Brian Salomonc3833b42018-07-09 18:23:58 +0000136 fMaskElements.addToTail(fAAClipRect, SkMatrix::I(), kIntersect_SkClipOp, true /*doAA*/);
Chris Dalton79471932017-10-27 01:50:57 -0600137 }
Brian Salomonc3833b42018-07-09 18:23:58 +0000138 fMaskRequiresAA = true;
139 fMaskGenID = fAAClipRectGenID;
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700140 }
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700141}
142
Chris Dalton584a79a2017-11-15 13:14:01 -0700143void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBounds) {
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700144 // walk backwards until we get to:
145 // a) the beginning
146 // b) an operation that is known to make the bounds all inside/outside
147 // c) a replace operation
148
149 enum class InitialTriState {
150 kUnknown = -1,
151 kAllIn = (int)GrReducedClip::InitialState::kAllIn,
152 kAllOut = (int)GrReducedClip::InitialState::kAllOut
153 } initialTriState = InitialTriState::kUnknown;
154
155 // During our backwards walk, track whether we've seen ops that either grow or shrink the clip.
156 // TODO: track these per saved clip so that we can consider them on the forward pass.
157 bool embiggens = false;
158 bool emsmallens = false;
159
160 // We use a slightly relaxed set of query bounds for element containment tests. This is to
161 // account for floating point rounding error that may have occurred during coord transforms.
162 SkRect relaxedQueryBounds = queryBounds.makeInset(GrClip::kBoundsTolerance,
163 GrClip::kBoundsTolerance);
Chris Dalton69824002017-10-31 00:37:52 -0600164 if (relaxedQueryBounds.isEmpty()) {
165 relaxedQueryBounds = queryBounds;
166 }
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700167
168 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
169 int numAAElements = 0;
170 while (InitialTriState::kUnknown == initialTriState) {
Brian Salomonc3833b42018-07-09 18:23:58 +0000171 const Element* element = iter.prev();
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700172 if (nullptr == element) {
173 initialTriState = InitialTriState::kAllIn;
174 break;
175 }
176 if (SkClipStack::kEmptyGenID == element->getGenID()) {
177 initialTriState = InitialTriState::kAllOut;
178 break;
179 }
180 if (SkClipStack::kWideOpenGenID == element->getGenID()) {
181 initialTriState = InitialTriState::kAllIn;
182 break;
183 }
184
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400185 if (element->getDeviceSpaceType() == Element::DeviceSpaceType::kShader) {
186 if (fShader) {
187 // Combine multiple shaders together with src-in blending. This works because all
188 // shaders are effectively intersections (difference ops have been modified to be
189 // 1 - alpha already).
190 fShader = SkShaders::Blend(SkBlendMode::kSrcIn, element->refShader(), fShader);
191 } else {
192 fShader = element->refShader();
193 }
194 continue;
195 }
196
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700197 bool skippable = false;
198 bool isFlip = false; // does this op just flip the in/out state of every point in the bounds
199
200 switch (element->getOp()) {
Mike Reedc1f77742016-12-09 09:00:50 -0500201 case kDifference_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700202 // check if the shape subtracted either contains the entire bounds (and makes
203 // the clip empty) or is outside the bounds and therefore can be skipped.
204 if (element->isInverseFilled()) {
205 if (element->contains(relaxedQueryBounds)) {
206 skippable = true;
207 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
208 initialTriState = InitialTriState::kAllOut;
209 skippable = true;
Chris Daltona32a3c32017-12-05 10:05:21 -0700210 } else if (!embiggens) {
211 ClipResult result = this->clipInsideElement(element);
212 if (ClipResult::kMadeEmpty == result) {
213 return;
214 }
215 skippable = (ClipResult::kClipped == result);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700216 }
217 } else {
218 if (element->contains(relaxedQueryBounds)) {
219 initialTriState = InitialTriState::kAllOut;
220 skippable = true;
221 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
222 skippable = true;
Chris Dalton79471932017-10-27 01:50:57 -0600223 } else if (!embiggens) {
Chris Dalton584a79a2017-11-15 13:14:01 -0700224 ClipResult result = this->clipOutsideElement(element);
Chris Dalton79471932017-10-27 01:50:57 -0600225 if (ClipResult::kMadeEmpty == result) {
226 return;
227 }
228 skippable = (ClipResult::kClipped == result);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700229 }
230 }
231 if (!skippable) {
232 emsmallens = true;
233 }
234 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500235 case kIntersect_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700236 // check if the shape intersected contains the entire bounds and therefore can
237 // be skipped or it is outside the entire bounds and therefore makes the clip
238 // empty.
239 if (element->isInverseFilled()) {
240 if (element->contains(relaxedQueryBounds)) {
241 initialTriState = InitialTriState::kAllOut;
242 skippable = true;
243 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
244 skippable = true;
Chris Daltona32a3c32017-12-05 10:05:21 -0700245 } else if (!embiggens) {
246 ClipResult result = this->clipOutsideElement(element);
247 if (ClipResult::kMadeEmpty == result) {
248 return;
249 }
250 skippable = (ClipResult::kClipped == result);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700251 }
252 } else {
253 if (element->contains(relaxedQueryBounds)) {
254 skippable = true;
255 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
256 initialTriState = InitialTriState::kAllOut;
257 skippable = true;
Chris Dalton79471932017-10-27 01:50:57 -0600258 } else if (!embiggens) {
259 ClipResult result = this->clipInsideElement(element);
260 if (ClipResult::kMadeEmpty == result) {
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700261 return;
262 }
Chris Dalton79471932017-10-27 01:50:57 -0600263 skippable = (ClipResult::kClipped == result);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700264 }
265 }
266 if (!skippable) {
267 emsmallens = true;
268 }
269 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500270 case kUnion_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700271 // If the union-ed shape contains the entire bounds then after this element
272 // the bounds is entirely inside the clip. If the union-ed shape is outside the
273 // bounds then this op can be skipped.
274 if (element->isInverseFilled()) {
275 if (element->contains(relaxedQueryBounds)) {
276 skippable = true;
277 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
278 initialTriState = InitialTriState::kAllIn;
279 skippable = true;
280 }
281 } else {
282 if (element->contains(relaxedQueryBounds)) {
283 initialTriState = InitialTriState::kAllIn;
284 skippable = true;
285 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
286 skippable = true;
287 }
288 }
289 if (!skippable) {
290 embiggens = true;
291 }
292 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500293 case kXOR_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700294 // If the bounds is entirely inside the shape being xor-ed then the effect is
295 // to flip the inside/outside state of every point in the bounds. We may be
296 // able to take advantage of this in the forward pass. If the xor-ed shape
297 // doesn't intersect the bounds then it can be skipped.
298 if (element->isInverseFilled()) {
299 if (element->contains(relaxedQueryBounds)) {
300 skippable = true;
301 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
302 isFlip = true;
303 }
304 } else {
305 if (element->contains(relaxedQueryBounds)) {
306 isFlip = true;
307 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
308 skippable = true;
309 }
310 }
311 if (!skippable) {
312 emsmallens = embiggens = true;
313 }
314 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500315 case kReverseDifference_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700316 // When the bounds is entirely within the rev-diff shape then this behaves like xor
317 // and reverses every point inside the bounds. If the shape is completely outside
318 // the bounds then we know after this element is applied that the bounds will be
319 // all outside the current clip.B
320 if (element->isInverseFilled()) {
321 if (element->contains(relaxedQueryBounds)) {
322 initialTriState = InitialTriState::kAllOut;
323 skippable = true;
324 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
325 isFlip = true;
326 }
327 } else {
328 if (element->contains(relaxedQueryBounds)) {
329 isFlip = true;
330 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
331 initialTriState = InitialTriState::kAllOut;
332 skippable = true;
333 }
334 }
335 if (!skippable) {
336 emsmallens = embiggens = true;
337 }
338 break;
339
Mike Reedc1f77742016-12-09 09:00:50 -0500340 case kReplace_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700341 // Replace will always terminate our walk. We will either begin the forward walk
342 // at the replace op or detect here than the shape is either completely inside
343 // or completely outside the bounds. In this latter case it can be skipped by
344 // setting the correct value for initialTriState.
345 if (element->isInverseFilled()) {
346 if (element->contains(relaxedQueryBounds)) {
347 initialTriState = InitialTriState::kAllOut;
348 skippable = true;
349 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
350 initialTriState = InitialTriState::kAllIn;
351 skippable = true;
Chris Daltona32a3c32017-12-05 10:05:21 -0700352 } else if (!embiggens) {
353 ClipResult result = this->clipOutsideElement(element);
354 if (ClipResult::kMadeEmpty == result) {
355 return;
356 }
357 if (ClipResult::kClipped == result) {
358 initialTriState = InitialTriState::kAllIn;
359 skippable = true;
360 }
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700361 }
362 } else {
363 if (element->contains(relaxedQueryBounds)) {
364 initialTriState = InitialTriState::kAllIn;
365 skippable = true;
366 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
367 initialTriState = InitialTriState::kAllOut;
368 skippable = true;
Chris Dalton79471932017-10-27 01:50:57 -0600369 } else if (!embiggens) {
370 ClipResult result = this->clipInsideElement(element);
371 if (ClipResult::kMadeEmpty == result) {
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700372 return;
373 }
Chris Dalton79471932017-10-27 01:50:57 -0600374 if (ClipResult::kClipped == result) {
375 initialTriState = InitialTriState::kAllIn;
376 skippable = true;
377 }
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700378 }
379 }
380 if (!skippable) {
381 initialTriState = InitialTriState::kAllOut;
382 embiggens = emsmallens = true;
383 }
384 break;
385 default:
386 SkDEBUGFAIL("Unexpected op.");
387 break;
388 }
389 if (!skippable) {
Brian Salomonc3833b42018-07-09 18:23:58 +0000390 if (fMaskElements.isEmpty()) {
391 // This will be the last element. Record the stricter genID.
392 fMaskGenID = element->getGenID();
393 }
394
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700395 // if it is a flip, change it to a bounds-filling rect
396 if (isFlip) {
Mike Reedc1f77742016-12-09 09:00:50 -0500397 SkASSERT(kXOR_SkClipOp == element->getOp() ||
398 kReverseDifference_SkClipOp == element->getOp());
Brian Salomonc3833b42018-07-09 18:23:58 +0000399 fMaskElements.addToHead(SkRect::Make(fScissor), SkMatrix::I(),
400 kReverseDifference_SkClipOp, false);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700401 } else {
Brian Salomonc3833b42018-07-09 18:23:58 +0000402 Element* newElement = fMaskElements.addToHead(*element);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700403 if (newElement->isAA()) {
404 ++numAAElements;
405 }
406 // Intersecting an inverse shape is the same as differencing the non-inverse shape.
407 // Replacing with an inverse shape is the same as setting initialState=kAllIn and
408 // differencing the non-inverse shape.
Mike Reedc1f77742016-12-09 09:00:50 -0500409 bool isReplace = kReplace_SkClipOp == newElement->getOp();
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700410 if (newElement->isInverseFilled() &&
Mike Reedc1f77742016-12-09 09:00:50 -0500411 (kIntersect_SkClipOp == newElement->getOp() || isReplace)) {
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700412 newElement->invertShapeFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500413 newElement->setOp(kDifference_SkClipOp);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700414 if (isReplace) {
415 SkASSERT(InitialTriState::kAllOut == initialTriState);
416 initialTriState = InitialTriState::kAllIn;
417 }
418 }
419 }
420 }
421 }
422
423 if ((InitialTriState::kAllOut == initialTriState && !embiggens) ||
424 (InitialTriState::kAllIn == initialTriState && !emsmallens)) {
Chris Dalton79471932017-10-27 01:50:57 -0600425 fMaskElements.reset();
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700426 numAAElements = 0;
427 } else {
Brian Salomonc3833b42018-07-09 18:23:58 +0000428 Element* element = fMaskElements.headIter().get();
429 while (element) {
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700430 bool skippable = false;
431 switch (element->getOp()) {
Mike Reedc1f77742016-12-09 09:00:50 -0500432 case kDifference_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700433 // subtracting from the empty set yields the empty set.
434 skippable = InitialTriState::kAllOut == initialTriState;
435 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500436 case kIntersect_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700437 // intersecting with the empty set yields the empty set
438 if (InitialTriState::kAllOut == initialTriState) {
439 skippable = true;
440 } else {
441 // We can clear to zero and then simply draw the clip element.
442 initialTriState = InitialTriState::kAllOut;
Mike Reedc1f77742016-12-09 09:00:50 -0500443 element->setOp(kReplace_SkClipOp);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700444 }
445 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500446 case kUnion_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700447 if (InitialTriState::kAllIn == initialTriState) {
448 // unioning the infinite plane with anything is a no-op.
449 skippable = true;
450 } else {
451 // unioning the empty set with a shape is the shape.
Mike Reedc1f77742016-12-09 09:00:50 -0500452 element->setOp(kReplace_SkClipOp);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700453 }
454 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500455 case kXOR_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700456 if (InitialTriState::kAllOut == initialTriState) {
457 // xor could be changed to diff in the kAllIn case, not sure it's a win.
Mike Reedc1f77742016-12-09 09:00:50 -0500458 element->setOp(kReplace_SkClipOp);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700459 }
460 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500461 case kReverseDifference_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700462 if (InitialTriState::kAllIn == initialTriState) {
463 // subtracting the whole plane will yield the empty set.
464 skippable = true;
465 initialTriState = InitialTriState::kAllOut;
466 } else {
467 // this picks up flips inserted in the backwards pass.
468 skippable = element->isInverseFilled() ?
469 GrClip::IsOutsideClip(element->getBounds(), queryBounds) :
470 element->contains(relaxedQueryBounds);
471 if (skippable) {
472 initialTriState = InitialTriState::kAllIn;
473 } else {
Mike Reedc1f77742016-12-09 09:00:50 -0500474 element->setOp(kReplace_SkClipOp);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700475 }
476 }
477 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500478 case kReplace_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700479 skippable = false; // we would have skipped it in the backwards walk if we
480 // could've.
481 break;
482 default:
483 SkDEBUGFAIL("Unexpected op.");
484 break;
485 }
Brian Salomonc3833b42018-07-09 18:23:58 +0000486 if (!skippable) {
487 break;
488 } else {
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700489 if (element->isAA()) {
490 --numAAElements;
491 }
Chris Dalton79471932017-10-27 01:50:57 -0600492 fMaskElements.popHead();
Brian Salomonc3833b42018-07-09 18:23:58 +0000493 element = fMaskElements.headIter().get();
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700494 }
495 }
496 }
Chris Dalton79471932017-10-27 01:50:57 -0600497 fMaskRequiresAA = numAAElements > 0;
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700498
499 SkASSERT(InitialTriState::kUnknown != initialTriState);
500 fInitialState = static_cast<GrReducedClip::InitialState>(initialTriState);
501}
502
Brian Salomonc3833b42018-07-09 18:23:58 +0000503GrReducedClip::ClipResult GrReducedClip::clipInsideElement(const Element* element) {
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400504 SkASSERT(element->getDeviceSpaceType() != Element::DeviceSpaceType::kShader);
505
Chris Dalton79471932017-10-27 01:50:57 -0600506 SkIRect elementIBounds;
507 if (!element->isAA()) {
508 element->getBounds().round(&elementIBounds);
509 } else {
510 elementIBounds = GrClip::GetPixelIBounds(element->getBounds());
511 }
512 SkASSERT(fHasScissor);
513 if (!fScissor.intersect(elementIBounds)) {
514 this->makeEmpty();
515 return ClipResult::kMadeEmpty;
516 }
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700517
Chris Dalton79471932017-10-27 01:50:57 -0600518 switch (element->getDeviceSpaceType()) {
Brian Salomonc3833b42018-07-09 18:23:58 +0000519 case Element::DeviceSpaceType::kEmpty:
Chris Dalton79471932017-10-27 01:50:57 -0600520 return ClipResult::kMadeEmpty;
521
Brian Salomonc3833b42018-07-09 18:23:58 +0000522 case Element::DeviceSpaceType::kRect:
Chris Dalton79471932017-10-27 01:50:57 -0600523 SkASSERT(element->getBounds() == element->getDeviceSpaceRect());
Chris Daltona32a3c32017-12-05 10:05:21 -0700524 SkASSERT(!element->isInverseFilled());
Chris Dalton79471932017-10-27 01:50:57 -0600525 if (element->isAA()) {
Brian Salomonc3833b42018-07-09 18:23:58 +0000526 if (SK_InvalidGenID == fAAClipRectGenID) { // No AA clip rect yet?
Chris Dalton79471932017-10-27 01:50:57 -0600527 fAAClipRect = element->getDeviceSpaceRect();
Brian Salomonc3833b42018-07-09 18:23:58 +0000528 // fAAClipRectGenID is the value we should use for fMaskGenID if we end up
529 // moving the AA clip rect into the mask. The mask GenID is simply the topmost
530 // element's GenID. And since we walk the stack backwards, this means it's just
531 // the first element we don't skip during our walk.
532 fAAClipRectGenID = fMaskElements.isEmpty() ? element->getGenID() : fMaskGenID;
533 SkASSERT(SK_InvalidGenID != fAAClipRectGenID);
Chris Dalton79471932017-10-27 01:50:57 -0600534 } else if (!fAAClipRect.intersect(element->getDeviceSpaceRect())) {
535 this->makeEmpty();
536 return ClipResult::kMadeEmpty;
537 }
538 }
539 return ClipResult::kClipped;
540
Brian Salomonc3833b42018-07-09 18:23:58 +0000541 case Element::DeviceSpaceType::kRRect:
Chris Daltona32a3c32017-12-05 10:05:21 -0700542 SkASSERT(!element->isInverseFilled());
Chris Dalton584a79a2017-11-15 13:14:01 -0700543 return this->addAnalyticFP(element->getDeviceSpaceRRect(), Invert::kNo,
Chris Dalton3b51df12017-11-27 14:33:06 -0700544 GrAA(element->isAA()));
Chris Dalton584a79a2017-11-15 13:14:01 -0700545
Brian Salomonc3833b42018-07-09 18:23:58 +0000546 case Element::DeviceSpaceType::kPath:
Chris Daltona32a3c32017-12-05 10:05:21 -0700547 return this->addAnalyticFP(element->getDeviceSpacePath(),
548 Invert(element->isInverseFilled()), GrAA(element->isAA()));
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400549
550 case Element::DeviceSpaceType::kShader:
551 SkUNREACHABLE;
Chris Dalton79471932017-10-27 01:50:57 -0600552 }
553
554 SK_ABORT("Unexpected DeviceSpaceType");
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700555}
556
Brian Salomonc3833b42018-07-09 18:23:58 +0000557GrReducedClip::ClipResult GrReducedClip::clipOutsideElement(const Element* element) {
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400558 SkASSERT(element->getDeviceSpaceType() != Element::DeviceSpaceType::kShader);
559
Chris Dalton79471932017-10-27 01:50:57 -0600560 switch (element->getDeviceSpaceType()) {
Brian Salomonc3833b42018-07-09 18:23:58 +0000561 case Element::DeviceSpaceType::kEmpty:
Chris Dalton79471932017-10-27 01:50:57 -0600562 return ClipResult::kMadeEmpty;
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700563
Brian Salomonc3833b42018-07-09 18:23:58 +0000564 case Element::DeviceSpaceType::kRect:
Chris Daltona32a3c32017-12-05 10:05:21 -0700565 SkASSERT(!element->isInverseFilled());
Chris Dalton584a79a2017-11-15 13:14:01 -0700566 if (fWindowRects.count() < fMaxWindowRectangles) {
567 // Clip out the inside of every rect. We won't be able to entirely skip the AA ones,
568 // but it saves processing time.
569 this->addWindowRectangle(element->getDeviceSpaceRect(), element->isAA());
570 if (!element->isAA()) {
571 return ClipResult::kClipped;
572 }
573 }
574 return this->addAnalyticFP(element->getDeviceSpaceRect(), Invert::kYes,
Chris Dalton3b51df12017-11-27 14:33:06 -0700575 GrAA(element->isAA()));
Chris Dalton79471932017-10-27 01:50:57 -0600576
Brian Salomonc3833b42018-07-09 18:23:58 +0000577 case Element::DeviceSpaceType::kRRect: {
Chris Daltona32a3c32017-12-05 10:05:21 -0700578 SkASSERT(!element->isInverseFilled());
Brian Osman554c1f02017-11-16 13:56:47 +0000579 const SkRRect& clipRRect = element->getDeviceSpaceRRect();
Chris Dalton3b51df12017-11-27 14:33:06 -0700580 ClipResult clipResult = this->addAnalyticFP(clipRRect, Invert::kYes,
581 GrAA(element->isAA()));
Chris Dalton584a79a2017-11-15 13:14:01 -0700582 if (fWindowRects.count() >= fMaxWindowRectangles) {
583 return clipResult;
584 }
585
586 // Clip out the interiors of round rects with two window rectangles in the shape of a
587 // "plus". This doesn't let us skip the clip element, but still saves processing time.
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700588 SkVector insetTL = clipRRect.radii(SkRRect::kUpperLeft_Corner);
589 SkVector insetBR = clipRRect.radii(SkRRect::kLowerRight_Corner);
590 if (SkRRect::kComplex_Type == clipRRect.getType()) {
591 const SkVector& insetTR = clipRRect.radii(SkRRect::kUpperRight_Corner);
592 const SkVector& insetBL = clipRRect.radii(SkRRect::kLowerLeft_Corner);
Brian Osman788b9162020-02-07 10:36:46 -0500593 insetTL.fX = std::max(insetTL.x(), insetBL.x());
594 insetTL.fY = std::max(insetTL.y(), insetTR.y());
595 insetBR.fX = std::max(insetBR.x(), insetTR.x());
596 insetBR.fY = std::max(insetBR.y(), insetBL.y());
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700597 }
598 const SkRect& bounds = clipRRect.getBounds();
599 if (insetTL.x() + insetBR.x() >= bounds.width() ||
600 insetTL.y() + insetBR.y() >= bounds.height()) {
Chris Dalton584a79a2017-11-15 13:14:01 -0700601 return clipResult; // The interior "plus" is empty.
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700602 }
603
604 SkRect horzRect = SkRect::MakeLTRB(bounds.left(), bounds.top() + insetTL.y(),
605 bounds.right(), bounds.bottom() - insetBR.y());
606 this->addWindowRectangle(horzRect, element->isAA());
Chris Dalton584a79a2017-11-15 13:14:01 -0700607
608 if (fWindowRects.count() < fMaxWindowRectangles) {
609 SkRect vertRect = SkRect::MakeLTRB(bounds.left() + insetTL.x(), bounds.top(),
610 bounds.right() - insetBR.x(), bounds.bottom());
611 this->addWindowRectangle(vertRect, element->isAA());
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700612 }
613
Chris Dalton584a79a2017-11-15 13:14:01 -0700614 return clipResult;
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700615 }
Chris Dalton79471932017-10-27 01:50:57 -0600616
Brian Salomonc3833b42018-07-09 18:23:58 +0000617 case Element::DeviceSpaceType::kPath:
Chris Daltona32a3c32017-12-05 10:05:21 -0700618 return this->addAnalyticFP(element->getDeviceSpacePath(),
619 Invert(!element->isInverseFilled()), GrAA(element->isAA()));
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400620
621 case Element::DeviceSpaceType::kShader:
622 SkUNREACHABLE;
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700623 }
Chris Dalton79471932017-10-27 01:50:57 -0600624
625 SK_ABORT("Unexpected DeviceSpaceType");
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700626}
627
628inline void GrReducedClip::addWindowRectangle(const SkRect& elementInteriorRect, bool elementIsAA) {
629 SkIRect window;
630 if (!elementIsAA) {
631 elementInteriorRect.round(&window);
632 } else {
633 elementInteriorRect.roundIn(&window);
634 }
635 if (!window.isEmpty()) { // Skip very thin windows that round to zero or negative dimensions.
636 fWindowRects.addWindow(window);
637 }
638}
639
Chris Daltona32a3c32017-12-05 10:05:21 -0700640GrClipEdgeType GrReducedClip::GetClipEdgeType(Invert invert, GrAA aa) {
641 if (Invert::kNo == invert) {
642 return (GrAA::kYes == aa) ? GrClipEdgeType::kFillAA : GrClipEdgeType::kFillBW;
643 } else {
644 return (GrAA::kYes == aa) ? GrClipEdgeType::kInverseFillAA : GrClipEdgeType::kInverseFillBW;
645 }
646}
647
Ethan Nicholaseace9352018-10-15 20:09:54 +0000648GrReducedClip::ClipResult GrReducedClip::addAnalyticFP(const SkRect& deviceSpaceRect,
649 Invert invert, GrAA aa) {
Chris Daltona32a3c32017-12-05 10:05:21 -0700650 if (this->numAnalyticFPs() >= fMaxAnalyticFPs) {
Chris Dalton584a79a2017-11-15 13:14:01 -0700651 return ClipResult::kNotClipped;
652 }
653
Ethan Nicholaseace9352018-10-15 20:09:54 +0000654 fAnalyticFPs.push_back(GrAARectEffect::Make(GetClipEdgeType(invert, aa), deviceSpaceRect));
Chris Daltona32a3c32017-12-05 10:05:21 -0700655 SkASSERT(fAnalyticFPs.back());
656
657 return ClipResult::kClipped;
658}
659
660GrReducedClip::ClipResult GrReducedClip::addAnalyticFP(const SkRRect& deviceSpaceRRect,
661 Invert invert, GrAA aa) {
662 if (this->numAnalyticFPs() >= fMaxAnalyticFPs) {
663 return ClipResult::kNotClipped;
Chris Dalton584a79a2017-11-15 13:14:01 -0700664 }
665
Ethan Nicholaseace9352018-10-15 20:09:54 +0000666 if (auto fp = GrRRectEffect::Make(GetClipEdgeType(invert, aa), deviceSpaceRRect,
667 *fCaps->shaderCaps())) {
Chris Dalton584a79a2017-11-15 13:14:01 -0700668 fAnalyticFPs.push_back(std::move(fp));
669 return ClipResult::kClipped;
670 }
671
Chris Daltona32a3c32017-12-05 10:05:21 -0700672 SkPath deviceSpacePath;
673 deviceSpacePath.setIsVolatile(true);
674 deviceSpacePath.addRRect(deviceSpaceRRect);
675 return this->addAnalyticFP(deviceSpacePath, invert, aa);
676}
677
678GrReducedClip::ClipResult GrReducedClip::addAnalyticFP(const SkPath& deviceSpacePath,
679 Invert invert, GrAA aa) {
680 if (this->numAnalyticFPs() >= fMaxAnalyticFPs) {
681 return ClipResult::kNotClipped;
682 }
683
Ethan Nicholaseace9352018-10-15 20:09:54 +0000684 if (auto fp = GrConvexPolyEffect::Make(GetClipEdgeType(invert, aa), deviceSpacePath)) {
Chris Daltona32a3c32017-12-05 10:05:21 -0700685 fAnalyticFPs.push_back(std::move(fp));
686 return ClipResult::kClipped;
687 }
688
Chris Dalton1dec19a2018-04-27 13:05:19 -0600689 if (fCCPRClipPaths.count() < fMaxCCPRClipPaths && GrAA::kYes == aa) {
Chris Daltona32a3c32017-12-05 10:05:21 -0700690 // 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 -0400691 // opsTask they will operate in.
Chris Daltona32a3c32017-12-05 10:05:21 -0700692 SkPath& ccprClipPath = fCCPRClipPaths.push_back(deviceSpacePath);
693 if (Invert::kYes == invert) {
694 ccprClipPath.toggleInverseFillType();
695 }
696 return ClipResult::kClipped;
697 }
698
Chris Dalton584a79a2017-11-15 13:14:01 -0700699 return ClipResult::kNotClipped;
700}
701
Chris Dalton79471932017-10-27 01:50:57 -0600702void GrReducedClip::makeEmpty() {
703 fHasScissor = false;
704 fAAClipRectGenID = SK_InvalidGenID;
705 fWindowRects.reset();
706 fMaskElements.reset();
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400707 fShader.reset();
Chris Dalton79471932017-10-27 01:50:57 -0600708 fInitialState = InitialState::kAllOut;
bsalomon@google.com34cd70a2012-12-06 14:23:20 +0000709}
csmartdaltonbde96c62016-08-31 12:54:46 -0700710
711////////////////////////////////////////////////////////////////////////////////
Chris Daltonc5348082018-03-30 15:59:38 +0000712// Create a 8-bit clip mask in alpha
713
714static bool stencil_element(GrRenderTargetContext* rtc,
715 const GrFixedClip& clip,
716 const GrUserStencilSettings* ss,
717 const SkMatrix& viewMatrix,
Brian Salomonc3833b42018-07-09 18:23:58 +0000718 const SkClipStack::Element* element) {
719 GrAA aa = GrAA(element->isAA());
720 switch (element->getDeviceSpaceType()) {
Chris Daltonc5348082018-03-30 15:59:38 +0000721 case SkClipStack::Element::DeviceSpaceType::kEmpty:
722 SkDEBUGFAIL("Should never get here with an empty element.");
723 break;
Michael Ludwigaa1b6b32019-05-29 14:43:13 -0400724 case SkClipStack::Element::DeviceSpaceType::kRect: {
725 GrPaint paint;
726 paint.setCoverageSetOpXPFactory((SkRegion::Op)element->getOp(),
727 element->isInverseFilled());
Michael Ludwig7c12e282020-05-29 09:54:07 -0400728 rtc->priv().stencilRect(&clip, ss, std::move(paint), aa, viewMatrix,
Michael Ludwigaa1b6b32019-05-29 14:43:13 -0400729 element->getDeviceSpaceRect());
730 return true;
731 }
Chris Daltonc5348082018-03-30 15:59:38 +0000732 default: {
733 SkPath path;
Brian Salomonc3833b42018-07-09 18:23:58 +0000734 element->asDeviceSpacePath(&path);
Chris Daltonc5348082018-03-30 15:59:38 +0000735 if (path.isInverseFillType()) {
736 path.toggleInverseFillType();
737 }
738
Michael Ludwig7c12e282020-05-29 09:54:07 -0400739 return rtc->priv().drawAndStencilPath(&clip, ss, (SkRegion::Op)element->getOp(),
Brian Salomonc3833b42018-07-09 18:23:58 +0000740 element->isInverseFilled(), aa, viewMatrix, path);
Chris Daltonc5348082018-03-30 15:59:38 +0000741 }
742 }
743
744 return false;
745}
746
747static void draw_element(GrRenderTargetContext* rtc,
748 const GrClip& clip, // TODO: can this just always be WideOpen?
749 GrPaint&& paint,
750 GrAA aa,
751 const SkMatrix& viewMatrix,
Brian Salomonc3833b42018-07-09 18:23:58 +0000752 const SkClipStack::Element* element) {
Chris Daltonc5348082018-03-30 15:59:38 +0000753 // TODO: Draw rrects directly here.
Brian Salomonc3833b42018-07-09 18:23:58 +0000754 switch (element->getDeviceSpaceType()) {
Chris Daltonc5348082018-03-30 15:59:38 +0000755 case SkClipStack::Element::DeviceSpaceType::kEmpty:
756 SkDEBUGFAIL("Should never get here with an empty element.");
757 break;
758 case SkClipStack::Element::DeviceSpaceType::kRect:
Michael Ludwig7c12e282020-05-29 09:54:07 -0400759 rtc->drawRect(&clip, std::move(paint), aa, viewMatrix, element->getDeviceSpaceRect());
Chris Daltonc5348082018-03-30 15:59:38 +0000760 break;
761 default: {
762 SkPath path;
Brian Salomonc3833b42018-07-09 18:23:58 +0000763 element->asDeviceSpacePath(&path);
Chris Daltonc5348082018-03-30 15:59:38 +0000764 if (path.isInverseFillType()) {
765 path.toggleInverseFillType();
766 }
767
Michael Ludwig7c12e282020-05-29 09:54:07 -0400768 rtc->drawPath(&clip, std::move(paint), aa, viewMatrix, path, GrStyle::SimpleFill());
Chris Daltonc5348082018-03-30 15:59:38 +0000769 break;
770 }
771 }
772}
773
774bool GrReducedClip::drawAlphaClipMask(GrRenderTargetContext* rtc) const {
775 // The texture may be larger than necessary, this rect represents the part of the texture
776 // we populate with a rasterization of the clip.
Michael Ludwigd1d997e2020-06-04 15:52:44 -0400777 GrFixedClip clip(rtc->dimensions(), SkIRect::MakeWH(fScissor.width(), fScissor.height()));
Chris Daltonc5348082018-03-30 15:59:38 +0000778
779 if (!fWindowRects.empty()) {
780 clip.setWindowRectangles(fWindowRects.makeOffset(-fScissor.left(), -fScissor.top()),
781 GrWindowRectsState::Mode::kExclusive);
782 }
783
784 // The scratch texture that we are drawing into can be substantially larger than the mask. Only
785 // clear the part that we care about.
Brian Osman9a9baae2018-11-05 15:06:26 -0500786 SkPMColor4f initialCoverage =
787 InitialState::kAllIn == this->initialState() ? SK_PMColor4fWHITE : SK_PMColor4fTRANSPARENT;
Michael Ludwig81d41722020-05-26 16:57:38 -0400788 if (clip.hasWindowRectangles()) {
789 GrPaint paint;
790 paint.setColor4f(initialCoverage);
791 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
Michael Ludwig7c12e282020-05-29 09:54:07 -0400792 rtc->drawRect(&clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
Michael Ludwig81d41722020-05-26 16:57:38 -0400793 SkRect::Make(clip.scissorRect()));
794 } else {
795 rtc->priv().clearAtLeast(clip.scissorRect(), initialCoverage);
796 }
Chris Daltonc5348082018-03-30 15:59:38 +0000797
798 // Set the matrix so that rendered clip elements are transformed to mask space from clip space.
799 SkMatrix translate;
800 translate.setTranslate(SkIntToScalar(-fScissor.left()), SkIntToScalar(-fScissor.top()));
801
802 // walk through each clip element and perform its set op
803 for (ElementList::Iter iter(fMaskElements); iter.get(); iter.next()) {
Brian Salomonc3833b42018-07-09 18:23:58 +0000804 const Element* element = iter.get();
805 SkRegion::Op op = (SkRegion::Op)element->getOp();
806 GrAA aa = GrAA(element->isAA());
807 bool invert = element->isInverseFilled();
Chris Daltonc5348082018-03-30 15:59:38 +0000808 if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) {
809 // draw directly into the result with the stencil set to make the pixels affected
810 // by the clip shape be non-zero.
811 static constexpr GrUserStencilSettings kStencilInElement(
812 GrUserStencilSettings::StaticInit<
813 0xffff,
814 GrUserStencilTest::kAlways,
815 0xffff,
816 GrUserStencilOp::kReplace,
817 GrUserStencilOp::kReplace,
818 0xffff>()
819 );
820 if (!stencil_element(rtc, clip, &kStencilInElement, translate, element)) {
821 return false;
822 }
823
824 // Draw to the exterior pixels (those with a zero stencil value).
825 static constexpr GrUserStencilSettings kDrawOutsideElement(
826 GrUserStencilSettings::StaticInit<
827 0x0000,
828 GrUserStencilTest::kEqual,
829 0xffff,
830 GrUserStencilOp::kZero,
831 GrUserStencilOp::kZero,
832 0xffff>()
833 );
Michael Ludwigaa1b6b32019-05-29 14:43:13 -0400834
835 GrPaint paint;
836 paint.setCoverageSetOpXPFactory(op, !invert);
Michael Ludwig7c12e282020-05-29 09:54:07 -0400837 rtc->priv().stencilRect(&clip, &kDrawOutsideElement, std::move(paint), GrAA::kNo,
Michael Ludwigaa1b6b32019-05-29 14:43:13 -0400838 translate, SkRect::Make(fScissor));
Chris Daltonc5348082018-03-30 15:59:38 +0000839 } else {
840 // all the remaining ops can just be directly draw into the accumulation buffer
841 GrPaint paint;
842 paint.setCoverageSetOpXPFactory(op, false);
843
844 draw_element(rtc, clip, std::move(paint), aa, translate, element);
845 }
846 }
847
848 return true;
849}
850
851////////////////////////////////////////////////////////////////////////////////
csmartdaltonbde96c62016-08-31 12:54:46 -0700852// Create a 1-bit clip mask in the stencil buffer.
853
Robert Phillips6f0e02f2019-02-13 11:02:28 -0500854bool GrReducedClip::drawStencilClipMask(GrRecordingContext* context,
Ethan Nicholaseace9352018-10-15 20:09:54 +0000855 GrRenderTargetContext* renderTargetContext) const {
Michael Ludwig828d3412020-05-12 13:15:35 -0400856 GrStencilMaskHelper helper(context, renderTargetContext);
857 if (!helper.init(fScissor, this->maskGenID(), fWindowRects, this->numAnalyticFPs())) {
858 // The stencil mask doesn't need updating
859 return true;
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700860 }
861
Michael Ludwig828d3412020-05-12 13:15:35 -0400862 helper.clear(InitialState::kAllIn == this->initialState());
csmartdaltonbde96c62016-08-31 12:54:46 -0700863
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500864 // walk through each clip element and perform its set op with the existing clip.
Chris Dalton79471932017-10-27 01:50:57 -0600865 for (ElementList::Iter iter(fMaskElements); iter.get(); iter.next()) {
Brian Salomonc3833b42018-07-09 18:23:58 +0000866 const Element* element = iter.get();
Michael Ludwigde228e52020-05-12 16:17:20 +0000867 SkRegion::Op op = (SkRegion::Op)element->getOp();
Michael Ludwig828d3412020-05-12 13:15:35 -0400868 GrAA aa = element->isAA() ? GrAA::kYes : GrAA::kNo;
Michael Ludwigde228e52020-05-12 16:17:20 +0000869
Brian Salomonc3833b42018-07-09 18:23:58 +0000870 if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) {
Michael Ludwig828d3412020-05-12 13:15:35 -0400871 helper.drawRect(element->getDeviceSpaceRect(), SkMatrix::I(), op, aa);
csmartdaltonbde96c62016-08-31 12:54:46 -0700872 } else {
Michael Ludwig828d3412020-05-12 13:15:35 -0400873 SkPath path;
874 element->asDeviceSpacePath(&path);
875 if (!helper.drawPath(path, SkMatrix::I(), op, aa)) {
csmartdaltonbde96c62016-08-31 12:54:46 -0700876 return false;
877 }
878 }
Michael Ludwigde228e52020-05-12 16:17:20 +0000879 }
Michael Ludwig828d3412020-05-12 13:15:35 -0400880
881 helper.finish();
csmartdaltonbde96c62016-08-31 12:54:46 -0700882 return true;
883}
Chris Daltona32a3c32017-12-05 10:05:21 -0700884
Robert Phillips777707b2018-01-17 11:40:14 -0500885std::unique_ptr<GrFragmentProcessor> GrReducedClip::finishAndDetachAnalyticFPs(
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400886 GrRecordingContext* context, const SkMatrixProvider& matrixProvider,
Greg Danielf41b2bd2019-08-22 16:19:24 -0400887 GrCoverageCountingPathRenderer* ccpr, uint32_t opsTaskID) {
Chris Daltona32a3c32017-12-05 10:05:21 -0700888 // Make sure finishAndDetachAnalyticFPs hasn't been called already.
889 SkDEBUGCODE(for (const auto& fp : fAnalyticFPs) { SkASSERT(fp); })
890
891 if (!fCCPRClipPaths.empty()) {
892 fAnalyticFPs.reserve(fAnalyticFPs.count() + fCCPRClipPaths.count());
893 for (const SkPath& ccprClipPath : fCCPRClipPaths) {
Chris Dalton1dec19a2018-04-27 13:05:19 -0600894 SkASSERT(ccpr);
Chris Daltona32a3c32017-12-05 10:05:21 -0700895 SkASSERT(fHasScissor);
Greg Danielf41b2bd2019-08-22 16:19:24 -0400896 auto fp = ccpr->makeClipProcessor(opsTaskID, ccprClipPath, fScissor, *fCaps);
Chris Daltona32a3c32017-12-05 10:05:21 -0700897 fAnalyticFPs.push_back(std::move(fp));
898 }
899 fCCPRClipPaths.reset();
900 }
901
Michael Ludwig4e221bd2020-06-05 11:29:36 -0400902 static const GrColorInfo kCoverageColorInfo = GrColorInfo(GrColorType::kUnknown,
903 kPremul_SkAlphaType,
904 nullptr);
905 if (fShader) {
906 GrFPArgs args(context, matrixProvider, kNone_SkFilterQuality, &kCoverageColorInfo);
907 auto fp = as_SB(fShader)->asFragmentProcessor(args);
908 if (fp) {
909 fp = GrFragmentProcessor::SwizzleOutput(std::move(fp), GrSwizzle::AAAA());
910 fAnalyticFPs.push_back(std::move(fp));
911 }
912 }
913
Chris Daltona32a3c32017-12-05 10:05:21 -0700914 return GrFragmentProcessor::RunInSeries(fAnalyticFPs.begin(), fAnalyticFPs.count());
915}