Home | History | Annotate | Download | only in gpu
      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 "GrReducedClip.h"
      9 
     10 #include "GrAppliedClip.h"
     11 #include "GrClip.h"
     12 #include "GrColor.h"
     13 #include "GrContextPriv.h"
     14 #include "GrRenderTargetContext.h"
     15 #include "GrRenderTargetContextPriv.h"
     16 #include "GrDrawingManager.h"
     17 #include "GrFixedClip.h"
     18 #include "GrPathRenderer.h"
     19 #include "GrStencilSettings.h"
     20 #include "GrStyle.h"
     21 #include "GrUserStencilSettings.h"
     22 #include "SkClipOpPriv.h"
     23 
     24 typedef SkClipStack::Element Element;
     25 
     26 /**
     27  * There are plenty of optimizations that could be added here. Maybe flips could be folded into
     28  * earlier operations. Or would inserting flips and reversing earlier ops ever be a win? Perhaps
     29  * for the case where the bounds are kInsideOut_BoundsType. We could restrict earlier operations
     30  * based on later intersect operations, and perhaps remove intersect-rects. We could optionally
     31  * take a rect in case the caller knows a bound on what is to be drawn through this clip.
     32  */
     33 GrReducedClip::GrReducedClip(const SkClipStack& stack, const SkRect& queryBounds,
     34                              int maxWindowRectangles) {
     35     SkASSERT(!queryBounds.isEmpty());
     36     fHasIBounds = false;
     37 
     38     if (stack.isWideOpen()) {
     39         fInitialState = InitialState::kAllIn;
     40         return;
     41     }
     42 
     43     SkClipStack::BoundsType stackBoundsType;
     44     SkRect stackBounds;
     45     bool iior;
     46     stack.getBounds(&stackBounds, &stackBoundsType, &iior);
     47 
     48     if (GrClip::IsOutsideClip(stackBounds, queryBounds)) {
     49         bool insideOut = SkClipStack::kInsideOut_BoundsType == stackBoundsType;
     50         fInitialState = insideOut ? InitialState::kAllIn : InitialState::kAllOut;
     51         return;
     52     }
     53 
     54     if (iior) {
     55         // "Is intersection of rects" means the clip is a single rect indicated by the stack bounds.
     56         // This should only be true if aa/non-aa status matches among all elements.
     57         SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType);
     58         SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
     59         if (!iter.prev()->isAA() || GrClip::IsPixelAligned(stackBounds)) {
     60             // The clip is a non-aa rect. This is the one spot where we can actually implement the
     61             // clip (using fIBounds) rather than just telling the caller what it should be.
     62             stackBounds.round(&fIBounds);
     63             fHasIBounds = true;
     64             fInitialState = fIBounds.isEmpty() ? InitialState::kAllOut : InitialState::kAllIn;
     65             return;
     66         }
     67         if (GrClip::IsInsideClip(stackBounds, queryBounds)) {
     68             fInitialState = InitialState::kAllIn;
     69             return;
     70         }
     71 
     72         SkRect tightBounds;
     73         SkAssertResult(tightBounds.intersect(stackBounds, queryBounds));
     74         fIBounds = GrClip::GetPixelIBounds(tightBounds);
     75         if (fIBounds.isEmpty()) {
     76             fInitialState = InitialState::kAllOut;
     77             return;
     78         }
     79         fHasIBounds = true;
     80 
     81         // Implement the clip with an AA rect element.
     82         fElements.addToHead(stackBounds, kReplace_SkClipOp, true/*doAA*/);
     83         fElementsGenID = stack.getTopmostGenID();
     84         fRequiresAA = true;
     85 
     86         fInitialState = InitialState::kAllOut;
     87         return;
     88     }
     89 
     90     SkRect tighterQuery = queryBounds;
     91     if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
     92         // Tighten the query by introducing a new clip at the stack's pixel boundaries. (This new
     93         // clip will be enforced by the scissor through fIBounds.)
     94         SkAssertResult(tighterQuery.intersect(GrClip::GetPixelBounds(stackBounds)));
     95     }
     96 
     97     fIBounds = GrClip::GetPixelIBounds(tighterQuery);
     98     if (fIBounds.isEmpty()) {
     99         fInitialState = InitialState::kAllOut;
    100         return;
    101     }
    102     fHasIBounds = true;
    103 
    104     // Now that we have determined the bounds to use and filtered out the trivial cases, call the
    105     // helper that actually walks the stack.
    106     this->walkStack(stack, tighterQuery, maxWindowRectangles);
    107 
    108     if (fWindowRects.count() < maxWindowRectangles) {
    109         this->addInteriorWindowRectangles(maxWindowRectangles);
    110     }
    111 }
    112 
    113 void GrReducedClip::walkStack(const SkClipStack& stack, const SkRect& queryBounds,
    114                               int maxWindowRectangles) {
    115     // walk backwards until we get to:
    116     //  a) the beginning
    117     //  b) an operation that is known to make the bounds all inside/outside
    118     //  c) a replace operation
    119 
    120     enum class InitialTriState {
    121         kUnknown = -1,
    122         kAllIn = (int)GrReducedClip::InitialState::kAllIn,
    123         kAllOut = (int)GrReducedClip::InitialState::kAllOut
    124     } initialTriState = InitialTriState::kUnknown;
    125 
    126     // During our backwards walk, track whether we've seen ops that either grow or shrink the clip.
    127     // TODO: track these per saved clip so that we can consider them on the forward pass.
    128     bool embiggens = false;
    129     bool emsmallens = false;
    130 
    131     // We use a slightly relaxed set of query bounds for element containment tests. This is to
    132     // account for floating point rounding error that may have occurred during coord transforms.
    133     SkRect relaxedQueryBounds = queryBounds.makeInset(GrClip::kBoundsTolerance,
    134                                                       GrClip::kBoundsTolerance);
    135 
    136     SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
    137     int numAAElements = 0;
    138     while (InitialTriState::kUnknown == initialTriState) {
    139         const Element* element = iter.prev();
    140         if (nullptr == element) {
    141             initialTriState = InitialTriState::kAllIn;
    142             break;
    143         }
    144         if (SkClipStack::kEmptyGenID == element->getGenID()) {
    145             initialTriState = InitialTriState::kAllOut;
    146             break;
    147         }
    148         if (SkClipStack::kWideOpenGenID == element->getGenID()) {
    149             initialTriState = InitialTriState::kAllIn;
    150             break;
    151         }
    152 
    153         bool skippable = false;
    154         bool isFlip = false; // does this op just flip the in/out state of every point in the bounds
    155 
    156         switch (element->getOp()) {
    157             case kDifference_SkClipOp:
    158                 // check if the shape subtracted either contains the entire bounds (and makes
    159                 // the clip empty) or is outside the bounds and therefore can be skipped.
    160                 if (element->isInverseFilled()) {
    161                     if (element->contains(relaxedQueryBounds)) {
    162                         skippable = true;
    163                     } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
    164                         initialTriState = InitialTriState::kAllOut;
    165                         skippable = true;
    166                     }
    167                 } else {
    168                     if (element->contains(relaxedQueryBounds)) {
    169                         initialTriState = InitialTriState::kAllOut;
    170                         skippable = true;
    171                     } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
    172                         skippable = true;
    173                     } else if (fWindowRects.count() < maxWindowRectangles && !embiggens &&
    174                                !element->isAA() && Element::kRect_Type == element->getType()) {
    175                         this->addWindowRectangle(element->getRect(), false);
    176                         skippable = true;
    177                     }
    178                 }
    179                 if (!skippable) {
    180                     emsmallens = true;
    181                 }
    182                 break;
    183             case kIntersect_SkClipOp:
    184                 // check if the shape intersected contains the entire bounds and therefore can
    185                 // be skipped or it is outside the entire bounds and therefore makes the clip
    186                 // empty.
    187                 if (element->isInverseFilled()) {
    188                     if (element->contains(relaxedQueryBounds)) {
    189                         initialTriState = InitialTriState::kAllOut;
    190                         skippable = true;
    191                     } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
    192                         skippable = true;
    193                     }
    194                 } else {
    195                     if (element->contains(relaxedQueryBounds)) {
    196                         skippable = true;
    197                     } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
    198                         initialTriState = InitialTriState::kAllOut;
    199                         skippable = true;
    200                     } else if (!embiggens && !element->isAA() &&
    201                                Element::kRect_Type == element->getType()) {
    202                         // fIBounds and queryBounds have already acccounted for this element via
    203                         // clip stack bounds; here we just apply the non-aa rounding effect.
    204                         SkIRect nonaaRect;
    205                         element->getRect().round(&nonaaRect);
    206                         if (!this->intersectIBounds(nonaaRect)) {
    207                             return;
    208                         }
    209                         skippable = true;
    210                     }
    211                 }
    212                 if (!skippable) {
    213                     emsmallens = true;
    214                 }
    215                 break;
    216             case kUnion_SkClipOp:
    217                 // If the union-ed shape contains the entire bounds then after this element
    218                 // the bounds is entirely inside the clip. If the union-ed shape is outside the
    219                 // bounds then this op can be skipped.
    220                 if (element->isInverseFilled()) {
    221                     if (element->contains(relaxedQueryBounds)) {
    222                         skippable = true;
    223                     } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
    224                         initialTriState = InitialTriState::kAllIn;
    225                         skippable = true;
    226                     }
    227                 } else {
    228                     if (element->contains(relaxedQueryBounds)) {
    229                         initialTriState = InitialTriState::kAllIn;
    230                         skippable = true;
    231                     } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
    232                         skippable = true;
    233                     }
    234                 }
    235                 if (!skippable) {
    236                     embiggens = true;
    237                 }
    238                 break;
    239             case kXOR_SkClipOp:
    240                 // If the bounds is entirely inside the shape being xor-ed then the effect is
    241                 // to flip the inside/outside state of every point in the bounds. We may be
    242                 // able to take advantage of this in the forward pass. If the xor-ed shape
    243                 // doesn't intersect the bounds then it can be skipped.
    244                 if (element->isInverseFilled()) {
    245                     if (element->contains(relaxedQueryBounds)) {
    246                         skippable = true;
    247                     } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
    248                         isFlip = true;
    249                     }
    250                 } else {
    251                     if (element->contains(relaxedQueryBounds)) {
    252                         isFlip = true;
    253                     } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
    254                         skippable = true;
    255                     }
    256                 }
    257                 if (!skippable) {
    258                     emsmallens = embiggens = true;
    259                 }
    260                 break;
    261             case kReverseDifference_SkClipOp:
    262                 // When the bounds is entirely within the rev-diff shape then this behaves like xor
    263                 // and reverses every point inside the bounds. If the shape is completely outside
    264                 // the bounds then we know after this element is applied that the bounds will be
    265                 // all outside the current clip.B
    266                 if (element->isInverseFilled()) {
    267                     if (element->contains(relaxedQueryBounds)) {
    268                         initialTriState = InitialTriState::kAllOut;
    269                         skippable = true;
    270                     } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
    271                         isFlip = true;
    272                     }
    273                 } else {
    274                     if (element->contains(relaxedQueryBounds)) {
    275                         isFlip = true;
    276                     } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
    277                         initialTriState = InitialTriState::kAllOut;
    278                         skippable = true;
    279                     }
    280                 }
    281                 if (!skippable) {
    282                     emsmallens = embiggens = true;
    283                 }
    284                 break;
    285 
    286             case kReplace_SkClipOp:
    287                 // Replace will always terminate our walk. We will either begin the forward walk
    288                 // at the replace op or detect here than the shape is either completely inside
    289                 // or completely outside the bounds. In this latter case it can be skipped by
    290                 // setting the correct value for initialTriState.
    291                 if (element->isInverseFilled()) {
    292                     if (element->contains(relaxedQueryBounds)) {
    293                         initialTriState = InitialTriState::kAllOut;
    294                         skippable = true;
    295                     } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
    296                         initialTriState = InitialTriState::kAllIn;
    297                         skippable = true;
    298                     }
    299                 } else {
    300                     if (element->contains(relaxedQueryBounds)) {
    301                         initialTriState = InitialTriState::kAllIn;
    302                         skippable = true;
    303                     } else if (GrClip::IsOutsideClip(element->getBounds(), queryBounds)) {
    304                         initialTriState = InitialTriState::kAllOut;
    305                         skippable = true;
    306                     } else if (!embiggens && !element->isAA() &&
    307                                Element::kRect_Type == element->getType()) {
    308                         // fIBounds and queryBounds have already acccounted for this element via
    309                         // clip stack bounds; here we just apply the non-aa rounding effect.
    310                         SkIRect nonaaRect;
    311                         element->getRect().round(&nonaaRect);
    312                         if (!this->intersectIBounds(nonaaRect)) {
    313                             return;
    314                         }
    315                         initialTriState = InitialTriState::kAllIn;
    316                         skippable = true;
    317                     }
    318                 }
    319                 if (!skippable) {
    320                     initialTriState = InitialTriState::kAllOut;
    321                     embiggens = emsmallens = true;
    322                 }
    323                 break;
    324             default:
    325                 SkDEBUGFAIL("Unexpected op.");
    326                 break;
    327         }
    328         if (!skippable) {
    329             if (0 == fElements.count()) {
    330                 // This will be the last element. Record the stricter genID.
    331                 fElementsGenID = element->getGenID();
    332             }
    333 
    334             // if it is a flip, change it to a bounds-filling rect
    335             if (isFlip) {
    336                 SkASSERT(kXOR_SkClipOp == element->getOp() ||
    337                          kReverseDifference_SkClipOp == element->getOp());
    338                 fElements.addToHead(SkRect::Make(fIBounds), kReverseDifference_SkClipOp, false);
    339             } else {
    340                 Element* newElement = fElements.addToHead(*element);
    341                 if (newElement->isAA()) {
    342                     ++numAAElements;
    343                 }
    344                 // Intersecting an inverse shape is the same as differencing the non-inverse shape.
    345                 // Replacing with an inverse shape is the same as setting initialState=kAllIn and
    346                 // differencing the non-inverse shape.
    347                 bool isReplace = kReplace_SkClipOp == newElement->getOp();
    348                 if (newElement->isInverseFilled() &&
    349                     (kIntersect_SkClipOp == newElement->getOp() || isReplace)) {
    350                     newElement->invertShapeFillType();
    351                     newElement->setOp(kDifference_SkClipOp);
    352                     if (isReplace) {
    353                         SkASSERT(InitialTriState::kAllOut == initialTriState);
    354                         initialTriState = InitialTriState::kAllIn;
    355                     }
    356                 }
    357             }
    358         }
    359     }
    360 
    361     if ((InitialTriState::kAllOut == initialTriState && !embiggens) ||
    362         (InitialTriState::kAllIn == initialTriState && !emsmallens)) {
    363         fElements.reset();
    364         numAAElements = 0;
    365     } else {
    366         Element* element = fElements.headIter().get();
    367         while (element) {
    368             bool skippable = false;
    369             switch (element->getOp()) {
    370                 case kDifference_SkClipOp:
    371                     // subtracting from the empty set yields the empty set.
    372                     skippable = InitialTriState::kAllOut == initialTriState;
    373                     break;
    374                 case kIntersect_SkClipOp:
    375                     // intersecting with the empty set yields the empty set
    376                     if (InitialTriState::kAllOut == initialTriState) {
    377                         skippable = true;
    378                     } else {
    379                         // We can clear to zero and then simply draw the clip element.
    380                         initialTriState = InitialTriState::kAllOut;
    381                         element->setOp(kReplace_SkClipOp);
    382                     }
    383                     break;
    384                 case kUnion_SkClipOp:
    385                     if (InitialTriState::kAllIn == initialTriState) {
    386                         // unioning the infinite plane with anything is a no-op.
    387                         skippable = true;
    388                     } else {
    389                         // unioning the empty set with a shape is the shape.
    390                         element->setOp(kReplace_SkClipOp);
    391                     }
    392                     break;
    393                 case kXOR_SkClipOp:
    394                     if (InitialTriState::kAllOut == initialTriState) {
    395                         // xor could be changed to diff in the kAllIn case, not sure it's a win.
    396                         element->setOp(kReplace_SkClipOp);
    397                     }
    398                     break;
    399                 case kReverseDifference_SkClipOp:
    400                     if (InitialTriState::kAllIn == initialTriState) {
    401                         // subtracting the whole plane will yield the empty set.
    402                         skippable = true;
    403                         initialTriState = InitialTriState::kAllOut;
    404                     } else {
    405                         // this picks up flips inserted in the backwards pass.
    406                         skippable = element->isInverseFilled() ?
    407                             GrClip::IsOutsideClip(element->getBounds(), queryBounds) :
    408                             element->contains(relaxedQueryBounds);
    409                         if (skippable) {
    410                             initialTriState = InitialTriState::kAllIn;
    411                         } else {
    412                             element->setOp(kReplace_SkClipOp);
    413                         }
    414                     }
    415                     break;
    416                 case kReplace_SkClipOp:
    417                     skippable = false; // we would have skipped it in the backwards walk if we
    418                                        // could've.
    419                     break;
    420                 default:
    421                     SkDEBUGFAIL("Unexpected op.");
    422                     break;
    423             }
    424             if (!skippable) {
    425                 break;
    426             } else {
    427                 if (element->isAA()) {
    428                     --numAAElements;
    429                 }
    430                 fElements.popHead();
    431                 element = fElements.headIter().get();
    432             }
    433         }
    434     }
    435     fRequiresAA = numAAElements > 0;
    436 
    437     SkASSERT(InitialTriState::kUnknown != initialTriState);
    438     fInitialState = static_cast<GrReducedClip::InitialState>(initialTriState);
    439 }
    440 
    441 static bool element_is_pure_subtract(SkClipOp op) {
    442     SkASSERT(static_cast<int>(op) >= 0);
    443     return static_cast<int>(op) <= static_cast<int>(kIntersect_SkClipOp);
    444 
    445     GR_STATIC_ASSERT(0 == static_cast<int>(kDifference_SkClipOp));
    446     GR_STATIC_ASSERT(1 == static_cast<int>(kIntersect_SkClipOp));
    447 }
    448 
    449 void GrReducedClip::addInteriorWindowRectangles(int maxWindowRectangles) {
    450     SkASSERT(fWindowRects.count() < maxWindowRectangles);
    451     // Walk backwards through the element list and add window rectangles to the interiors of
    452     // "difference" elements. Quit if we encounter an element that may grow the clip.
    453     ElementList::Iter iter(fElements, ElementList::Iter::kTail_IterStart);
    454     for (; iter.get() && element_is_pure_subtract(iter.get()->getOp()); iter.prev()) {
    455         const Element* element = iter.get();
    456         if (kDifference_SkClipOp != element->getOp()) {
    457             continue;
    458         }
    459 
    460         if (Element::kRect_Type == element->getType()) {
    461             SkASSERT(element->isAA());
    462             this->addWindowRectangle(element->getRect(), true);
    463             if (fWindowRects.count() >= maxWindowRectangles) {
    464                 return;
    465             }
    466             continue;
    467         }
    468 
    469         if (Element::kRRect_Type == element->getType()) {
    470             // For round rects we add two overlapping windows in the shape of a plus.
    471             const SkRRect& clipRRect = element->getRRect();
    472             SkVector insetTL = clipRRect.radii(SkRRect::kUpperLeft_Corner);
    473             SkVector insetBR = clipRRect.radii(SkRRect::kLowerRight_Corner);
    474             if (SkRRect::kComplex_Type == clipRRect.getType()) {
    475                 const SkVector& insetTR = clipRRect.radii(SkRRect::kUpperRight_Corner);
    476                 const SkVector& insetBL = clipRRect.radii(SkRRect::kLowerLeft_Corner);
    477                 insetTL.fX = SkTMax(insetTL.x(), insetBL.x());
    478                 insetTL.fY = SkTMax(insetTL.y(), insetTR.y());
    479                 insetBR.fX = SkTMax(insetBR.x(), insetTR.x());
    480                 insetBR.fY = SkTMax(insetBR.y(), insetBL.y());
    481             }
    482             const SkRect& bounds = clipRRect.getBounds();
    483             if (insetTL.x() + insetBR.x() >= bounds.width() ||
    484                 insetTL.y() + insetBR.y() >= bounds.height()) {
    485                 continue; // The interior "plus" is empty.
    486             }
    487 
    488             SkRect horzRect = SkRect::MakeLTRB(bounds.left(), bounds.top() + insetTL.y(),
    489                                                bounds.right(), bounds.bottom() - insetBR.y());
    490             this->addWindowRectangle(horzRect, element->isAA());
    491             if (fWindowRects.count() >= maxWindowRectangles) {
    492                 return;
    493             }
    494 
    495             SkRect vertRect = SkRect::MakeLTRB(bounds.left() + insetTL.x(), bounds.top(),
    496                                                bounds.right() - insetBR.x(), bounds.bottom());
    497             this->addWindowRectangle(vertRect, element->isAA());
    498             if (fWindowRects.count() >= maxWindowRectangles) {
    499                 return;
    500             }
    501             continue;
    502         }
    503     }
    504 }
    505 
    506 inline void GrReducedClip::addWindowRectangle(const SkRect& elementInteriorRect, bool elementIsAA) {
    507     SkIRect window;
    508     if (!elementIsAA) {
    509         elementInteriorRect.round(&window);
    510     } else {
    511         elementInteriorRect.roundIn(&window);
    512     }
    513     if (!window.isEmpty()) { // Skip very thin windows that round to zero or negative dimensions.
    514         fWindowRects.addWindow(window);
    515     }
    516 }
    517 
    518 inline bool GrReducedClip::intersectIBounds(const SkIRect& irect) {
    519     SkASSERT(fHasIBounds);
    520     if (!fIBounds.intersect(irect)) {
    521         fHasIBounds = false;
    522         fWindowRects.reset();
    523         fElements.reset();
    524         fRequiresAA = false;
    525         fInitialState = InitialState::kAllOut;
    526         return false;
    527     }
    528     return true;
    529 }
    530 
    531 ////////////////////////////////////////////////////////////////////////////////
    532 // Create a 8-bit clip mask in alpha
    533 
    534 static bool stencil_element(GrRenderTargetContext* rtc,
    535                             const GrFixedClip& clip,
    536                             const GrUserStencilSettings* ss,
    537                             const SkMatrix& viewMatrix,
    538                             const SkClipStack::Element* element) {
    539     GrAA aa = GrBoolToAA(element->isAA());
    540     switch (element->getType()) {
    541         case Element::kEmpty_Type:
    542             SkDEBUGFAIL("Should never get here with an empty element.");
    543             break;
    544         case Element::kRect_Type:
    545             return rtc->priv().drawAndStencilRect(clip, ss,
    546                                                   (SkRegion::Op)element->getOp(),
    547                                                   element->isInverseFilled(), aa, viewMatrix,
    548                                                   element->getRect());
    549             break;
    550         default: {
    551             SkPath path;
    552             element->asPath(&path);
    553             if (path.isInverseFillType()) {
    554                 path.toggleInverseFillType();
    555             }
    556 
    557             return rtc->priv().drawAndStencilPath(clip, ss, (SkRegion::Op)element->getOp(),
    558                                                   element->isInverseFilled(), aa, viewMatrix, path);
    559             break;
    560         }
    561     }
    562 
    563     return false;
    564 }
    565 
    566 static void draw_element(GrRenderTargetContext* rtc,
    567                          const GrClip& clip,  // TODO: can this just always be WideOpen?
    568                          GrPaint&& paint,
    569                          GrAA aa,
    570                          const SkMatrix& viewMatrix,
    571                          const SkClipStack::Element* element) {
    572     // TODO: Draw rrects directly here.
    573     switch (element->getType()) {
    574         case Element::kEmpty_Type:
    575             SkDEBUGFAIL("Should never get here with an empty element.");
    576             break;
    577         case Element::kRect_Type:
    578             rtc->drawRect(clip, std::move(paint), aa, viewMatrix, element->getRect());
    579             break;
    580         default: {
    581             SkPath path;
    582             element->asPath(&path);
    583             if (path.isInverseFillType()) {
    584                 path.toggleInverseFillType();
    585             }
    586 
    587             rtc->drawPath(clip, std::move(paint), aa, viewMatrix, path, GrStyle::SimpleFill());
    588             break;
    589         }
    590     }
    591 }
    592 
    593 bool GrReducedClip::drawAlphaClipMask(GrRenderTargetContext* rtc) const {
    594     // The texture may be larger than necessary, this rect represents the part of the texture
    595     // we populate with a rasterization of the clip.
    596     GrFixedClip clip(SkIRect::MakeWH(fIBounds.width(), fIBounds.height()));
    597 
    598     if (!fWindowRects.empty()) {
    599         clip.setWindowRectangles(fWindowRects.makeOffset(-fIBounds.left(), -fIBounds.top()),
    600                                  GrWindowRectsState::Mode::kExclusive);
    601     }
    602 
    603     // The scratch texture that we are drawing into can be substantially larger than the mask. Only
    604     // clear the part that we care about.
    605     GrColor initialCoverage = InitialState::kAllIn == this->initialState() ? -1 : 0;
    606     rtc->priv().clear(clip, initialCoverage, true);
    607 
    608     // Set the matrix so that rendered clip elements are transformed to mask space from clip space.
    609     SkMatrix translate;
    610     translate.setTranslate(SkIntToScalar(-fIBounds.left()), SkIntToScalar(-fIBounds.top()));
    611 
    612     // walk through each clip element and perform its set op
    613     for (ElementList::Iter iter(fElements); iter.get(); iter.next()) {
    614         const Element* element = iter.get();
    615         SkRegion::Op op = (SkRegion::Op)element->getOp();
    616         GrAA aa = GrBoolToAA(element->isAA());
    617         bool invert = element->isInverseFilled();
    618         if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) {
    619             // draw directly into the result with the stencil set to make the pixels affected
    620             // by the clip shape be non-zero.
    621             static constexpr GrUserStencilSettings kStencilInElement(
    622                  GrUserStencilSettings::StaticInit<
    623                      0xffff,
    624                      GrUserStencilTest::kAlways,
    625                      0xffff,
    626                      GrUserStencilOp::kReplace,
    627                      GrUserStencilOp::kReplace,
    628                      0xffff>()
    629             );
    630             if (!stencil_element(rtc, clip, &kStencilInElement, translate, element)) {
    631                 return false;
    632             }
    633 
    634             // Draw to the exterior pixels (those with a zero stencil value).
    635             static constexpr GrUserStencilSettings kDrawOutsideElement(
    636                  GrUserStencilSettings::StaticInit<
    637                      0x0000,
    638                      GrUserStencilTest::kEqual,
    639                      0xffff,
    640                      GrUserStencilOp::kZero,
    641                      GrUserStencilOp::kZero,
    642                      0xffff>()
    643             );
    644             if (!rtc->priv().drawAndStencilRect(clip, &kDrawOutsideElement, op, !invert, GrAA::kNo,
    645                                                 translate, SkRect::Make(fIBounds))) {
    646                 return false;
    647             }
    648         } else {
    649             // all the remaining ops can just be directly draw into the accumulation buffer
    650             GrPaint paint;
    651             paint.setCoverageSetOpXPFactory(op, false);
    652 
    653             draw_element(rtc, clip, std::move(paint), aa, translate, element);
    654         }
    655     }
    656 
    657     return true;
    658 }
    659 
    660 ////////////////////////////////////////////////////////////////////////////////
    661 // Create a 1-bit clip mask in the stencil buffer.
    662 
    663 class StencilClip final : public GrClip {
    664 public:
    665     StencilClip(const SkIRect& scissorRect, uint32_t clipStackID)
    666         : fFixedClip(scissorRect)
    667         , fClipStackID(clipStackID) {
    668     }
    669 
    670     const GrFixedClip& fixedClip() const { return fFixedClip; }
    671 
    672     void setWindowRectangles(const GrWindowRectangles& windows, GrWindowRectsState::Mode mode) {
    673         fFixedClip.setWindowRectangles(windows, mode);
    674     }
    675 
    676 private:
    677     bool quickContains(const SkRect&) const override {
    678         return false;
    679     }
    680     void getConservativeBounds(int width, int height, SkIRect* bounds, bool* iior) const override {
    681         fFixedClip.getConservativeBounds(width, height, bounds, iior);
    682     }
    683     bool isRRect(const SkRect& rtBounds, SkRRect* rr, GrAA*) const override {
    684         return false;
    685     }
    686     bool apply(GrContext* context, GrRenderTargetContext* renderTargetContext, bool useHWAA,
    687                bool hasUserStencilSettings, GrAppliedClip* out, SkRect* bounds) const override {
    688         if (!fFixedClip.apply(context, renderTargetContext, useHWAA, hasUserStencilSettings, out,
    689                               bounds)) {
    690             return false;
    691         }
    692         out->addStencilClip(fClipStackID);
    693         return true;
    694     }
    695 
    696     GrFixedClip fFixedClip;
    697     uint32_t    fClipStackID;
    698 
    699     typedef GrClip INHERITED;
    700 };
    701 
    702 bool GrReducedClip::drawStencilClipMask(GrContext* context,
    703                                         GrRenderTargetContext* renderTargetContext) const {
    704     // We set the current clip to the bounds so that our recursive draws are scissored to them.
    705     StencilClip stencilClip(fIBounds, this->elementsGenID());
    706 
    707     if (!fWindowRects.empty()) {
    708         stencilClip.setWindowRectangles(fWindowRects, GrWindowRectsState::Mode::kExclusive);
    709     }
    710 
    711     bool initialState = InitialState::kAllIn == this->initialState();
    712     renderTargetContext->priv().clearStencilClip(stencilClip.fixedClip(), initialState);
    713 
    714     // walk through each clip element and perform its set op with the existing clip.
    715     for (ElementList::Iter iter(fElements); iter.get(); iter.next()) {
    716         const Element* element = iter.get();
    717         GrAAType aaType = GrAAType::kNone;
    718         if (element->isAA() && GrFSAAType::kNone != renderTargetContext->fsaaType()) {
    719             aaType = GrAAType::kMSAA;
    720         }
    721 
    722         bool fillInverted = false;
    723 
    724         // This will be used to determine whether the clip shape can be rendered into the
    725         // stencil with arbitrary stencil settings.
    726         GrPathRenderer::StencilSupport stencilSupport;
    727 
    728         SkRegion::Op op = (SkRegion::Op)element->getOp();
    729 
    730         GrPathRenderer* pr = nullptr;
    731         SkPath clipPath;
    732         if (Element::kRect_Type == element->getType()) {
    733             stencilSupport = GrPathRenderer::kNoRestriction_StencilSupport;
    734             fillInverted = false;
    735         } else {
    736             element->asPath(&clipPath);
    737             fillInverted = clipPath.isInverseFillType();
    738             if (fillInverted) {
    739                 clipPath.toggleInverseFillType();
    740             }
    741 
    742             GrShape shape(clipPath, GrStyle::SimpleFill());
    743             GrPathRenderer::CanDrawPathArgs canDrawArgs;
    744             canDrawArgs.fCaps = context->caps();
    745             canDrawArgs.fViewMatrix = &SkMatrix::I();
    746             canDrawArgs.fShape = &shape;
    747             canDrawArgs.fAAType = aaType;
    748             canDrawArgs.fHasUserStencilSettings = false;
    749 
    750             GrDrawingManager* dm = context->contextPriv().drawingManager();
    751             pr = dm->getPathRenderer(canDrawArgs, false, GrPathRendererChain::DrawType::kStencil,
    752                                      &stencilSupport);
    753             if (!pr) {
    754                 return false;
    755             }
    756         }
    757 
    758         bool canRenderDirectToStencil =
    759             GrPathRenderer::kNoRestriction_StencilSupport == stencilSupport;
    760         bool drawDirectToClip; // Given the renderer, the element,
    761                                // fill rule, and set operation should
    762                                // we render the element directly to
    763                                // stencil bit used for clipping.
    764         GrUserStencilSettings const* const* stencilPasses =
    765             GrStencilSettings::GetClipPasses(op, canRenderDirectToStencil, fillInverted,
    766                                              &drawDirectToClip);
    767 
    768         // draw the element to the client stencil bits if necessary
    769         if (!drawDirectToClip) {
    770             static constexpr GrUserStencilSettings kDrawToStencil(
    771                  GrUserStencilSettings::StaticInit<
    772                      0x0000,
    773                      GrUserStencilTest::kAlways,
    774                      0xffff,
    775                      GrUserStencilOp::kIncMaybeClamp,
    776                      GrUserStencilOp::kIncMaybeClamp,
    777                      0xffff>()
    778             );
    779             if (Element::kRect_Type == element->getType()) {
    780                 renderTargetContext->priv().stencilRect(stencilClip.fixedClip(), &kDrawToStencil,
    781                                                         aaType, SkMatrix::I(), element->getRect());
    782             } else {
    783                 if (!clipPath.isEmpty()) {
    784                     GrShape shape(clipPath, GrStyle::SimpleFill());
    785                     if (canRenderDirectToStencil) {
    786                         GrPaint paint;
    787                         paint.setXPFactory(GrDisableColorXPFactory::Get());
    788 
    789                         GrPathRenderer::DrawPathArgs args{context,
    790                                                           std::move(paint),
    791                                                           &kDrawToStencil,
    792                                                           renderTargetContext,
    793                                                           &stencilClip.fixedClip(),
    794                                                           &SkMatrix::I(),
    795                                                           &shape,
    796                                                           aaType,
    797                                                           false};
    798                         pr->drawPath(args);
    799                     } else {
    800                         GrPathRenderer::StencilPathArgs args;
    801                         args.fContext = context;
    802                         args.fRenderTargetContext = renderTargetContext;
    803                         args.fClip = &stencilClip.fixedClip();
    804                         args.fViewMatrix = &SkMatrix::I();
    805                         args.fAAType = aaType;
    806                         args.fShape = &shape;
    807                         pr->stencilPath(args);
    808                     }
    809                 }
    810             }
    811         }
    812 
    813         // now we modify the clip bit by rendering either the clip
    814         // element directly or a bounding rect of the entire clip.
    815         for (GrUserStencilSettings const* const* pass = stencilPasses; *pass; ++pass) {
    816             if (drawDirectToClip) {
    817                 if (Element::kRect_Type == element->getType()) {
    818                     renderTargetContext->priv().stencilRect(stencilClip, *pass, aaType,
    819                                                             SkMatrix::I(), element->getRect());
    820                 } else {
    821                     GrShape shape(clipPath, GrStyle::SimpleFill());
    822                     GrPaint paint;
    823                     paint.setXPFactory(GrDisableColorXPFactory::Get());
    824                     GrPathRenderer::DrawPathArgs args{context,
    825                                                       std::move(paint),
    826                                                       *pass,
    827                                                       renderTargetContext,
    828                                                       &stencilClip,
    829                                                       &SkMatrix::I(),
    830                                                       &shape,
    831                                                       aaType,
    832                                                       false};
    833                     pr->drawPath(args);
    834                 }
    835             } else {
    836                 // The view matrix is setup to do clip space -> stencil space translation, so
    837                 // draw rect in clip space.
    838                 renderTargetContext->priv().stencilRect(stencilClip, *pass, aaType, SkMatrix::I(),
    839                                                         SkRect::Make(fIBounds));
    840             }
    841         }
    842     }
    843     return true;
    844 }
    845