diff options
-rw-r--r-- | graphics/java/android/graphics/RenderEffect.java | 145 | ||||
-rw-r--r-- | graphics/java/android/graphics/RenderNode.java | 20 | ||||
-rw-r--r-- | libs/hwui/Android.bp | 1 | ||||
-rw-r--r-- | libs/hwui/RenderProperties.cpp | 7 | ||||
-rw-r--r-- | libs/hwui/RenderProperties.h | 7 | ||||
-rw-r--r-- | libs/hwui/apex/LayoutlibLoader.cpp | 2 | ||||
-rw-r--r-- | libs/hwui/apex/jni_runtime.cpp | 2 | ||||
-rw-r--r-- | libs/hwui/hwui/Paint.h | 1 | ||||
-rw-r--r-- | libs/hwui/jni/RenderEffect.cpp | 69 | ||||
-rw-r--r-- | libs/hwui/jni/android_graphics_RenderNode.cpp | 7 | ||||
-rw-r--r-- | libs/hwui/pipeline/skia/RenderNodeDrawable.cpp | 4 | ||||
-rw-r--r-- | tests/HwAccelerationTest/AndroidManifest.xml | 2 | ||||
-rw-r--r-- | tests/HwAccelerationTest/res/layout/image_filter_activity.xml | 28 | ||||
-rw-r--r-- | tests/HwAccelerationTest/src/com/android/test/hwui/BlurActivity.java | 86 | ||||
-rw-r--r-- | tests/SilkFX/src/com/android/test/silkfx/materials/GlassView.kt | 60 |
15 files changed, 402 insertions, 39 deletions
diff --git a/graphics/java/android/graphics/RenderEffect.java b/graphics/java/android/graphics/RenderEffect.java new file mode 100644 index 000000000000..9fc0c8eb9d90 --- /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 8aacbc7bc109..d812c1a5595d 100644 --- a/graphics/java/android/graphics/RenderNode.java +++ b/graphics/java/android/graphics/RenderNode.java @@ -850,6 +850,23 @@ public final class RenderNode { } /** + * 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 @@ public final class RenderNode { 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 90d2537d97a8..9f4c9a1eddfa 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -327,6 +327,7 @@ cc_defaults { "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 ff9cf45cdc73..8fba9cf21df1 100644 --- a/libs/hwui/RenderProperties.cpp +++ b/libs/hwui/RenderProperties.cpp @@ -49,6 +49,12 @@ bool LayerProperties::setColorFilter(SkColorFilter* filter) { 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 @@ LayerProperties& LayerProperties::operator=(const LayerProperties& other) { 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 ef4cd1f1eb62..aeb60e6ce355 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 @@ public: 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 @@ private: uint8_t mAlpha; SkBlendMode mMode; sk_sp<SkColorFilter> mColorFilter; + sk_sp<SkImageFilter> mImageFilter; }; /* @@ -541,6 +547,7 @@ public: 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 4bbf1214bdcf..dca10e29cbb8 100644 --- a/libs/hwui/apex/LayoutlibLoader.cpp +++ b/libs/hwui/apex/LayoutlibLoader.cpp @@ -47,6 +47,7 @@ extern int register_android_graphics_MaskFilter(JNIEnv* env); 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 @@ static const std::unordered_map<std::string, RegJNIRec> gRegJNIMap = { {"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 12e2e8135278..deccc802dbb2 100644 --- a/libs/hwui/apex/jni_runtime.cpp +++ b/libs/hwui/apex/jni_runtime.cpp @@ -43,6 +43,7 @@ extern int register_android_graphics_Movie(JNIEnv* env); 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 @@ static const RegJNIRec gRegJNI[] = { 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 0bb689c19079..4e2016a41f5e 100644 --- a/libs/hwui/hwui/Paint.h +++ b/libs/hwui/hwui/Paint.h @@ -157,7 +157,6 @@ public: 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 000000000000..0ebd0ca720d8 --- /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 85c802b40459..4b4aa92b97b7 100644 --- a/libs/hwui/jni/android_graphics_RenderNode.cpp +++ b/libs/hwui/jni/android_graphics_RenderNode.cpp @@ -215,6 +215,12 @@ static jboolean android_view_RenderNode_setAlpha(CRITICAL_JNI_PARAMS_COMMA jlong 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 @@ static const JNINativeMethod gMethods[] = { { "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 00ceb2d84f9e..1473b3e5abb7 100644 --- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp +++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp @@ -172,10 +172,12 @@ static bool layerNeedsPaint(const LayerProperties& properties, float alphaMultip 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 05a59ef7fc72..9a2def935f5d 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 000000000000..a0ee67ae0bef --- /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 033fb0ec35d2..e4ca7881f796 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 @@ package com.android.test.hwui; 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 class BlurActivity extends Activity { } 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 @@ public class BlurActivity extends Activity { 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 onDraw(Canvas canvas) { - super.onDraw(canvas); + 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.BLUE); - canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint); + mPaint.setColor(Color.RED); + canvas.drawCircle((right - left) / 2f, (bottom - top) / 2f, 50f, mPaint); + + mRenderNode.endRecording(); + } + } - mPaint.setColor(Color.RED); - canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, 50f, mPaint); + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + 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 711758476a62..2f2578b87f35 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.Bitmap 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.util.AttributeSet 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 @@ class GlassView(context: Context, attributeSet: AttributeSet) : FrameLayout(cont 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 @@ class GlassView(context: Context, attributeSet: AttributeSet) : FrameLayout(cont 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 @@ class GlassView(context: Context, attributeSet: AttributeSet) : FrameLayout(cont } 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 @@ class GlassView(context: Context, attributeSet: AttributeSet) : FrameLayout(cont 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 |