blob: b25098e624c31ed0e39919f6fff4c13f8dcfc804 [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);
csmartdaltoncbecb082016-07-22 08:59:08 -0700118 }
119
Brian Salomonc3833b42018-07-09 18:23:58 +0000120 if (SK_InvalidGenID != fAAClipRectGenID && // Is there an AA clip rect?
121 ClipResult::kNotClipped == this->addAnalyticFP(fAAClipRect, Invert::kNo, GrAA::kYes)) {
122 if (fMaskElements.isEmpty()) {
123 // Use a replace since it is faster than intersect.
124 fMaskElements.addToHead(fAAClipRect, SkMatrix::I(), kReplace_SkClipOp, true /*doAA*/);
125 fInitialState = InitialState::kAllOut;
Chris Dalton79471932017-10-27 01:50:57 -0600126 } else {
Brian Salomonc3833b42018-07-09 18:23:58 +0000127 fMaskElements.addToTail(fAAClipRect, SkMatrix::I(), kIntersect_SkClipOp, true /*doAA*/);
Chris Dalton79471932017-10-27 01:50:57 -0600128 }
Brian Salomonc3833b42018-07-09 18:23:58 +0000129 fMaskRequiresAA = true;
130 fMaskGenID = fAAClipRectGenID;
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700131 }
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700132}
133
Chris Dalton584a79a2017-11-15 13:14:01 -0700134void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBounds) {
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700135 // walk backwards until we get to:
136 // a) the beginning
137 // b) an operation that is known to make the bounds all inside/outside
138 // c) a replace operation
139
140 enum class InitialTriState {
141 kUnknown = -1,
142 kAllIn = (int)GrReducedClip::InitialState::kAllIn,
143 kAllOut = (int)GrReducedClip::InitialState::kAllOut
144 } initialTriState = InitialTriState::kUnknown;
145
146 // During our backwards walk, track whether we've seen ops that either grow or shrink the clip.
147 // TODO: track these per saved clip so that we can consider them on the forward pass.
148 bool embiggens = false;
149 bool emsmallens = false;
150
151 // We use a slightly relaxed set of query bounds for element containment tests. This is to
152 // account for floating point rounding error that may have occurred during coord transforms.
153 SkRect relaxedQueryBounds = queryBounds.makeInset(GrClip::kBoundsTolerance,
154 GrClip::kBoundsTolerance);
Chris Dalton69824002017-10-31 00:37:52 -0600155 if (relaxedQueryBounds.isEmpty()) {
156 relaxedQueryBounds = queryBounds;
157 }
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700158
159 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
160 int numAAElements = 0;
161 while (InitialTriState::kUnknown == initialTriState) {
Brian Salomonc3833b42018-07-09 18:23:58 +0000162 const Element* element = iter.prev();
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700163 if (nullptr == element) {
164 initialTriState = InitialTriState::kAllIn;
165 break;
166 }
167 if (SkClipStack::kEmptyGenID == element->getGenID()) {
168 initialTriState = InitialTriState::kAllOut;
169 break;
170 }
171 if (SkClipStack::kWideOpenGenID == element->getGenID()) {
172 initialTriState = InitialTriState::kAllIn;
173 break;
174 }
175
176 bool skippable = false;
177 bool isFlip = false; // does this op just flip the in/out state of every point in the bounds
178
179 switch (element->getOp()) {
Mike Reedc1f77742016-12-09 09:00:50 -0500180 case kDifference_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700181 // check if the shape subtracted either contains the entire bounds (and makes
182 // the clip empty) or is outside the bounds and therefore can be skipped.
183 if (element->isInverseFilled()) {
184 if (element->contains(relaxedQueryBounds)) {
185 skippable = true;
186 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
187 initialTriState = InitialTriState::kAllOut;
188 skippable = true;
Chris Daltona32a3c32017-12-05 10:05:21 -0700189 } else if (!embiggens) {
190 ClipResult result = this->clipInsideElement(element);
191 if (ClipResult::kMadeEmpty == result) {
192 return;
193 }
194 skippable = (ClipResult::kClipped == result);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700195 }
196 } else {
197 if (element->contains(relaxedQueryBounds)) {
198 initialTriState = InitialTriState::kAllOut;
199 skippable = true;
200 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
201 skippable = true;
Chris Dalton79471932017-10-27 01:50:57 -0600202 } else if (!embiggens) {
Chris Dalton584a79a2017-11-15 13:14:01 -0700203 ClipResult result = this->clipOutsideElement(element);
Chris Dalton79471932017-10-27 01:50:57 -0600204 if (ClipResult::kMadeEmpty == result) {
205 return;
206 }
207 skippable = (ClipResult::kClipped == result);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700208 }
209 }
210 if (!skippable) {
211 emsmallens = true;
212 }
213 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500214 case kIntersect_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700215 // check if the shape intersected contains the entire bounds and therefore can
216 // be skipped or it is outside the entire bounds and therefore makes the clip
217 // empty.
218 if (element->isInverseFilled()) {
219 if (element->contains(relaxedQueryBounds)) {
220 initialTriState = InitialTriState::kAllOut;
221 skippable = true;
222 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
223 skippable = true;
Chris Daltona32a3c32017-12-05 10:05:21 -0700224 } else if (!embiggens) {
225 ClipResult result = this->clipOutsideElement(element);
226 if (ClipResult::kMadeEmpty == result) {
227 return;
228 }
229 skippable = (ClipResult::kClipped == result);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700230 }
231 } else {
232 if (element->contains(relaxedQueryBounds)) {
233 skippable = true;
234 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
235 initialTriState = InitialTriState::kAllOut;
236 skippable = true;
Chris Dalton79471932017-10-27 01:50:57 -0600237 } else if (!embiggens) {
238 ClipResult result = this->clipInsideElement(element);
239 if (ClipResult::kMadeEmpty == result) {
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700240 return;
241 }
Chris Dalton79471932017-10-27 01:50:57 -0600242 skippable = (ClipResult::kClipped == result);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700243 }
244 }
245 if (!skippable) {
246 emsmallens = true;
247 }
248 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500249 case kUnion_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700250 // If the union-ed shape contains the entire bounds then after this element
251 // the bounds is entirely inside the clip. If the union-ed shape is outside the
252 // bounds then this op can be skipped.
253 if (element->isInverseFilled()) {
254 if (element->contains(relaxedQueryBounds)) {
255 skippable = true;
256 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
257 initialTriState = InitialTriState::kAllIn;
258 skippable = true;
259 }
260 } else {
261 if (element->contains(relaxedQueryBounds)) {
262 initialTriState = InitialTriState::kAllIn;
263 skippable = true;
264 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
265 skippable = true;
266 }
267 }
268 if (!skippable) {
269 embiggens = true;
270 }
271 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500272 case kXOR_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700273 // If the bounds is entirely inside the shape being xor-ed then the effect is
274 // to flip the inside/outside state of every point in the bounds. We may be
275 // able to take advantage of this in the forward pass. If the xor-ed shape
276 // doesn't intersect the bounds then it can be skipped.
277 if (element->isInverseFilled()) {
278 if (element->contains(relaxedQueryBounds)) {
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 skippable = true;
288 }
289 }
290 if (!skippable) {
291 emsmallens = embiggens = true;
292 }
293 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500294 case kReverseDifference_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700295 // When the bounds is entirely within the rev-diff shape then this behaves like xor
296 // and reverses every point inside the bounds. If the shape is completely outside
297 // the bounds then we know after this element is applied that the bounds will be
298 // all outside the current clip.B
299 if (element->isInverseFilled()) {
300 if (element->contains(relaxedQueryBounds)) {
301 initialTriState = InitialTriState::kAllOut;
302 skippable = true;
303 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
304 isFlip = true;
305 }
306 } else {
307 if (element->contains(relaxedQueryBounds)) {
308 isFlip = true;
309 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
310 initialTriState = InitialTriState::kAllOut;
311 skippable = true;
312 }
313 }
314 if (!skippable) {
315 emsmallens = embiggens = true;
316 }
317 break;
318
Mike Reedc1f77742016-12-09 09:00:50 -0500319 case kReplace_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700320 // Replace will always terminate our walk. We will either begin the forward walk
321 // at the replace op or detect here than the shape is either completely inside
322 // or completely outside the bounds. In this latter case it can be skipped by
323 // setting the correct value for initialTriState.
324 if (element->isInverseFilled()) {
325 if (element->contains(relaxedQueryBounds)) {
326 initialTriState = InitialTriState::kAllOut;
327 skippable = true;
328 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
329 initialTriState = InitialTriState::kAllIn;
330 skippable = true;
Chris Daltona32a3c32017-12-05 10:05:21 -0700331 } else if (!embiggens) {
332 ClipResult result = this->clipOutsideElement(element);
333 if (ClipResult::kMadeEmpty == result) {
334 return;
335 }
336 if (ClipResult::kClipped == result) {
337 initialTriState = InitialTriState::kAllIn;
338 skippable = true;
339 }
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700340 }
341 } else {
342 if (element->contains(relaxedQueryBounds)) {
343 initialTriState = InitialTriState::kAllIn;
344 skippable = true;
345 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
346 initialTriState = InitialTriState::kAllOut;
347 skippable = true;
Chris Dalton79471932017-10-27 01:50:57 -0600348 } else if (!embiggens) {
349 ClipResult result = this->clipInsideElement(element);
350 if (ClipResult::kMadeEmpty == result) {
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700351 return;
352 }
Chris Dalton79471932017-10-27 01:50:57 -0600353 if (ClipResult::kClipped == result) {
354 initialTriState = InitialTriState::kAllIn;
355 skippable = true;
356 }
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700357 }
358 }
359 if (!skippable) {
360 initialTriState = InitialTriState::kAllOut;
361 embiggens = emsmallens = true;
362 }
363 break;
364 default:
365 SkDEBUGFAIL("Unexpected op.");
366 break;
367 }
368 if (!skippable) {
Brian Salomonc3833b42018-07-09 18:23:58 +0000369 if (fMaskElements.isEmpty()) {
370 // This will be the last element. Record the stricter genID.
371 fMaskGenID = element->getGenID();
372 }
373
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700374 // if it is a flip, change it to a bounds-filling rect
375 if (isFlip) {
Mike Reedc1f77742016-12-09 09:00:50 -0500376 SkASSERT(kXOR_SkClipOp == element->getOp() ||
377 kReverseDifference_SkClipOp == element->getOp());
Brian Salomonc3833b42018-07-09 18:23:58 +0000378 fMaskElements.addToHead(SkRect::Make(fScissor), SkMatrix::I(),
379 kReverseDifference_SkClipOp, false);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700380 } else {
Brian Salomonc3833b42018-07-09 18:23:58 +0000381 Element* newElement = fMaskElements.addToHead(*element);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700382 if (newElement->isAA()) {
383 ++numAAElements;
384 }
385 // Intersecting an inverse shape is the same as differencing the non-inverse shape.
386 // Replacing with an inverse shape is the same as setting initialState=kAllIn and
387 // differencing the non-inverse shape.
Mike Reedc1f77742016-12-09 09:00:50 -0500388 bool isReplace = kReplace_SkClipOp == newElement->getOp();
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700389 if (newElement->isInverseFilled() &&
Mike Reedc1f77742016-12-09 09:00:50 -0500390 (kIntersect_SkClipOp == newElement->getOp() || isReplace)) {
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700391 newElement->invertShapeFillType();
Mike Reedc1f77742016-12-09 09:00:50 -0500392 newElement->setOp(kDifference_SkClipOp);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700393 if (isReplace) {
394 SkASSERT(InitialTriState::kAllOut == initialTriState);
395 initialTriState = InitialTriState::kAllIn;
396 }
397 }
398 }
399 }
400 }
401
402 if ((InitialTriState::kAllOut == initialTriState && !embiggens) ||
403 (InitialTriState::kAllIn == initialTriState && !emsmallens)) {
Chris Dalton79471932017-10-27 01:50:57 -0600404 fMaskElements.reset();
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700405 numAAElements = 0;
406 } else {
Brian Salomonc3833b42018-07-09 18:23:58 +0000407 Element* element = fMaskElements.headIter().get();
408 while (element) {
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700409 bool skippable = false;
410 switch (element->getOp()) {
Mike Reedc1f77742016-12-09 09:00:50 -0500411 case kDifference_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700412 // subtracting from the empty set yields the empty set.
413 skippable = InitialTriState::kAllOut == initialTriState;
414 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500415 case kIntersect_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700416 // intersecting with the empty set yields the empty set
417 if (InitialTriState::kAllOut == initialTriState) {
418 skippable = true;
419 } else {
420 // We can clear to zero and then simply draw the clip element.
421 initialTriState = InitialTriState::kAllOut;
Mike Reedc1f77742016-12-09 09:00:50 -0500422 element->setOp(kReplace_SkClipOp);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700423 }
424 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500425 case kUnion_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700426 if (InitialTriState::kAllIn == initialTriState) {
427 // unioning the infinite plane with anything is a no-op.
428 skippable = true;
429 } else {
430 // unioning the empty set with a shape is the shape.
Mike Reedc1f77742016-12-09 09:00:50 -0500431 element->setOp(kReplace_SkClipOp);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700432 }
433 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500434 case kXOR_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700435 if (InitialTriState::kAllOut == initialTriState) {
436 // xor could be changed to diff in the kAllIn case, not sure it's a win.
Mike Reedc1f77742016-12-09 09:00:50 -0500437 element->setOp(kReplace_SkClipOp);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700438 }
439 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500440 case kReverseDifference_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700441 if (InitialTriState::kAllIn == initialTriState) {
442 // subtracting the whole plane will yield the empty set.
443 skippable = true;
444 initialTriState = InitialTriState::kAllOut;
445 } else {
446 // this picks up flips inserted in the backwards pass.
447 skippable = element->isInverseFilled() ?
448 GrClip::IsOutsideClip(element->getBounds(), queryBounds) :
449 element->contains(relaxedQueryBounds);
450 if (skippable) {
451 initialTriState = InitialTriState::kAllIn;
452 } else {
Mike Reedc1f77742016-12-09 09:00:50 -0500453 element->setOp(kReplace_SkClipOp);
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700454 }
455 }
456 break;
Mike Reedc1f77742016-12-09 09:00:50 -0500457 case kReplace_SkClipOp:
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700458 skippable = false; // we would have skipped it in the backwards walk if we
459 // could've.
460 break;
461 default:
462 SkDEBUGFAIL("Unexpected op.");
463 break;
464 }
Brian Salomonc3833b42018-07-09 18:23:58 +0000465 if (!skippable) {
466 break;
467 } else {
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700468 if (element->isAA()) {
469 --numAAElements;
470 }
Chris Dalton79471932017-10-27 01:50:57 -0600471 fMaskElements.popHead();
Brian Salomonc3833b42018-07-09 18:23:58 +0000472 element = fMaskElements.headIter().get();
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700473 }
474 }
475 }
Chris Dalton79471932017-10-27 01:50:57 -0600476 fMaskRequiresAA = numAAElements > 0;
csmartdalton5ecbbbe2016-08-23 13:26:40 -0700477
478 SkASSERT(InitialTriState::kUnknown != initialTriState);
479 fInitialState = static_cast<GrReducedClip::InitialState>(initialTriState);
480}
481
Brian Salomonc3833b42018-07-09 18:23:58 +0000482GrReducedClip::ClipResult GrReducedClip::clipInsideElement(const Element* element) {
Chris Dalton79471932017-10-27 01:50:57 -0600483 SkIRect elementIBounds;
484 if (!element->isAA()) {
485 element->getBounds().round(&elementIBounds);
486 } else {
487 elementIBounds = GrClip::GetPixelIBounds(element->getBounds());
488 }
489 SkASSERT(fHasScissor);
490 if (!fScissor.intersect(elementIBounds)) {
491 this->makeEmpty();
492 return ClipResult::kMadeEmpty;
493 }
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700494
Chris Dalton79471932017-10-27 01:50:57 -0600495 switch (element->getDeviceSpaceType()) {
Brian Salomonc3833b42018-07-09 18:23:58 +0000496 case Element::DeviceSpaceType::kEmpty:
Chris Dalton79471932017-10-27 01:50:57 -0600497 return ClipResult::kMadeEmpty;
498
Brian Salomonc3833b42018-07-09 18:23:58 +0000499 case Element::DeviceSpaceType::kRect:
Chris Dalton79471932017-10-27 01:50:57 -0600500 SkASSERT(element->getBounds() == element->getDeviceSpaceRect());
Chris Daltona32a3c32017-12-05 10:05:21 -0700501 SkASSERT(!element->isInverseFilled());
Chris Dalton79471932017-10-27 01:50:57 -0600502 if (element->isAA()) {
Brian Salomonc3833b42018-07-09 18:23:58 +0000503 if (SK_InvalidGenID == fAAClipRectGenID) { // No AA clip rect yet?
Chris Dalton79471932017-10-27 01:50:57 -0600504 fAAClipRect = element->getDeviceSpaceRect();
Brian Salomonc3833b42018-07-09 18:23:58 +0000505 // fAAClipRectGenID is the value we should use for fMaskGenID if we end up
506 // moving the AA clip rect into the mask. The mask GenID is simply the topmost
507 // element's GenID. And since we walk the stack backwards, this means it's just
508 // the first element we don't skip during our walk.
509 fAAClipRectGenID = fMaskElements.isEmpty() ? element->getGenID() : fMaskGenID;
510 SkASSERT(SK_InvalidGenID != fAAClipRectGenID);
Chris Dalton79471932017-10-27 01:50:57 -0600511 } else if (!fAAClipRect.intersect(element->getDeviceSpaceRect())) {
512 this->makeEmpty();
513 return ClipResult::kMadeEmpty;
514 }
515 }
516 return ClipResult::kClipped;
517
Brian Salomonc3833b42018-07-09 18:23:58 +0000518 case Element::DeviceSpaceType::kRRect:
Chris Daltona32a3c32017-12-05 10:05:21 -0700519 SkASSERT(!element->isInverseFilled());
Chris Dalton584a79a2017-11-15 13:14:01 -0700520 return this->addAnalyticFP(element->getDeviceSpaceRRect(), Invert::kNo,
Chris Dalton3b51df12017-11-27 14:33:06 -0700521 GrAA(element->isAA()));
Chris Dalton584a79a2017-11-15 13:14:01 -0700522
Brian Salomonc3833b42018-07-09 18:23:58 +0000523 case Element::DeviceSpaceType::kPath:
Chris Daltona32a3c32017-12-05 10:05:21 -0700524 return this->addAnalyticFP(element->getDeviceSpacePath(),
525 Invert(element->isInverseFilled()), GrAA(element->isAA()));
Chris Dalton79471932017-10-27 01:50:57 -0600526 }
527
528 SK_ABORT("Unexpected DeviceSpaceType");
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700529}
530
Brian Salomonc3833b42018-07-09 18:23:58 +0000531GrReducedClip::ClipResult GrReducedClip::clipOutsideElement(const Element* element) {
Chris Dalton79471932017-10-27 01:50:57 -0600532 switch (element->getDeviceSpaceType()) {
Brian Salomonc3833b42018-07-09 18:23:58 +0000533 case Element::DeviceSpaceType::kEmpty:
Chris Dalton79471932017-10-27 01:50:57 -0600534 return ClipResult::kMadeEmpty;
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700535
Brian Salomonc3833b42018-07-09 18:23:58 +0000536 case Element::DeviceSpaceType::kRect:
Chris Daltona32a3c32017-12-05 10:05:21 -0700537 SkASSERT(!element->isInverseFilled());
Chris Dalton584a79a2017-11-15 13:14:01 -0700538 if (fWindowRects.count() < fMaxWindowRectangles) {
539 // Clip out the inside of every rect. We won't be able to entirely skip the AA ones,
540 // but it saves processing time.
541 this->addWindowRectangle(element->getDeviceSpaceRect(), element->isAA());
542 if (!element->isAA()) {
543 return ClipResult::kClipped;
544 }
545 }
546 return this->addAnalyticFP(element->getDeviceSpaceRect(), Invert::kYes,
Chris Dalton3b51df12017-11-27 14:33:06 -0700547 GrAA(element->isAA()));
Chris Dalton79471932017-10-27 01:50:57 -0600548
Brian Salomonc3833b42018-07-09 18:23:58 +0000549 case Element::DeviceSpaceType::kRRect: {
Chris Daltona32a3c32017-12-05 10:05:21 -0700550 SkASSERT(!element->isInverseFilled());
Brian Osman554c1f02017-11-16 13:56:47 +0000551 const SkRRect& clipRRect = element->getDeviceSpaceRRect();
Chris Dalton3b51df12017-11-27 14:33:06 -0700552 ClipResult clipResult = this->addAnalyticFP(clipRRect, Invert::kYes,
553 GrAA(element->isAA()));
Chris Dalton584a79a2017-11-15 13:14:01 -0700554 if (fWindowRects.count() >= fMaxWindowRectangles) {
555 return clipResult;
556 }
557
558 // Clip out the interiors of round rects with two window rectangles in the shape of a
559 // "plus". This doesn't let us skip the clip element, but still saves processing time.
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700560 SkVector insetTL = clipRRect.radii(SkRRect::kUpperLeft_Corner);
561 SkVector insetBR = clipRRect.radii(SkRRect::kLowerRight_Corner);
562 if (SkRRect::kComplex_Type == clipRRect.getType()) {
563 const SkVector& insetTR = clipRRect.radii(SkRRect::kUpperRight_Corner);
564 const SkVector& insetBL = clipRRect.radii(SkRRect::kLowerLeft_Corner);
Brian Osman788b9162020-02-07 10:36:46 -0500565 insetTL.fX = std::max(insetTL.x(), insetBL.x());
566 insetTL.fY = std::max(insetTL.y(), insetTR.y());
567 insetBR.fX = std::max(insetBR.x(), insetTR.x());
568 insetBR.fY = std::max(insetBR.y(), insetBL.y());
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700569 }
570 const SkRect& bounds = clipRRect.getBounds();
571 if (insetTL.x() + insetBR.x() >= bounds.width() ||
572 insetTL.y() + insetBR.y() >= bounds.height()) {
Chris Dalton584a79a2017-11-15 13:14:01 -0700573 return clipResult; // The interior "plus" is empty.
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700574 }
575
576 SkRect horzRect = SkRect::MakeLTRB(bounds.left(), bounds.top() + insetTL.y(),
577 bounds.right(), bounds.bottom() - insetBR.y());
578 this->addWindowRectangle(horzRect, element->isAA());
Chris Dalton584a79a2017-11-15 13:14:01 -0700579
580 if (fWindowRects.count() < fMaxWindowRectangles) {
581 SkRect vertRect = SkRect::MakeLTRB(bounds.left() + insetTL.x(), bounds.top(),
582 bounds.right() - insetBR.x(), bounds.bottom());
583 this->addWindowRectangle(vertRect, element->isAA());
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700584 }
585
Chris Dalton584a79a2017-11-15 13:14:01 -0700586 return clipResult;
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700587 }
Chris Dalton79471932017-10-27 01:50:57 -0600588
Brian Salomonc3833b42018-07-09 18:23:58 +0000589 case Element::DeviceSpaceType::kPath:
Chris Daltona32a3c32017-12-05 10:05:21 -0700590 return this->addAnalyticFP(element->getDeviceSpacePath(),
591 Invert(!element->isInverseFilled()), GrAA(element->isAA()));
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700592 }
Chris Dalton79471932017-10-27 01:50:57 -0600593
594 SK_ABORT("Unexpected DeviceSpaceType");
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700595}
596
597inline void GrReducedClip::addWindowRectangle(const SkRect& elementInteriorRect, bool elementIsAA) {
598 SkIRect window;
599 if (!elementIsAA) {
600 elementInteriorRect.round(&window);
601 } else {
602 elementInteriorRect.roundIn(&window);
603 }
604 if (!window.isEmpty()) { // Skip very thin windows that round to zero or negative dimensions.
605 fWindowRects.addWindow(window);
606 }
607}
608
Chris Daltona32a3c32017-12-05 10:05:21 -0700609GrClipEdgeType GrReducedClip::GetClipEdgeType(Invert invert, GrAA aa) {
610 if (Invert::kNo == invert) {
611 return (GrAA::kYes == aa) ? GrClipEdgeType::kFillAA : GrClipEdgeType::kFillBW;
612 } else {
613 return (GrAA::kYes == aa) ? GrClipEdgeType::kInverseFillAA : GrClipEdgeType::kInverseFillBW;
614 }
615}
616
Ethan Nicholaseace9352018-10-15 20:09:54 +0000617GrReducedClip::ClipResult GrReducedClip::addAnalyticFP(const SkRect& deviceSpaceRect,
618 Invert invert, GrAA aa) {
Chris Daltona32a3c32017-12-05 10:05:21 -0700619 if (this->numAnalyticFPs() >= fMaxAnalyticFPs) {
Chris Dalton584a79a2017-11-15 13:14:01 -0700620 return ClipResult::kNotClipped;
621 }
622
Ethan Nicholaseace9352018-10-15 20:09:54 +0000623 fAnalyticFPs.push_back(GrAARectEffect::Make(GetClipEdgeType(invert, aa), deviceSpaceRect));
Chris Daltona32a3c32017-12-05 10:05:21 -0700624 SkASSERT(fAnalyticFPs.back());
625
626 return ClipResult::kClipped;
627}
628
629GrReducedClip::ClipResult GrReducedClip::addAnalyticFP(const SkRRect& deviceSpaceRRect,
630 Invert invert, GrAA aa) {
631 if (this->numAnalyticFPs() >= fMaxAnalyticFPs) {
632 return ClipResult::kNotClipped;
Chris Dalton584a79a2017-11-15 13:14:01 -0700633 }
634
Ethan Nicholaseace9352018-10-15 20:09:54 +0000635 if (auto fp = GrRRectEffect::Make(GetClipEdgeType(invert, aa), deviceSpaceRRect,
636 *fCaps->shaderCaps())) {
Chris Dalton584a79a2017-11-15 13:14:01 -0700637 fAnalyticFPs.push_back(std::move(fp));
638 return ClipResult::kClipped;
639 }
640
Chris Daltona32a3c32017-12-05 10:05:21 -0700641 SkPath deviceSpacePath;
642 deviceSpacePath.setIsVolatile(true);
643 deviceSpacePath.addRRect(deviceSpaceRRect);
644 return this->addAnalyticFP(deviceSpacePath, invert, aa);
645}
646
647GrReducedClip::ClipResult GrReducedClip::addAnalyticFP(const SkPath& deviceSpacePath,
648 Invert invert, GrAA aa) {
649 if (this->numAnalyticFPs() >= fMaxAnalyticFPs) {
650 return ClipResult::kNotClipped;
651 }
652
Ethan Nicholaseace9352018-10-15 20:09:54 +0000653 if (auto fp = GrConvexPolyEffect::Make(GetClipEdgeType(invert, aa), deviceSpacePath)) {
Chris Daltona32a3c32017-12-05 10:05:21 -0700654 fAnalyticFPs.push_back(std::move(fp));
655 return ClipResult::kClipped;
656 }
657
Chris Dalton1dec19a2018-04-27 13:05:19 -0600658 if (fCCPRClipPaths.count() < fMaxCCPRClipPaths && GrAA::kYes == aa) {
Chris Daltona32a3c32017-12-05 10:05:21 -0700659 // 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 -0400660 // opsTask they will operate in.
Chris Daltona32a3c32017-12-05 10:05:21 -0700661 SkPath& ccprClipPath = fCCPRClipPaths.push_back(deviceSpacePath);
662 if (Invert::kYes == invert) {
663 ccprClipPath.toggleInverseFillType();
664 }
665 return ClipResult::kClipped;
666 }
667
Chris Dalton584a79a2017-11-15 13:14:01 -0700668 return ClipResult::kNotClipped;
669}
670
Chris Dalton79471932017-10-27 01:50:57 -0600671void GrReducedClip::makeEmpty() {
672 fHasScissor = false;
673 fAAClipRectGenID = SK_InvalidGenID;
674 fWindowRects.reset();
675 fMaskElements.reset();
676 fInitialState = InitialState::kAllOut;
bsalomon@google.com34cd70a2012-12-06 14:23:20 +0000677}
csmartdaltonbde96c62016-08-31 12:54:46 -0700678
679////////////////////////////////////////////////////////////////////////////////
Chris Daltonc5348082018-03-30 15:59:38 +0000680// Create a 8-bit clip mask in alpha
681
682static bool stencil_element(GrRenderTargetContext* rtc,
683 const GrFixedClip& clip,
684 const GrUserStencilSettings* ss,
685 const SkMatrix& viewMatrix,
Brian Salomonc3833b42018-07-09 18:23:58 +0000686 const SkClipStack::Element* element) {
687 GrAA aa = GrAA(element->isAA());
688 switch (element->getDeviceSpaceType()) {
Chris Daltonc5348082018-03-30 15:59:38 +0000689 case SkClipStack::Element::DeviceSpaceType::kEmpty:
690 SkDEBUGFAIL("Should never get here with an empty element.");
691 break;
Michael Ludwigaa1b6b32019-05-29 14:43:13 -0400692 case SkClipStack::Element::DeviceSpaceType::kRect: {
693 GrPaint paint;
694 paint.setCoverageSetOpXPFactory((SkRegion::Op)element->getOp(),
695 element->isInverseFilled());
Weston Tracey074414f2020-05-29 12:55:06 +0000696 rtc->priv().stencilRect(clip, ss, std::move(paint), aa, viewMatrix,
Michael Ludwigaa1b6b32019-05-29 14:43:13 -0400697 element->getDeviceSpaceRect());
698 return true;
699 }
Chris Daltonc5348082018-03-30 15:59:38 +0000700 default: {
701 SkPath path;
Brian Salomonc3833b42018-07-09 18:23:58 +0000702 element->asDeviceSpacePath(&path);
Chris Daltonc5348082018-03-30 15:59:38 +0000703 if (path.isInverseFillType()) {
704 path.toggleInverseFillType();
705 }
706
Weston Tracey074414f2020-05-29 12:55:06 +0000707 return rtc->priv().drawAndStencilPath(clip, ss, (SkRegion::Op)element->getOp(),
Brian Salomonc3833b42018-07-09 18:23:58 +0000708 element->isInverseFilled(), aa, viewMatrix, path);
Chris Daltonc5348082018-03-30 15:59:38 +0000709 }
710 }
711
712 return false;
713}
714
715static void draw_element(GrRenderTargetContext* rtc,
716 const GrClip& clip, // TODO: can this just always be WideOpen?
717 GrPaint&& paint,
718 GrAA aa,
719 const SkMatrix& viewMatrix,
Brian Salomonc3833b42018-07-09 18:23:58 +0000720 const SkClipStack::Element* element) {
Chris Daltonc5348082018-03-30 15:59:38 +0000721 // TODO: Draw rrects directly here.
Brian Salomonc3833b42018-07-09 18:23:58 +0000722 switch (element->getDeviceSpaceType()) {
Chris Daltonc5348082018-03-30 15:59:38 +0000723 case SkClipStack::Element::DeviceSpaceType::kEmpty:
724 SkDEBUGFAIL("Should never get here with an empty element.");
725 break;
726 case SkClipStack::Element::DeviceSpaceType::kRect:
Weston Tracey074414f2020-05-29 12:55:06 +0000727 rtc->drawRect(clip, std::move(paint), aa, viewMatrix, element->getDeviceSpaceRect());
Chris Daltonc5348082018-03-30 15:59:38 +0000728 break;
729 default: {
730 SkPath path;
Brian Salomonc3833b42018-07-09 18:23:58 +0000731 element->asDeviceSpacePath(&path);
Chris Daltonc5348082018-03-30 15:59:38 +0000732 if (path.isInverseFillType()) {
733 path.toggleInverseFillType();
734 }
735
Weston Tracey074414f2020-05-29 12:55:06 +0000736 rtc->drawPath(clip, std::move(paint), aa, viewMatrix, path, GrStyle::SimpleFill());
Chris Daltonc5348082018-03-30 15:59:38 +0000737 break;
738 }
739 }
740}
741
742bool GrReducedClip::drawAlphaClipMask(GrRenderTargetContext* rtc) const {
743 // The texture may be larger than necessary, this rect represents the part of the texture
744 // we populate with a rasterization of the clip.
745 GrFixedClip clip(SkIRect::MakeWH(fScissor.width(), fScissor.height()));
746
747 if (!fWindowRects.empty()) {
748 clip.setWindowRectangles(fWindowRects.makeOffset(-fScissor.left(), -fScissor.top()),
749 GrWindowRectsState::Mode::kExclusive);
750 }
751
752 // The scratch texture that we are drawing into can be substantially larger than the mask. Only
753 // clear the part that we care about.
Brian Osman9a9baae2018-11-05 15:06:26 -0500754 SkPMColor4f initialCoverage =
755 InitialState::kAllIn == this->initialState() ? SK_PMColor4fWHITE : SK_PMColor4fTRANSPARENT;
Michael Ludwig81d41722020-05-26 16:57:38 -0400756 if (clip.hasWindowRectangles()) {
757 GrPaint paint;
758 paint.setColor4f(initialCoverage);
759 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
Weston Tracey074414f2020-05-29 12:55:06 +0000760 rtc->drawRect(clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
Michael Ludwig81d41722020-05-26 16:57:38 -0400761 SkRect::Make(clip.scissorRect()));
762 } else {
763 rtc->priv().clearAtLeast(clip.scissorRect(), initialCoverage);
764 }
Chris Daltonc5348082018-03-30 15:59:38 +0000765
766 // Set the matrix so that rendered clip elements are transformed to mask space from clip space.
767 SkMatrix translate;
768 translate.setTranslate(SkIntToScalar(-fScissor.left()), SkIntToScalar(-fScissor.top()));
769
770 // walk through each clip element and perform its set op
771 for (ElementList::Iter iter(fMaskElements); iter.get(); iter.next()) {
Brian Salomonc3833b42018-07-09 18:23:58 +0000772 const Element* element = iter.get();
773 SkRegion::Op op = (SkRegion::Op)element->getOp();
774 GrAA aa = GrAA(element->isAA());
775 bool invert = element->isInverseFilled();
Chris Daltonc5348082018-03-30 15:59:38 +0000776 if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) {
777 // draw directly into the result with the stencil set to make the pixels affected
778 // by the clip shape be non-zero.
779 static constexpr GrUserStencilSettings kStencilInElement(
780 GrUserStencilSettings::StaticInit<
781 0xffff,
782 GrUserStencilTest::kAlways,
783 0xffff,
784 GrUserStencilOp::kReplace,
785 GrUserStencilOp::kReplace,
786 0xffff>()
787 );
788 if (!stencil_element(rtc, clip, &kStencilInElement, translate, element)) {
789 return false;
790 }
791
792 // Draw to the exterior pixels (those with a zero stencil value).
793 static constexpr GrUserStencilSettings kDrawOutsideElement(
794 GrUserStencilSettings::StaticInit<
795 0x0000,
796 GrUserStencilTest::kEqual,
797 0xffff,
798 GrUserStencilOp::kZero,
799 GrUserStencilOp::kZero,
800 0xffff>()
801 );
Michael Ludwigaa1b6b32019-05-29 14:43:13 -0400802
803 GrPaint paint;
804 paint.setCoverageSetOpXPFactory(op, !invert);
Weston Tracey074414f2020-05-29 12:55:06 +0000805 rtc->priv().stencilRect(clip, &kDrawOutsideElement, std::move(paint), GrAA::kNo,
Michael Ludwigaa1b6b32019-05-29 14:43:13 -0400806 translate, SkRect::Make(fScissor));
Chris Daltonc5348082018-03-30 15:59:38 +0000807 } else {
808 // all the remaining ops can just be directly draw into the accumulation buffer
809 GrPaint paint;
810 paint.setCoverageSetOpXPFactory(op, false);
811
812 draw_element(rtc, clip, std::move(paint), aa, translate, element);
813 }
814 }
815
816 return true;
817}
818
819////////////////////////////////////////////////////////////////////////////////
csmartdaltonbde96c62016-08-31 12:54:46 -0700820// Create a 1-bit clip mask in the stencil buffer.
821
Robert Phillips6f0e02f2019-02-13 11:02:28 -0500822bool GrReducedClip::drawStencilClipMask(GrRecordingContext* context,
Ethan Nicholaseace9352018-10-15 20:09:54 +0000823 GrRenderTargetContext* renderTargetContext) const {
Michael Ludwig828d3412020-05-12 13:15:35 -0400824 GrStencilMaskHelper helper(context, renderTargetContext);
825 if (!helper.init(fScissor, this->maskGenID(), fWindowRects, this->numAnalyticFPs())) {
826 // The stencil mask doesn't need updating
827 return true;
csmartdaltonbf4a8f92016-09-06 10:01:06 -0700828 }
829
Michael Ludwig828d3412020-05-12 13:15:35 -0400830 helper.clear(InitialState::kAllIn == this->initialState());
csmartdaltonbde96c62016-08-31 12:54:46 -0700831
Brian Salomon0e8fc8b2016-12-09 15:10:07 -0500832 // walk through each clip element and perform its set op with the existing clip.
Chris Dalton79471932017-10-27 01:50:57 -0600833 for (ElementList::Iter iter(fMaskElements); iter.get(); iter.next()) {
Brian Salomonc3833b42018-07-09 18:23:58 +0000834 const Element* element = iter.get();
Michael Ludwigde228e52020-05-12 16:17:20 +0000835 SkRegion::Op op = (SkRegion::Op)element->getOp();
Michael Ludwig828d3412020-05-12 13:15:35 -0400836 GrAA aa = element->isAA() ? GrAA::kYes : GrAA::kNo;
Michael Ludwigde228e52020-05-12 16:17:20 +0000837
Brian Salomonc3833b42018-07-09 18:23:58 +0000838 if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) {
Michael Ludwig828d3412020-05-12 13:15:35 -0400839 helper.drawRect(element->getDeviceSpaceRect(), SkMatrix::I(), op, aa);
csmartdaltonbde96c62016-08-31 12:54:46 -0700840 } else {
Michael Ludwig828d3412020-05-12 13:15:35 -0400841 SkPath path;
842 element->asDeviceSpacePath(&path);
843 if (!helper.drawPath(path, SkMatrix::I(), op, aa)) {
csmartdaltonbde96c62016-08-31 12:54:46 -0700844 return false;
845 }
846 }
Michael Ludwigde228e52020-05-12 16:17:20 +0000847 }
Michael Ludwig828d3412020-05-12 13:15:35 -0400848
849 helper.finish();
csmartdaltonbde96c62016-08-31 12:54:46 -0700850 return true;
851}
Chris Daltona32a3c32017-12-05 10:05:21 -0700852
Robert Phillips777707b2018-01-17 11:40:14 -0500853std::unique_ptr<GrFragmentProcessor> GrReducedClip::finishAndDetachAnalyticFPs(
Greg Danielf41b2bd2019-08-22 16:19:24 -0400854 GrCoverageCountingPathRenderer* ccpr, uint32_t opsTaskID) {
Chris Daltona32a3c32017-12-05 10:05:21 -0700855 // Make sure finishAndDetachAnalyticFPs hasn't been called already.
856 SkDEBUGCODE(for (const auto& fp : fAnalyticFPs) { SkASSERT(fp); })
857
858 if (!fCCPRClipPaths.empty()) {
859 fAnalyticFPs.reserve(fAnalyticFPs.count() + fCCPRClipPaths.count());
860 for (const SkPath& ccprClipPath : fCCPRClipPaths) {
Chris Dalton1dec19a2018-04-27 13:05:19 -0600861 SkASSERT(ccpr);
Chris Daltona32a3c32017-12-05 10:05:21 -0700862 SkASSERT(fHasScissor);
Greg Danielf41b2bd2019-08-22 16:19:24 -0400863 auto fp = ccpr->makeClipProcessor(opsTaskID, ccprClipPath, fScissor, *fCaps);
Chris Daltona32a3c32017-12-05 10:05:21 -0700864 fAnalyticFPs.push_back(std::move(fp));
865 }
866 fCCPRClipPaths.reset();
867 }
868
869 return GrFragmentProcessor::RunInSeries(fAnalyticFPs.begin(), fAnalyticFPs.count());
870}