diff options
author | commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2014-02-16 13:25:24 +0000 |
---|---|---|
committer | commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2014-02-16 13:25:24 +0000 |
commit | e5b2af955b7d06815ddd405659ad62a2a8355ca3 (patch) | |
tree | 8c40834f5ae1be2503c049d583f06cbcb971d072 | |
parent | 969588f0c9030d5a4942085a4b5a5ea7e8d2bc25 (diff) |
Store SkRRects in SkClipStack
BUG=skia:2181
R=robertphillips@google.com
Author: bsalomon@google.com
Review URL: https://codereview.chromium.org/163683002
git-svn-id: http://skia.googlecode.com/svn/trunk@13465 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r-- | gm/complexclip2.cpp | 105 | ||||
-rw-r--r-- | include/core/SkCanvas.h | 1 | ||||
-rw-r--r-- | include/core/SkClipStack.h | 70 | ||||
-rw-r--r-- | src/core/SkCanvas.cpp | 11 | ||||
-rw-r--r-- | src/core/SkClipStack.cpp | 240 | ||||
-rw-r--r-- | src/gpu/GrClipMaskManager.cpp | 99 | ||||
-rw-r--r-- | src/gpu/GrReducedClip.cpp | 18 | ||||
-rw-r--r-- | src/pdf/SkPDFDevice.cpp | 6 | ||||
-rw-r--r-- | src/utils/SkCanvasStateUtils.cpp | 4 | ||||
-rw-r--r-- | tests/CanvasTest.cpp | 7 | ||||
-rw-r--r-- | tests/ClipStackTest.cpp | 95 |
11 files changed, 383 insertions, 273 deletions
diff --git a/gm/complexclip2.cpp b/gm/complexclip2.cpp index 0e8e0bc116..2b94ffd661 100644 --- a/gm/complexclip2.cpp +++ b/gm/complexclip2.cpp @@ -15,8 +15,14 @@ namespace skiagm { class ComplexClip2GM : public GM { public: - ComplexClip2GM(bool doPaths, bool antiAlias) - : fDoPaths(doPaths) + enum Clip { + kRect_Clip, + kRRect_Clip, + kPath_Clip + }; + + ComplexClip2GM(Clip clip, bool antiAlias) + : fClip(clip) , fAntiAlias(antiAlias) { this->setBGColor(SkColorSetRGB(0xDD,0xA0,0xDD)); @@ -39,23 +45,28 @@ public: fHeight = yF - yA; fRects[0].set(xB, yB, xE, yE); - fPaths[0].addRoundRect(fRects[0], SkIntToScalar(5), SkIntToScalar(5)); + fRRects[0].setRectXY(fRects[0], 7, 7); + fPaths[0].addRoundRect(fRects[0], 5, 5); fRectColors[0] = SK_ColorRED; fRects[1].set(xA, yA, xD, yD); - fPaths[1].addRoundRect(fRects[1], SkIntToScalar(5), SkIntToScalar(5)); + fRRects[1].setRectXY(fRects[1], 7, 7); + fPaths[1].addRoundRect(fRects[1], 5, 5); fRectColors[1] = SK_ColorGREEN; fRects[2].set(xC, yA, xF, yD); - fPaths[2].addRoundRect(fRects[2], SkIntToScalar(5), SkIntToScalar(5)); + fRRects[2].setRectXY(fRects[2], 7, 7); + fPaths[2].addRoundRect(fRects[2], 5, 5); fRectColors[2] = SK_ColorBLUE; fRects[3].set(xA, yC, xD, yF); - fPaths[3].addRoundRect(fRects[3], SkIntToScalar(5), SkIntToScalar(5)); + fRRects[3].setRectXY(fRects[3], 7, 7); + fPaths[3].addRoundRect(fRects[3], 5, 5); fRectColors[3] = SK_ColorYELLOW; fRects[4].set(xC, yC, xF, yF); - fPaths[4].addRoundRect(fRects[4], SkIntToScalar(5), SkIntToScalar(5)); + fRRects[4].setRectXY(fRects[4], 7, 7); + fPaths[4].addRoundRect(fRects[4], 5, 5); fRectColors[4] = SK_ColorCYAN; fTotalWidth = kCols * fWidth + SK_Scalar1 * (kCols + 1) * kPadX; @@ -87,14 +98,27 @@ protected: static const int kPadX = 20; static const int kPadY = 20; + static const char* ClipStr(Clip clip) { + switch (clip) { + case kRect_Clip: + return "rect"; + case kRRect_Clip: + return "rrect"; + case kPath_Clip: + return "path"; + } + SkDEBUGFAIL("Unknown clip type."); + return ""; + } + virtual SkString onShortName() { - if (!fDoPaths && !fAntiAlias) { + if (kRect_Clip == fClip && !fAntiAlias) { return SkString("complexclip2"); } SkString str; str.printf("complexclip2_%s_%s", - fDoPaths ? "path" : "rect", + ClipStr(fClip), fAntiAlias ? "aa" : "bw"); return str; } @@ -123,22 +147,36 @@ protected: // antialiasing on the clipped draw for (int k = 0; k < 5; ++k) { rectPaint.setColor(fRectColors[k]); - if (fDoPaths) { - canvas->drawPath(fPaths[k], rectPaint); - } else { - canvas->drawRect(fRects[k], rectPaint); + switch (fClip) { + case kRect_Clip: + canvas->drawRect(fRects[k], rectPaint); + break; + case kRRect_Clip: + canvas->drawRRect(fRRects[k], rectPaint); + break; + case kPath_Clip: + canvas->drawPath(fPaths[k], rectPaint); + break; } } for (int k = 0; k < 5; ++k) { - if (fDoPaths) { - canvas->clipPath(fPaths[k], - fOps[j*kRows+i][k], - fAntiAlias); - } else { - canvas->clipRect(fRects[k], - fOps[j*kRows+i][k], - fAntiAlias); + switch (fClip) { + case kRect_Clip: + canvas->clipRect(fRects[k], + fOps[j*kRows+i][k], + fAntiAlias); + break; + case kRRect_Clip: + canvas->clipRRect(fRRects[k], + fOps[j*kRows+i][k], + fAntiAlias); + break; + case kPath_Clip: + canvas->clipPath(fPaths[k], + fOps[j*kRows+i][k], + fAntiAlias); + break; } } canvas->drawRect(SkRect::MakeWH(fWidth, fHeight), fillPaint); @@ -147,9 +185,10 @@ protected: } } private: - bool fDoPaths; + Clip fClip; bool fAntiAlias; SkRect fRects[5]; + SkRRect fRRects[5]; SkPath fPaths[5]; SkColor fRectColors[5]; SkRegion::Op fOps[kRows * kCols][5]; @@ -163,20 +202,14 @@ private: ////////////////////////////////////////////////////////////////////////////// -// bw rects -static GM* MyFactory(void*) { return new ComplexClip2GM(false, false); } -static GMRegistry reg(MyFactory); - -// bw paths -static GM* MyFactory2(void*) { return new ComplexClip2GM(true, false); } -static GMRegistry reg2(MyFactory2); - -// aa rects -static GM* MyFactory3(void*) { return new ComplexClip2GM(false, true); } -static GMRegistry reg3(MyFactory3); +// bw +DEF_GM( return new ComplexClip2GM(ComplexClip2GM::kRect_Clip, false); ) +DEF_GM( return new ComplexClip2GM(ComplexClip2GM::kRRect_Clip, false); ) +DEF_GM( return new ComplexClip2GM(ComplexClip2GM::kPath_Clip, false); ) -// aa paths -static GM* MyFactory4(void*) { return new ComplexClip2GM(true, true); } -static GMRegistry reg4(MyFactory4); +// aa +DEF_GM( return new ComplexClip2GM(ComplexClip2GM::kRect_Clip, true); ) +DEF_GM( return new ComplexClip2GM(ComplexClip2GM::kRRect_Clip, true); ) +DEF_GM( return new ComplexClip2GM(ComplexClip2GM::kPath_Clip, true); ) } diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h index aaaeb3b7e6..684541feb2 100644 --- a/include/core/SkCanvas.h +++ b/include/core/SkCanvas.h @@ -1004,6 +1004,7 @@ public: public: virtual ~ClipVisitor(); virtual void clipRect(const SkRect&, SkRegion::Op, bool antialias) = 0; + virtual void clipRRect(const SkRRect&, SkRegion::Op, bool antialias) = 0; virtual void clipPath(const SkPath&, SkRegion::Op, bool antialias) = 0; }; diff --git a/include/core/SkClipStack.h b/include/core/SkClipStack.h index 0d6cfb296f..0896db4284 100644 --- a/include/core/SkClipStack.h +++ b/include/core/SkClipStack.h @@ -11,6 +11,7 @@ #include "SkDeque.h" #include "SkPath.h" #include "SkRect.h" +#include "SkRRect.h" #include "SkRegion.h" #include "SkTDArray.h" @@ -41,6 +42,8 @@ public: kEmpty_Type, //!< This element combines a rect with the current clip using a set operation kRect_Type, + //!< This element combines a round-rect with the current clip using a set operation + kRRect_Type, //!< This element combines a path with the current clip using a set operation kPath_Type, }; @@ -54,46 +57,35 @@ public: this->initRect(0, rect, op, doAA); } + Element(const SkRRect& rrect, SkRegion::Op op, bool doAA) { + this->initRRect(0, rrect, op, doAA); + } + Element(const SkPath& path, SkRegion::Op op, bool doAA) { this->initPath(0, path, op, doAA); } - bool operator== (const Element& element) const { - if (this == &element) { - return true; - } - if (fOp != element.fOp || - fType != element.fType || - fDoAA != element.fDoAA || - fSaveCount != element.fSaveCount) { - return false; - } - switch (fType) { - case kPath_Type: - return fPath == element.fPath; - case kRect_Type: - return fRect == element.fRect; - case kEmpty_Type: - return true; - default: - SkDEBUGFAIL("Unexpected type."); - return false; - } - } + bool operator== (const Element& element) const; bool operator!= (const Element& element) const { return !(*this == element); } //!< Call to get the type of the clip element. Type getType() const { return fType; } //!< Call if getType() is kPath to get the path. - const SkPath& getPath() const { return fPath; } + const SkPath& getPath() const { SkASSERT(kPath_Type == fType); return fPath; } + + //!< Call if getType() is kRRect to get the round-rect. + const SkRRect& getRRect() const { SkASSERT(kRRect_Type == fType); return fRRect; } //!< Call if getType() is kRect to get the rect. - const SkRect& getRect() const { return fRect; } + const SkRect& getRect() const { SkASSERT(kRect_Type == fType); return fRect; } //!< Call if getType() is not kEmpty to get the set operation used to combine this element. SkRegion::Op getOp() const { return fOp; } + //!< Call to get the element as a path, regardless of its type. + void asPath(SkPath* path) const; + /** If getType() is not kEmpty this indicates whether the clip shape should be anti-aliased when it is rasterized. */ bool isAA() const { return fDoAA; } @@ -120,6 +112,8 @@ public: switch (fType) { case kRect_Type: return fRect; + case kRRect_Type: + return fRRect.getBounds(); case kPath_Type: return fPath.getBounds(); case kEmpty_Type: @@ -138,6 +132,8 @@ public: switch (fType) { case kRect_Type: return fRect.contains(rect); + case kRRect_Type: + return fRRect.contains(rect); case kPath_Type: return fPath.conservativelyContainsRect(rect); case kEmpty_Type: @@ -160,6 +156,7 @@ public: SkPath fPath; SkRect fRect; + SkRRect fRRect; int fSaveCount; // save count of stack when this element was added. SkRegion::Op fOp; Type fType; @@ -189,6 +186,10 @@ public: this->setEmpty(); } + Element(int saveCount, const SkRRect& rrect, SkRegion::Op op, bool doAA) { + this->initRRect(saveCount, rrect, op, doAA); + } + Element(int saveCount, const SkRect& rect, SkRegion::Op op, bool doAA) { this->initRect(saveCount, rect, op, doAA); } @@ -215,20 +216,29 @@ public: this->initCommon(saveCount, op, doAA); } - void initPath(int saveCount, const SkPath& path, SkRegion::Op op, bool doAA) { - fPath = path; - fType = kPath_Type; + void initRRect(int saveCount, const SkRRect& rrect, SkRegion::Op op, bool doAA) { + if (rrect.isRect()) { + fRect = rrect.getBounds(); + fType = kRect_Type; + } else { + fRRect = rrect; + fType = kRRect_Type; + } this->initCommon(saveCount, op, doAA); } + void initPath(int saveCount, const SkPath& path, SkRegion::Op op, bool doAA); + void setEmpty() { fType = kEmpty_Type; fFiniteBound.setEmpty(); fFiniteBoundType = kNormal_BoundsType; fIsIntersectionOfRects = false; fRect.setEmpty(); + fRRect.setEmpty(); fPath.reset(); fGenID = kEmptyGenID; + SkDEBUGCODE(this->checkEmpty();) } // All Element methods below are only used within SkClipStack.cpp @@ -305,6 +315,7 @@ public: this->clipDevRect(r, op, false); } void clipDevRect(const SkRect&, SkRegion::Op, bool doAA); + void clipDevRRect(const SkRRect&, SkRegion::Op, bool doAA); void clipDevPath(const SkPath&, SkRegion::Op, bool doAA); // An optimized version of clipDevRect(emptyRect, kIntersect, ...) void clipEmpty(); @@ -428,6 +439,11 @@ private: static int32_t gGenID; /** + * Helper for clipDevPath, etc. + */ + void pushElement(const Element& element); + + /** * Restore the stack back to the specified save count. */ void restoreTo(int saveCount); diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp index 61acf269b4..2cc504fb55 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -1295,13 +1295,7 @@ bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) { continue; } SkPath operand; - if (type == SkClipStack::Element::kRect_Type) { - operand.addRect(element->getRect()); - } else if (type == SkClipStack::Element::kPath_Type) { - operand = element->getPath(); - } else { - SkDEBUGFAIL("Unexpected type."); - } + element->asPath(&operand); SkRegion::Op elementOp = element->getOp(); if (elementOp == SkRegion::kReplace_Op) { devPath = operand; @@ -1458,6 +1452,9 @@ void SkCanvas::replayClips(ClipVisitor* visitor) const { case SkClipStack::Element::kPath_Type: visitor->clipPath(element->getPath(), element->getOp(), element->isAA()); break; + case SkClipStack::Element::kRRect_Type: + visitor->clipRRect(element->getRRect(), element->getOp(), element->isAA()); + break; case SkClipStack::Element::kRect_Type: visitor->clipRect(element->getRect(), element->getOp(), element->isAA()); break; diff --git a/src/core/SkClipStack.cpp b/src/core/SkClipStack.cpp index c66a219b62..17eb6f931d 100644 --- a/src/core/SkClipStack.cpp +++ b/src/core/SkClipStack.cpp @@ -16,17 +16,89 @@ static const int32_t kFirstUnreservedGenID = 3; int32_t SkClipStack::gGenID = kFirstUnreservedGenID; +bool SkClipStack::Element::operator== (const Element& element) const { + if (this == &element) { + return true; + } + if (fOp != element.fOp || + fType != element.fType || + fDoAA != element.fDoAA || + fSaveCount != element.fSaveCount) { + return false; + } + switch (fType) { + case kPath_Type: + return fPath == element.fPath; + case kRRect_Type: + return fRRect == element.fRRect; + case kRect_Type: + return fRect == element.fRect; + case kEmpty_Type: + return true; + default: + SkDEBUGFAIL("Unexpected type."); + return false; + } +} + void SkClipStack::Element::invertShapeFillType() { switch (fType) { case kRect_Type: fPath.reset(); fPath.addRect(fRect); - fPath.setFillType(SkPath::kInverseWinding_FillType); + fPath.setFillType(SkPath::kInverseEvenOdd_FillType); + fType = kPath_Type; + break; + case kRRect_Type: + fPath.reset(); + fPath.addRRect(fRRect); + fPath.setFillType(SkPath::kInverseEvenOdd_FillType); fType = kPath_Type; break; case kPath_Type: fPath.toggleInverseFillType(); + break; case kEmpty_Type: + // Should this set to an empty, inverse filled path? + break; + } +} + +void SkClipStack::Element::initPath(int saveCount, const SkPath& path, SkRegion::Op op, + bool doAA) { + if (!path.isInverseFillType()) { + if (SkPath::kNone_PathAsRect != path.asRect()) { + this->initRect(saveCount, path.getBounds(), op, doAA); + return; + } + SkRect ovalRect; + if (path.isOval(&ovalRect)) { + SkRRect rrect; + rrect.setOval(ovalRect); + this->initRRect(saveCount, rrect, op, doAA); + return; + } + } + fPath = path; + fType = kPath_Type; + this->initCommon(saveCount, op, doAA); +} + +void SkClipStack::Element::asPath(SkPath* path) const { + switch (fType) { + case kEmpty_Type: + path->reset(); + break; + case kRect_Type: + path->reset(); + path->addRect(fRect); + break; + case kRRect_Type: + path->reset(); + path->addRRect(fRRect); + break; + case kPath_Type: + *path = fPath; break; } } @@ -100,10 +172,10 @@ void SkClipStack::Element::combineBoundsDiff(FillCombo combination, const SkRect // is erased, so the only pixels that can remain set // occur w/in the intersection of the two finite bounds if (!fFiniteBound.intersect(prevFinite)) { - fFiniteBound.setEmpty(); - fGenID = kEmptyGenID; + this->setEmpty(); + } else { + fFiniteBoundType = kNormal_BoundsType; } - fFiniteBoundType = kNormal_BoundsType; break; case kPrev_Cur_FillCombo: // The most conservative result bound is that of the @@ -205,8 +277,7 @@ void SkClipStack::Element::combineBoundsIntersection(int combination, const SkRe break; case kPrev_Cur_FillCombo: if (!fFiniteBound.intersect(prevFinite)) { - fFiniteBound.setEmpty(); - fGenID = kEmptyGenID; + this->setEmpty(); } break; default: @@ -228,10 +299,10 @@ void SkClipStack::Element::combineBoundsRevDiff(int combination, const SkRect& p break; case kInvPrev_Cur_FillCombo: if (!fFiniteBound.intersect(prevFinite)) { - fFiniteBound.setEmpty(); - fGenID = kEmptyGenID; + this->setEmpty(); + } else { + fFiniteBoundType = kNormal_BoundsType; } - fFiniteBoundType = kNormal_BoundsType; break; case kPrev_InvCur_FillCombo: fFiniteBound.join(prevFinite); @@ -258,27 +329,34 @@ void SkClipStack::Element::updateBoundAndGenID(const Element* prior) { // First, optimistically update the current Element's bound information // with the current clip's bound fIsIntersectionOfRects = false; - if (kRect_Type == fType) { - fFiniteBound = fRect; - fFiniteBoundType = kNormal_BoundsType; - - if (SkRegion::kReplace_Op == fOp || - (SkRegion::kIntersect_Op == fOp && NULL == prior) || - (SkRegion::kIntersect_Op == fOp && prior->fIsIntersectionOfRects && - prior->rectRectIntersectAllowed(fRect, fDoAA))) { - fIsIntersectionOfRects = true; - } - - } else { - SkASSERT(kPath_Type == fType); - - fFiniteBound = fPath.getBounds(); + switch (fType) { + case kRect_Type: + fFiniteBound = fRect; + fFiniteBoundType = kNormal_BoundsType; - if (fPath.isInverseFillType()) { - fFiniteBoundType = kInsideOut_BoundsType; - } else { + if (SkRegion::kReplace_Op == fOp || + (SkRegion::kIntersect_Op == fOp && NULL == prior) || + (SkRegion::kIntersect_Op == fOp && prior->fIsIntersectionOfRects && + prior->rectRectIntersectAllowed(fRect, fDoAA))) { + fIsIntersectionOfRects = true; + } + break; + case kRRect_Type: + fFiniteBound = fRRect.getBounds(); fFiniteBoundType = kNormal_BoundsType; - } + break; + case kPath_Type: + fFiniteBound = fPath.getBounds(); + + if (fPath.isInverseFillType()) { + fFiniteBoundType = kInsideOut_BoundsType; + } else { + fFiniteBoundType = kNormal_BoundsType; + } + break; + case kEmpty_Type: + SkDEBUGFAIL("We shouldn't get here with an empty element."); + break; } if (!fDoAA) { @@ -344,7 +422,7 @@ void SkClipStack::Element::updateBoundAndGenID(const Element* prior) { // so nothing to do break; default: - SkDebugf("SkRegion::Op error/n"); + SkDebugf("SkRegion::Op error\n"); SkASSERT(0); break; } @@ -528,97 +606,69 @@ bool SkClipStack::quickContains(const SkRect& rect) const { return true; } -void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) { - +void SkClipStack::pushElement(const Element& element) { // Use reverse iterator instead of back because Rect path may need previous SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart); - Element* element = (Element*) iter.prev(); + Element* prior = (Element*) iter.prev(); - if (NULL != element) { - if (element->canBeIntersectedInPlace(fSaveCount, op)) { - switch (element->fType) { + if (NULL != prior) { + if (prior->canBeIntersectedInPlace(fSaveCount, element.getOp())) { + switch (prior->fType) { case Element::kEmpty_Type: - element->checkEmpty(); + SkDEBUGCODE(prior->checkEmpty();) return; case Element::kRect_Type: - if (element->rectRectIntersectAllowed(rect, doAA)) { - if (!element->fRect.intersect(rect)) { - element->setEmpty(); + if (Element::kRect_Type == element.getType()) { + if (prior->rectRectIntersectAllowed(element.getRect(), element.isAA())) { + if (!prior->fRect.intersect(element.getRect())) { + prior->setEmpty(); + return; + } + + prior->fDoAA = element.isAA(); + Element* priorPrior = (Element*) iter.prev(); + prior->updateBoundAndGenID(priorPrior); return; } - - element->fDoAA = doAA; - Element* prev = (Element*) iter.prev(); - element->updateBoundAndGenID(prev); - return; + break; } - break; - case Element::kPath_Type: - if (!SkRect::Intersects(element->fPath.getBounds(), rect)) { - element->setEmpty(); + // fallthrough + default: + if (!SkRect::Intersects(prior->getBounds(), element.getBounds())) { + prior->setEmpty(); return; } break; } - } else if (SkRegion::kReplace_Op == op) { + } else if (SkRegion::kReplace_Op == element.getOp()) { this->restoreTo(fSaveCount - 1); - element = (Element*) fDeque.back(); + prior = (Element*) fDeque.back(); } } - new (fDeque.push_back()) Element(fSaveCount, rect, op, doAA); - ((Element*) fDeque.back())->updateBoundAndGenID(element); + Element* newElement = SkNEW_PLACEMENT_ARGS(fDeque.push_back(), Element, (element)); + newElement->updateBoundAndGenID(prior); } -void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op, bool doAA) { - SkRect alt; - if (path.isRect(&alt) && !path.isInverseFillType()) { - return this->clipDevRect(alt, op, doAA); - } +void SkClipStack::clipDevRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) { + Element element(fSaveCount, rrect, op, doAA); + this->pushElement(element); +} - Element* element = (Element*)fDeque.back(); - if (NULL != element) { - if (element->canBeIntersectedInPlace(fSaveCount, op)) { - const SkRect& pathBounds = path.getBounds(); - switch (element->fType) { - case Element::kEmpty_Type: - element->checkEmpty(); - return; - case Element::kRect_Type: - if (!SkRect::Intersects(element->fRect, pathBounds)) { - element->setEmpty(); - return; - } - break; - case Element::kPath_Type: - if (!SkRect::Intersects(element->fPath.getBounds(), pathBounds)) { - element->setEmpty(); - return; - } - break; - } - } else if (SkRegion::kReplace_Op == op) { - this->restoreTo(fSaveCount - 1); - element = (Element*) fDeque.back(); - } - } - new (fDeque.push_back()) Element(fSaveCount, path, op, doAA); - ((Element*) fDeque.back())->updateBoundAndGenID(element); +void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) { + Element element(fSaveCount, rect, op, doAA); + this->pushElement(element); } -void SkClipStack::clipEmpty() { +void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op, bool doAA) { + Element element(fSaveCount, path, op, doAA); + this->pushElement(element); +} +void SkClipStack::clipEmpty() { Element* element = (Element*) fDeque.back(); if (element && element->canBeIntersectedInPlace(fSaveCount, SkRegion::kIntersect_Op)) { - switch (element->fType) { - case Element::kEmpty_Type: - element->checkEmpty(); - return; - case Element::kRect_Type: - case Element::kPath_Type: - element->setEmpty(); - return; - } + element->setEmpty(); } new (fDeque.push_back()) Element(fSaveCount); diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp index 83b3b01159..8a7aac247a 100644 --- a/src/gpu/GrClipMaskManager.cpp +++ b/src/gpu/GrClipMaskManager.cpp @@ -94,13 +94,13 @@ bool GrClipMaskManager::useSWOnlyPath(const ElementList& elements) { for (ElementList::Iter iter(elements.headIter()); iter.get(); iter.next()) { const Element* element = iter.get(); // rects can always be drawn directly w/o using the software path - // so only paths need to be checked - if (Element::kPath_Type == element->getType() && - path_needs_SW_renderer(this->getContext(), fGpu, - element->getPath(), - stroke, - element->isAA())) { - return true; + // Skip rrects once we're drawing them directly. + if (Element::kRect_Type != element->getType()) { + SkPath path; + element->asPath(&path); + if (path_needs_SW_renderer(this->getContext(), fGpu, path, stroke, element->isAA())) { + return true; + } } } return false; @@ -169,10 +169,11 @@ bool GrClipMaskManager::setupClipping(const GrClipData* clipDataIn, return true; } } + Element::Type type = elements.tail()->getType(); + bool isAA = GR_AA_CLIP && elements.tail()->isAA(); SkAutoTUnref<GrEffectRef> effect; - if (SkClipStack::Element::kPath_Type == elements.tail()->getType()) { + if (SkClipStack::Element::kPath_Type == type) { const SkPath& path = elements.tail()->getPath(); - bool isAA = GR_AA_CLIP && elements.tail()->isAA(); if (rt->isMultisampled()) { // A coverage effect for AA clipping won't play nicely with MSAA. if (!isAA) { @@ -188,13 +189,12 @@ bool GrClipMaskManager::setupClipping(const GrClipData* clipDataIn, GrConvexPolyEffect::kFillNoAA_EdgeType; effect.reset(GrConvexPolyEffect::Create(type, path, &offset)); } - } else if (GR_AA_CLIP && elements.tail()->isAA() && !rt->isMultisampled()) { + } else if (isAA && SkClipStack::Element::kRect_Type == type && !rt->isMultisampled()) { // We only handle AA/non-MSAA rects here. Coverage effect AA isn't MSAA friendly and // non-AA rect clips are handled by the scissor. - SkASSERT(SkClipStack::Element::kRect_Type == elements.tail()->getType()); SkRect rect = elements.tail()->getRect(); SkVector offset = { SkIntToScalar(-clipDataIn->fOrigin.fX), - SkIntToScalar(-clipDataIn->fOrigin.fY) }; + SkIntToScalar(-clipDataIn->fOrigin.fY) }; rect.offset(offset); effect.reset(GrConvexPolyEffect::CreateForAAFillRect(rect)); // This should never fail. @@ -332,7 +332,11 @@ bool GrClipMaskManager::drawElement(GrTexture* target, drawState->setRenderTarget(target->asRenderTarget()); + // TODO: Draw rrects directly here. switch (element->getType()) { + case Element::kEmpty_Type: + SkDEBUGFAIL("Should never get here with an empty element."); + break; case Element::kRect_Type: // TODO: Do rects directly to the accumulator using a aa-rect GrEffect that covers the // entire mask bounds and writes 0 outside the rect. @@ -347,28 +351,25 @@ bool GrClipMaskManager::drawElement(GrTexture* target, fGpu->drawSimpleRect(element->getRect(), NULL); } return true; - case Element::kPath_Type: { - SkTCopyOnFirstWrite<SkPath> path(element->getPath()); - if (path->isInverseFillType()) { - path.writable()->toggleInverseFillType(); + default: { + SkPath path; + element->asPath(&path); + if (path.isInverseFillType()) { + path.toggleInverseFillType(); } SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); if (NULL == pr) { GrPathRendererChain::DrawType type; type = element->isAA() ? GrPathRendererChain::kColorAntiAlias_DrawType : GrPathRendererChain::kColor_DrawType; - pr = this->getContext()->getPathRenderer(*path, stroke, fGpu, false, type); + pr = this->getContext()->getPathRenderer(path, stroke, fGpu, false, type); } if (NULL == pr) { return false; } - pr->drawPath(element->getPath(), stroke, fGpu, element->isAA()); + pr->drawPath(path, stroke, fGpu, element->isAA()); break; } - default: - // something is wrong if we're trying to draw an empty element. - GrCrash("Unexpected element type"); - return false; } return true; } @@ -379,25 +380,22 @@ bool GrClipMaskManager::canStencilAndDrawElement(GrTexture* target, GrDrawState* drawState = fGpu->drawState(); drawState->setRenderTarget(target->asRenderTarget()); - switch (element->getType()) { - case Element::kRect_Type: - return true; - case Element::kPath_Type: { - SkTCopyOnFirstWrite<SkPath> path(element->getPath()); - if (path->isInverseFillType()) { - path.writable()->toggleInverseFillType(); - } - SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); - GrPathRendererChain::DrawType type = element->isAA() ? - GrPathRendererChain::kStencilAndColorAntiAlias_DrawType : - GrPathRendererChain::kStencilAndColor_DrawType; - *pr = this->getContext()->getPathRenderer(*path, stroke, fGpu, false, type); - return NULL != *pr; + if (Element::kRect_Type == element->getType()) { + return true; + } else { + // We shouldn't get here with an empty clip element. + SkASSERT(Element::kEmpty_Type != element->getType()); + SkPath path; + element->asPath(&path); + if (path.isInverseFillType()) { + path.toggleInverseFillType(); } - default: - // something is wrong if we're trying to draw an empty element. - GrCrash("Unexpected element type"); - return false; + SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); + GrPathRendererChain::DrawType type = element->isAA() ? + GrPathRendererChain::kStencilAndColorAntiAlias_DrawType : + GrPathRendererChain::kStencilAndColor_DrawType; + *pr = this->getContext()->getPathRenderer(path, stroke, fGpu, false, type); + return NULL != *pr; } } @@ -700,18 +698,17 @@ bool GrClipMaskManager::createStencilClipMask(int32_t elementsGenID, SkRegion::Op op = element->getOp(); GrPathRenderer* pr = NULL; - SkTCopyOnFirstWrite<SkPath> clipPath; + SkPath clipPath; if (Element::kRect_Type == element->getType()) { stencilSupport = GrPathRenderer::kNoRestriction_StencilSupport; fillInverted = false; } else { - SkASSERT(Element::kPath_Type == element->getType()); - clipPath.init(element->getPath()); - fillInverted = clipPath->isInverseFillType(); + element->asPath(&clipPath); + fillInverted = clipPath.isInverseFillType(); if (fillInverted) { - clipPath.writable()->toggleInverseFillType(); + clipPath.toggleInverseFillType(); } - pr = this->getContext()->getPathRenderer(*clipPath, + pr = this->getContext()->getPathRenderer(clipPath, stroke, fGpu, false, @@ -752,13 +749,12 @@ bool GrClipMaskManager::createStencilClipMask(int32_t elementsGenID, *drawState->stencil() = gDrawToStencil; fGpu->drawSimpleRect(element->getRect(), NULL); } else { - SkASSERT(Element::kPath_Type == element->getType()); - if (!clipPath->isEmpty()) { + if (!clipPath.isEmpty()) { if (canRenderDirectToStencil) { *drawState->stencil() = gDrawToStencil; - pr->drawPath(*clipPath, stroke, fGpu, false); + pr->drawPath(clipPath, stroke, fGpu, false); } else { - pr->stencilPath(*clipPath, stroke, fGpu); + pr->stencilPath(clipPath, stroke, fGpu); } } } @@ -774,9 +770,8 @@ bool GrClipMaskManager::createStencilClipMask(int32_t elementsGenID, SET_RANDOM_COLOR fGpu->drawSimpleRect(element->getRect(), NULL); } else { - SkASSERT(Element::kPath_Type == element->getType()); SET_RANDOM_COLOR - pr->drawPath(*clipPath, stroke, fGpu, false); + pr->drawPath(clipPath, stroke, fGpu, false); } } else { SET_RANDOM_COLOR diff --git a/src/gpu/GrReducedClip.cpp b/src/gpu/GrReducedClip.cpp index 8480e041b8..6ad9cc6d8b 100644 --- a/src/gpu/GrReducedClip.cpp +++ b/src/gpu/GrReducedClip.cpp @@ -72,6 +72,8 @@ void ReduceClipStack(const SkClipStack& stack, *requiresAA = false; } } else if (isectRect.intersect(stackBounds, scalarQueryBounds)) { + // If the caller asked for tighter integer bounds we may be able to + // return kAllIn and give the bounds with no elements if (NULL != tighterBounds) { isectRect.roundOut(tighterBounds); SkRect scalarTighterBounds = SkRect::Make(*tighterBounds); @@ -83,14 +85,14 @@ void ReduceClipStack(const SkClipStack& stack, *initialState = kAllIn_InitialState; return; } - *initialState = kAllOut_InitialState; - // iior should only be true if aa/non-aa status matches among all elements. - SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); - bool doAA = iter.prev()->isAA(); - SkNEW_INSERT_AT_LLIST_HEAD(result, Element, (isectRect, SkRegion::kReplace_Op, doAA)); - if (NULL != requiresAA) { - *requiresAA = doAA; - } + } + *initialState = kAllOut_InitialState; + // iior should only be true if aa/non-aa status matches among all elements. + SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart); + bool doAA = iter.prev()->isAA(); + SkNEW_INSERT_AT_LLIST_HEAD(result, Element, (isectRect, SkRegion::kReplace_Op, doAA)); + if (NULL != requiresAA) { + *requiresAA = doAA; } } else { *initialState = kAllOut_InitialState; diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp index 9487dfd0a5..ce036d1112 100644 --- a/src/pdf/SkPDFDevice.cpp +++ b/src/pdf/SkPDFDevice.cpp @@ -404,10 +404,8 @@ static bool get_clip_stack_path(const SkMatrix& transform, outClipPath->reset(); outClipPath->setFillType(SkPath::kInverseWinding_FillType); continue; - } else if (SkClipStack::Element::kRect_Type == clipEntry->getType()) { - entryPath.addRect(clipEntry->getRect()); - } else if (SkClipStack::Element::kPath_Type == clipEntry->getType()) { - entryPath = clipEntry->getPath(); + } else { + clipEntry->asPath(&entryPath); } entryPath.transform(transform); diff --git a/src/utils/SkCanvasStateUtils.cpp b/src/utils/SkCanvasStateUtils.cpp index eb92c37b19..77b0e20c2d 100644 --- a/src/utils/SkCanvasStateUtils.cpp +++ b/src/utils/SkCanvasStateUtils.cpp @@ -130,6 +130,10 @@ public: fFailed |= antialias; } + virtual void clipRRect(const SkRRect& rrect, SkRegion::Op op, bool antialias) SK_OVERRIDE { + fFailed |= antialias; + } + virtual void clipPath(const SkPath&, SkRegion::Op, bool antialias) SK_OVERRIDE { fFailed |= antialias; } diff --git a/tests/CanvasTest.cpp b/tests/CanvasTest.cpp index b3b1a37dfc..5f3d79ceeb 100644 --- a/tests/CanvasTest.cpp +++ b/tests/CanvasTest.cpp @@ -69,10 +69,13 @@ class Canvas2CanvasClipVisitor : public SkCanvas::ClipVisitor { public: Canvas2CanvasClipVisitor(SkCanvas* target) : fTarget(target) {} - virtual void clipRect(const SkRect& r, SkRegion::Op op, bool aa) { + virtual void clipRect(const SkRect& r, SkRegion::Op op, bool aa) SK_OVERRIDE { fTarget->clipRect(r, op, aa); } - virtual void clipPath(const SkPath& p, SkRegion::Op op, bool aa) { + virtual void clipRRect(const SkRRect& r, SkRegion::Op op, bool aa) SK_OVERRIDE { + fTarget->clipRRect(r, op, aa); + } + virtual void clipPath(const SkPath& p, SkRegion::Op op, bool aa) SK_OVERRIDE { fTarget->clipPath(p, op, aa); } diff --git a/tests/ClipStackTest.cpp b/tests/ClipStackTest.cpp index 11504315c8..0620df947b 100644 --- a/tests/ClipStackTest.cpp +++ b/tests/ClipStackTest.cpp @@ -58,7 +58,6 @@ static void test_assign_and_comparison(skiatest::Reporter* reporter) { // Test that an equal, but not copied version is equal. s.save(); REPORTER_ASSERT(reporter, 3 == s.getSaveCount()); - r = SkRect::MakeLTRB(14, 15, 16, 17); s.clipDevRect(r, SkRegion::kUnion_Op, doAA); REPORTER_ASSERT(reporter, s == copy); @@ -68,23 +67,17 @@ static void test_assign_and_comparison(skiatest::Reporter* reporter) { REPORTER_ASSERT(reporter, 2 == s.getSaveCount()); s.save(); REPORTER_ASSERT(reporter, 3 == s.getSaveCount()); - r = SkRect::MakeLTRB(14, 15, 16, 17); s.clipDevRect(r, SkRegion::kIntersect_Op, doAA); REPORTER_ASSERT(reporter, s != copy); - // Test that different state (clip type) triggers not equal. - // NO LONGER VALID: if a path contains only a rect, we turn - // it into a bare rect for performance reasons (working - // around Chromium/JavaScript bad pattern). -/* + // Test that version constructed with rect-path rather than a rect is still considered equal. s.restore(); s.save(); SkPath rp; rp.addRect(r); s.clipDevPath(rp, SkRegion::kUnion_Op, doAA); - REPORTER_ASSERT(reporter, s != copy); -*/ + REPORTER_ASSERT(reporter, s == copy); // Test that different rects triggers not equal. s.restore(); @@ -192,8 +185,7 @@ static void test_iterators(skiatest::Reporter* reporter) { } // Exercise the SkClipStack's getConservativeBounds computation -static void test_bounds(skiatest::Reporter* reporter, bool useRects) { - +static void test_bounds(skiatest::Reporter* reporter, SkClipStack::Element::Type primType) { static const int gNumCases = 20; static const SkRect gAnswerRectsBW[gNumCases] = { // A op B @@ -238,17 +230,21 @@ static void test_bounds(skiatest::Reporter* reporter, bool useRects) { rectA.iset(10, 10, 50, 50); rectB.iset(40, 40, 80, 80); - SkPath clipA, clipB; + SkRRect rrectA, rrectB; + rrectA.setOval(rectA); + rrectB.setRectXY(rectB, SkIntToScalar(1), SkIntToScalar(2)); - clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5)); - clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5)); + SkPath pathA, pathB; + + pathA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5)); + pathB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5)); SkClipStack stack; SkRect devClipBound; bool isIntersectionOfRects = false; int testCase = 0; - int numBitTests = useRects ? 1 : 4; + int numBitTests = SkClipStack::Element::kPath_Type == primType ? 4 : 1; for (int invBits = 0; invBits < numBitTests; ++invBits) { for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) { @@ -256,17 +252,27 @@ static void test_bounds(skiatest::Reporter* reporter, bool useRects) { bool doInvA = SkToBool(invBits & 1); bool doInvB = SkToBool(invBits & 2); - clipA.setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType : + pathA.setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType : SkPath::kEvenOdd_FillType); - clipB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType : + pathB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType : SkPath::kEvenOdd_FillType); - if (useRects) { - stack.clipDevRect(rectA, SkRegion::kIntersect_Op, false); - stack.clipDevRect(rectB, gOps[op], false); - } else { - stack.clipDevPath(clipA, SkRegion::kIntersect_Op, false); - stack.clipDevPath(clipB, gOps[op], false); + switch (primType) { + case SkClipStack::Element::kEmpty_Type: + SkDEBUGFAIL("Don't call this with kEmpty."); + break; + case SkClipStack::Element::kRect_Type: + stack.clipDevRect(rectA, SkRegion::kIntersect_Op, false); + stack.clipDevRect(rectB, gOps[op], false); + break; + case SkClipStack::Element::kRRect_Type: + stack.clipDevRRect(rrectA, SkRegion::kIntersect_Op, false); + stack.clipDevRRect(rrectB, gOps[op], false); + break; + case SkClipStack::Element::kPath_Type: + stack.clipDevPath(pathA, SkRegion::kIntersect_Op, false); + stack.clipDevPath(pathB, gOps[op], false); + break; } REPORTER_ASSERT(reporter, !stack.isWideOpen()); @@ -275,7 +281,7 @@ static void test_bounds(skiatest::Reporter* reporter, bool useRects) { stack.getConservativeBounds(0, 0, 100, 100, &devClipBound, &isIntersectionOfRects); - if (useRects) { + if (SkClipStack::Element::kRect_Type == primType) { REPORTER_ASSERT(reporter, isIntersectionOfRects == (gOps[op] == SkRegion::kIntersect_Op)); } else { @@ -801,14 +807,18 @@ typedef void (*AddElementFunc) (const SkRect& rect, SkClipStack* stack); static void add_round_rect(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) { - SkPath path; SkScalar rx = rect.width() / 10; SkScalar ry = rect.height() / 20; - path.addRoundRect(rect, rx, ry); if (invert) { + SkPath path; + path.addRoundRect(rect, rx, ry); path.setFillType(SkPath::kInverseWinding_FillType); + stack->clipDevPath(path, op, false); + } else { + SkRRect rrect; + rrect.setRectXY(rect, rx, ry); + stack->clipDevRRect(rrect, op, false); } - stack->clipDevPath(path, op, false); }; static void add_rect(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) { @@ -836,6 +846,9 @@ static void add_elem_to_stack(const SkClipStack::Element& element, SkClipStack* case SkClipStack::Element::kRect_Type: stack->clipDevRect(element.getRect(), element.getOp(), element.isAA()); break; + case SkClipStack::Element::kRRect_Type: + stack->clipDevRRect(element.getRRect(), element.getOp(), element.isAA()); + break; case SkClipStack::Element::kPath_Type: stack->clipDevPath(element.getPath(), element.getOp(), element.isAA()); break; @@ -851,21 +864,16 @@ static void add_elem_to_region(const SkClipStack::Element& element, SkRegion* region) { SkRegion elemRegion; SkRegion boundsRgn(bounds); + SkPath path; switch (element.getType()) { - case SkClipStack::Element::kRect_Type: { - SkPath path; - path.addRect(element.getRect()); - elemRegion.setPath(path, boundsRgn); + case SkClipStack::Element::kEmpty_Type: + elemRegion.setEmpty(); break; - } - case SkClipStack::Element::kPath_Type: - elemRegion.setPath(element.getPath(), boundsRgn); + default: + element.asPath(&path); + elemRegion.setPath(path, boundsRgn); break; - case SkClipStack::Element::kEmpty_Type: - // - region->setEmpty(); - return; } region->op(elemRegion, element.getOp()); } @@ -938,6 +946,7 @@ static void test_reduced_clip_stack(skiatest::Reporter* reporter) { SkRect rect = SkRect::MakeXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight); bool invert = r.nextBiasedBool(kFractionInverted); + kElementFuncs[r.nextULessThan(SK_ARRAY_COUNT(kElementFuncs))](rect, invert, op, &stack); if (doSave) { stack.save(); @@ -996,8 +1005,9 @@ static void test_reduced_clip_stack(skiatest::Reporter* reporter) { while ((element = iter.next())) { add_elem_to_region(*element, inflatedIBounds, &reducedRegion); } - - REPORTER_ASSERT(reporter, region == reducedRegion); + SkString testCase; + testCase.printf("Iteration %d", i); + REPORTER_ASSERT_MESSAGE(reporter, region == reducedRegion, testCase.c_str()); } } @@ -1194,8 +1204,9 @@ DEF_TEST(ClipStack, reporter) { test_assign_and_comparison(reporter); test_iterators(reporter); - test_bounds(reporter, true); // once with rects - test_bounds(reporter, false); // once with paths + test_bounds(reporter, SkClipStack::Element::kRect_Type); + test_bounds(reporter, SkClipStack::Element::kRRect_Type); + test_bounds(reporter, SkClipStack::Element::kPath_Type); test_isWideOpen(reporter); test_rect_merging(reporter); test_rect_replace(reporter); |