Added BlurShader API
Created BlurShader framework API along with
native implementation as an implementation of
the Shader wrapper that maps to either
SkShader or SkImageFilter
Bug: 143468037
Test: Added CTS test to verify BlurShader
Change-Id: I05fcf7ba79e9d552f8b0738f7382f7826cd94e21
diff --git a/api/current.txt b/api/current.txt
index 532c3d9..f037d27 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -14250,6 +14250,10 @@
enum_constant public static final android.graphics.BlurMaskFilter.Blur SOLID;
}
+ public final class BlurShader extends android.graphics.Shader {
+ ctor public BlurShader(float, float, @Nullable android.graphics.Shader);
+ }
+
public class Camera {
ctor public Camera();
method public void applyToCanvas(android.graphics.Canvas);
diff --git a/graphics/java/android/graphics/BlurShader.java b/graphics/java/android/graphics/BlurShader.java
new file mode 100644
index 0000000..779a890
--- /dev/null
+++ b/graphics/java/android/graphics/BlurShader.java
@@ -0,0 +1,65 @@
+/*
+ * 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.Nullable;
+
+/**
+ * A subclass of shader that blurs input from another {@link android.graphics.Shader} instance
+ * or all the drawing commands with the {@link android.graphics.Paint} that this shader is
+ * attached to.
+ */
+public final class BlurShader extends Shader {
+
+ private final float mRadiusX;
+ private final float mRadiusY;
+ private final Shader mInputShader;
+
+ private long mNativeInputShader = 0;
+
+ /**
+ * Create a {@link BlurShader} that blurs the contents of the optional input shader
+ * with the specified radius along the x and y axis. If no input shader is provided
+ * then all drawing commands issued with a {@link android.graphics.Paint} that this
+ * shader 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 inputShader Input shader that provides the content to be blurred
+ */
+ public BlurShader(float radiusX, float radiusY, @Nullable Shader inputShader) {
+ mRadiusX = radiusX;
+ mRadiusY = radiusY;
+ mInputShader = inputShader;
+ }
+
+ /** @hide **/
+ @Override
+ protected long createNativeInstance(long nativeMatrix) {
+ mNativeInputShader = mInputShader != null ? mInputShader.getNativeInstance() : 0;
+ return nativeCreate(nativeMatrix, mRadiusX, mRadiusY, mNativeInputShader);
+ }
+
+ /** @hide **/
+ @Override
+ protected boolean shouldDiscardNativeInstance() {
+ long currentNativeInstance = mInputShader != null ? mInputShader.getNativeInstance() : 0;
+ return mNativeInputShader != currentNativeInstance;
+ }
+
+ private static native long nativeCreate(long nativeMatrix, float radiusX, float radiusY,
+ long inputShader);
+}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 0b13754..90d2537 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -464,6 +464,7 @@
"RootRenderNode.cpp",
"shader/Shader.cpp",
"shader/BitmapShader.cpp",
+ "shader/BlurShader.cpp",
"shader/ComposeShader.cpp",
"shader/LinearGradientShader.cpp",
"shader/RadialGradientShader.cpp",
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index 9b1972e..7cb7723 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -7,6 +7,7 @@
#include "include/effects/SkRuntimeEffect.h"
#include "shader/Shader.h"
#include "shader/BitmapShader.h"
+#include "shader/BlurShader.h"
#include "shader/ComposeShader.h"
#include "shader/LinearGradientShader.h"
#include "shader/RadialGradientShader.h"
@@ -222,6 +223,22 @@
///////////////////////////////////////////////////////////////////////////////////////////////
+static jlong BlurShader_create(JNIEnv* env , jobject o, jlong matrixPtr, jfloat sigmaX,
+ jfloat sigmaY, jlong shaderHandle) {
+ auto* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
+ auto* inputShader = reinterpret_cast<Shader*>(shaderHandle);
+
+ auto* blurShader = new BlurShader(
+ sigmaX,
+ sigmaY,
+ inputShader,
+ matrix
+ );
+ return reinterpret_cast<jlong>(blurShader);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderFactory, jlong matrixPtr,
jbyteArray inputs, jlong colorSpaceHandle, jboolean isOpaque) {
auto* effect = reinterpret_cast<SkRuntimeEffect*>(shaderFactory);
@@ -273,6 +290,10 @@
{ "nativeCreate", "(JJII)J", (void*)BitmapShader_constructor },
};
+static const JNINativeMethod gBlurShaderMethods[] = {
+ { "nativeCreate", "(JFFJ)J", (void*)BlurShader_create }
+};
+
static const JNINativeMethod gLinearGradientMethods[] = {
{ "nativeCreate", "(JFFFF[J[FIJ)J", (void*)LinearGradient_create },
};
@@ -304,6 +325,8 @@
NELEM(gShaderMethods));
android::RegisterMethodsOrDie(env, "android/graphics/BitmapShader", gBitmapShaderMethods,
NELEM(gBitmapShaderMethods));
+ android::RegisterMethodsOrDie(env, "android/graphics/BlurShader", gBlurShaderMethods,
+ NELEM(gBlurShaderMethods));
android::RegisterMethodsOrDie(env, "android/graphics/LinearGradient", gLinearGradientMethods,
NELEM(gLinearGradientMethods));
android::RegisterMethodsOrDie(env, "android/graphics/RadialGradient", gRadialGradientMethods,
diff --git a/libs/hwui/shader/BlurShader.cpp b/libs/hwui/shader/BlurShader.cpp
new file mode 100644
index 0000000..4d18cdd
--- /dev/null
+++ b/libs/hwui/shader/BlurShader.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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 "BlurShader.h"
+#include "SkImageFilters.h"
+#include "SkRefCnt.h"
+#include "utils/Blur.h"
+
+namespace android::uirenderer {
+BlurShader::BlurShader(float radiusX, float radiusY, Shader* inputShader, const SkMatrix* matrix)
+ : Shader(matrix)
+ , skImageFilter(
+ SkImageFilters::Blur(
+ Blur::convertRadiusToSigma(radiusX),
+ Blur::convertRadiusToSigma(radiusY),
+ inputShader ? inputShader->asSkImageFilter() : nullptr)
+ ) { }
+
+sk_sp<SkImageFilter> BlurShader::makeSkImageFilter() {
+ return skImageFilter;
+}
+
+BlurShader::~BlurShader() {}
+
+} // namespace android::uirenderer
\ No newline at end of file
diff --git a/libs/hwui/shader/BlurShader.h b/libs/hwui/shader/BlurShader.h
new file mode 100644
index 0000000..9eb22bd
--- /dev/null
+++ b/libs/hwui/shader/BlurShader.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#pragma once
+#include "Shader.h"
+
+namespace android::uirenderer {
+
+/**
+ * Shader implementation that blurs another Shader instance or the source bitmap
+ */
+class BlurShader : public Shader {
+public:
+ /**
+ * Creates a BlurShader instance with the provided radius values to blur along the x and y
+ * axis accordingly.
+ *
+ * This will blur the contents of the provided input shader if it is non-null, otherwise
+ * the source bitmap will be blurred instead.
+ */
+ BlurShader(float radiusX, float radiusY, Shader* inputShader, const SkMatrix* matrix);
+ ~BlurShader() override;
+protected:
+ sk_sp<SkImageFilter> makeSkImageFilter() override;
+private:
+ sk_sp<SkImageFilter> skImageFilter;
+};
+
+} // namespace android::uirenderer
\ No newline at end of file
diff --git a/libs/hwui/shader/Shader.h b/libs/hwui/shader/Shader.h
index 3c0cdaa..6403e11 100644
--- a/libs/hwui/shader/Shader.h
+++ b/libs/hwui/shader/Shader.h
@@ -32,7 +32,9 @@
class Shader: public SkRefCnt {
public:
/**
- * Creates a Shader instance with an optional transformation matrix
+ * Creates a Shader instance with an optional transformation matrix. The transformation matrix
+ * is copied internally and ownership is unchanged. It is the responsibility of the caller to
+ * deallocate it appropriately.
* @param matrix Optional matrix to transform the underlying SkShader or SkImageFilter
*/
Shader(const SkMatrix* matrix);
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index f9d0d14..5966869 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -14250,6 +14250,10 @@
enum_constant public static final android.graphics.BlurMaskFilter.Blur SOLID;
}
+ public final class BlurShader extends android.graphics.Shader {
+ ctor public BlurShader(float, float, @Nullable android.graphics.Shader);
+ }
+
public class Camera {
ctor public Camera();
method public void applyToCanvas(android.graphics.Canvas);
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 7790043..05a59ef 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -744,6 +744,15 @@
</intent-filter>
</activity>
+ <activity android:name="BlurActivity"
+ android:label="Shaders/Blur"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="com.android.test.hwui.TEST"/>
+ </intent-filter>
+ </activity>
+
<activity android:name="TextActivity"
android:label="Text/Simple Text"
android:theme="@android:style/Theme.NoTitleBar"
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/BlurActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/BlurActivity.java
new file mode 100644
index 0000000..033fb0e
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/BlurActivity.java
@@ -0,0 +1,110 @@
+/*
+ * 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 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.Shader;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class BlurActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ LinearLayout layout = new LinearLayout(this);
+ layout.setClipChildren(false);
+ layout.setGravity(Gravity.CENTER);
+ layout.setOrientation(LinearLayout.VERTICAL);
+
+
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(500, 500);
+ params.bottomMargin = 100;
+
+ layout.addView(new BlurGradientView(this), params);
+ layout.addView(new BlurView(this), params);
+
+ setContentView(layout);
+ }
+
+ public static class BlurGradientView extends View {
+ private BlurShader mBlurShader = null;
+ private Paint mPaint;
+
+ public BlurGradientView(Context c) {
+ super(c);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ if (changed || mBlurShader == null) {
+ LinearGradient gradient = new LinearGradient(
+ 0f,
+ 0f,
+ right - left,
+ bottom - top,
+ Color.CYAN,
+ Color.YELLOW,
+ Shader.TileMode.CLAMP
+ );
+ mBlurShader = new BlurShader(30f, 40f, gradient);
+ mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mPaint.setShader(mBlurShader);
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
+ }
+ }
+
+ public static class BlurView extends View {
+
+ private final BlurShader mBlurShader;
+ private final Paint mPaint;
+
+ public BlurView(Context c) {
+ super(c);
+
+ mBlurShader = new BlurShader(20f, 20f, null);
+ mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mPaint.setShader(mBlurShader);
+ }
+
+ @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);
+ }
+ }
+}