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);
+        }
+    }
+}