Add filtering and directSampling features for BitmapShader

This adds filterMode directly to BitmapShader's public API
which allows users to override the global flag on the paint
or for BitmapShaders used in a RuntimeShader this provides
some explicit control over how the hardware will sample.

Addtionally BitmapShader adds an internal directSampling API
that is exposed only for RuntimeShader via the setInputBuffer
API. Previously, if you want to sample the unaltered contents
of a bitmap you must ensure that the bitmaps contents have the
same format (premul, colorspace, etc) of the destination. This
API enables you to do so without knowledge of the destination
format. This is necessary for use cases where the bitmap content
is data like a normal map or lighting values.

Bug: 189102731
Bug: 201546136
Test: atest CtsGraphicsTestCases:BitmapShaderTest
Test: atest CtsUiRenderingTestCases:RuntimeShaderTests
Change-Id: I8e9eaa8fae1d3ff45ab8c98b3878bb9da24dac47
diff --git a/core/api/current.txt b/core/api/current.txt
index 4f15fa9..d98eb16 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -15229,6 +15229,11 @@
 
   public class BitmapShader extends android.graphics.Shader {
     ctor public BitmapShader(@NonNull android.graphics.Bitmap, @NonNull android.graphics.Shader.TileMode, @NonNull android.graphics.Shader.TileMode);
+    method public int getFilterMode();
+    method public void setFilterMode(int);
+    field public static final int FILTER_MODE_DEFAULT = 0; // 0x0
+    field public static final int FILTER_MODE_LINEAR = 2; // 0x2
+    field public static final int FILTER_MODE_NEAREST = 1; // 0x1
   }
 
   public enum BlendMode {
@@ -16635,6 +16640,7 @@
     method public void setFloatUniform(@NonNull String, float, float, float);
     method public void setFloatUniform(@NonNull String, float, float, float, float);
     method public void setFloatUniform(@NonNull String, @NonNull float[]);
+    method public void setInputBuffer(@NonNull String, @NonNull android.graphics.BitmapShader);
     method public void setInputShader(@NonNull String, @NonNull android.graphics.Shader);
     method public void setIntUniform(@NonNull String, int);
     method public void setIntUniform(@NonNull String, int, int);
diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java
index e6ff187..43cb5ee 100644
--- a/graphics/java/android/graphics/BitmapShader.java
+++ b/graphics/java/android/graphics/BitmapShader.java
@@ -16,8 +16,12 @@
 
 package android.graphics;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Shader used to draw a bitmap as a texture. The bitmap can be repeated or
  * mirrored by setting the tiling mode.
@@ -31,6 +35,47 @@
     private int mTileX;
     private int mTileY;
 
+    /** @hide */
+    @IntDef(prefix = {"FILTER_MODE"}, value = {
+            FILTER_MODE_DEFAULT,
+            FILTER_MODE_NEAREST,
+            FILTER_MODE_LINEAR
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FilterMode {}
+
+    /**
+     * This FilterMode value will respect the value of the Paint#isFilterBitmap flag while the
+     * shader is attached to the Paint.
+     *
+     * <p>The exception to this rule is when a Shader is attached as input to a RuntimeShader. In
+     *    that case this mode will default to FILTER_MODE_NEAREST.</p>
+     *
+     * @see #setFilterMode(int)
+     */
+    public static final int FILTER_MODE_DEFAULT = 0;
+    /**
+     * This FilterMode value will cause the shader to sample from the nearest pixel to the requested
+     * sample point.
+     *
+     * <p>This value will override the effect of Paint#isFilterBitmap.</p>
+     *
+     * @see #setFilterMode(int)
+     */
+    public static final int FILTER_MODE_NEAREST = 1;
+    /**
+     * This FilterMode value will cause the shader to interpolate the output of the shader from a
+     * 2x2 grid of pixels nearest to the sample point (i.e. bilinear interpolation).
+     *
+     * <p>This value will override the effect of Paint#isFilterBitmap.</p>
+     *
+     * @see #setFilterMode(int)
+     */
+    public static final int FILTER_MODE_LINEAR = 2;
+
+    @FilterMode
+    private int mFilterMode;
+
     /*
      *  This is cache of the last value from the Paint of bitmap-filtering.
      *  In the future, BitmapShaders will carry their own (expanded) data for this
@@ -49,6 +94,15 @@
     private boolean mFilterFromPaint;
 
     /**
+     *  Stores whether or not the contents of this shader's bitmap will be sampled
+     *  without modification or if the bitmap's properties, like colorspace and
+     *  premultiplied alpha, will be respected when sampling from the bitmap's buffer.
+     */
+    private boolean mIsDirectSampled;
+
+    private boolean mRequestDirectSampling;
+
+    /**
      * Call this to create a new shader that will draw with a bitmap.
      *
      * @param bitmap The bitmap to use inside the shader
@@ -66,24 +120,60 @@
         mBitmap = bitmap;
         mTileX = tileX;
         mTileY = tileY;
+        mFilterMode = FILTER_MODE_DEFAULT;
         mFilterFromPaint = false;
+        mIsDirectSampled = false;
+        mRequestDirectSampling = false;
+    }
+
+    /**
+     * Returns the filter mode used when sampling from this shader
+     */
+    @FilterMode
+    public int getFilterMode() {
+        return mFilterMode;
+    }
+
+    /**
+     * Set the filter mode to be used when sampling from this shader
+     */
+    public void setFilterMode(@FilterMode int mode) {
+        if (mode != mFilterMode) {
+            mFilterMode = mode;
+            discardNativeInstance();
+        }
+    }
+
+    /** @hide */
+    /* package */ synchronized long getNativeInstanceWithDirectSampling() {
+        mRequestDirectSampling = true;
+        return getNativeInstance();
     }
 
     /** @hide */
     @Override
     protected long createNativeInstance(long nativeMatrix, boolean filterFromPaint) {
-        mFilterFromPaint = filterFromPaint;
+        boolean enableLinearFilter = mFilterMode == FILTER_MODE_LINEAR;
+        if (mFilterMode == FILTER_MODE_DEFAULT) {
+            mFilterFromPaint = filterFromPaint;
+            enableLinearFilter = mFilterFromPaint;
+        }
+
+        mIsDirectSampled = mRequestDirectSampling;
+        mRequestDirectSampling = false;
+
         return nativeCreate(nativeMatrix, mBitmap.getNativeInstance(), mTileX, mTileY,
-                            mFilterFromPaint);
+                            enableLinearFilter, mIsDirectSampled);
     }
 
     /** @hide */
     @Override
     protected boolean shouldDiscardNativeInstance(boolean filterFromPaint) {
-        return mFilterFromPaint != filterFromPaint;
+        return mIsDirectSampled != mRequestDirectSampling
+                || (mFilterMode == FILTER_MODE_DEFAULT && mFilterFromPaint != filterFromPaint);
     }
 
     private static native long nativeCreate(long nativeMatrix, long bitmapHandle,
-            int shaderTileModeX, int shaderTileModeY, boolean filter);
+            int shaderTileModeX, int shaderTileModeY, boolean filter, boolean isDirectSampled);
 }
 
diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java
index ef57f4a..57046f5 100644
--- a/graphics/java/android/graphics/RuntimeShader.java
+++ b/graphics/java/android/graphics/RuntimeShader.java
@@ -279,11 +279,11 @@
     }
 
     /**
-     * Sets the uniform shader that is declares as input to this shader.  If the shader does not
+     * Assigns the uniform shader to the provided shader parameter.  If the shader program does not
      * have a uniform shader with that name then an IllegalArgumentException is thrown.
      *
-     * @param shaderName name matching the uniform declared in the SKSL shader
-     * @param shader shader passed into the SKSL shader for sampling
+     * @param shaderName name matching the uniform declared in the AGSL shader program
+     * @param shader shader passed into the AGSL shader program for sampling
      */
     public void setInputShader(@NonNull String shaderName, @NonNull Shader shader) {
         if (shaderName == null) {
@@ -297,6 +297,28 @@
         discardNativeInstance();
     }
 
+    /**
+     * Assigns the uniform shader to the provided shader parameter.  If the shader program does not
+     * have a uniform shader with that name then an IllegalArgumentException is thrown.
+     *
+     * Unlike setInputShader this method returns samples directly from the bitmap's buffer. This
+     * means that there will be no transformation of the sampled pixels, such as colorspace
+     * conversion or alpha premultiplication.
+     */
+    public void setInputBuffer(@NonNull String shaderName, @NonNull BitmapShader shader) {
+        if (shaderName == null) {
+            throw new NullPointerException("The shaderName parameter must not be null");
+        }
+        if (shader == null) {
+            throw new NullPointerException("The shader parameter must not be null");
+        }
+
+        nativeUpdateShader(mNativeInstanceRuntimeShaderBuilder, shaderName,
+                shader.getNativeInstanceWithDirectSampling());
+        discardNativeInstance();
+    }
+
+
     /** @hide */
     @Override
     protected long createNativeInstance(long nativeMatrix, boolean filterFromPaint) {
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index c4366f75..c505b53 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -64,7 +64,8 @@
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
 static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, jlong bitmapHandle,
-        jint tileModeX, jint tileModeY, bool filter) {
+                                      jint tileModeX, jint tileModeY, bool filter,
+                                      bool isDirectSampled) {
     const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
     sk_sp<SkImage> image;
     if (bitmapHandle) {
@@ -79,8 +80,12 @@
     }
     SkSamplingOptions sampling(filter ? SkFilterMode::kLinear : SkFilterMode::kNearest,
                                SkMipmapMode::kNone);
-    sk_sp<SkShader> shader = image->makeShader(
-            (SkTileMode)tileModeX, (SkTileMode)tileModeY, sampling);
+    sk_sp<SkShader> shader;
+    if (isDirectSampled) {
+        shader = image->makeRawShader((SkTileMode)tileModeX, (SkTileMode)tileModeY, sampling);
+    } else {
+        shader = image->makeShader((SkTileMode)tileModeX, (SkTileMode)tileModeY, sampling);
+    }
     ThrowIAE_IfNull(env, shader.get());
 
     if (matrix) {
@@ -393,7 +398,7 @@
 };
 
 static const JNINativeMethod gBitmapShaderMethods[] = {
-    { "nativeCreate",      "(JJIIZ)J",  (void*)BitmapShader_constructor },
+        {"nativeCreate", "(JJIIZZ)J", (void*)BitmapShader_constructor},
 };
 
 static const JNINativeMethod gLinearGradientMethods[] = {