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));
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;
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;
}
// 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);
}
}
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];
//////////////////////////////////////////////////////////////////////////////
-// 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); )
}
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;
};
#include "SkDeque.h"
#include "SkPath.h"
#include "SkRect.h"
+#include "SkRRect.h"
#include "SkRegion.h"
#include "SkTDArray.h"
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,
};
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; }
switch (fType) {
case kRect_Type:
return fRect;
+ case kRRect_Type:
+ return fRRect.getBounds();
case kPath_Type:
return fPath.getBounds();
case kEmpty_Type:
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:
SkPath fPath;
SkRect fRect;
+ SkRRect fRRect;
int fSaveCount; // save count of stack when this element was added.
SkRegion::Op fOp;
Type fType;
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);
}
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
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();
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);
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;
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;
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;
}
}
// 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
break;
case kPrev_Cur_FillCombo:
if (!fFiniteBound.intersect(prevFinite)) {
- fFiniteBound.setEmpty();
- fGenID = kEmptyGenID;
+ this->setEmpty();
}
break;
default:
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);
// 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) {
// so nothing to do
break;
default:
- SkDebugf("SkRegion::Op error/n");
+ SkDebugf("SkRegion::Op error\n");
SkASSERT(0);
break;
}
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);
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;
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) {
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.
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.
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;
}
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;
}
}
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,
*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);
}
}
}
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
*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);
*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;
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);
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;
}
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);
}
// 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);
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();
}
// 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
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) {
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());
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 {
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) {
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;
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());
}
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();
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());
}
}
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);