Merge "Emulate replace clip ops using SkAndroidFrameworkUtils::ResetClip"

This commit is contained in:
Michael Ludwig
2021-08-03 19:52:16 +00:00
committed by Android (Google) Code Review
10 changed files with 58 additions and 20 deletions

View File

@@ -27,6 +27,7 @@ X(ClipPath)
X(ClipRect)
X(ClipRRect)
X(ClipRegion)
X(ResetClip)
X(DrawPaint)
X(DrawBehind)
X(DrawPath)

View File

@@ -187,6 +187,11 @@ struct ClipRegion final : Op {
SkClipOp op;
void draw(SkCanvas* c, const SkMatrix&) const { c->clipRegion(region, op); }
};
struct ResetClip final : Op {
static const auto kType = Type::ResetClip;
ResetClip() {}
void draw(SkCanvas* c, const SkMatrix&) const { SkAndroidFrameworkUtils::ResetClip(c); }
};
struct DrawPaint final : Op {
static const auto kType = Type::DrawPaint;
@@ -662,6 +667,9 @@ void DisplayListData::clipRRect(const SkRRect& rrect, SkClipOp op, bool aa) {
void DisplayListData::clipRegion(const SkRegion& region, SkClipOp op) {
this->push<ClipRegion>(0, region, op);
}
void DisplayListData::resetClip() {
this->push<ResetClip>(0);
}
void DisplayListData::drawPaint(const SkPaint& paint) {
this->push<DrawPaint>(0, paint);
@@ -969,6 +977,14 @@ void RecordingCanvas::onClipRegion(const SkRegion& region, SkClipOp op) {
fDL->clipRegion(region, op);
this->INHERITED::onClipRegion(region, op);
}
void RecordingCanvas::onResetClip() {
// This is part of "replace op" emulation, but rely on the following intersection
// clip to potentially mark the clip as complex. If we are already complex, we do
// not reset the complexity so that we don't break the contract that no higher
// save point has a complex clip when "not complex".
fDL->resetClip();
this->INHERITED::onResetClip();
}
void RecordingCanvas::onDrawPaint(const SkPaint& paint) {
fDL->drawPaint(paint);

View File

@@ -97,6 +97,7 @@ private:
void clipRect(const SkRect&, SkClipOp, bool aa);
void clipRRect(const SkRRect&, SkClipOp, bool aa);
void clipRegion(const SkRegion&, SkClipOp);
void resetClip();
void drawPaint(const SkPaint&);
void drawBehind(const SkPaint&);
@@ -169,6 +170,7 @@ public:
void onClipRRect(const SkRRect&, SkClipOp, ClipEdgeStyle) override;
void onClipPath(const SkPath&, SkClipOp, ClipEdgeStyle) override;
void onClipRegion(const SkRegion&, SkClipOp) override;
void onResetClip() override;
void onDrawPaint(const SkPaint&) override;
void onDrawBehind(const SkPaint&) override;

View File

@@ -396,6 +396,22 @@ bool SkiaCanvas::clipPath(const SkPath* path, SkClipOp op) {
return !mCanvas->isClipEmpty();
}
bool SkiaCanvas::replaceClipRect_deprecated(float left, float top, float right, float bottom) {
SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);
// Emulated clip rects are not recorded for partial saves, since
// partial saves have been removed from the public API.
SkAndroidFrameworkUtils::ResetClip(mCanvas);
mCanvas->clipRect(rect, SkClipOp::kIntersect);
return !mCanvas->isClipEmpty();
}
bool SkiaCanvas::replaceClipPath_deprecated(const SkPath* path) {
SkAndroidFrameworkUtils::ResetClip(mCanvas);
mCanvas->clipPath(*path, SkClipOp::kIntersect, true);
return !mCanvas->isClipEmpty();
}
// ----------------------------------------------------------------------------
// Canvas state operations: Filters
// ----------------------------------------------------------------------------

View File

@@ -94,6 +94,9 @@ public:
virtual bool quickRejectPath(const SkPath& path) const override;
virtual bool clipRect(float left, float top, float right, float bottom, SkClipOp op) override;
virtual bool clipPath(const SkPath* path, SkClipOp op) override;
virtual bool replaceClipRect_deprecated(float left, float top, float right,
float bottom) override;
virtual bool replaceClipPath_deprecated(const SkPath* path) override;
virtual PaintFilter* getPaintFilter() override;
virtual void setPaintFilter(sk_sp<PaintFilter> paintFilter) override;

View File

@@ -54,13 +54,10 @@ bool CanvasStateHelper::internalSave(SaveEntry saveEntry) {
return false;
}
// Assert that the cast from SkClipOp to SkRegion::Op is valid
// Assert that the cast from SkClipOp to SkRegion::Op is valid.
// SkClipOp is a subset of SkRegion::Op and only supports difference and intersect.
static_assert(static_cast<int>(SkClipOp::kDifference) == SkRegion::Op::kDifference_Op);
static_assert(static_cast<int>(SkClipOp::kIntersect) == SkRegion::Op::kIntersect_Op);
static_assert(static_cast<int>(SkClipOp::kUnion_deprecated) == SkRegion::Op::kUnion_Op);
static_assert(static_cast<int>(SkClipOp::kXOR_deprecated) == SkRegion::Op::kXOR_Op);
static_assert(static_cast<int>(SkClipOp::kReverseDifference_deprecated) == SkRegion::Op::kReverseDifference_Op);
static_assert(static_cast<int>(SkClipOp::kReplace_deprecated) == SkRegion::Op::kReplace_Op);
void CanvasStateHelper::internalClipRect(const SkRect& rect, SkClipOp op) {
clip().opRect(rect, transform(), mInitialBounds, (SkRegion::Op)op, false);

View File

@@ -185,6 +185,12 @@ public:
virtual bool clipRect(float left, float top, float right, float bottom, SkClipOp op) = 0;
virtual bool clipPath(const SkPath* path, SkClipOp op) = 0;
// Resets clip to wide open, used to emulate the now-removed SkClipOp::kReplace on
// apps with compatibility < P. Canvases for version P and later are restricted to
// intersect and difference at the Java level, matching SkClipOp's definition.
// NOTE: These functions are deprecated and will be removed in a future release
virtual bool replaceClipRect_deprecated(float left, float top, float right, float bottom) = 0;
virtual bool replaceClipPath_deprecated(const SkPath* path) = 0;
// filters
virtual PaintFilter* getPaintFilter() = 0;

View File

@@ -188,14 +188,10 @@ static jboolean quickRejectPath(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jl
return result ? JNI_TRUE : JNI_FALSE;
}
// SkRegion::Op and SkClipOp are numerically identical, so we can freely cast
// from one to the other (though SkClipOp is destined to become a strict subset)
// SkClipOp is a strict subset of SkRegion::Op and is castable back and forth for their
// shared operations (intersect and difference).
static_assert(SkRegion::kDifference_Op == static_cast<SkRegion::Op>(SkClipOp::kDifference), "");
static_assert(SkRegion::kIntersect_Op == static_cast<SkRegion::Op>(SkClipOp::kIntersect), "");
static_assert(SkRegion::kUnion_Op == static_cast<SkRegion::Op>(SkClipOp::kUnion_deprecated), "");
static_assert(SkRegion::kXOR_Op == static_cast<SkRegion::Op>(SkClipOp::kXOR_deprecated), "");
static_assert(SkRegion::kReverseDifference_Op == static_cast<SkRegion::Op>(SkClipOp::kReverseDifference_deprecated), "");
static_assert(SkRegion::kReplace_Op == static_cast<SkRegion::Op>(SkClipOp::kReplace_deprecated), "");
static jboolean clipRect(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat l, jfloat t,
jfloat r, jfloat b, jint opHandle) {
@@ -203,16 +199,16 @@ static jboolean clipRect(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat l,
SkRegion::Op rgnOp = static_cast<SkRegion::Op>(opHandle);
bool nonEmptyClip;
switch (rgnOp) {
case SkRegion::Op::kReplace_Op:
// For now replace can still be handled as an SkClipOp but will be emulated in the
// future
[[fallthrough]];
case SkRegion::Op::kIntersect_Op:
case SkRegion::Op::kDifference_Op:
// Intersect and difference are supported clip operations
nonEmptyClip =
get_canvas(canvasHandle)->clipRect(l, t, r, b, static_cast<SkClipOp>(rgnOp));
break;
case SkRegion::Op::kReplace_Op:
// Replace is emulated to support legacy apps older than P
nonEmptyClip = get_canvas(canvasHandle)->replaceClipRect_deprecated(l, t, r, b);
break;
default:
// All other operations would expand the clip and are no longer supported,
// so log and skip (to avoid breaking legacy apps).
@@ -230,14 +226,13 @@ static jboolean clipPath(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong pat
SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
bool nonEmptyClip;
switch (rgnOp) {
case SkRegion::Op::kReplace_Op:
// For now replace can still be handled as an SkClipOp but will be emulated in the
// future
[[fallthrough]];
case SkRegion::Op::kIntersect_Op:
case SkRegion::Op::kDifference_Op:
nonEmptyClip = get_canvas(canvasHandle)->clipPath(path, static_cast<SkClipOp>(rgnOp));
break;
case SkRegion::Op::kReplace_Op:
nonEmptyClip = get_canvas(canvasHandle)->replaceClipPath_deprecated(path);
break;
default:
ALOGW("Ignoring unsupported clip operation %d", opHandle);
SkRect clipBounds; // ignored

View File

@@ -52,6 +52,8 @@ protected:
mOutput << mIdent << "clipRegion" << std::endl;
}
void onResetClip() override { mOutput << mIdent << "resetClip" << std::endl; }
void onDrawPaint(const SkPaint&) override { mOutput << mIdent << "drawPaint" << std::endl; }
void onDrawPath(const SkPath&, const SkPaint&) override {

View File

@@ -382,7 +382,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clip_replace) {
std::vector<sp<RenderNode>> nodes;
nodes.push_back(TestUtils::createSkiaNode(
20, 20, 30, 30, [](RenderProperties& props, SkiaRecordingCanvas& canvas) {
canvas.clipRect(0, -20, 10, 30, SkClipOp::kReplace_deprecated);
canvas.replaceClipRect_deprecated(0, -20, 10, 30);
canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
}));