Added support for BitmapShader anisotropic filtering

Relnote: "Created new BitmapShader constructor that
enables support for anisotropic filtering."

Bug: 267687306
Test: Added tests to BitmapShaderTest
Change-Id: Ie6e5551f3ae6140dc3afb2d65967b5a6bc08523c
diff --git a/core/api/current.txt b/core/api/current.txt
index fcb3a14..bfdddb5 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -14908,7 +14908,9 @@
   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 int getMaxAnisotropy();
     method public void setFilterMode(int);
+    method public void setMaxAnisotropy(@IntRange(from=1) 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
diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java
index 43cb5ee..2f6dd46 100644
--- a/graphics/java/android/graphics/BitmapShader.java
+++ b/graphics/java/android/graphics/BitmapShader.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 
 import java.lang.annotation.Retention;
@@ -102,6 +103,8 @@
 
     private boolean mRequestDirectSampling;
 
+    private int mMaxAniso = 0;
+
     /**
      * Call this to create a new shader that will draw with a bitmap.
      *
@@ -135,15 +138,47 @@
     }
 
     /**
-     * Set the filter mode to be used when sampling from this shader
+     * Set the filter mode to be used when sampling from this shader. If this is configured
+     * then the anisotropic filtering value specified in any previous call to
+     * {@link #setMaxAnisotropy(int)} is ignored.
      */
     public void setFilterMode(@FilterMode int mode) {
         if (mode != mFilterMode) {
             mFilterMode = mode;
+            mMaxAniso = 0;
             discardNativeInstance();
         }
     }
 
+    /**
+     * Enables and configures the max anisotropy sampling value. If this value is configured,
+     * {@link #setFilterMode(int)} is ignored.
+     *
+     * Anisotropic filtering can enhance visual quality by removing aliasing effects of images
+     * that are at oblique viewing angles. This value is typically consumed as a power of 2 and
+     * anisotropic values of the next power of 2 typically provide twice the quality improvement
+     * as the previous value. For example, a sampling value of 4 would provide twice the improvement
+     * of a sampling value of 2. It is important to note that higher sampling values reach
+     * diminishing returns as the improvements between 8 and 16 can be slight.
+     *
+     * @param maxAnisotropy The Anisotropy value to use for filtering. Must be greater than 0.
+     */
+    public void setMaxAnisotropy(@IntRange(from = 1) int maxAnisotropy) {
+        if (mMaxAniso != maxAnisotropy && maxAnisotropy > 0) {
+            mMaxAniso = maxAnisotropy;
+            mFilterMode = FILTER_MODE_DEFAULT;
+            discardNativeInstance();
+        }
+    }
+
+    /**
+     * Returns the current max anisotropic filtering value configured by
+     * {@link #setFilterMode(int)}. If {@link #setFilterMode(int)} is invoked this returns zero.
+     */
+    public int getMaxAnisotropy() {
+        return mMaxAniso;
+    }
+
     /** @hide */
     /* package */ synchronized long getNativeInstanceWithDirectSampling() {
         mRequestDirectSampling = true;
@@ -162,8 +197,13 @@
         mIsDirectSampled = mRequestDirectSampling;
         mRequestDirectSampling = false;
 
-        return nativeCreate(nativeMatrix, mBitmap.getNativeInstance(), mTileX, mTileY,
-                            enableLinearFilter, mIsDirectSampled);
+        if (mMaxAniso > 0) {
+            return nativeCreateWithMaxAniso(nativeMatrix, mBitmap.getNativeInstance(), mTileX,
+                    mTileY, mMaxAniso, mIsDirectSampled);
+        } else {
+            return nativeCreate(nativeMatrix, mBitmap.getNativeInstance(), mTileX, mTileY,
+                    enableLinearFilter, mIsDirectSampled);
+        }
     }
 
     /** @hide */
@@ -175,5 +215,8 @@
 
     private static native long nativeCreate(long nativeMatrix, long bitmapHandle,
             int shaderTileModeX, int shaderTileModeY, boolean filter, boolean isDirectSampled);
+
+    private static native long nativeCreateWithMaxAniso(long nativeMatrix, long bitmapHandle,
+            int shaderTileModeX, int shaderTileModeY, int maxAniso, boolean isDirectSampled);
 }
 
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index fa8e2e7..8a0db1c 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -71,11 +71,9 @@
     return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Shader_safeUnref));
 }
 
-///////////////////////////////////////////////////////////////////////////////////////////////
-
-static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, jlong bitmapHandle,
-                                      jint tileModeX, jint tileModeY, bool filter,
-                                      bool isDirectSampled) {
+static jlong createBitmapShaderHelper(JNIEnv* env, jobject o, jlong matrixPtr, jlong bitmapHandle,
+                                      jint tileModeX, jint tileModeY, bool isDirectSampled,
+                                      const SkSamplingOptions& sampling) {
     const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
     sk_sp<SkImage> image;
     if (bitmapHandle) {
@@ -88,8 +86,7 @@
         SkBitmap bitmap;
         image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
     }
-    SkSamplingOptions sampling(filter ? SkFilterMode::kLinear : SkFilterMode::kNearest,
-                               SkMipmapMode::kNone);
+
     sk_sp<SkShader> shader;
     if (isDirectSampled) {
         shader = image->makeRawShader((SkTileMode)tileModeX, (SkTileMode)tileModeY, sampling);
@@ -107,6 +104,26 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
+static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, jlong bitmapHandle,
+                                      jint tileModeX, jint tileModeY, bool filter,
+                                      bool isDirectSampled) {
+    SkSamplingOptions sampling(filter ? SkFilterMode::kLinear : SkFilterMode::kNearest,
+                               SkMipmapMode::kNone);
+    return createBitmapShaderHelper(env, o, matrixPtr, bitmapHandle, tileModeX, tileModeY,
+                                    isDirectSampled, sampling);
+}
+
+static jlong BitmapShader_constructorWithMaxAniso(JNIEnv* env, jobject o, jlong matrixPtr,
+                                                  jlong bitmapHandle, jint tileModeX,
+                                                  jint tileModeY, jint maxAniso,
+                                                  bool isDirectSampled) {
+    auto sampling = SkSamplingOptions::Aniso(static_cast<int>(maxAniso));
+    return createBitmapShaderHelper(env, o, matrixPtr, bitmapHandle, tileModeX, tileModeY,
+                                    isDirectSampled, sampling);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
 static std::vector<SkColor4f> convertColorLongs(JNIEnv* env, jlongArray colorArray) {
     const size_t count = env->GetArrayLength(colorArray);
     const jlong* colorValues = env->GetLongArrayElements(colorArray, nullptr);
@@ -408,6 +425,8 @@
 
 static const JNINativeMethod gBitmapShaderMethods[] = {
         {"nativeCreate", "(JJIIZZ)J", (void*)BitmapShader_constructor},
+        {"nativeCreateWithMaxAniso", "(JJIIIZ)J", (void*)BitmapShader_constructorWithMaxAniso},
+
 };
 
 static const JNINativeMethod gLinearGradientMethods[] = {