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