1 /*
2 * Copyright 2016 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "src/core/SkClipOpPriv.h"
9 #include "src/gpu/GrAppliedClip.h"
10 #include "src/gpu/GrClip.h"
11 #include "src/gpu/GrColor.h"
12 #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/GrStencilClip.h"
18 #include "src/gpu/GrStencilMaskHelper.h"
19 #include "src/gpu/GrStencilSettings.h"
20 #include "src/gpu/GrStyle.h"
21 #include "src/gpu/GrSurfaceDrawContext.h"
22 #include "src/gpu/GrUserStencilSettings.h"
23 #include "src/gpu/ccpr/GrCoverageCountingPathRenderer.h"
24 #include "src/gpu/effects/GrConvexPolyEffect.h"
25 #include "src/gpu/effects/GrRRectEffect.h"
26 #include "src/gpu/effects/generated/GrAARectEffect.h"
27 #include "src/gpu/effects/generated/GrDeviceSpaceEffect.h"
28 #include "src/gpu/geometry/GrStyledShape.h"
29 #include "src/shaders/SkShaderBase.h"
30
31 /**
32 * There are plenty of optimizations that could be added here. Maybe flips could be folded into
33 * earlier operations. Or would inserting flips and reversing earlier ops ever be a win? Perhaps
34 * for the case where the bounds are kInsideOut_BoundsType. We could restrict earlier operations
35 * based on later intersect operations, and perhaps remove intersect-rects. We could optionally
36 * take a rect in case the caller knows a bound on what is to be drawn through this clip.
37 */
GrReducedClip(const SkClipStack & stack,const SkRect & queryBounds,const GrCaps * caps,int maxWindowRectangles,int maxAnalyticElements,int maxCCPRClipPaths)38 GrReducedClip::GrReducedClip(const SkClipStack& stack, const SkRect& queryBounds,
39 const GrCaps* caps, int maxWindowRectangles, int maxAnalyticElements,
40 int maxCCPRClipPaths)
41 : fCaps(caps)
42 , fMaxWindowRectangles(maxWindowRectangles)
43 , fMaxAnalyticElements(maxAnalyticElements)
44 , fMaxCCPRClipPaths(maxCCPRClipPaths) {
45 SkASSERT(!queryBounds.isEmpty());
46 SkASSERT(fMaxWindowRectangles <= GrWindowRectangles::kMaxWindows);
47 SkASSERT(fMaxCCPRClipPaths <= fMaxAnalyticElements);
48
49 if (stack.isWideOpen()) {
50 fInitialState = InitialState::kAllIn;
51 return;
52 }
53
54 SkClipStack::BoundsType stackBoundsType;
55 SkRect stackBounds;
56 bool iior;
57 stack.getBounds(&stackBounds, &stackBoundsType, &iior);
58
59 if (GrClip::IsOutsideClip(stackBounds, queryBounds)) {
60 bool insideOut = SkClipStack::kInsideOut_BoundsType == stackBoundsType;
61 fInitialState = insideOut ? InitialState::kAllIn : InitialState::kAllOut;
62 return;
63 }
64
65 if (iior) {
66 // "Is intersection of rects" means the clip is a single rect indicated by the stack bounds.
67 // This should only be true if aa/non-aa status matches among all elements.
68 SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType);
69
70 if (GrClip::IsInsideClip(stackBounds, queryBounds)) {
71 fInitialState = InitialState::kAllIn;
72 return;
73 }
74
75 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
76
77 if (!iter.prev()->isAA() || GrClip::IsPixelAligned(stackBounds)) {
78 // The clip is a non-aa rect. Here we just implement the entire thing using fScissor.
79 stackBounds.round(&fScissor);
80 fHasScissor = true;
81 fInitialState = fScissor.isEmpty() ? InitialState::kAllOut : InitialState::kAllIn;
82 return;
83 }
84
85 SkRect tightBounds;
86 SkAssertResult(tightBounds.intersect(stackBounds, queryBounds));
87 fScissor = GrClip::GetPixelIBounds(tightBounds);
88 if (fScissor.isEmpty()) {
89 fInitialState = InitialState::kAllOut;
90 return;
91 }
92 fHasScissor = true;
93
94 fAAClipRect = stackBounds;
95 fAAClipRectGenID = stack.getTopmostGenID();
96 SkASSERT(SK_InvalidGenID != fAAClipRectGenID);
97
98 fInitialState = InitialState::kAllIn;
99 } else {
100 SkRect tighterQuery = queryBounds;
101 if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
102 // Tighten the query by introducing a new clip at the stack's pixel boundaries. (This
103 // new clip will be enforced by the scissor.)
104 SkAssertResult(tighterQuery.intersect(GrClip::GetPixelBounds(stackBounds)));
105 }
106
107 fScissor = GrClip::GetPixelIBounds(tighterQuery);
108 if (fScissor.isEmpty()) {
109 fInitialState = InitialState::kAllOut;
110 return;
111 }
112 fHasScissor = true;
113
114 // Now that we have determined the bounds to use and filtered out the trivial cases, call
115 // the helper that actually walks the stack.
116 this->walkStack(stack, tighterQuery);
117
118 if (fInitialState == InitialState::kAllOut && fMaskElements.isEmpty()) {
119 // The clip starts with no coverage and there are no elements to add coverage with
120 // expanding ops. We ignore the AAClipRectGenID since it is an implied intersection.
121 this->makeEmpty();
122 return;
123 }
124 }
125
126 if (SK_InvalidGenID != fAAClipRectGenID && // Is there an AA clip rect?
127 ClipResult::kNotClipped == this->addAnalyticRect(fAAClipRect, Invert::kNo, GrAA::kYes)) {
128 if (fMaskElements.isEmpty()) {
129 // Use a replace since it is faster than intersect.
130 fMaskElements.addToHead(fAAClipRect, SkMatrix::I(), kReplace_SkClipOp, true /*doAA*/);
131 fInitialState = InitialState::kAllOut;
132 } else {
133 fMaskElements.addToTail(fAAClipRect, SkMatrix::I(), kIntersect_SkClipOp, true /*doAA*/);
134 }
135 fMaskRequiresAA = true;
136 fMaskGenID = fAAClipRectGenID;
137 }
138 }
139
walkStack(const SkClipStack & stack,const SkRect & queryBounds)140 void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBounds) {
141 // walk backwards until we get to:
142 // a) the beginning
143 // b) an operation that is known to make the bounds all inside/outside
144 // c) a replace operation
145
146 enum class InitialTriState {
147 kUnknown = -1,
148 kAllIn = (int)GrReducedClip::InitialState::kAllIn,
149 kAllOut = (int)GrReducedClip::InitialState::kAllOut
150 } initialTriState = InitialTriState::kUnknown;
151
152 // During our backwards walk, track whether we've seen ops that either grow or shrink the clip.
153 // TODO: track these per saved clip so that we can consider them on the forward pass.
154 bool embiggens = false;
155 bool emsmallens = false;
156
157 // We use a slightly relaxed set of query bounds for element containment tests. This is to
158 // account for floating point rounding error that may have occurred during coord transforms.
159 SkRect relaxedQueryBounds = queryBounds.makeInset(GrClip::kBoundsTolerance,
160 GrClip::kBoundsTolerance);
161 if (relaxedQueryBounds.isEmpty()) {
162 relaxedQueryBounds = queryBounds;
163 }
164
165 SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
166 int numAAElements = 0;
167 while (InitialTriState::kUnknown == initialTriState) {
168 const Element* element = iter.prev();
169 if (nullptr == element) {
170 initialTriState = InitialTriState::kAllIn;
171 break;
172 }
173 if (SkClipStack::kEmptyGenID == element->getGenID()) {
174 initialTriState = InitialTriState::kAllOut;
175 break;
176 }
177 if (SkClipStack::kWideOpenGenID == element->getGenID()) {
178 initialTriState = InitialTriState::kAllIn;
179 break;
180 }
181
182 if (element->getDeviceSpaceType() == Element::DeviceSpaceType::kShader) {
183 if (fShader) {
184 // Combine multiple shaders together with src-in blending. This works because all
185 // shaders are effectively intersections (difference ops have been modified to be
186 // 1 - alpha already).
187 fShader = SkShaders::Blend(SkBlendMode::kSrcIn, element->refShader(), fShader);
188 } else {
189 fShader = element->refShader();
190 }
191 continue;
192 }
193
194 bool skippable = false;
195 bool isFlip = false; // does this op just flip the in/out state of every point in the bounds
196
197 switch (element->getOp()) {
198 case kDifference_SkClipOp:
199 // check if the shape subtracted either contains the entire bounds (and makes
200 // the clip empty) or is outside the bounds and therefore can be skipped.
201 if (element->isInverseFilled()) {
202 if (element->contains(relaxedQueryBounds)) {
203 skippable = true;
204 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
205 initialTriState = InitialTriState::kAllOut;
206 skippable = true;
207 } else if (!embiggens) {
208 ClipResult result = this->clipInsideElement(element);
209 if (ClipResult::kMadeEmpty == result) {
210 return;
211 }
212 skippable = (ClipResult::kClipped == result);
213 }
214 } else {
215 if (element->contains(relaxedQueryBounds)) {
216 initialTriState = InitialTriState::kAllOut;
217 skippable = true;
218 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
219 skippable = true;
220 } else if (!embiggens) {
221 ClipResult result = this->clipOutsideElement(element);
222 if (ClipResult::kMadeEmpty == result) {
223 return;
224 }
225 skippable = (ClipResult::kClipped == result);
226 }
227 }
228 if (!skippable) {
229 emsmallens = true;
230 }
231 break;
232 case kIntersect_SkClipOp:
233 // check if the shape intersected contains the entire bounds and therefore can
234 // be skipped or it is outside the entire bounds and therefore makes the clip
235 // empty.
236 if (element->isInverseFilled()) {
237 if (element->contains(relaxedQueryBounds)) {
238 initialTriState = InitialTriState::kAllOut;
239 skippable = true;
240 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
241 skippable = true;
242 } else if (!embiggens) {
243 ClipResult result = this->clipOutsideElement(element);
244 if (ClipResult::kMadeEmpty == result) {
245 return;
246 }
247 skippable = (ClipResult::kClipped == result);
248 }
249 } else {
250 if (element->contains(relaxedQueryBounds)) {
251 skippable = true;
252 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
253 initialTriState = InitialTriState::kAllOut;
254 skippable = true;
255 } else if (!embiggens) {
256 ClipResult result = this->clipInsideElement(element);
257 if (ClipResult::kMadeEmpty == result) {
258 return;
259 }
260 skippable = (ClipResult::kClipped == result);
261 }
262 }
263 if (!skippable) {
264 emsmallens = true;
265 }
266 break;
267 case kUnion_SkClipOp:
268 // If the union-ed shape contains the entire bounds then after this element
269 // the bounds is entirely inside the clip. If the union-ed shape is outside the
270 // bounds then this op can be skipped.
271 if (element->isInverseFilled()) {
272 if (element->contains(relaxedQueryBounds)) {
273 skippable = true;
274 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
275 initialTriState = InitialTriState::kAllIn;
276 skippable = true;
277 }
278 } else {
279 if (element->contains(relaxedQueryBounds)) {
280 initialTriState = InitialTriState::kAllIn;
281 skippable = true;
282 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
283 skippable = true;
284 }
285 }
286 if (!skippable) {
287 embiggens = true;
288 }
289 break;
290 case kXOR_SkClipOp:
291 // If the bounds is entirely inside the shape being xor-ed then the effect is
292 // to flip the inside/outside state of every point in the bounds. We may be
293 // able to take advantage of this in the forward pass. If the xor-ed shape
294 // doesn't intersect the bounds then it can be skipped.
295 if (element->isInverseFilled()) {
296 if (element->contains(relaxedQueryBounds)) {
297 skippable = true;
298 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
299 isFlip = true;
300 }
301 } else {
302 if (element->contains(relaxedQueryBounds)) {
303 isFlip = true;
304 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
305 skippable = true;
306 }
307 }
308 if (!skippable) {
309 emsmallens = embiggens = true;
310 }
311 break;
312 case kReverseDifference_SkClipOp:
313 // When the bounds is entirely within the rev-diff shape then this behaves like xor
314 // and reverses every point inside the bounds. If the shape is completely outside
315 // the bounds then we know after this element is applied that the bounds will be
316 // all outside the current clip.B
317 if (element->isInverseFilled()) {
318 if (element->contains(relaxedQueryBounds)) {
319 initialTriState = InitialTriState::kAllOut;
320 skippable = true;
321 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
322 isFlip = true;
323 }
324 } else {
325 if (element->contains(relaxedQueryBounds)) {
326 isFlip = true;
327 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
328 initialTriState = InitialTriState::kAllOut;
329 skippable = true;
330 }
331 }
332 if (!skippable) {
333 emsmallens = embiggens = true;
334 }
335 break;
336
337 case kReplace_SkClipOp:
338 // Replace will always terminate our walk. We will either begin the forward walk
339 // at the replace op or detect here than the shape is either completely inside
340 // or completely outside the bounds. In this latter case it can be skipped by
341 // setting the correct value for initialTriState.
342 if (element->isInverseFilled()) {
343 if (element->contains(relaxedQueryBounds)) {
344 initialTriState = InitialTriState::kAllOut;
345 skippable = true;
346 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
347 initialTriState = InitialTriState::kAllIn;
348 skippable = true;
349 } else if (!embiggens) {
350 ClipResult result = this->clipOutsideElement(element);
351 if (ClipResult::kMadeEmpty == result) {
352 return;
353 }
354 if (ClipResult::kClipped == result) {
355 initialTriState = InitialTriState::kAllIn;
356 skippable = true;
357 }
358 }
359 } else {
360 if (element->contains(relaxedQueryBounds)) {
361 initialTriState = InitialTriState::kAllIn;
362 skippable = true;
363 } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
364 initialTriState = InitialTriState::kAllOut;
365 skippable = true;
366 } else if (!embiggens) {
367 ClipResult result = this->clipInsideElement(element);
368 if (ClipResult::kMadeEmpty == result) {
369 return;
370 }
371 if (ClipResult::kClipped == result) {
372 initialTriState = InitialTriState::kAllIn;
373 skippable = true;
374 }
375 }
376 }
377 if (!skippable) {
378 initialTriState = InitialTriState::kAllOut;
379 embiggens = emsmallens = true;
380 }
381 break;
382 default:
383 SkDEBUGFAIL("Unexpected op.");
384 break;
385 }
386 if (!skippable) {
387 if (fMaskElements.isEmpty()) {
388 // This will be the last element. Record the stricter genID.
389 fMaskGenID = element->getGenID();
390 }
391
392 // if it is a flip, change it to a bounds-filling rect
393 if (isFlip) {
394 SkASSERT(kXOR_SkClipOp == element->getOp() ||
395 kReverseDifference_SkClipOp == element->getOp());
396 fMaskElements.addToHead(SkRect::Make(fScissor), SkMatrix::I(),
397 kReverseDifference_SkClipOp, false);
398 } else {
399 Element* newElement = fMaskElements.addToHead(*element);
400 if (newElement->isAA()) {
401 ++numAAElements;
402 }
403 // Intersecting an inverse shape is the same as differencing the non-inverse shape.
404 // Replacing with an inverse shape is the same as setting initialState=kAllIn and
405 // differencing the non-inverse shape.
406 bool isReplace = kReplace_SkClipOp == newElement->getOp();
407 if (newElement->isInverseFilled() &&
408 (kIntersect_SkClipOp == newElement->getOp() || isReplace)) {
409 newElement->invertShapeFillType();
410 newElement->setOp(kDifference_SkClipOp);
411 if (isReplace) {
412 SkASSERT(InitialTriState::kAllOut == initialTriState);
413 initialTriState = InitialTriState::kAllIn;
414 }
415 }
416 }
417 }
418 }
419
420 if ((InitialTriState::kAllOut == initialTriState && !embiggens) ||
421 (InitialTriState::kAllIn == initialTriState && !emsmallens)) {
422 fMaskElements.reset();
423 numAAElements = 0;
424 } else {
425 Element* element = fMaskElements.headIter().get();
426 while (element) {
427 bool skippable = false;
428 switch (element->getOp()) {
429 case kDifference_SkClipOp:
430 // subtracting from the empty set yields the empty set.
431 skippable = InitialTriState::kAllOut == initialTriState;
432 break;
433 case kIntersect_SkClipOp:
434 // intersecting with the empty set yields the empty set
435 if (InitialTriState::kAllOut == initialTriState) {
436 skippable = true;
437 } else {
438 // We can clear to zero and then simply draw the clip element.
439 initialTriState = InitialTriState::kAllOut;
440 element->setOp(kReplace_SkClipOp);
441 }
442 break;
443 case kUnion_SkClipOp:
444 if (InitialTriState::kAllIn == initialTriState) {
445 // unioning the infinite plane with anything is a no-op.
446 skippable = true;
447 } else {
448 // unioning the empty set with a shape is the shape.
449 element->setOp(kReplace_SkClipOp);
450 }
451 break;
452 case kXOR_SkClipOp:
453 if (InitialTriState::kAllOut == initialTriState) {
454 // xor could be changed to diff in the kAllIn case, not sure it's a win.
455 element->setOp(kReplace_SkClipOp);
456 }
457 break;
458 case kReverseDifference_SkClipOp:
459 if (InitialTriState::kAllIn == initialTriState) {
460 // subtracting the whole plane will yield the empty set.
461 skippable = true;
462 initialTriState = InitialTriState::kAllOut;
463 } else {
464 // this picks up flips inserted in the backwards pass.
465 skippable = element->isInverseFilled() ?
466 GrClip::IsOutsideClip(element->getBounds(), queryBounds) :
467 element->contains(relaxedQueryBounds);
468 if (skippable) {
469 initialTriState = InitialTriState::kAllIn;
470 } else {
471 element->setOp(kReplace_SkClipOp);
472 }
473 }
474 break;
475 case kReplace_SkClipOp:
476 skippable = false; // we would have skipped it in the backwards walk if we
477 // could've.
478 break;
479 default:
480 SkDEBUGFAIL("Unexpected op.");
481 break;
482 }
483 if (!skippable) {
484 break;
485 } else {
486 if (element->isAA()) {
487 --numAAElements;
488 }
489 fMaskElements.popHead();
490 element = fMaskElements.headIter().get();
491 }
492 }
493 }
494 fMaskRequiresAA = numAAElements > 0;
495
496 SkASSERT(InitialTriState::kUnknown != initialTriState);
497 fInitialState = static_cast<GrReducedClip::InitialState>(initialTriState);
498 }
499
clipInsideElement(const Element * element)500 GrReducedClip::ClipResult GrReducedClip::clipInsideElement(const Element* element) {
501 SkASSERT(element->getDeviceSpaceType() != Element::DeviceSpaceType::kShader);
502
503 SkIRect elementIBounds;
504 if (!element->isAA()) {
505 element->getBounds().round(&elementIBounds);
506 } else {
507 elementIBounds = GrClip::GetPixelIBounds(element->getBounds());
508 }
509 SkASSERT(fHasScissor);
510 if (!fScissor.intersect(elementIBounds)) {
511 this->makeEmpty();
512 return ClipResult::kMadeEmpty;
513 }
514
515 switch (element->getDeviceSpaceType()) {
516 case Element::DeviceSpaceType::kEmpty:
517 return ClipResult::kMadeEmpty;
518
519 case Element::DeviceSpaceType::kRect:
520 SkASSERT(element->getBounds() == element->getDeviceSpaceRect());
521 SkASSERT(!element->isInverseFilled());
522 if (element->isAA()) {
523 if (SK_InvalidGenID == fAAClipRectGenID) { // No AA clip rect yet?
524 fAAClipRect = element->getDeviceSpaceRect();
525 // fAAClipRectGenID is the value we should use for fMaskGenID if we end up
526 // moving the AA clip rect into the mask. The mask GenID is simply the topmost
527 // element's GenID. And since we walk the stack backwards, this means it's just
528 // the first element we don't skip during our walk.
529 fAAClipRectGenID = fMaskElements.isEmpty() ? element->getGenID() : fMaskGenID;
530 SkASSERT(SK_InvalidGenID != fAAClipRectGenID);
531 } else if (!fAAClipRect.intersect(element->getDeviceSpaceRect())) {
532 this->makeEmpty();
533 return ClipResult::kMadeEmpty;
534 }
535 }
536 return ClipResult::kClipped;
537
538 case Element::DeviceSpaceType::kRRect:
539 SkASSERT(!element->isInverseFilled());
540 return this->addAnalyticRRect(element->getDeviceSpaceRRect(), Invert::kNo,
541 GrAA(element->isAA()));
542
543 case Element::DeviceSpaceType::kPath:
544 return this->addAnalyticPath(element->getDeviceSpacePath(),
545 Invert(element->isInverseFilled()), GrAA(element->isAA()));
546
547 case Element::DeviceSpaceType::kShader:
548 SkUNREACHABLE;
549 }
550
551 SK_ABORT("Unexpected DeviceSpaceType");
552 }
553
clipOutsideElement(const Element * element)554 GrReducedClip::ClipResult GrReducedClip::clipOutsideElement(const Element* element) {
555 SkASSERT(element->getDeviceSpaceType() != Element::DeviceSpaceType::kShader);
556
557 switch (element->getDeviceSpaceType()) {
558 case Element::DeviceSpaceType::kEmpty:
559 return ClipResult::kMadeEmpty;
560
561 case Element::DeviceSpaceType::kRect:
562 SkASSERT(!element->isInverseFilled());
563 if (fWindowRects.count() < fMaxWindowRectangles) {
564 // Clip out the inside of every rect. We won't be able to entirely skip the AA ones,
565 // but it saves processing time.
566 this->addWindowRectangle(element->getDeviceSpaceRect(), element->isAA());
567 if (!element->isAA()) {
568 return ClipResult::kClipped;
569 }
570 }
571 return this->addAnalyticRect(element->getDeviceSpaceRect(), Invert::kYes,
572 GrAA(element->isAA()));
573
574 case Element::DeviceSpaceType::kRRect: {
575 SkASSERT(!element->isInverseFilled());
576 const SkRRect& clipRRect = element->getDeviceSpaceRRect();
577 ClipResult clipResult = this->addAnalyticRRect(clipRRect, Invert::kYes,
578 GrAA(element->isAA()));
579 if (fWindowRects.count() >= fMaxWindowRectangles) {
580 return clipResult;
581 }
582
583 // Clip out the interiors of round rects with two window rectangles in the shape of a
584 // "plus". This doesn't let us skip the clip element, but still saves processing time.
585 SkVector insetTL = clipRRect.radii(SkRRect::kUpperLeft_Corner);
586 SkVector insetBR = clipRRect.radii(SkRRect::kLowerRight_Corner);
587 if (SkRRect::kComplex_Type == clipRRect.getType()) {
588 const SkVector& insetTR = clipRRect.radii(SkRRect::kUpperRight_Corner);
589 const SkVector& insetBL = clipRRect.radii(SkRRect::kLowerLeft_Corner);
590 insetTL.fX = std::max(insetTL.x(), insetBL.x());
591 insetTL.fY = std::max(insetTL.y(), insetTR.y());
592 insetBR.fX = std::max(insetBR.x(), insetTR.x());
593 insetBR.fY = std::max(insetBR.y(), insetBL.y());
594 }
595 const SkRect& bounds = clipRRect.getBounds();
596 if (insetTL.x() + insetBR.x() >= bounds.width() ||
597 insetTL.y() + insetBR.y() >= bounds.height()) {
598 return clipResult; // The interior "plus" is empty.
599 }
600
601 SkRect horzRect = SkRect::MakeLTRB(bounds.left(), bounds.top() + insetTL.y(),
602 bounds.right(), bounds.bottom() - insetBR.y());
603 this->addWindowRectangle(horzRect, element->isAA());
604
605 if (fWindowRects.count() < fMaxWindowRectangles) {
606 SkRect vertRect = SkRect::MakeLTRB(bounds.left() + insetTL.x(), bounds.top(),
607 bounds.right() - insetBR.x(), bounds.bottom());
608 this->addWindowRectangle(vertRect, element->isAA());
609 }
610
611 return clipResult;
612 }
613
614 case Element::DeviceSpaceType::kPath:
615 return this->addAnalyticPath(element->getDeviceSpacePath(),
616 Invert(!element->isInverseFilled()),
617 GrAA(element->isAA()));
618
619 case Element::DeviceSpaceType::kShader:
620 SkUNREACHABLE;
621 }
622
623 SK_ABORT("Unexpected DeviceSpaceType");
624 }
625
addWindowRectangle(const SkRect & elementInteriorRect,bool elementIsAA)626 inline void GrReducedClip::addWindowRectangle(const SkRect& elementInteriorRect, bool elementIsAA) {
627 SkIRect window;
628 if (!elementIsAA) {
629 elementInteriorRect.round(&window);
630 } else {
631 elementInteriorRect.roundIn(&window);
632 }
633 if (!window.isEmpty()) { // Skip very thin windows that round to zero or negative dimensions.
634 fWindowRects.addWindow(window);
635 }
636 }
637
GetClipEdgeType(Invert invert,GrAA aa)638 GrClipEdgeType GrReducedClip::GetClipEdgeType(Invert invert, GrAA aa) {
639 if (Invert::kNo == invert) {
640 return (GrAA::kYes == aa) ? GrClipEdgeType::kFillAA : GrClipEdgeType::kFillBW;
641 } else {
642 return (GrAA::kYes == aa) ? GrClipEdgeType::kInverseFillAA : GrClipEdgeType::kInverseFillBW;
643 }
644 }
645
addAnalyticRect(const SkRect & deviceSpaceRect,Invert invert,GrAA aa)646 GrReducedClip::ClipResult GrReducedClip::addAnalyticRect(const SkRect& deviceSpaceRect,
647 Invert invert, GrAA aa) {
648 if (this->numAnalyticElements() >= fMaxAnalyticElements) {
649 return ClipResult::kNotClipped;
650 }
651
652 fAnalyticFP = GrAARectEffect::Make(std::move(fAnalyticFP), GetClipEdgeType(invert, aa),
653 deviceSpaceRect);
654
655 SkASSERT(fAnalyticFP != nullptr);
656 ++fNumAnalyticElements;
657
658 return ClipResult::kClipped;
659 }
660
addAnalyticRRect(const SkRRect & deviceSpaceRRect,Invert invert,GrAA aa)661 GrReducedClip::ClipResult GrReducedClip::addAnalyticRRect(const SkRRect& deviceSpaceRRect,
662 Invert invert, GrAA aa) {
663 if (this->numAnalyticElements() >= fMaxAnalyticElements) {
664 return ClipResult::kNotClipped;
665 }
666
667 // Combine this analytic effect with the previous effect in the stack.
668 bool success;
669 std::tie(success, fAnalyticFP) = GrRRectEffect::Make(std::move(fAnalyticFP),
670 GetClipEdgeType(invert, aa),
671 deviceSpaceRRect, *fCaps->shaderCaps());
672 if (success) {
673 ++fNumAnalyticElements;
674 return ClipResult::kClipped;
675 }
676
677 SkPathBuilder deviceSpacePath;
678 deviceSpacePath.setIsVolatile(true);
679 deviceSpacePath.addRRect(deviceSpaceRRect);
680 return this->addAnalyticPath(deviceSpacePath.detach(), invert, aa);
681 }
682
addAnalyticPath(const SkPath & deviceSpacePath,Invert invert,GrAA aa)683 GrReducedClip::ClipResult GrReducedClip::addAnalyticPath(const SkPath& deviceSpacePath,
684 Invert invert, GrAA aa) {
685 if (this->numAnalyticElements() >= fMaxAnalyticElements) {
686 return ClipResult::kNotClipped;
687 }
688
689 // Combine this analytic effect with the previous effect in the stack.
690 bool success;
691 std::tie(success, fAnalyticFP) = GrConvexPolyEffect::Make(std::move(fAnalyticFP),
692 GetClipEdgeType(invert, aa),
693 deviceSpacePath);
694 if (success) {
695 ++fNumAnalyticElements;
696 return ClipResult::kClipped;
697 }
698
699 if (fCCPRClipPaths.count() < fMaxCCPRClipPaths && GrAA::kYes == aa) {
700 const SkRect& bounds = deviceSpacePath.getBounds();
701 if (bounds.height() * bounds.width() <= GrCoverageCountingPathRenderer::kMaxClipPathArea) {
702 // Set aside CCPR paths for later. We will create their clip FPs once we know the ID of
703 // the opsTask they will operate in.
704 SkPath& ccprClipPath = fCCPRClipPaths.push_back(deviceSpacePath);
705 if (Invert::kYes == invert) {
706 ccprClipPath.toggleInverseFillType();
707 }
708 return ClipResult::kClipped;
709 }
710 }
711
712 return ClipResult::kNotClipped;
713 }
714
makeEmpty()715 void GrReducedClip::makeEmpty() {
716 fHasScissor = false;
717 fAAClipRectGenID = SK_InvalidGenID;
718 fWindowRects.reset();
719 fMaskElements.reset();
720 fShader.reset();
721 fInitialState = InitialState::kAllOut;
722 fAnalyticFP = nullptr;
723 fNumAnalyticElements = 0;
724 fCCPRClipPaths.reset();
725 }
726
727 ////////////////////////////////////////////////////////////////////////////////
728 // Create a 8-bit clip mask in alpha
729
stencil_element(GrSurfaceDrawContext * rtc,const GrFixedClip & clip,const GrUserStencilSettings * ss,const SkMatrix & viewMatrix,const SkClipStack::Element * element)730 static bool stencil_element(GrSurfaceDrawContext* rtc,
731 const GrFixedClip& clip,
732 const GrUserStencilSettings* ss,
733 const SkMatrix& viewMatrix,
734 const SkClipStack::Element* element) {
735 GrAA aa = GrAA(element->isAA());
736 switch (element->getDeviceSpaceType()) {
737 case SkClipStack::Element::DeviceSpaceType::kEmpty:
738 SkDEBUGFAIL("Should never get here with an empty element.");
739 break;
740 case SkClipStack::Element::DeviceSpaceType::kRect: {
741 GrPaint paint;
742 paint.setCoverageSetOpXPFactory((SkRegion::Op)element->getOp(),
743 element->isInverseFilled());
744 rtc->stencilRect(&clip, ss, std::move(paint), aa, viewMatrix,
745 element->getDeviceSpaceRect());
746 return true;
747 }
748 default: {
749 SkPath path;
750 element->asDeviceSpacePath(&path);
751 if (path.isInverseFillType()) {
752 path.toggleInverseFillType();
753 }
754
755 return rtc->drawAndStencilPath(&clip, ss, (SkRegion::Op)element->getOp(),
756 element->isInverseFilled(), aa, viewMatrix, path);
757 }
758 }
759
760 return false;
761 }
762
draw_element(GrSurfaceDrawContext * rtc,const GrClip & clip,GrPaint && paint,GrAA aa,const SkMatrix & viewMatrix,const SkClipStack::Element * element)763 static void draw_element(GrSurfaceDrawContext* rtc,
764 const GrClip& clip, // TODO: can this just always be WideOpen?
765 GrPaint&& paint,
766 GrAA aa,
767 const SkMatrix& viewMatrix,
768 const SkClipStack::Element* element) {
769 // TODO: Draw rrects directly here.
770 switch (element->getDeviceSpaceType()) {
771 case SkClipStack::Element::DeviceSpaceType::kEmpty:
772 SkDEBUGFAIL("Should never get here with an empty element.");
773 break;
774 case SkClipStack::Element::DeviceSpaceType::kRect:
775 rtc->drawRect(&clip, std::move(paint), aa, viewMatrix, element->getDeviceSpaceRect());
776 break;
777 default: {
778 SkPath path;
779 element->asDeviceSpacePath(&path);
780 if (path.isInverseFillType()) {
781 path.toggleInverseFillType();
782 }
783
784 rtc->drawPath(&clip, std::move(paint), aa, viewMatrix, path, GrStyle::SimpleFill());
785 break;
786 }
787 }
788 }
789
drawAlphaClipMask(GrSurfaceDrawContext * rtc) const790 bool GrReducedClip::drawAlphaClipMask(GrSurfaceDrawContext* rtc) const {
791 // The texture may be larger than necessary, this rect represents the part of the texture
792 // we populate with a rasterization of the clip.
793 GrFixedClip clip(rtc->dimensions(), SkIRect::MakeWH(fScissor.width(), fScissor.height()));
794
795 if (!fWindowRects.empty()) {
796 clip.setWindowRectangles(fWindowRects.makeOffset(-fScissor.left(), -fScissor.top()),
797 GrWindowRectsState::Mode::kExclusive);
798 }
799
800 // The scratch texture that we are drawing into can be substantially larger than the mask. Only
801 // clear the part that we care about.
802 SkPMColor4f initialCoverage =
803 InitialState::kAllIn == this->initialState() ? SK_PMColor4fWHITE : SK_PMColor4fTRANSPARENT;
804 if (clip.hasWindowRectangles()) {
805 GrPaint paint;
806 paint.setColor4f(initialCoverage);
807 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
808 rtc->drawRect(&clip, std::move(paint), GrAA::kNo, SkMatrix::I(),
809 SkRect::Make(clip.scissorRect()));
810 } else {
811 rtc->clearAtLeast(clip.scissorRect(), initialCoverage);
812 }
813
814 // Set the matrix so that rendered clip elements are transformed to mask space from clip space.
815 SkMatrix translate;
816 translate.setTranslate(SkIntToScalar(-fScissor.left()), SkIntToScalar(-fScissor.top()));
817
818 // walk through each clip element and perform its set op
819 for (ElementList::Iter iter(fMaskElements); iter.get(); iter.next()) {
820 const Element* element = iter.get();
821 SkRegion::Op op = (SkRegion::Op)element->getOp();
822 GrAA aa = GrAA(element->isAA());
823 bool invert = element->isInverseFilled();
824 if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) {
825 // draw directly into the result with the stencil set to make the pixels affected
826 // by the clip shape be non-zero.
827 static constexpr GrUserStencilSettings kStencilInElement(
828 GrUserStencilSettings::StaticInit<
829 0xffff,
830 GrUserStencilTest::kAlways,
831 0xffff,
832 GrUserStencilOp::kReplace,
833 GrUserStencilOp::kReplace,
834 0xffff>()
835 );
836 if (!stencil_element(rtc, clip, &kStencilInElement, translate, element)) {
837 return false;
838 }
839
840 // Draw to the exterior pixels (those with a zero stencil value).
841 static constexpr GrUserStencilSettings kDrawOutsideElement(
842 GrUserStencilSettings::StaticInit<
843 0x0000,
844 GrUserStencilTest::kEqual,
845 0xffff,
846 GrUserStencilOp::kZero,
847 GrUserStencilOp::kZero,
848 0xffff>()
849 );
850
851 GrPaint paint;
852 paint.setCoverageSetOpXPFactory(op, !invert);
853 rtc->stencilRect(&clip, &kDrawOutsideElement, std::move(paint), GrAA::kNo, translate,
854 SkRect::Make(fScissor));
855 } else {
856 // all the remaining ops can just be directly draw into the accumulation buffer
857 GrPaint paint;
858 paint.setCoverageSetOpXPFactory(op, false);
859
860 draw_element(rtc, clip, std::move(paint), aa, translate, element);
861 }
862 }
863
864 return true;
865 }
866
867 ////////////////////////////////////////////////////////////////////////////////
868 // Create a 1-bit clip mask in the stencil buffer.
869
drawStencilClipMask(GrRecordingContext * context,GrSurfaceDrawContext * surfaceDrawContext) const870 bool GrReducedClip::drawStencilClipMask(GrRecordingContext* context,
871 GrSurfaceDrawContext* surfaceDrawContext) const {
872 GrStencilMaskHelper helper(context, surfaceDrawContext);
873 if (!helper.init(fScissor, this->maskGenID(), fWindowRects, this->numAnalyticElements())) {
874 // The stencil mask doesn't need updating
875 return true;
876 }
877
878 helper.clear(InitialState::kAllIn == this->initialState());
879
880 // walk through each clip element and perform its set op with the existing clip.
881 for (ElementList::Iter iter(fMaskElements); iter.get(); iter.next()) {
882 const Element* element = iter.get();
883 SkRegion::Op op = (SkRegion::Op)element->getOp();
884 GrAA aa = element->isAA() ? GrAA::kYes : GrAA::kNo;
885
886 if (Element::DeviceSpaceType::kRect == element->getDeviceSpaceType()) {
887 helper.drawRect(element->getDeviceSpaceRect(), SkMatrix::I(), op, aa);
888 } else {
889 SkPath path;
890 element->asDeviceSpacePath(&path);
891 if (!helper.drawPath(path, SkMatrix::I(), op, aa)) {
892 return false;
893 }
894 }
895 }
896
897 helper.finish();
898 return true;
899 }
900
numAnalyticElements() const901 int GrReducedClip::numAnalyticElements() const {
902 return fCCPRClipPaths.size() + fNumAnalyticElements;
903 }
904
finishAndDetachAnalyticElements(GrRecordingContext * context,const SkMatrixProvider & matrixProvider,GrCoverageCountingPathRenderer * ccpr,uint32_t opsTaskID)905 GrFPResult GrReducedClip::finishAndDetachAnalyticElements(GrRecordingContext* context,
906 const SkMatrixProvider& matrixProvider,
907 GrCoverageCountingPathRenderer* ccpr,
908 uint32_t opsTaskID) {
909 // Combine the analytic FP with any CCPR clip processors.
910 std::unique_ptr<GrFragmentProcessor> clipFP = std::move(fAnalyticFP);
911 fNumAnalyticElements = 0;
912
913 for (const SkPath& ccprClipPath : fCCPRClipPaths) {
914 SkASSERT(ccpr);
915 SkASSERT(fHasScissor);
916 bool success;
917 std::tie(success, clipFP) = ccpr->makeClipProcessor(std::move(clipFP), opsTaskID,
918 ccprClipPath, fScissor, *fCaps);
919 if (!success) {
920 return GrFPFailure(nullptr);
921 }
922 }
923 fCCPRClipPaths.reset();
924
925 // Create the shader.
926 std::unique_ptr<GrFragmentProcessor> shaderFP;
927 if (fShader != nullptr) {
928 static const GrColorInfo kCoverageColorInfo{GrColorType::kUnknown, kPremul_SkAlphaType,
929 nullptr};
930 GrFPArgs args(context, matrixProvider, &kCoverageColorInfo);
931 shaderFP = as_SB(fShader)->asFragmentProcessor(args);
932 if (shaderFP != nullptr) {
933 shaderFP = GrFragmentProcessor::MulInputByChildAlpha(std::move(shaderFP));
934 }
935 }
936
937 // Compose the clip and shader FPs.
938 return GrFPSuccess(GrFragmentProcessor::Compose(std::move(shaderFP), std::move(clipFP)));
939 }
940