Update hole punch logic in HWUI
--Updated HWUI holepunch logic for SurfaceView to
also apply the stretch to the hole punch
--Updated RenderNode callbacks to also include
an offset from the ancestor RenderNode that also
has a stretch configured on it
--Added new test activity to verify hole punch
logic
Bug: 179047472
Test: manual
Change-Id: Ibbaf8248a31839ba9dc352ecb9fef54e1276918e
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index f2c48bb..0212309 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -467,6 +467,7 @@
"pipeline/skia/HolePunch.cpp",
"pipeline/skia/SkiaDisplayList.cpp",
"pipeline/skia/SkiaRecordingCanvas.cpp",
+ "pipeline/skia/StretchMask.cpp",
"pipeline/skia/RenderNodeDrawable.cpp",
"pipeline/skia/ReorderBarrierDrawables.cpp",
"pipeline/skia/TransformCanvas.cpp",
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
index 0bf9480..94fe243 100644
--- a/libs/hwui/DamageAccumulator.cpp
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -197,6 +197,27 @@
}
}
+static void computeTransformImpl(const DirtyStack* frame, const DirtyStack* end,
+ Matrix4* outMatrix) {
+ while (frame != end) {
+ switch (frame->type) {
+ case TransformRenderNode:
+ frame->renderNode->applyViewPropertyTransforms(*outMatrix);
+ break;
+ case TransformMatrix4:
+ outMatrix->multiply(*frame->matrix4);
+ break;
+ case TransformNone:
+ // nothing to be done
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Tried to compute transform with an invalid type: %d",
+ frame->type);
+ }
+ frame = frame->prev;
+ }
+}
+
void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) {
if (frame->pendingDirty.isEmpty()) {
return;
@@ -249,19 +270,38 @@
mHead->pendingDirty.setEmpty();
}
-const StretchEffect* DamageAccumulator::findNearestStretchEffect() const {
+DamageAccumulator::StretchResult DamageAccumulator::findNearestStretchEffect() const {
DirtyStack* frame = mHead;
while (frame->prev != frame) {
- frame = frame->prev;
if (frame->type == TransformRenderNode) {
+ const auto& renderNode = frame->renderNode;
+ const auto& frameRenderNodeProperties = renderNode->properties();
const auto& effect =
- frame->renderNode->properties().layerProperties().getStretchEffect();
+ frameRenderNodeProperties.layerProperties().getStretchEffect();
+ const float width = (float) renderNode->getWidth();
+ const float height = (float) renderNode->getHeight();
if (!effect.isEmpty()) {
- return &effect;
+ Matrix4 stretchMatrix;
+ computeTransformImpl(mHead, frame, &stretchMatrix);
+ Rect stretchRect = Rect(0.f, 0.f, width, height);
+ stretchMatrix.mapRect(stretchRect);
+
+ return StretchResult{
+ .stretchEffect = &effect,
+ .childRelativeBounds = SkRect::MakeLTRB(
+ stretchRect.left,
+ stretchRect.top,
+ stretchRect.right,
+ stretchRect.bottom
+ ),
+ .width = width,
+ .height = height
+ };
}
}
+ frame = frame->prev;
}
- return nullptr;
+ return StretchResult{};
}
} /* namespace uirenderer */
diff --git a/libs/hwui/DamageAccumulator.h b/libs/hwui/DamageAccumulator.h
index 89ee0e3..90a3517 100644
--- a/libs/hwui/DamageAccumulator.h
+++ b/libs/hwui/DamageAccumulator.h
@@ -21,6 +21,7 @@
#include <SkMatrix.h>
#include <SkRect.h>
+#include <effects/StretchEffect.h>
#include "utils/Macros.h"
@@ -35,7 +36,6 @@
struct DirtyStack;
class RenderNode;
class Matrix4;
-class StretchEffect;
class DamageAccumulator {
PREVENT_COPY_AND_ASSIGN(DamageAccumulator);
@@ -63,7 +63,29 @@
void finish(SkRect* totalDirty);
- const StretchEffect* findNearestStretchEffect() const;
+ struct StretchResult {
+ /**
+ * Stretch parameters configured on the stretch container
+ */
+ const StretchEffect* stretchEffect;
+
+ /**
+ * Bounds of the child relative to the stretch container
+ */
+ const SkRect childRelativeBounds;
+
+ /**
+ * Width of the stretch container
+ */
+ const float width;
+
+ /**
+ * Height of the stretch container
+ */
+ const float height;
+ };
+
+ [[nodiscard]] StretchResult findNearestStretchEffect() const;
private:
void pushCommon();
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index e9eae3d..fce2e1f 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -194,6 +194,9 @@
SkRect dirty;
info.damageAccumulator->peekAtDirty(&dirty);
info.layerUpdateQueue->enqueueLayerWithDamage(this, dirty);
+ if (!dirty.isEmpty()) {
+ mStretchMask.markDirty();
+ }
// There might be prefetched layers that need to be accounted for.
// That might be us, so tell CanvasContext that this layer is in the
@@ -302,6 +305,12 @@
damageSelf(info);
info.damageAccumulator->popTransform();
syncProperties();
+
+ const StretchEffect& stagingStretch =
+ mProperties.layerProperties().getStretchEffect();
+ if (stagingStretch.isEmpty()) {
+ mStretchMask.clear();
+ }
// We could try to be clever and only re-damage if the matrix changed.
// However, we don't need to worry about that. The cost of over-damaging
// here is only going to be a single additional map rect of this node
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 988141f..6a0b1aa 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -40,6 +40,7 @@
#include "pipeline/skia/SkiaLayer.h"
#include <vector>
+#include <pipeline/skia/StretchMask.h>
class SkBitmap;
class SkPaint;
@@ -127,6 +128,8 @@
}
}
+ StretchMask& getStretchMask() { return mStretchMask; }
+
VirtualLightRefBase* getUserContext() const { return mUserContext.get(); }
void setUserContext(VirtualLightRefBase* context) { mUserContext = context; }
@@ -286,6 +289,7 @@
UsageHint mUsageHint = UsageHint::Unknown;
bool mHasHolePunches;
+ StretchMask mStretchMask;
// METHODS & FIELDS ONLY USED BY THE SKIA RENDERER
public:
diff --git a/libs/hwui/effects/StretchEffect.cpp b/libs/hwui/effects/StretchEffect.cpp
index 6eb6e1e..1519d69 100644
--- a/libs/hwui/effects/StretchEffect.cpp
+++ b/libs/hwui/effects/StretchEffect.cpp
@@ -189,17 +189,12 @@
static const float CONTENT_DISTANCE_STRETCHED = 1.f;
static const float INTERPOLATION_STRENGTH_VALUE = 0.7f;
-sk_sp<SkShader> StretchEffect::getShader(const sk_sp<SkImage>& snapshotImage) const {
+sk_sp<SkShader> StretchEffect::getShader(float width, float height,
+ const sk_sp<SkImage>& snapshotImage) const {
if (isEmpty()) {
return nullptr;
}
- if (mStretchShader != nullptr) {
- return mStretchShader;
- }
-
- float viewportWidth = stretchArea.width();
- float viewportHeight = stretchArea.height();
float normOverScrollDistX = mStretchDirection.x();
float normOverScrollDistY = mStretchDirection.y();
float distanceStretchedX = CONTENT_DISTANCE_STRETCHED / (1 + abs(normOverScrollDistX));
@@ -228,12 +223,10 @@
mBuilder->uniform("uOverscrollY").set(&normOverScrollDistY, 1);
mBuilder->uniform("uScrollX").set(&ZERO, 1);
mBuilder->uniform("uScrollY").set(&ZERO, 1);
- mBuilder->uniform("viewportWidth").set(&viewportWidth, 1);
- mBuilder->uniform("viewportHeight").set(&viewportHeight, 1);
+ mBuilder->uniform("viewportWidth").set(&width, 1);
+ mBuilder->uniform("viewportHeight").set(&height, 1);
- mStretchShader = mBuilder->makeShader(nullptr, false);
-
- return mStretchShader;
+ return mBuilder->makeShader(nullptr, false);
}
sk_sp<SkRuntimeEffect> StretchEffect::getStretchEffect() {
diff --git a/libs/hwui/effects/StretchEffect.h b/libs/hwui/effects/StretchEffect.h
index 546d53b..61537f0 100644
--- a/libs/hwui/effects/StretchEffect.h
+++ b/libs/hwui/effects/StretchEffect.h
@@ -26,19 +26,15 @@
namespace android::uirenderer {
-// TODO: Inherit from base RenderEffect type?
class StretchEffect {
public:
- enum class StretchInterpolator {
- SmoothStep,
- };
- StretchEffect(const SkRect& area, const SkVector& direction, float maxStretchAmountX,
+ StretchEffect(const SkVector& direction,
+ float maxStretchAmountX,
float maxStretchAmountY)
- : stretchArea(area)
- , maxStretchAmountX(maxStretchAmountX)
+ : maxStretchAmountX(maxStretchAmountX)
, maxStretchAmountY(maxStretchAmountY)
- , mStretchDirection(direction) {}
+ , mStretchDirection(direction) { }
StretchEffect() {}
@@ -51,14 +47,18 @@
}
StretchEffect& operator=(const StretchEffect& other) {
- this->stretchArea = other.stretchArea;
this->mStretchDirection = other.mStretchDirection;
- this->mStretchShader = other.mStretchShader;
this->maxStretchAmountX = other.maxStretchAmountX;
this->maxStretchAmountY = other.maxStretchAmountY;
return *this;
}
+ bool operator==(const StretchEffect& other) const {
+ return mStretchDirection == other.mStretchDirection &&
+ maxStretchAmountX == other.maxStretchAmountX &&
+ maxStretchAmountY == other.maxStretchAmountY;
+ }
+
void mergeWith(const StretchEffect& other) {
if (other.isEmpty()) {
return;
@@ -67,33 +67,26 @@
*this = other;
return;
}
- setStretchDirection(mStretchDirection + other.mStretchDirection);
+ mStretchDirection += other.mStretchDirection;
if (isEmpty()) {
return setEmpty();
}
- stretchArea.join(other.stretchArea);
maxStretchAmountX = std::max(maxStretchAmountX, other.maxStretchAmountX);
maxStretchAmountY = std::max(maxStretchAmountY, other.maxStretchAmountY);
}
- sk_sp<SkShader> getShader(const sk_sp<SkImage>& snapshotImage) const;
+ sk_sp<SkShader> getShader(float width, float height,
+ const sk_sp<SkImage>& snapshotImage) const;
- SkRect stretchArea {0, 0, 0, 0};
float maxStretchAmountX = 0;
float maxStretchAmountY = 0;
- void setStretchDirection(const SkVector& direction) {
- mStretchShader = nullptr;
- mStretchDirection = direction;
- }
-
const SkVector getStretchDirection() const { return mStretchDirection; }
private:
static sk_sp<SkRuntimeEffect> getStretchEffect();
mutable SkVector mStretchDirection{0, 0};
mutable std::unique_ptr<SkRuntimeShaderBuilder> mBuilder;
- mutable sk_sp<SkShader> mStretchShader;
};
} // namespace android::uirenderer
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
index fffa806..5131c64 100644
--- a/libs/hwui/jni/android_graphics_RenderNode.cpp
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -180,12 +180,10 @@
}
static jboolean android_view_RenderNode_stretch(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
- jfloat left, jfloat top, jfloat right,
- jfloat bottom, jfloat vX, jfloat vY, jfloat maxX,
+ jfloat vX, jfloat vY, jfloat maxX,
jfloat maxY) {
- StretchEffect effect = StretchEffect(SkRect::MakeLTRB(left, top, right, bottom),
- {.fX = vX, .fY = vY}, maxX, maxY);
- RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+ auto* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
+ StretchEffect effect = StretchEffect({.fX = vX, .fY = vY}, maxX, maxY);
renderNode->mutateStagingProperties().mutateLayerProperties().mutableStretchEffect().mergeWith(
effect);
renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
@@ -643,13 +641,15 @@
void handleStretchEffect(const TreeInfo& info, const Matrix4& transform) {
// Search up to find the nearest stretcheffect parent
- const StretchEffect* effect = info.damageAccumulator->findNearestStretchEffect();
+ const DamageAccumulator::StretchResult result =
+ info.damageAccumulator->findNearestStretchEffect();
+ const StretchEffect* effect = result.stretchEffect;
if (!effect) {
return;
}
- uirenderer::Rect area = effect->stretchArea;
- transform.mapRect(area);
+ const auto& childRelativeBounds = result.childRelativeBounds;
+
JNIEnv* env = jnienv();
jobject localref = env->NewLocalRef(mWeakRef);
@@ -661,9 +661,17 @@
#ifdef __ANDROID__ // Layoutlib does not support CanvasContext
SkVector stretchDirection = effect->getStretchDirection();
env->CallVoidMethod(localref, gPositionListener_ApplyStretchMethod,
- info.canvasContext.getFrameNumber(), area.left, area.top,
- area.right, area.bottom, stretchDirection.fX, stretchDirection.fY,
- effect->maxStretchAmountX, effect->maxStretchAmountY);
+ info.canvasContext.getFrameNumber(),
+ result.width,
+ result.height,
+ stretchDirection.fX,
+ stretchDirection.fY,
+ effect->maxStretchAmountX,
+ effect->maxStretchAmountY,
+ childRelativeBounds.left(),
+ childRelativeBounds.top(),
+ childRelativeBounds.right(),
+ childRelativeBounds.bottom());
#endif
env->DeleteLocalRef(localref);
}
@@ -739,7 +747,7 @@
{"nSetOutlineEmpty", "(J)Z", (void*)android_view_RenderNode_setOutlineEmpty},
{"nSetOutlineNone", "(J)Z", (void*)android_view_RenderNode_setOutlineNone},
{"nClearStretch", "(J)Z", (void*)android_view_RenderNode_clearStretch},
- {"nStretch", "(JFFFFFFFF)Z", (void*)android_view_RenderNode_stretch},
+ {"nStretch", "(JFFFF)Z", (void*)android_view_RenderNode_stretch},
{"nHasShadow", "(J)Z", (void*)android_view_RenderNode_hasShadow},
{"nSetSpotShadowColor", "(JI)Z", (void*)android_view_RenderNode_setSpotShadowColor},
{"nGetSpotShadowColor", "(J)I", (void*)android_view_RenderNode_getSpotShadowColor},
@@ -814,7 +822,7 @@
gPositionListener_PositionChangedMethod = GetMethodIDOrDie(env, clazz,
"positionChanged", "(JIIII)V");
gPositionListener_ApplyStretchMethod =
- GetMethodIDOrDie(env, clazz, "applyStretch", "(JFFFFFFF)V");
+ GetMethodIDOrDie(env, clazz, "applyStretch", "(JFFFFFFFFFF)V");
gPositionListener_PositionLostMethod = GetMethodIDOrDie(env, clazz,
"positionLost", "(J)V");
return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 77d99a6..1ae06d0 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -16,6 +16,7 @@
#include "RenderNodeDrawable.h"
#include <SkPaintFilterCanvas.h>
+#include "StretchMask.h"
#include "RenderNode.h"
#include "SkiaDisplayList.h"
#include "TransformCanvas.h"
@@ -245,17 +246,37 @@
"SurfaceID|%" PRId64, renderNode->uniqueId()).c_str(), nullptr);
}
- if (renderNode->hasHolePunches()) {
- TransformCanvas transformCanvas(canvas);
- displayList->draw(&transformCanvas);
- }
-
const StretchEffect& stretch = properties.layerProperties().getStretchEffect();
if (stretch.isEmpty()) {
+ // If we don't have any stretch effects, issue the filtered
+ // canvas draw calls to make sure we still punch a hole
+ // with the same canvas transformation + clip into the target
+ // canvas then draw the layer on top
+ if (renderNode->hasHolePunches()) {
+ TransformCanvas transformCanvas(canvas, SkBlendMode::kClear);
+ displayList->draw(&transformCanvas);
+ }
canvas->drawImageRect(snapshotImage, bounds, bounds, sampling, &paint,
SkCanvas::kStrict_SrcRectConstraint);
} else {
- sk_sp<SkShader> stretchShader = stretch.getShader(snapshotImage);
+ // If we do have stretch effects and have hole punches,
+ // then create a mask and issue the filtered draw calls to
+ // get the corresponding hole punches.
+ // Then apply the stretch to the mask and draw the mask to
+ // the destination
+ if (renderNode->hasHolePunches()) {
+ GrRecordingContext* context = canvas->recordingContext();
+ StretchMask& stretchMask = renderNode->getStretchMask();
+ stretchMask.draw(context,
+ stretch,
+ bounds,
+ displayList,
+ canvas);
+ }
+
+ sk_sp<SkShader> stretchShader = stretch.getShader(bounds.width(),
+ bounds.height(),
+ snapshotImage);
paint.setShader(stretchShader);
canvas->drawRect(bounds, paint);
}
diff --git a/libs/hwui/pipeline/skia/StretchMask.cpp b/libs/hwui/pipeline/skia/StretchMask.cpp
new file mode 100644
index 0000000..2bbd8a4
--- /dev/null
+++ b/libs/hwui/pipeline/skia/StretchMask.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "StretchMask.h"
+#include "SkSurface.h"
+#include "SkCanvas.h"
+#include "TransformCanvas.h"
+#include "SkiaDisplayList.h"
+
+using android::uirenderer::StretchMask;
+
+void StretchMask::draw(GrRecordingContext* context,
+ const StretchEffect& stretch,
+ const SkRect& bounds,
+ skiapipeline::SkiaDisplayList* displayList,
+ SkCanvas* canvas) {
+ float width = bounds.width();
+ float height = bounds.height();
+ if (mMaskSurface == nullptr || mMaskSurface->width() != width ||
+ mMaskSurface->height() != height) {
+ // Create a new surface if we don't have one or our existing size does
+ // not match.
+ mMaskSurface = SkSurface::MakeRenderTarget(
+ context,
+ SkBudgeted::kYes,
+ SkImageInfo::Make(
+ width,
+ height,
+ SkColorType::kAlpha_8_SkColorType,
+ SkAlphaType::kPremul_SkAlphaType)
+ );
+ mIsDirty = true;
+ }
+
+ if (mIsDirty) {
+ SkCanvas* maskCanvas = mMaskSurface->getCanvas();
+ maskCanvas->drawColor(0, SkBlendMode::kClear);
+ TransformCanvas transformCanvas(maskCanvas, SkBlendMode::kSrcOver);
+ displayList->draw(&transformCanvas);
+ }
+
+ sk_sp<SkImage> maskImage = mMaskSurface->makeImageSnapshot();
+ sk_sp<SkShader> maskStretchShader = stretch.getShader(
+ width, height, maskImage);
+
+ SkPaint maskPaint;
+ maskPaint.setShader(maskStretchShader);
+ maskPaint.setBlendMode(SkBlendMode::kDstOut);
+ canvas->drawRect(bounds, maskPaint);
+
+ mIsDirty = false;
+}
\ No newline at end of file
diff --git a/libs/hwui/pipeline/skia/StretchMask.h b/libs/hwui/pipeline/skia/StretchMask.h
new file mode 100644
index 0000000..dc698b8
--- /dev/null
+++ b/libs/hwui/pipeline/skia/StretchMask.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "GrRecordingContext.h"
+#include <effects/StretchEffect.h>
+#include <SkSurface.h>
+#include "SkiaDisplayList.h"
+
+namespace android::uirenderer {
+
+/**
+ * Helper class used to create/cache an SkSurface instance
+ * to create a mask that is used to draw a stretched hole punch
+ */
+class StretchMask {
+ public:
+ /**
+ * Release the current surface used for the stretch mask
+ */
+ void clear() {
+ mMaskSurface = nullptr;
+ }
+
+ /**
+ * Reset the dirty flag to re-create the stretch mask on the next draw
+ * pass
+ */
+ void markDirty() {
+ mIsDirty = true;
+ }
+
+ /**
+ * Draws the stretch mask into the given target canvas
+ * @param context GrRecordingContext used to create the surface if necessary
+ * @param stretch StretchEffect to apply to the mask
+ * @param bounds Target bounds to draw into the given canvas
+ * @param displayList List of drawing commands to render into the stretch mask
+ * @param canvas Target canvas to draw the mask into
+ */
+ void draw(GrRecordingContext* context,
+ const StretchEffect& stretch, const SkRect& bounds,
+ skiapipeline::SkiaDisplayList* displayList, SkCanvas* canvas);
+private:
+ sk_sp<SkSurface> mMaskSurface;
+ bool mIsDirty = true;
+};
+
+}
diff --git a/libs/hwui/pipeline/skia/TransformCanvas.cpp b/libs/hwui/pipeline/skia/TransformCanvas.cpp
index a6e4c4c..6777c00 100644
--- a/libs/hwui/pipeline/skia/TransformCanvas.cpp
+++ b/libs/hwui/pipeline/skia/TransformCanvas.cpp
@@ -28,8 +28,8 @@
SkRRect roundRect = SkRRect::MakeRectXY(rect, radiusX, radiusY);
SkPaint paint;
- paint.setColor(0);
- paint.setBlendMode(SkBlendMode::kClear);
+ paint.setColor(SkColors::kBlack);
+ paint.setBlendMode(mHolePunchBlendMode);
mWrappedCanvas->drawRRect(roundRect, paint);
}
}
diff --git a/libs/hwui/pipeline/skia/TransformCanvas.h b/libs/hwui/pipeline/skia/TransformCanvas.h
index 47f77f1..685b71d 100644
--- a/libs/hwui/pipeline/skia/TransformCanvas.h
+++ b/libs/hwui/pipeline/skia/TransformCanvas.h
@@ -17,10 +17,12 @@
#include <include/core/SkCanvas.h>
#include "SkPaintFilterCanvas.h"
+#include <effects/StretchEffect.h>
class TransformCanvas : public SkPaintFilterCanvas {
public:
- TransformCanvas(SkCanvas* target) : SkPaintFilterCanvas(target), mWrappedCanvas(target) {}
+ TransformCanvas(SkCanvas* target, SkBlendMode blendmode) :
+ SkPaintFilterCanvas(target), mWrappedCanvas(target), mHolePunchBlendMode(blendmode) {}
protected:
bool onFilter(SkPaint& paint) const override;
@@ -32,4 +34,5 @@
private:
// We don't own the canvas so just maintain a raw pointer to it
SkCanvas* mWrappedCanvas;
+ const SkBlendMode mHolePunchBlendMode;
};