}
}
+ bool contains(const SkRRect& rrect) const {
+ switch (fType) {
+ case kRect_Type:
+ return this->getRect().contains(rrect.getBounds());
+ case kRRect_Type:
+ // We don't currently have a generalized rrect-rrect containment.
+ return fRRect.contains(rrect.getBounds()) || rrect == fRRect;
+ case kPath_Type:
+ return fPath.get()->conservativelyContainsRect(rrect.getBounds());
+ case kEmpty_Type:
+ return false;
+ default:
+ SkDEBUGFAIL("Unexpected type.");
+ return false;
+ }
+ }
+
/**
* Is the clip shape inverse filled.
*/
bool* isIntersectionOfRects = NULL) const;
/**
- * Returns true if the input rect in device space is entirely contained
- * by the clip. A return value of false does not guarantee that the rect
+ * Returns true if the input (r)rect in device space is entirely contained
+ * by the clip. A return value of false does not guarantee that the (r)rect
* is not contained by the clip.
*/
bool quickContains(const SkRect& devRect) const;
+ bool quickContains(const SkRRect& devRRect) const;
/**
* Flattens the clip stack into a single SkPath. Returns true if any of
*/
class SK_API SkRRect {
public:
+ SkRRect() { /* unititialized */ }
+ SkRRect(const SkRRect&) = default;
+ SkRRect& operator=(const SkRRect&) = default;
+
/**
* Enum to capture the various possible subtypes of RR. Accessed
* by type(). The subtypes become progressively less restrictive.
fRect.offset(dx, dy);
}
+ SkRRect SK_WARN_UNUSED_RESULT makeOffset(SkScalar dx, SkScalar dy) const {
+ return SkRRect(fRect.makeOffset(dx, dy), fRadii, fType);
+ }
+
/**
* Returns true if 'rect' is wholy inside the RR, and both
* are not empty.
void dumpHex() const { this->dump(true); }
private:
+ SkRRect(const SkRect& rect, const SkVector radii[4], int32_t type)
+ : fRect(rect)
+ , fRadii{radii[0], radii[1], radii[2], radii[3]}
+ , fType(type) {}
+
SkRect fRect;
// Radii order is UL, UR, LR, LL. Use Corner enum to index into fRadii[]
SkVector fRadii[4];
class GrClip {
public:
virtual bool quickContains(const SkRect&) const = 0;
+ virtual bool quickContains(const SkRRect& rrect) const {
+ return this->quickContains(rrect.getBounds());
+ }
virtual void getConservativeBounds(int width, int height, SkIRect* devResult,
bool* isIntersectionOfRects = nullptr) const = 0;
virtual bool apply(GrContext*, GrDrawContext*, bool useHWAA, bool hasUserStencilSettings,
return true;
}
+bool SkClipStack::quickContains(const SkRRect& rrect) const {
+
+ Iter iter(*this, Iter::kTop_IterStart);
+ const Element* element = iter.prev();
+ while (element != nullptr) {
+ if (SkRegion::kIntersect_Op != element->getOp() && SkRegion::kReplace_Op != element->getOp())
+ return false;
+ if (element->isInverseFilled()) {
+ // Part of 'rrect' could be trimmed off by the inverse-filled clip element
+ if (SkRect::Intersects(element->getBounds(), rrect.getBounds())) {
+ return false;
+ }
+ } else {
+ if (!element->contains(rrect)) {
+ return false;
+ }
+ }
+ if (SkRegion::kReplace_Op == element->getOp()) {
+ break;
+ }
+ element = iter.prev();
+ }
+ return true;
+}
+
bool SkClipStack::asPath(SkPath *path) const {
bool isAA = false;
SkIntToScalar(fOrigin.y())));
}
+bool GrClipStackClip::quickContains(const SkRRect& rrect) const {
+ if (!fStack) {
+ return true;
+ }
+ return fStack->quickContains(rrect.makeOffset(SkIntToScalar(fOrigin.fX),
+ SkIntToScalar(fOrigin.fY)));
+}
+
void GrClipStackClip::getConservativeBounds(int width, int height, SkIRect* devResult,
bool* isIntersectionOfRects) const {
if (!fStack) {
}
bool quickContains(const SkRect&) const final;
+ bool quickContains(const SkRRect&) const final;
void getConservativeBounds(int width, int height, SkIRect* devResult,
bool* isIntersectionOfRects) const final;
bool apply(GrContext*, GrDrawContext*, bool useHWAA, bool hasUserStencilSettings,
///////////////////////////////////////////////////////////////////////////////
-void GrDrawContext::drawRRect(const GrClip& clip,
+void GrDrawContext::drawRRect(const GrClip& origClip,
const GrPaint& paint,
const SkMatrix& viewMatrix,
const SkRRect& rrect,
RETURN_IF_ABANDONED
SkDEBUGCODE(this->validate();)
GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrDrawContext::drawRRect");
-
if (rrect.isEmpty()) {
return;
}
+ GrNoClip noclip;
+ const GrClip* clip = &origClip;
+#ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK
+ // The Android framework frequently clips rrects to themselves where the clip is non-aa and the
+ // draw is aa. Since our lower level clip code works from batch bounds, which are SkRects, it
+ // doesn't detect that the clip can be ignored (modulo antialiasing). The following test
+ // attempts to mitigate the stencil clip cost but will only help when the entire clip stack
+ // can be ignored. We'd prefer to fix this in the framework by removing the clips calls.
+ SkRRect devRRect;
+ if (rrect.transform(viewMatrix, &devRRect) && clip->quickContains(devRRect)) {
+ clip = &noclip;
+ }
+#endif
SkASSERT(!style.pathEffect()); // this should've been devolved to a path in SkGpuDevice
AutoCheckFlush acf(fDrawingManager);
&useHWAA));
if (batch) {
GrPipelineBuilder pipelineBuilder(paint, useHWAA);
- this->getDrawTarget()->drawBatch(pipelineBuilder, this, clip, batch);
+ this->getDrawTarget()->drawBatch(pipelineBuilder, this, *clip, batch);
return;
}
}
shaderCaps));
if (batch) {
GrPipelineBuilder pipelineBuilder(paint, useHWAA);
- this->getDrawTarget()->drawBatch(pipelineBuilder, this, clip, batch);
+ this->getDrawTarget()->drawBatch(pipelineBuilder, this, *clip, batch);
return;
}
}
SkPath path;
path.setIsVolatile(true);
path.addRRect(rrect);
- this->internalDrawPath(clip, paint, viewMatrix, path, style);
+ this->internalDrawPath(*clip, paint, viewMatrix, path, style);
}
bool GrDrawContext::drawFilledDRRect(const GrClip& clip,