LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/core - SkClipStack.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 539 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 48 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright 2011 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 "SkAtomics.h"
       9             : #include "SkCanvas.h"
      10             : #include "SkClipStack.h"
      11             : #include "SkPath.h"
      12             : #include "SkPathOps.h"
      13             : #include "SkClipOpPriv.h"
      14             : 
      15             : #include <new>
      16             : 
      17             : 
      18             : // 0-2 are reserved for invalid, empty & wide-open
      19             : static const int32_t kFirstUnreservedGenID = 3;
      20             : int32_t SkClipStack::gGenID = kFirstUnreservedGenID;
      21             : 
      22           0 : SkClipStack::Element::Element(const Element& that) {
      23           0 :     switch (that.getType()) {
      24             :         case kEmpty_Type:
      25           0 :             fRRect.setEmpty();
      26           0 :             fPath.reset();
      27           0 :             break;
      28             :         case kRect_Type: // Rect uses rrect
      29             :         case kRRect_Type:
      30           0 :             fPath.reset();
      31           0 :             fRRect = that.fRRect;
      32           0 :             break;
      33             :         case kPath_Type:
      34           0 :             fPath.set(that.getPath());
      35           0 :             break;
      36             :     }
      37             : 
      38           0 :     fSaveCount = that.fSaveCount;
      39           0 :     fOp = that.fOp;
      40           0 :     fType = that.fType;
      41           0 :     fDoAA = that.fDoAA;
      42           0 :     fFiniteBoundType = that.fFiniteBoundType;
      43           0 :     fFiniteBound = that.fFiniteBound;
      44           0 :     fIsIntersectionOfRects = that.fIsIntersectionOfRects;
      45           0 :     fGenID = that.fGenID;
      46           0 : }
      47             : 
      48           0 : bool SkClipStack::Element::operator== (const Element& element) const {
      49           0 :     if (this == &element) {
      50           0 :         return true;
      51             :     }
      52           0 :     if (fOp != element.fOp ||
      53           0 :         fType != element.fType ||
      54           0 :         fDoAA != element.fDoAA ||
      55           0 :         fSaveCount != element.fSaveCount) {
      56           0 :         return false;
      57             :     }
      58           0 :     switch (fType) {
      59             :         case kPath_Type:
      60           0 :             return this->getPath() == element.getPath();
      61             :         case kRRect_Type:
      62           0 :             return fRRect == element.fRRect;
      63             :         case kRect_Type:
      64           0 :             return this->getRect() == element.getRect();
      65             :         case kEmpty_Type:
      66           0 :             return true;
      67             :         default:
      68           0 :             SkDEBUGFAIL("Unexpected type.");
      69           0 :             return false;
      70             :     }
      71             : }
      72             : 
      73             : #ifdef SK_SUPPORT_OBSOLETE_REPLAYCLIP
      74             : void SkClipStack::Element::replay(SkCanvasClipVisitor* visitor) const {
      75             :     static const SkRect kEmptyRect = { 0, 0, 0, 0 };
      76             : 
      77             :     switch (fType) {
      78             :         case kPath_Type:
      79             :             visitor->clipPath(this->getPath(), this->getOp(), this->isAA());
      80             :             break;
      81             :         case kRRect_Type:
      82             :             visitor->clipRRect(this->getRRect(), this->getOp(), this->isAA());
      83             :             break;
      84             :         case kRect_Type:
      85             :             visitor->clipRect(this->getRect(), this->getOp(), this->isAA());
      86             :             break;
      87             :         case kEmpty_Type:
      88             :             visitor->clipRect(kEmptyRect, kIntersect_SkClipOp, false);
      89             :             break;
      90             :     }
      91             : }
      92             : #endif
      93             : 
      94           0 : void SkClipStack::Element::invertShapeFillType() {
      95           0 :     switch (fType) {
      96             :         case kRect_Type:
      97           0 :             fPath.init();
      98           0 :             fPath.get()->addRect(this->getRect());
      99           0 :             fPath.get()->setFillType(SkPath::kInverseEvenOdd_FillType);
     100           0 :             fType = kPath_Type;
     101           0 :             break;
     102             :         case kRRect_Type:
     103           0 :             fPath.init();
     104           0 :             fPath.get()->addRRect(fRRect);
     105           0 :             fPath.get()->setFillType(SkPath::kInverseEvenOdd_FillType);
     106           0 :             fType = kPath_Type;
     107           0 :             break;
     108             :         case kPath_Type:
     109           0 :             fPath.get()->toggleInverseFillType();
     110           0 :             break;
     111             :         case kEmpty_Type:
     112             :             // Should this set to an empty, inverse filled path?
     113           0 :             break;
     114             :     }
     115           0 : }
     116             : 
     117           0 : void SkClipStack::Element::initPath(int saveCount, const SkPath& path, SkClipOp op,
     118             :                                     bool doAA) {
     119           0 :     if (!path.isInverseFillType()) {
     120             :         SkRect r;
     121           0 :         if (path.isRect(&r)) {
     122           0 :             this->initRect(saveCount, r, op, doAA);
     123           0 :             return;
     124             :         }
     125             :         SkRect ovalRect;
     126           0 :         if (path.isOval(&ovalRect)) {
     127           0 :             SkRRect rrect;
     128           0 :             rrect.setOval(ovalRect);
     129           0 :             this->initRRect(saveCount, rrect, op, doAA);
     130           0 :             return;
     131             :         }
     132             :     }
     133           0 :     fPath.set(path);
     134           0 :     fPath.get()->setIsVolatile(true);
     135           0 :     fType = kPath_Type;
     136           0 :     this->initCommon(saveCount, op, doAA);
     137             : }
     138             : 
     139           0 : void SkClipStack::Element::asPath(SkPath* path) const {
     140           0 :     switch (fType) {
     141             :         case kEmpty_Type:
     142           0 :             path->reset();
     143           0 :             path->setIsVolatile(true);
     144           0 :             break;
     145             :         case kRect_Type:
     146           0 :             path->reset();
     147           0 :             path->addRect(this->getRect());
     148           0 :             path->setIsVolatile(true);
     149           0 :             break;
     150             :         case kRRect_Type:
     151           0 :             path->reset();
     152           0 :             path->addRRect(fRRect);
     153           0 :             path->setIsVolatile(true);
     154           0 :             break;
     155             :         case kPath_Type:
     156           0 :             *path = *fPath.get();
     157           0 :             break;
     158             :     }
     159           0 :     path->setIsVolatile(true);
     160           0 : }
     161             : 
     162           0 : void SkClipStack::Element::setEmpty() {
     163           0 :     fType = kEmpty_Type;
     164           0 :     fFiniteBound.setEmpty();
     165           0 :     fFiniteBoundType = kNormal_BoundsType;
     166           0 :     fIsIntersectionOfRects = false;
     167           0 :     fRRect.setEmpty();
     168           0 :     fPath.reset();
     169           0 :     fGenID = kEmptyGenID;
     170           0 :     SkDEBUGCODE(this->checkEmpty();)
     171           0 : }
     172             : 
     173           0 : void SkClipStack::Element::checkEmpty() const {
     174           0 :     SkASSERT(fFiniteBound.isEmpty());
     175           0 :     SkASSERT(kNormal_BoundsType == fFiniteBoundType);
     176           0 :     SkASSERT(!fIsIntersectionOfRects);
     177           0 :     SkASSERT(kEmptyGenID == fGenID);
     178           0 :     SkASSERT(fRRect.isEmpty());
     179           0 :     SkASSERT(!fPath.isValid());
     180           0 : }
     181             : 
     182           0 : bool SkClipStack::Element::canBeIntersectedInPlace(int saveCount, SkClipOp op) const {
     183           0 :     if (kEmpty_Type == fType &&
     184           0 :         (kDifference_SkClipOp == op || kIntersect_SkClipOp == op)) {
     185           0 :         return true;
     186             :     }
     187             :     // Only clips within the same save/restore frame (as captured by
     188             :     // the save count) can be merged
     189           0 :     return  fSaveCount == saveCount &&
     190           0 :             kIntersect_SkClipOp == op &&
     191           0 :             (kIntersect_SkClipOp == fOp || kReplace_SkClipOp == fOp);
     192             : }
     193             : 
     194           0 : bool SkClipStack::Element::rectRectIntersectAllowed(const SkRect& newR, bool newAA) const {
     195           0 :     SkASSERT(kRect_Type == fType);
     196             : 
     197           0 :     if (fDoAA == newAA) {
     198             :         // if the AA setting is the same there is no issue
     199           0 :         return true;
     200             :     }
     201             : 
     202           0 :     if (!SkRect::Intersects(this->getRect(), newR)) {
     203             :         // The calling code will correctly set the result to the empty clip
     204           0 :         return true;
     205             :     }
     206             : 
     207           0 :     if (this->getRect().contains(newR)) {
     208             :         // if the new rect carves out a portion of the old one there is no
     209             :         // issue
     210           0 :         return true;
     211             :     }
     212             : 
     213             :     // So either the two overlap in some complex manner or newR contains oldR.
     214             :     // In the first, case the edges will require different AA. In the second,
     215             :     // the AA setting that would be carried forward is incorrect (e.g., oldR
     216             :     // is AA while newR is BW but since newR contains oldR, oldR will be
     217             :     // drawn BW) since the new AA setting will predominate.
     218           0 :     return false;
     219             : }
     220             : 
     221             : // a mirror of combineBoundsRevDiff
     222           0 : void SkClipStack::Element::combineBoundsDiff(FillCombo combination, const SkRect& prevFinite) {
     223           0 :     switch (combination) {
     224             :         case kInvPrev_InvCur_FillCombo:
     225             :             // In this case the only pixels that can remain set
     226             :             // are inside the current clip rect since the extensions
     227             :             // to infinity of both clips cancel out and whatever
     228             :             // is outside of the current clip is removed
     229           0 :             fFiniteBoundType = kNormal_BoundsType;
     230           0 :             break;
     231             :         case kInvPrev_Cur_FillCombo:
     232             :             // In this case the current op is finite so the only pixels
     233             :             // that aren't set are whatever isn't set in the previous
     234             :             // clip and whatever this clip carves out
     235           0 :             fFiniteBound.join(prevFinite);
     236           0 :             fFiniteBoundType = kInsideOut_BoundsType;
     237           0 :             break;
     238             :         case kPrev_InvCur_FillCombo:
     239             :             // In this case everything outside of this clip's bound
     240             :             // is erased, so the only pixels that can remain set
     241             :             // occur w/in the intersection of the two finite bounds
     242           0 :             if (!fFiniteBound.intersect(prevFinite)) {
     243           0 :                 fFiniteBound.setEmpty();
     244           0 :                 fGenID = kEmptyGenID;
     245             :             }
     246           0 :             fFiniteBoundType = kNormal_BoundsType;
     247           0 :             break;
     248             :         case kPrev_Cur_FillCombo:
     249             :             // The most conservative result bound is that of the
     250             :             // prior clip. This could be wildly incorrect if the
     251             :             // second clip either exactly matches the first clip
     252             :             // (which should yield the empty set) or reduces the
     253             :             // size of the prior bound (e.g., if the second clip
     254             :             // exactly matched the bottom half of the prior clip).
     255             :             // We ignore these two possibilities.
     256           0 :             fFiniteBound = prevFinite;
     257           0 :             break;
     258             :         default:
     259           0 :             SkDEBUGFAIL("SkClipStack::Element::combineBoundsDiff Invalid fill combination");
     260           0 :             break;
     261             :     }
     262           0 : }
     263             : 
     264           0 : void SkClipStack::Element::combineBoundsXOR(int combination, const SkRect& prevFinite) {
     265             : 
     266           0 :     switch (combination) {
     267             :         case kInvPrev_Cur_FillCombo:       // fall through
     268             :         case kPrev_InvCur_FillCombo:
     269             :             // With only one of the clips inverted the result will always
     270             :             // extend to infinity. The only pixels that may be un-writeable
     271             :             // lie within the union of the two finite bounds
     272           0 :             fFiniteBound.join(prevFinite);
     273           0 :             fFiniteBoundType = kInsideOut_BoundsType;
     274           0 :             break;
     275             :         case kInvPrev_InvCur_FillCombo:
     276             :             // The only pixels that can survive are within the
     277             :             // union of the two bounding boxes since the extensions
     278             :             // to infinity of both clips cancel out
     279             :             // fall through!
     280             :         case kPrev_Cur_FillCombo:
     281             :             // The most conservative bound for xor is the
     282             :             // union of the two bounds. If the two clips exactly overlapped
     283             :             // the xor could yield the empty set. Similarly the xor
     284             :             // could reduce the size of the original clip's bound (e.g.,
     285             :             // if the second clip exactly matched the bottom half of the
     286             :             // first clip). We ignore these two cases.
     287           0 :             fFiniteBound.join(prevFinite);
     288           0 :             fFiniteBoundType = kNormal_BoundsType;
     289           0 :             break;
     290             :         default:
     291           0 :             SkDEBUGFAIL("SkClipStack::Element::combineBoundsXOR Invalid fill combination");
     292           0 :             break;
     293             :     }
     294           0 : }
     295             : 
     296             : // a mirror of combineBoundsIntersection
     297           0 : void SkClipStack::Element::combineBoundsUnion(int combination, const SkRect& prevFinite) {
     298             : 
     299           0 :     switch (combination) {
     300             :         case kInvPrev_InvCur_FillCombo:
     301           0 :             if (!fFiniteBound.intersect(prevFinite)) {
     302           0 :                 fFiniteBound.setEmpty();
     303           0 :                 fGenID = kWideOpenGenID;
     304             :             }
     305           0 :             fFiniteBoundType = kInsideOut_BoundsType;
     306           0 :             break;
     307             :         case kInvPrev_Cur_FillCombo:
     308             :             // The only pixels that won't be drawable are inside
     309             :             // the prior clip's finite bound
     310           0 :             fFiniteBound = prevFinite;
     311           0 :             fFiniteBoundType = kInsideOut_BoundsType;
     312           0 :             break;
     313             :         case kPrev_InvCur_FillCombo:
     314             :             // The only pixels that won't be drawable are inside
     315             :             // this clip's finite bound
     316           0 :             break;
     317             :         case kPrev_Cur_FillCombo:
     318           0 :             fFiniteBound.join(prevFinite);
     319           0 :             break;
     320             :         default:
     321           0 :             SkDEBUGFAIL("SkClipStack::Element::combineBoundsUnion Invalid fill combination");
     322           0 :             break;
     323             :     }
     324           0 : }
     325             : 
     326             : // a mirror of combineBoundsUnion
     327           0 : void SkClipStack::Element::combineBoundsIntersection(int combination, const SkRect& prevFinite) {
     328             : 
     329           0 :     switch (combination) {
     330             :         case kInvPrev_InvCur_FillCombo:
     331             :             // The only pixels that aren't writable in this case
     332             :             // occur in the union of the two finite bounds
     333           0 :             fFiniteBound.join(prevFinite);
     334           0 :             fFiniteBoundType = kInsideOut_BoundsType;
     335           0 :             break;
     336             :         case kInvPrev_Cur_FillCombo:
     337             :             // In this case the only pixels that will remain writeable
     338             :             // are within the current clip
     339           0 :             break;
     340             :         case kPrev_InvCur_FillCombo:
     341             :             // In this case the only pixels that will remain writeable
     342             :             // are with the previous clip
     343           0 :             fFiniteBound = prevFinite;
     344           0 :             fFiniteBoundType = kNormal_BoundsType;
     345           0 :             break;
     346             :         case kPrev_Cur_FillCombo:
     347           0 :             if (!fFiniteBound.intersect(prevFinite)) {
     348           0 :                 this->setEmpty();
     349             :             }
     350           0 :             break;
     351             :         default:
     352           0 :             SkDEBUGFAIL("SkClipStack::Element::combineBoundsIntersection Invalid fill combination");
     353           0 :             break;
     354             :     }
     355           0 : }
     356             : 
     357             : // a mirror of combineBoundsDiff
     358           0 : void SkClipStack::Element::combineBoundsRevDiff(int combination, const SkRect& prevFinite) {
     359             : 
     360           0 :     switch (combination) {
     361             :         case kInvPrev_InvCur_FillCombo:
     362             :             // The only pixels that can survive are in the
     363             :             // previous bound since the extensions to infinity in
     364             :             // both clips cancel out
     365           0 :             fFiniteBound = prevFinite;
     366           0 :             fFiniteBoundType = kNormal_BoundsType;
     367           0 :             break;
     368             :         case kInvPrev_Cur_FillCombo:
     369           0 :             if (!fFiniteBound.intersect(prevFinite)) {
     370           0 :                 this->setEmpty();
     371             :             } else {
     372           0 :                 fFiniteBoundType = kNormal_BoundsType;
     373             :             }
     374           0 :             break;
     375             :         case kPrev_InvCur_FillCombo:
     376           0 :             fFiniteBound.join(prevFinite);
     377           0 :             fFiniteBoundType = kInsideOut_BoundsType;
     378           0 :             break;
     379             :         case kPrev_Cur_FillCombo:
     380             :             // Fall through - as with the kDifference_Op case, the
     381             :             // most conservative result bound is the bound of the
     382             :             // current clip. The prior clip could reduce the size of this
     383             :             // bound (as in the kDifference_Op case) but we are ignoring
     384             :             // those cases.
     385           0 :             break;
     386             :         default:
     387           0 :             SkDEBUGFAIL("SkClipStack::Element::combineBoundsRevDiff Invalid fill combination");
     388           0 :             break;
     389             :     }
     390           0 : }
     391             : 
     392           0 : void SkClipStack::Element::updateBoundAndGenID(const Element* prior) {
     393             :     // We set this first here but we may overwrite it later if we determine that the clip is
     394             :     // either wide-open or empty.
     395           0 :     fGenID = GetNextGenID();
     396             : 
     397             :     // First, optimistically update the current Element's bound information
     398             :     // with the current clip's bound
     399           0 :     fIsIntersectionOfRects = false;
     400           0 :     switch (fType) {
     401             :         case kRect_Type:
     402           0 :             fFiniteBound = this->getRect();
     403           0 :             fFiniteBoundType = kNormal_BoundsType;
     404             : 
     405           0 :             if (kReplace_SkClipOp == fOp ||
     406           0 :                 (kIntersect_SkClipOp == fOp && nullptr == prior) ||
     407           0 :                 (kIntersect_SkClipOp == fOp && prior->fIsIntersectionOfRects &&
     408           0 :                     prior->rectRectIntersectAllowed(this->getRect(), fDoAA))) {
     409           0 :                 fIsIntersectionOfRects = true;
     410             :             }
     411           0 :             break;
     412             :         case kRRect_Type:
     413           0 :             fFiniteBound = fRRect.getBounds();
     414           0 :             fFiniteBoundType = kNormal_BoundsType;
     415           0 :             break;
     416             :         case kPath_Type:
     417           0 :             fFiniteBound = fPath.get()->getBounds();
     418             : 
     419           0 :             if (fPath.get()->isInverseFillType()) {
     420           0 :                 fFiniteBoundType = kInsideOut_BoundsType;
     421             :             } else {
     422           0 :                 fFiniteBoundType = kNormal_BoundsType;
     423             :             }
     424           0 :             break;
     425             :         case kEmpty_Type:
     426           0 :             SkDEBUGFAIL("We shouldn't get here with an empty element.");
     427           0 :             break;
     428             :     }
     429             : 
     430           0 :     if (!fDoAA) {
     431           0 :         fFiniteBound.set(SkScalarFloorToScalar(fFiniteBound.fLeft+0.45f),
     432           0 :                          SkScalarRoundToScalar(fFiniteBound.fTop),
     433           0 :                          SkScalarRoundToScalar(fFiniteBound.fRight),
     434           0 :                          SkScalarRoundToScalar(fFiniteBound.fBottom));
     435             :     }
     436             : 
     437             :     // Now determine the previous Element's bound information taking into
     438             :     // account that there may be no previous clip
     439             :     SkRect prevFinite;
     440             :     SkClipStack::BoundsType prevType;
     441             : 
     442           0 :     if (nullptr == prior) {
     443             :         // no prior clip means the entire plane is writable
     444           0 :         prevFinite.setEmpty();   // there are no pixels that cannot be drawn to
     445           0 :         prevType = kInsideOut_BoundsType;
     446             :     } else {
     447           0 :         prevFinite = prior->fFiniteBound;
     448           0 :         prevType = prior->fFiniteBoundType;
     449             :     }
     450             : 
     451           0 :     FillCombo combination = kPrev_Cur_FillCombo;
     452           0 :     if (kInsideOut_BoundsType == fFiniteBoundType) {
     453           0 :         combination = (FillCombo) (combination | 0x01);
     454             :     }
     455           0 :     if (kInsideOut_BoundsType == prevType) {
     456           0 :         combination = (FillCombo) (combination | 0x02);
     457             :     }
     458             : 
     459           0 :     SkASSERT(kInvPrev_InvCur_FillCombo == combination ||
     460             :                 kInvPrev_Cur_FillCombo == combination ||
     461             :                 kPrev_InvCur_FillCombo == combination ||
     462             :                 kPrev_Cur_FillCombo == combination);
     463             : 
     464             :     // Now integrate with clip with the prior clips
     465           0 :     switch (fOp) {
     466             :         case kDifference_SkClipOp:
     467           0 :             this->combineBoundsDiff(combination, prevFinite);
     468           0 :             break;
     469             :         case kXOR_SkClipOp:
     470           0 :             this->combineBoundsXOR(combination, prevFinite);
     471           0 :             break;
     472             :         case kUnion_SkClipOp:
     473           0 :             this->combineBoundsUnion(combination, prevFinite);
     474           0 :             break;
     475             :         case kIntersect_SkClipOp:
     476           0 :             this->combineBoundsIntersection(combination, prevFinite);
     477           0 :             break;
     478             :         case kReverseDifference_SkClipOp:
     479           0 :             this->combineBoundsRevDiff(combination, prevFinite);
     480           0 :             break;
     481             :         case kReplace_SkClipOp:
     482             :             // Replace just ignores everything prior
     483             :             // The current clip's bound information is already filled in
     484             :             // so nothing to do
     485           0 :             break;
     486             :         default:
     487           0 :             SkDebugf("SkClipOp error\n");
     488           0 :             SkASSERT(0);
     489           0 :             break;
     490             :     }
     491           0 : }
     492             : 
     493             : // This constant determines how many Element's are allocated together as a block in
     494             : // the deque. As such it needs to balance allocating too much memory vs.
     495             : // incurring allocation/deallocation thrashing. It should roughly correspond to
     496             : // the deepest save/restore stack we expect to see.
     497             : static const int kDefaultElementAllocCnt = 8;
     498             : 
     499           0 : SkClipStack::SkClipStack()
     500             :     : fDeque(sizeof(Element), kDefaultElementAllocCnt)
     501           0 :     , fSaveCount(0) {
     502           0 : }
     503             : 
     504           0 : SkClipStack::SkClipStack(void* storage, size_t size)
     505             :     : fDeque(sizeof(Element), storage, size, kDefaultElementAllocCnt)
     506           0 :     , fSaveCount(0) {
     507           0 : }
     508             : 
     509           0 : SkClipStack::SkClipStack(const SkClipStack& b)
     510           0 :     : fDeque(sizeof(Element), kDefaultElementAllocCnt) {
     511           0 :     *this = b;
     512           0 : }
     513             : 
     514           0 : SkClipStack::~SkClipStack() {
     515           0 :     reset();
     516           0 : }
     517             : 
     518           0 : SkClipStack& SkClipStack::operator=(const SkClipStack& b) {
     519           0 :     if (this == &b) {
     520           0 :         return *this;
     521             :     }
     522           0 :     reset();
     523             : 
     524           0 :     fSaveCount = b.fSaveCount;
     525           0 :     SkDeque::F2BIter recIter(b.fDeque);
     526           0 :     for (const Element* element = (const Element*)recIter.next();
     527           0 :          element != nullptr;
     528             :          element = (const Element*)recIter.next()) {
     529           0 :         new (fDeque.push_back()) Element(*element);
     530             :     }
     531             : 
     532           0 :     return *this;
     533             : }
     534             : 
     535           0 : bool SkClipStack::operator==(const SkClipStack& b) const {
     536           0 :     if (this->getTopmostGenID() == b.getTopmostGenID()) {
     537           0 :         return true;
     538             :     }
     539           0 :     if (fSaveCount != b.fSaveCount ||
     540           0 :         fDeque.count() != b.fDeque.count()) {
     541           0 :         return false;
     542             :     }
     543           0 :     SkDeque::F2BIter myIter(fDeque);
     544           0 :     SkDeque::F2BIter bIter(b.fDeque);
     545           0 :     const Element* myElement = (const Element*)myIter.next();
     546           0 :     const Element* bElement = (const Element*)bIter.next();
     547             : 
     548           0 :     while (myElement != nullptr && bElement != nullptr) {
     549           0 :         if (*myElement != *bElement) {
     550           0 :             return false;
     551             :         }
     552           0 :         myElement = (const Element*)myIter.next();
     553           0 :         bElement = (const Element*)bIter.next();
     554             :     }
     555           0 :     return myElement == nullptr && bElement == nullptr;
     556             : }
     557             : 
     558           0 : void SkClipStack::reset() {
     559             :     // We used a placement new for each object in fDeque, so we're responsible
     560             :     // for calling the destructor on each of them as well.
     561           0 :     while (!fDeque.empty()) {
     562           0 :         Element* element = (Element*)fDeque.back();
     563           0 :         element->~Element();
     564           0 :         fDeque.pop_back();
     565             :     }
     566             : 
     567           0 :     fSaveCount = 0;
     568           0 : }
     569             : 
     570           0 : void SkClipStack::save() {
     571           0 :     fSaveCount += 1;
     572           0 : }
     573             : 
     574           0 : void SkClipStack::restore() {
     575           0 :     fSaveCount -= 1;
     576           0 :     restoreTo(fSaveCount);
     577           0 : }
     578             : 
     579           0 : void SkClipStack::restoreTo(int saveCount) {
     580           0 :     while (!fDeque.empty()) {
     581           0 :         Element* element = (Element*)fDeque.back();
     582           0 :         if (element->fSaveCount <= saveCount) {
     583           0 :             break;
     584             :         }
     585           0 :         element->~Element();
     586           0 :         fDeque.pop_back();
     587             :     }
     588           0 : }
     589             : 
     590           0 : SkRect SkClipStack::bounds(const SkIRect& deviceBounds) const {
     591             :     // TODO: optimize this.
     592             :     SkRect r;
     593             :     SkClipStack::BoundsType bounds;
     594           0 :     this->getBounds(&r, &bounds);
     595           0 :     if (bounds == SkClipStack::kInsideOut_BoundsType) {
     596           0 :         return SkRect::Make(deviceBounds);
     597             :     }
     598           0 :     return r.intersect(SkRect::Make(deviceBounds)) ? r : SkRect::MakeEmpty();
     599             : }
     600             : 
     601             : // TODO: optimize this.
     602           0 : bool SkClipStack::isEmpty(const SkIRect& r) const { return this->bounds(r).isEmpty(); }
     603             : 
     604           0 : void SkClipStack::getBounds(SkRect* canvFiniteBound,
     605             :                             BoundsType* boundType,
     606             :                             bool* isIntersectionOfRects) const {
     607           0 :     SkASSERT(canvFiniteBound && boundType);
     608             : 
     609           0 :     Element* element = (Element*)fDeque.back();
     610             : 
     611           0 :     if (nullptr == element) {
     612             :         // the clip is wide open - the infinite plane w/ no pixels un-writeable
     613           0 :         canvFiniteBound->setEmpty();
     614           0 :         *boundType = kInsideOut_BoundsType;
     615           0 :         if (isIntersectionOfRects) {
     616           0 :             *isIntersectionOfRects = false;
     617             :         }
     618           0 :         return;
     619             :     }
     620             : 
     621           0 :     *canvFiniteBound = element->fFiniteBound;
     622           0 :     *boundType = element->fFiniteBoundType;
     623           0 :     if (isIntersectionOfRects) {
     624           0 :         *isIntersectionOfRects = element->fIsIntersectionOfRects;
     625             :     }
     626             : }
     627             : 
     628           0 : bool SkClipStack::internalQuickContains(const SkRect& rect) const {
     629             : 
     630           0 :     Iter iter(*this, Iter::kTop_IterStart);
     631           0 :     const Element* element = iter.prev();
     632           0 :     while (element != nullptr) {
     633           0 :         if (kIntersect_SkClipOp != element->getOp() && kReplace_SkClipOp != element->getOp())
     634           0 :             return false;
     635           0 :         if (element->isInverseFilled()) {
     636             :             // Part of 'rect' could be trimmed off by the inverse-filled clip element
     637           0 :             if (SkRect::Intersects(element->getBounds(), rect)) {
     638           0 :                 return false;
     639             :             }
     640             :         } else {
     641           0 :             if (!element->contains(rect)) {
     642           0 :                 return false;
     643             :             }
     644             :         }
     645           0 :         if (kReplace_SkClipOp == element->getOp()) {
     646           0 :             break;
     647             :         }
     648           0 :         element = iter.prev();
     649             :     }
     650           0 :     return true;
     651             : }
     652             : 
     653           0 : bool SkClipStack::internalQuickContains(const SkRRect& rrect) const {
     654             : 
     655           0 :     Iter iter(*this, Iter::kTop_IterStart);
     656           0 :     const Element* element = iter.prev();
     657           0 :     while (element != nullptr) {
     658           0 :         if (kIntersect_SkClipOp != element->getOp() && kReplace_SkClipOp != element->getOp())
     659           0 :             return false;
     660           0 :         if (element->isInverseFilled()) {
     661             :             // Part of 'rrect' could be trimmed off by the inverse-filled clip element
     662           0 :             if (SkRect::Intersects(element->getBounds(), rrect.getBounds())) {
     663           0 :                 return false;
     664             :             }
     665             :         } else {
     666           0 :             if (!element->contains(rrect)) {
     667           0 :                 return false;
     668             :             }
     669             :         }
     670           0 :         if (kReplace_SkClipOp == element->getOp()) {
     671           0 :             break;
     672             :         }
     673           0 :         element = iter.prev();
     674             :     }
     675           0 :     return true;
     676             : }
     677             : 
     678           0 : bool SkClipStack::asPath(SkPath *path) const {
     679           0 :     bool isAA = false;
     680             : 
     681           0 :     path->reset();
     682           0 :     path->setFillType(SkPath::kInverseEvenOdd_FillType);
     683             : 
     684           0 :     SkClipStack::Iter iter(*this, SkClipStack::Iter::kBottom_IterStart);
     685           0 :     while (const SkClipStack::Element* element = iter.next()) {
     686           0 :         SkPath operand;
     687           0 :         if (element->getType() != SkClipStack::Element::kEmpty_Type) {
     688           0 :             element->asPath(&operand);
     689             :         }
     690             : 
     691           0 :         SkClipOp elementOp = element->getOp();
     692           0 :         if (elementOp == kReplace_SkClipOp) {
     693           0 :             *path = operand;
     694             :         } else {
     695           0 :             Op(*path, operand, (SkPathOp)elementOp, path);
     696             :         }
     697             : 
     698             :         // if the prev and curr clips disagree about aa -vs- not, favor the aa request.
     699             :         // perhaps we need an API change to avoid this sort of mixed-signals about
     700             :         // clipping.
     701           0 :         isAA = (isAA || element->isAA());
     702           0 :     }
     703             : 
     704           0 :     return isAA;
     705             : }
     706             : 
     707           0 : void SkClipStack::pushElement(const Element& element) {
     708             :     // Use reverse iterator instead of back because Rect path may need previous
     709           0 :     SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart);
     710           0 :     Element* prior = (Element*) iter.prev();
     711             : 
     712           0 :     if (prior) {
     713           0 :         if (prior->canBeIntersectedInPlace(fSaveCount, element.getOp())) {
     714           0 :             switch (prior->fType) {
     715             :                 case Element::kEmpty_Type:
     716           0 :                     SkDEBUGCODE(prior->checkEmpty();)
     717           0 :                     return;
     718             :                 case Element::kRect_Type:
     719           0 :                     if (Element::kRect_Type == element.getType()) {
     720           0 :                         if (prior->rectRectIntersectAllowed(element.getRect(), element.isAA())) {
     721             :                             SkRect isectRect;
     722           0 :                             if (!isectRect.intersect(prior->getRect(), element.getRect())) {
     723           0 :                                 prior->setEmpty();
     724           0 :                                 return;
     725             :                             }
     726             : 
     727           0 :                             prior->fRRect.setRect(isectRect);
     728           0 :                             prior->fDoAA = element.isAA();
     729           0 :                             Element* priorPrior = (Element*) iter.prev();
     730           0 :                             prior->updateBoundAndGenID(priorPrior);
     731           0 :                             return;
     732             :                         }
     733           0 :                         break;
     734             :                     }
     735             :                     // fallthrough
     736             :                 default:
     737           0 :                     if (!SkRect::Intersects(prior->getBounds(), element.getBounds())) {
     738           0 :                         prior->setEmpty();
     739           0 :                         return;
     740             :                     }
     741           0 :                     break;
     742             :             }
     743           0 :         } else if (kReplace_SkClipOp == element.getOp()) {
     744           0 :             this->restoreTo(fSaveCount - 1);
     745           0 :             prior = (Element*) fDeque.back();
     746             :         }
     747             :     }
     748           0 :     Element* newElement = new (fDeque.push_back()) Element(element);
     749           0 :     newElement->updateBoundAndGenID(prior);
     750             : }
     751             : 
     752           0 : void SkClipStack::clipRRect(const SkRRect& rrect, const SkMatrix& matrix, SkClipOp op,
     753             :                             bool doAA) {
     754           0 :     SkRRect transformedRRect;
     755           0 :     if (rrect.transform(matrix, &transformedRRect)) {
     756           0 :         Element element(fSaveCount, transformedRRect, op, doAA);
     757           0 :         this->pushElement(element);
     758           0 :         if (this->hasClipRestriction(op)) {
     759           0 :             Element element(fSaveCount, fClipRestrictionRect, kIntersect_SkClipOp, false);
     760           0 :             this->pushElement(element);
     761             :         }
     762           0 :         return;
     763             :     }
     764           0 :     SkPath path;
     765           0 :     path.addRRect(rrect);
     766           0 :     path.setIsVolatile(true);
     767           0 :     this->clipPath(path, matrix, op, doAA);
     768             : }
     769             : 
     770           0 : void SkClipStack::clipRect(const SkRect& rect, const SkMatrix& matrix, SkClipOp op,
     771             :                            bool doAA) {
     772           0 :     if (matrix.rectStaysRect()) {
     773             :         SkRect devRect;
     774           0 :         matrix.mapRect(&devRect, rect);
     775           0 :         if (this->hasClipRestriction(op)) {
     776           0 :             if (!devRect.intersect(fClipRestrictionRect)) {
     777           0 :                 devRect.setEmpty();
     778             :             }
     779             :         }
     780           0 :         Element element(fSaveCount, devRect, op, doAA);
     781           0 :         this->pushElement(element);
     782           0 :         return;
     783             :     }
     784           0 :     SkPath path;
     785           0 :     path.addRect(rect);
     786           0 :     path.setIsVolatile(true);
     787           0 :     this->clipPath(path, matrix, op, doAA);
     788             : }
     789             : 
     790           0 : void SkClipStack::clipPath(const SkPath& path, const SkMatrix& matrix, SkClipOp op,
     791             :                            bool doAA) {
     792           0 :     SkPath devPath;
     793           0 :     path.transform(matrix, &devPath);
     794           0 :     Element element(fSaveCount, devPath, op, doAA);
     795           0 :     this->pushElement(element);
     796           0 :     if (this->hasClipRestriction(op)) {
     797           0 :         Element element(fSaveCount, fClipRestrictionRect, kIntersect_SkClipOp, false);
     798           0 :         this->pushElement(element);
     799             :     }
     800           0 : }
     801             : 
     802           0 : void SkClipStack::clipEmpty() {
     803           0 :     Element* element = (Element*) fDeque.back();
     804             : 
     805           0 :     if (element && element->canBeIntersectedInPlace(fSaveCount, kIntersect_SkClipOp)) {
     806           0 :         element->setEmpty();
     807             :     }
     808           0 :     new (fDeque.push_back()) Element(fSaveCount);
     809             : 
     810           0 :     ((Element*)fDeque.back())->fGenID = kEmptyGenID;
     811           0 : }
     812             : 
     813             : ///////////////////////////////////////////////////////////////////////////////
     814             : 
     815           0 : SkClipStack::Iter::Iter() : fStack(nullptr) {
     816           0 : }
     817             : 
     818           0 : SkClipStack::Iter::Iter(const SkClipStack& stack, IterStart startLoc)
     819           0 :     : fStack(&stack) {
     820           0 :     this->reset(stack, startLoc);
     821           0 : }
     822             : 
     823           0 : const SkClipStack::Element* SkClipStack::Iter::next() {
     824           0 :     return (const SkClipStack::Element*)fIter.next();
     825             : }
     826             : 
     827           0 : const SkClipStack::Element* SkClipStack::Iter::prev() {
     828           0 :     return (const SkClipStack::Element*)fIter.prev();
     829             : }
     830             : 
     831           0 : const SkClipStack::Element* SkClipStack::Iter::skipToTopmost(SkClipOp op) {
     832             : 
     833           0 :     if (nullptr == fStack) {
     834           0 :         return nullptr;
     835             :     }
     836             : 
     837           0 :     fIter.reset(fStack->fDeque, SkDeque::Iter::kBack_IterStart);
     838             : 
     839           0 :     const SkClipStack::Element* element = nullptr;
     840             : 
     841           0 :     for (element = (const SkClipStack::Element*) fIter.prev();
     842           0 :          element;
     843           0 :          element = (const SkClipStack::Element*) fIter.prev()) {
     844             : 
     845           0 :         if (op == element->fOp) {
     846             :             // The Deque's iterator is actually one pace ahead of the
     847             :             // returned value. So while "element" is the element we want to
     848             :             // return, the iterator is actually pointing at (and will
     849             :             // return on the next "next" or "prev" call) the element
     850             :             // in front of it in the deque. Bump the iterator forward a
     851             :             // step so we get the expected result.
     852           0 :             if (nullptr == fIter.next()) {
     853             :                 // The reverse iterator has run off the front of the deque
     854             :                 // (i.e., the "op" clip is the first clip) and can't
     855             :                 // recover. Reset the iterator to start at the front.
     856           0 :                 fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
     857             :             }
     858           0 :             break;
     859             :         }
     860             :     }
     861             : 
     862           0 :     if (nullptr == element) {
     863             :         // There were no "op" clips
     864           0 :         fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
     865             :     }
     866             : 
     867           0 :     return this->next();
     868             : }
     869             : 
     870           0 : void SkClipStack::Iter::reset(const SkClipStack& stack, IterStart startLoc) {
     871           0 :     fStack = &stack;
     872           0 :     fIter.reset(stack.fDeque, static_cast<SkDeque::Iter::IterStart>(startLoc));
     873           0 : }
     874             : 
     875             : // helper method
     876           0 : void SkClipStack::getConservativeBounds(int offsetX,
     877             :                                         int offsetY,
     878             :                                         int maxWidth,
     879             :                                         int maxHeight,
     880             :                                         SkRect* devBounds,
     881             :                                         bool* isIntersectionOfRects) const {
     882           0 :     SkASSERT(devBounds);
     883             : 
     884           0 :     devBounds->setLTRB(0, 0,
     885           0 :                        SkIntToScalar(maxWidth), SkIntToScalar(maxHeight));
     886             : 
     887             :     SkRect temp;
     888             :     SkClipStack::BoundsType boundType;
     889             : 
     890             :     // temp starts off in canvas space here
     891           0 :     this->getBounds(&temp, &boundType, isIntersectionOfRects);
     892           0 :     if (SkClipStack::kInsideOut_BoundsType == boundType) {
     893           0 :         return;
     894             :     }
     895             : 
     896             :     // but is converted to device space here
     897           0 :     temp.offset(SkIntToScalar(offsetX), SkIntToScalar(offsetY));
     898             : 
     899           0 :     if (!devBounds->intersect(temp)) {
     900           0 :         devBounds->setEmpty();
     901             :     }
     902             : }
     903             : 
     904           0 : bool SkClipStack::isRRect(const SkRect& bounds, SkRRect* rrect, bool* aa) const {
     905             :     // We limit to 5 elements. This means the back element will be bounds checked at most 4 times if
     906             :     // it is an rrect.
     907           0 :     int cnt = fDeque.count();
     908           0 :     if (!cnt || cnt > 5) {
     909           0 :         return false;
     910             :     }
     911           0 :     const Element* back = static_cast<const Element*>(fDeque.back());
     912           0 :     if (back->getType() != SkClipStack::Element::kRect_Type &&
     913           0 :         back->getType() != SkClipStack::Element::kRRect_Type) {
     914           0 :         return false;
     915             :     }
     916           0 :     if (back->getOp() == kReplace_SkClipOp) {
     917           0 :         *rrect = back->asRRect();
     918           0 :         *aa = back->isAA();
     919           0 :         return true;
     920             :     }
     921             : 
     922           0 :     if (back->getOp() == kIntersect_SkClipOp) {
     923             :         SkRect backBounds;
     924           0 :         if (!backBounds.intersect(bounds, back->asRRect().rect())) {
     925           0 :             return false;
     926             :         }
     927           0 :         if (cnt > 1) {
     928           0 :             SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart);
     929           0 :             SkAssertResult(static_cast<const Element*>(iter.prev()) == back);
     930           0 :             while (const Element* prior = (const Element*)iter.prev()) {
     931           0 :                 if ((prior->getOp() != kIntersect_SkClipOp &&
     932           0 :                      prior->getOp() != kReplace_SkClipOp) ||
     933           0 :                     !prior->contains(backBounds)) {
     934           0 :                     return false;
     935             :                 }
     936           0 :                 if (prior->getOp() == kReplace_SkClipOp) {
     937           0 :                     break;
     938             :                 }
     939           0 :             }
     940             :         }
     941           0 :         *rrect = back->asRRect();
     942           0 :         *aa = back->isAA();
     943           0 :         return true;
     944             :     }
     945           0 :     return false;
     946             : }
     947             : 
     948           0 : int32_t SkClipStack::GetNextGenID() {
     949             :     // TODO: handle overflow.
     950           0 :     return sk_atomic_inc(&gGenID);
     951             : }
     952             : 
     953           0 : int32_t SkClipStack::getTopmostGenID() const {
     954           0 :     if (fDeque.empty()) {
     955           0 :         return kWideOpenGenID;
     956             :     }
     957             : 
     958           0 :     const Element* back = static_cast<const Element*>(fDeque.back());
     959           0 :     if (kInsideOut_BoundsType == back->fFiniteBoundType && back->fFiniteBound.isEmpty()) {
     960           0 :         return kWideOpenGenID;
     961             :     }
     962             : 
     963           0 :     return back->getGenID();
     964             : }
     965             : 
     966             : #ifdef SK_DEBUG
     967           0 : void SkClipStack::Element::dump() const {
     968             :     static const char* kTypeStrings[] = {
     969             :         "empty",
     970             :         "rect",
     971             :         "rrect",
     972             :         "path"
     973             :     };
     974             :     static_assert(0 == kEmpty_Type, "type_str");
     975             :     static_assert(1 == kRect_Type, "type_str");
     976             :     static_assert(2 == kRRect_Type, "type_str");
     977             :     static_assert(3 == kPath_Type, "type_str");
     978             :     static_assert(SK_ARRAY_COUNT(kTypeStrings) == kTypeCnt, "type_str");
     979             : 
     980             :     static const char* kOpStrings[] = {
     981             :         "difference",
     982             :         "intersect",
     983             :         "union",
     984             :         "xor",
     985             :         "reverse-difference",
     986             :         "replace",
     987             :     };
     988             :     static_assert(0 == static_cast<int>(kDifference_SkClipOp), "op_str");
     989             :     static_assert(1 == static_cast<int>(kIntersect_SkClipOp), "op_str");
     990             :     static_assert(2 == static_cast<int>(kUnion_SkClipOp), "op_str");
     991             :     static_assert(3 == static_cast<int>(kXOR_SkClipOp), "op_str");
     992             :     static_assert(4 == static_cast<int>(kReverseDifference_SkClipOp), "op_str");
     993             :     static_assert(5 == static_cast<int>(kReplace_SkClipOp), "op_str");
     994             :     static_assert(SK_ARRAY_COUNT(kOpStrings) == SkRegion::kOpCnt, "op_str");
     995             : 
     996           0 :     SkDebugf("Type: %s, Op: %s, AA: %s, Save Count: %d\n", kTypeStrings[fType],
     997           0 :              kOpStrings[static_cast<int>(fOp)], (fDoAA ? "yes" : "no"), fSaveCount);
     998           0 :     switch (fType) {
     999             :         case kEmpty_Type:
    1000           0 :             SkDebugf("\n");
    1001           0 :             break;
    1002             :         case kRect_Type:
    1003           0 :             this->getRect().dump();
    1004           0 :             SkDebugf("\n");
    1005           0 :             break;
    1006             :         case kRRect_Type:
    1007           0 :             this->getRRect().dump();
    1008           0 :             SkDebugf("\n");
    1009           0 :             break;
    1010             :         case kPath_Type:
    1011           0 :             this->getPath().dump(nullptr, true, false);
    1012           0 :             break;
    1013             :     }
    1014           0 : }
    1015             : 
    1016           0 : void SkClipStack::dump() const {
    1017           0 :     B2TIter iter(*this);
    1018             :     const Element* e;
    1019           0 :     while ((e = iter.next())) {
    1020           0 :         e->dump();
    1021           0 :         SkDebugf("\n");
    1022             :     }
    1023           0 : }
    1024             : #endif

Generated by: LCOV version 1.13