Added RenderEffect property on RenderNode
Introduced RenderEffect API to handle consuming
SkImageFilter parameters on RenderNode objects
in order to support blur.
Updated SilkFX demo to use RenderEffect
APIs on RenderNode instead of BlurShader
Bug: 168549524
Test: Added tests to RenderNode CTS test cases
Change-Id: I5005a322a6d75438dd104e6915630264406cf771
diff --git a/graphics/java/android/graphics/RenderEffect.java b/graphics/java/android/graphics/RenderEffect.java
new file mode 100644
index 0000000..9fc0c8e
--- /dev/null
+++ b/graphics/java/android/graphics/RenderEffect.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+package android.graphics;
+
+import android.annotation.NonNull;
+import android.graphics.Shader.TileMode;
+
+import libcore.util.NativeAllocationRegistry;
+
+/**
+ * Intermediate rendering step used to render drawing commands with a corresponding
+ * visual effect
+ *
+ * @hide
+ */
+public final class RenderEffect {
+
+ private static class RenderEffectHolder {
+ public static final NativeAllocationRegistry RENDER_EFFECT_REGISTRY =
+ NativeAllocationRegistry.createMalloced(
+ RenderEffect.class.getClassLoader(), nativeGetFinalizer());
+ }
+
+ /**
+ * Create a {@link RenderEffect} instance that will offset the drawing content
+ * by the provided x and y offset.
+ * @param offsetX offset along the x axis in pixels
+ * @param offsetY offset along the y axis in pixels
+ */
+ @NonNull
+ public static RenderEffect createOffsetEffect(float offsetX, float offsetY) {
+ return new RenderEffect(nativeCreateOffsetEffect(offsetX, offsetY, 0));
+ }
+
+ /**
+ * Create a {@link RenderEffect} instance with the provided x and y offset
+ * @param offsetX offset along the x axis in pixels
+ * @param offsetY offset along the y axis in pixels
+ * @param input target RenderEffect used to render in the offset coordinates.
+ */
+ @NonNull
+ public static RenderEffect createOffsetEffect(
+ float offsetX,
+ float offsetY,
+ @NonNull RenderEffect input
+ ) {
+ return new RenderEffect(nativeCreateOffsetEffect(
+ offsetX,
+ offsetY,
+ input.getNativeInstance()
+ )
+ );
+ }
+
+ /**
+ * Create a {@link RenderEffect} that blurs the contents of the optional input RenderEffect
+ * with the specified radius along the x and y axis. If no input RenderEffect is provided
+ * then all drawing commands issued with a {@link android.graphics.RenderNode} that this
+ * RenderEffect is installed in will be blurred
+ * @param radiusX Radius of blur along the X axis
+ * @param radiusY Radius of blur along the Y axis
+ * @param inputEffect Input RenderEffect that provides the content to be blurred, can be null
+ * to indicate that the drawing commands on the RenderNode are to be
+ * blurred instead of the input RenderEffect
+ * @param edgeTreatment Policy for how to blur content near edges of the blur kernel
+ */
+ @NonNull
+ public static RenderEffect createBlurEffect(
+ float radiusX,
+ float radiusY,
+ @NonNull RenderEffect inputEffect,
+ @NonNull TileMode edgeTreatment
+ ) {
+ long nativeInputEffect = inputEffect != null ? inputEffect.mNativeRenderEffect : 0;
+ return new RenderEffect(
+ nativeCreateBlurEffect(
+ radiusX,
+ radiusY,
+ nativeInputEffect,
+ edgeTreatment.nativeInt
+ )
+ );
+ }
+
+ /**
+ * Create a {@link RenderEffect} that blurs the contents of the
+ * {@link android.graphics.RenderNode} that this RenderEffect is installed on with the
+ * specified radius along hte x and y axis.
+ * @param radiusX Radius of blur along the X axis
+ * @param radiusY Radius of blur along the Y axis
+ * @param edgeTreatment Policy for how to blur content near edges of the blur kernel
+ */
+ @NonNull
+ public static RenderEffect createBlurEffect(
+ float radiusX,
+ float radiusY,
+ @NonNull TileMode edgeTreatment
+ ) {
+ return new RenderEffect(
+ nativeCreateBlurEffect(
+ radiusX,
+ radiusY,
+ 0,
+ edgeTreatment.nativeInt
+ )
+ );
+ }
+
+ private final long mNativeRenderEffect;
+
+ /* only constructed from static factory methods */
+ private RenderEffect(long nativeRenderEffect) {
+ mNativeRenderEffect = nativeRenderEffect;
+ RenderEffectHolder.RENDER_EFFECT_REGISTRY.registerNativeAllocation(
+ this, mNativeRenderEffect);
+ }
+
+ /**
+ * Obtain the pointer to the underlying RenderEffect to be configured
+ * on a RenderNode object via {@link RenderNode#setRenderEffect(RenderEffect)}
+ */
+ /* package */ long getNativeInstance() {
+ return mNativeRenderEffect;
+ }
+
+ private static native long nativeCreateOffsetEffect(
+ float offsetX, float offsetY, long nativeInput);
+ private static native long nativeCreateBlurEffect(
+ float radiusX, float radiusY, long nativeInput, int edgeTreatment);
+ private static native long nativeGetFinalizer();
+}
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 8aacbc7..d812c1a 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -850,6 +850,23 @@
}
/**
+ * Configure the {@link android.graphics.RenderEffect} to apply to this RenderNode. This
+ * will apply a visual effect to the end result of the contents of this RenderNode before
+ * it is drawn into the destination. For example if
+ * {@link RenderEffect#createBlurEffect(float, float, RenderEffect, Shader.TileMode)}
+ * is provided, the contents will be drawn in a separate layer, then this layer will
+ * be blurred when this RenderNode is drawn into the destination.
+ * @param renderEffect to be applied to the RenderNode. Passing null clears all previously
+ * configured RenderEffects
+ *
+ * @hide
+ */
+ public void setRenderEffect(@Nullable RenderEffect renderEffect) {
+ nSetRenderEffect(mNativeRenderNode,
+ renderEffect != null ? renderEffect.getNativeInstance() : 0);
+ }
+
+ /**
* Returns the translucency level of this display list.
*
* @return A value between 0.0f and 1.0f
@@ -1655,6 +1672,9 @@
private static native boolean nSetAlpha(long renderNode, float alpha);
@CriticalNative
+ private static native void nSetRenderEffect(long renderNode, long renderEffect);
+
+ @CriticalNative
private static native boolean nSetHasOverlappingRendering(long renderNode,
boolean hasOverlappingRendering);
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 90d2537..9f4c9a1 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -327,6 +327,7 @@
"jni/PathMeasure.cpp",
"jni/Picture.cpp",
"jni/Shader.cpp",
+ "jni/RenderEffect.cpp",
"jni/Typeface.cpp",
"jni/Utils.cpp",
"jni/YuvToJpegEncoder.cpp",
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index ff9cf45..8fba9cf 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -49,6 +49,12 @@
return true;
}
+bool LayerProperties::setImageFilter(SkImageFilter* imageFilter) {
+ if(mImageFilter.get() == imageFilter) return false;
+ mImageFilter = sk_ref_sp(imageFilter);
+ return true;
+}
+
bool LayerProperties::setFromPaint(const SkPaint* paint) {
bool changed = false;
changed |= setAlpha(static_cast<uint8_t>(PaintUtils::getAlphaDirect(paint)));
@@ -63,6 +69,7 @@
setAlpha(other.alpha());
setXferMode(other.xferMode());
setColorFilter(other.getColorFilter());
+ setImageFilter(other.getImageFilter());
return *this;
}
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index ef4cd1f..aeb60e6 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -27,6 +27,7 @@
#include "utils/PaintUtils.h"
#include <SkBlendMode.h>
+#include <SkImageFilter.h>
#include <SkCamera.h>
#include <SkColor.h>
#include <SkMatrix.h>
@@ -93,6 +94,10 @@
SkColorFilter* getColorFilter() const { return mColorFilter.get(); }
+ bool setImageFilter(SkImageFilter* imageFilter);
+
+ SkImageFilter* getImageFilter() const { return mImageFilter.get(); }
+
// Sets alpha, xfermode, and colorfilter from an SkPaint
// paint may be NULL, in which case defaults will be set
bool setFromPaint(const SkPaint* paint);
@@ -118,6 +123,7 @@
uint8_t mAlpha;
SkBlendMode mMode;
sk_sp<SkColorFilter> mColorFilter;
+ sk_sp<SkImageFilter> mImageFilter;
};
/*
@@ -541,6 +547,7 @@
bool promotedToLayer() const {
return mLayerProperties.mType == LayerType::None && fitsOnLayer() &&
(mComputedFields.mNeedLayerForFunctors ||
+ mLayerProperties.mImageFilter != nullptr ||
(!MathUtils::isZero(mPrimitiveFields.mAlpha) && mPrimitiveFields.mAlpha < 1 &&
mPrimitiveFields.mHasOverlappingRendering));
}
diff --git a/libs/hwui/apex/LayoutlibLoader.cpp b/libs/hwui/apex/LayoutlibLoader.cpp
index 4bbf121..dca10e2 100644
--- a/libs/hwui/apex/LayoutlibLoader.cpp
+++ b/libs/hwui/apex/LayoutlibLoader.cpp
@@ -47,6 +47,7 @@
extern int register_android_graphics_NinePatch(JNIEnv*);
extern int register_android_graphics_PathEffect(JNIEnv* env);
extern int register_android_graphics_Shader(JNIEnv* env);
+extern int register_android_graphics_RenderEffect(JNIEnv* env);
extern int register_android_graphics_Typeface(JNIEnv* env);
namespace android {
@@ -108,6 +109,7 @@
{"android.graphics.RecordingCanvas", REG_JNI(register_android_view_DisplayListCanvas)},
// {"android.graphics.Region", REG_JNI(register_android_graphics_Region)},
{"android.graphics.Shader", REG_JNI(register_android_graphics_Shader)},
+ {"android.graphics.RenderEffect", REG_JNI(register_android_graphics_RenderEffect)},
{"android.graphics.Typeface", REG_JNI(register_android_graphics_Typeface)},
{"android.graphics.animation.NativeInterpolatorFactory",
REG_JNI(register_android_graphics_animation_NativeInterpolatorFactory)},
diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp
index 12e2e81..deccc80 100644
--- a/libs/hwui/apex/jni_runtime.cpp
+++ b/libs/hwui/apex/jni_runtime.cpp
@@ -43,6 +43,7 @@
extern int register_android_graphics_NinePatch(JNIEnv*);
extern int register_android_graphics_PathEffect(JNIEnv* env);
extern int register_android_graphics_Shader(JNIEnv* env);
+extern int register_android_graphics_RenderEffect(JNIEnv* env);
extern int register_android_graphics_Typeface(JNIEnv* env);
extern int register_android_graphics_YuvImage(JNIEnv* env);
@@ -123,6 +124,7 @@
REG_JNI(register_android_graphics_Picture),
REG_JNI(register_android_graphics_Region),
REG_JNI(register_android_graphics_Shader),
+ REG_JNI(register_android_graphics_RenderEffect),
REG_JNI(register_android_graphics_TextureLayer),
REG_JNI(register_android_graphics_Typeface),
REG_JNI(register_android_graphics_YuvImage),
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index 0bb689c..4e2016a 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -157,7 +157,6 @@
private:
using SkPaint::setShader;
- using SkPaint::setImageFilter;
SkFont mFont;
sk_sp<SkDrawLooper> mLooper;
diff --git a/libs/hwui/jni/RenderEffect.cpp b/libs/hwui/jni/RenderEffect.cpp
new file mode 100644
index 0000000..0ebd0ca
--- /dev/null
+++ b/libs/hwui/jni/RenderEffect.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 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 "Bitmap.h"
+#include "GraphicsJNI.h"
+#include "SkImageFilter.h"
+#include "SkImageFilters.h"
+#include "graphics_jni_helpers.h"
+#include "utils/Blur.h"
+#include <utils/Log.h>
+
+using namespace android::uirenderer;
+
+static jlong createOffsetEffect(
+ JNIEnv* env,
+ jobject,
+ jfloat offsetX,
+ jfloat offsetY,
+ jlong inputFilterHandle
+) {
+ auto* inputFilter = reinterpret_cast<const SkImageFilter*>(inputFilterHandle);
+ sk_sp<SkImageFilter> offset = SkImageFilters::Offset(offsetX, offsetY, sk_ref_sp(inputFilter));
+ return reinterpret_cast<jlong>(offset.release());
+}
+
+static jlong createBlurEffect(JNIEnv* env , jobject, jfloat radiusX,
+ jfloat radiusY, jlong inputFilterHandle, jint edgeTreatment) {
+ auto* inputImageFilter = reinterpret_cast<SkImageFilter*>(inputFilterHandle);
+ sk_sp<SkImageFilter> blurFilter =
+ SkImageFilters::Blur(
+ Blur::convertRadiusToSigma(radiusX),
+ Blur::convertRadiusToSigma(radiusY),
+ static_cast<SkTileMode>(edgeTreatment),
+ sk_ref_sp(inputImageFilter),
+ nullptr);
+ return reinterpret_cast<jlong>(blurFilter.release());
+}
+
+static void RenderEffect_safeUnref(SkImageFilter* filter) {
+ SkSafeUnref(filter);
+}
+
+static jlong getRenderEffectFinalizer(JNIEnv*, jobject) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&RenderEffect_safeUnref));
+}
+
+static const JNINativeMethod gRenderEffectMethods[] = {
+ {"nativeGetFinalizer", "()J", (void*)getRenderEffectFinalizer},
+ {"nativeCreateOffsetEffect", "(FFJ)J", (void*)createOffsetEffect},
+ {"nativeCreateBlurEffect", "(FFJI)J", (void*)createBlurEffect}
+};
+
+int register_android_graphics_RenderEffect(JNIEnv* env) {
+ android::RegisterMethodsOrDie(env, "android/graphics/RenderEffect",
+ gRenderEffectMethods, NELEM(gRenderEffectMethods));
+ return 0;
+}
\ No newline at end of file
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
index 85c802b..4b4aa92 100644
--- a/libs/hwui/jni/android_graphics_RenderNode.cpp
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -215,6 +215,12 @@
return SET_AND_DIRTY(setAlpha, alpha, RenderNode::ALPHA);
}
+static jboolean android_view_RenderNode_setRenderEffect(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
+ jlong renderEffectPtr) {
+ SkImageFilter* imageFilter = reinterpret_cast<SkImageFilter*>(renderEffectPtr);
+ return SET_AND_DIRTY(mutateLayerProperties().setImageFilter, imageFilter, RenderNode::GENERIC);
+}
+
static jboolean android_view_RenderNode_setHasOverlappingRendering(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
bool hasOverlappingRendering) {
return SET_AND_DIRTY(setHasOverlappingRendering, hasOverlappingRendering,
@@ -690,6 +696,7 @@
{ "nSetRevealClip", "(JZFFF)Z", (void*) android_view_RenderNode_setRevealClip },
{ "nSetAlpha", "(JF)Z", (void*) android_view_RenderNode_setAlpha },
+ { "nSetRenderEffect", "(JJ)V", (void*) android_view_RenderNode_setRenderEffect },
{ "nSetHasOverlappingRendering", "(JZ)Z",
(void*) android_view_RenderNode_setHasOverlappingRendering },
{ "nSetUsageHint", "(JI)V", (void*) android_view_RenderNode_setUsageHint },
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 00ceb2d..1473b3e 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -172,10 +172,12 @@
SkPaint* paint) {
paint->setFilterQuality(kLow_SkFilterQuality);
if (alphaMultiplier < 1.0f || properties.alpha() < 255 ||
- properties.xferMode() != SkBlendMode::kSrcOver || properties.getColorFilter() != nullptr) {
+ properties.xferMode() != SkBlendMode::kSrcOver || properties.getColorFilter() != nullptr ||
+ properties.getImageFilter() != nullptr) {
paint->setAlpha(properties.alpha() * alphaMultiplier);
paint->setBlendMode(properties.xferMode());
paint->setColorFilter(sk_ref_sp(properties.getColorFilter()));
+ paint->setImageFilter(sk_ref_sp(properties.getImageFilter()));
return true;
}
return false;
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 05a59ef..9a2def9 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -745,7 +745,7 @@
</activity>
<activity android:name="BlurActivity"
- android:label="Shaders/Blur"
+ android:label="RenderEffect/Blur"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
diff --git a/tests/HwAccelerationTest/res/layout/image_filter_activity.xml b/tests/HwAccelerationTest/res/layout/image_filter_activity.xml
new file mode 100644
index 0000000..a0ee67a
--- /dev/null
+++ b/tests/HwAccelerationTest/res/layout/image_filter_activity.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 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.
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center">
+
+ <ImageView
+ android:id="@+id/image_filter_test_view"
+ android:background="#FF0000"
+ android:layout_width="200dp"
+ android:layout_height="200dp" />
+</FrameLayout>
\ No newline at end of file
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/BlurActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/BlurActivity.java
index 033fb0e..e4ca788 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/BlurActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/BlurActivity.java
@@ -18,11 +18,12 @@
import android.app.Activity;
import android.content.Context;
-import android.graphics.BlurShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
+import android.graphics.RenderEffect;
+import android.graphics.RenderNode;
import android.graphics.Shader;
import android.os.Bundle;
import android.view.Gravity;
@@ -51,16 +52,27 @@
}
public static class BlurGradientView extends View {
- private BlurShader mBlurShader = null;
- private Paint mPaint;
+ private final float mBlurRadius = 25f;
+ private final Paint mPaint;
+ private final RenderNode mRenderNode;
public BlurGradientView(Context c) {
super(c);
+ mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mRenderNode = new RenderNode("BlurGradientView");
+ mRenderNode.setRenderEffect(
+ RenderEffect.createBlurEffect(
+ mBlurRadius,
+ mBlurRadius,
+ null,
+ Shader.TileMode.DECAL
+ )
+ );
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- if (changed || mBlurShader == null) {
+ if (changed) {
LinearGradient gradient = new LinearGradient(
0f,
0f,
@@ -70,41 +82,81 @@
Color.YELLOW,
Shader.TileMode.CLAMP
);
- mBlurShader = new BlurShader(30f, 40f, gradient);
- mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mPaint.setShader(mBlurShader);
+
+ mPaint.setShader(gradient);
+
+ final int width = right - left;
+ final int height = bottom - top;
+ mRenderNode.setPosition(0, 0, width, height);
+
+ Canvas canvas = mRenderNode.beginRecording();
+ canvas.drawRect(
+ mBlurRadius * 2,
+ mBlurRadius * 2,
+ width - mBlurRadius * 2,
+ height - mBlurRadius * 2,
+ mPaint
+ );
+ mRenderNode.endRecording();
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
- canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
+ canvas.drawRenderNode(mRenderNode);
}
}
public static class BlurView extends View {
- private final BlurShader mBlurShader;
private final Paint mPaint;
+ private final RenderNode mRenderNode;
+ private final float mBlurRadius = 20f;
public BlurView(Context c) {
super(c);
- mBlurShader = new BlurShader(20f, 20f, null);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mPaint.setShader(mBlurShader);
+ mRenderNode = new RenderNode("blurNode");
+ mRenderNode.setRenderEffect(
+ RenderEffect.createBlurEffect(
+ mBlurRadius,
+ mBlurRadius,
+ null,
+ Shader.TileMode.DECAL
+ )
+ );
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ if (changed) {
+ int width = right - left;
+ int height = bottom - top;
+ mRenderNode.setPosition(0, 0, width, height);
+ Canvas canvas = mRenderNode.beginRecording(width, height);
+ mPaint.setColor(Color.BLUE);
+
+ canvas.drawRect(
+ mBlurRadius * 2,
+ mBlurRadius * 2,
+ width - mBlurRadius * 2,
+ height - mBlurRadius * 2,
+ mPaint
+ );
+
+ mPaint.setColor(Color.RED);
+ canvas.drawCircle((right - left) / 2f, (bottom - top) / 2f, 50f, mPaint);
+
+ mRenderNode.endRecording();
+ }
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
-
- mPaint.setColor(Color.BLUE);
- canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
-
- mPaint.setColor(Color.RED);
- canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, 50f, mPaint);
+ canvas.drawRenderNode(mRenderNode);
}
}
}
diff --git a/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt b/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt
index 71175847..2f2578b 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt
@@ -20,13 +20,13 @@
import android.graphics.BitmapFactory
import android.graphics.BitmapShader
import android.graphics.BlendMode
-import android.graphics.BlurShader
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Outline
import android.graphics.Paint
-import android.graphics.RadialGradient
import android.graphics.Rect
+import android.graphics.RenderEffect
+import android.graphics.RenderNode
import android.graphics.Shader
import android.hardware.Sensor
import android.hardware.SensorEvent
@@ -36,7 +36,6 @@
import android.view.View
import android.view.ViewOutlineProvider
import android.widget.FrameLayout
-import com.android.internal.graphics.ColorUtils
import com.android.test.silkfx.R
import kotlin.math.sin
import kotlin.math.sqrt
@@ -152,10 +151,19 @@
var blurRadius = 150f
set(value) {
field = value
- blurPaint.shader = BlurShader(value, value, null)
+ renderNode.setRenderEffect(
+ RenderEffect.createBlurEffect(value, value, Shader.TileMode.CLAMP))
invalidate()
}
+ private var renderNodeIsDirty = true
+ private val renderNode = RenderNode("GlassRenderNode")
+
+ override fun invalidate() {
+ renderNodeIsDirty = true
+ super.invalidate()
+ }
+
init {
setWillNotDraw(false)
materialPaint.blendMode = BlendMode.SOFT_LIGHT
@@ -164,7 +172,6 @@
scrimPaint.alpha = (scrimOpacity * 255).toInt()
noisePaint.alpha = (noiseOpacity * 255).toInt()
materialPaint.alpha = (materialOpacity * 255).toInt()
- blurPaint.shader = BlurShader(blurRadius, blurRadius, null)
outlineProvider = object : ViewOutlineProvider() {
override fun getOutline(view: View?, outline: Outline?) {
outline?.setRoundRect(Rect(0, 0, width, height), 100f)
@@ -184,20 +191,8 @@
}
override fun onDraw(canvas: Canvas?) {
- src.set(-width / 2, -height / 2, width / 2, height / 2)
- src.scale(1.0f + zoom)
- val centerX = left + width / 2
- val centerY = top + height / 2
- val textureXOffset = (textureTranslationMultiplier * gyroYRotation).toInt()
- val textureYOffset = (textureTranslationMultiplier * gyroXRotation).toInt()
- src.set(src.left + centerX + textureXOffset, src.top + centerY + textureYOffset,
- src.right + centerX + textureXOffset, src.bottom + centerY + textureYOffset)
-
- dst.set(0, 0, width, height)
- canvas?.drawBitmap(backgroundBitmap, src, dst, blurPaint)
- canvas?.drawRect(dst, materialPaint)
- canvas?.drawRect(dst, noisePaint)
- canvas?.drawRect(dst, scrimPaint)
+ updateGlassRenderNode()
+ canvas?.drawRenderNode(renderNode)
}
fun resetGyroOffsets() {
@@ -205,4 +200,31 @@
gyroYRotation = 0f
invalidate()
}
+
+ private fun updateGlassRenderNode() {
+ if (renderNodeIsDirty) {
+ renderNode.setPosition(0, 0, getWidth(), getHeight())
+
+ val canvas = renderNode.beginRecording()
+
+ src.set(-width / 2, -height / 2, width / 2, height / 2)
+ src.scale(1.0f + zoom)
+ val centerX = left + width / 2
+ val centerY = top + height / 2
+ val textureXOffset = (textureTranslationMultiplier * gyroYRotation).toInt()
+ val textureYOffset = (textureTranslationMultiplier * gyroXRotation).toInt()
+ src.set(src.left + centerX + textureXOffset, src.top + centerY + textureYOffset,
+ src.right + centerX + textureXOffset, src.bottom + centerY + textureYOffset)
+
+ dst.set(0, 0, width, height)
+ canvas.drawBitmap(backgroundBitmap, src, dst, blurPaint)
+ canvas.drawRect(dst, materialPaint)
+ canvas.drawRect(dst, noisePaint)
+ canvas.drawRect(dst, scrimPaint)
+
+ renderNode.endRecording()
+
+ renderNodeIsDirty = false
+ }
+ }
}
\ No newline at end of file