Override the SetFilterBitmap and IsFilterBitmap functions for AnimatedImageDrawable API
This change in Skia (https://skia-review.googlesource.com/c/skia/+/909816) makes it so that the default sampling method for animated image drawables is linear sampling. This CL implements these drawable API's (https://developer.android.com/reference/android/graphics/drawable/Drawable#setFilterBitmap(boolean)) so that the user can choose to use nearest neighbor sampling if they want pixel perfect scaling.
Bug: 370523334
Bug: 355264141
Change-Id: I36b7154016e40f3c1f34fd98e347de9a980d0ad9
Test: atest CtsGraphicsTestCases:android.graphics.drawable.cts.AnimatedImageDrawableTest#testSetFilterBitmap -- --template:map preparers=template/preparers/feature-flags --flag-value core_graphics/com.android.graphics.hwui.flags.animated_image_drawable_filter_bitmap=true
Flag: com.android.graphics.hwui.flags.animated_image_drawable_filter_bitmap
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index 82b3f68..71baed8 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -37,6 +37,7 @@
import android.util.TypedValue;
import android.view.View;
+import com.android.graphics.hwui.flags.Flags;
import com.android.internal.R;
import dalvik.annotation.optimization.FastNative;
@@ -525,6 +526,35 @@
}
}
+ @Override
+ public void setFilterBitmap(boolean filterBitmap) {
+ if (!Flags.animatedImageDrawableFilterBitmap()) {
+ super.setFilterBitmap(filterBitmap);
+ return;
+ }
+ if (mState.mNativePtr == 0) {
+ throw new IllegalStateException(
+ "called setFilterBitmap on empty AnimatedImageDrawable"
+ );
+ }
+ if (nSetFilterBitmap(mState.mNativePtr, filterBitmap)) {
+ invalidateSelf();
+ }
+ }
+
+ @Override
+ public boolean isFilterBitmap() {
+ if (!Flags.animatedImageDrawableFilterBitmap()) {
+ return super.isFilterBitmap();
+ }
+ if (mState.mNativePtr == 0) {
+ throw new IllegalStateException(
+ "called isFilterBitmap on empty AnimatedImageDrawable"
+ );
+ }
+ return nGetFilterBitmap(mState.mNativePtr);
+ }
+
private void postOnAnimationStart() {
if (mAnimationCallbacks == null) {
return;
@@ -618,4 +648,8 @@
private static native void nSetMirrored(long nativePtr, boolean mirror);
@FastNative
private static native void nSetBounds(long nativePtr, Rect rect);
+ @FastNative
+ private static native boolean nSetFilterBitmap(long nativePtr, boolean filterBitmap);
+ @FastNative
+ private static native boolean nGetFilterBitmap(long nativePtr);
}
diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig
index f255967..5ad788c 100644
--- a/libs/hwui/aconfig/hwui_flags.aconfig
+++ b/libs/hwui/aconfig/hwui_flags.aconfig
@@ -146,3 +146,11 @@
description: "Whether to have more information in ashmem filenames for bitmaps"
bug: "369619160"
}
+
+flag {
+ name: "animated_image_drawable_filter_bitmap"
+ is_exported: true
+ namespace: "core_graphics"
+ description: "API's that enable animated image drawables to use nearest sampling when scaling."
+ bug: "370523334"
+}
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp
index 69613c7..5e379aa 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.cpp
+++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp
@@ -347,4 +347,26 @@
return adjustFrameDuration(mSkAnimatedImage->currentFrameDuration());
}
+bool AnimatedImageDrawable::getFilterBitmap() const {
+ const SkFilterMode kFilterBitmap = mSkAnimatedImage->getFilterMode();
+ if (kFilterBitmap == SkFilterMode::kLinear) {
+ return true;
+ }
+ return false;
+}
+
+bool AnimatedImageDrawable::setFilterBitmap(bool filterBitmap) {
+ if (filterBitmap) {
+ if (mSkAnimatedImage->getFilterMode() == SkFilterMode::kLinear) {
+ return false;
+ }
+ mSkAnimatedImage->setFilterMode(SkFilterMode::kLinear);
+ } else {
+ if (mSkAnimatedImage->getFilterMode() == SkFilterMode::kNearest) {
+ return false;
+ }
+ mSkAnimatedImage->setFilterMode(SkFilterMode::kNearest);
+ }
+ return true;
+}
} // namespace android
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h
index 1e965ab..2212324 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.h
+++ b/libs/hwui/hwui/AnimatedImageDrawable.h
@@ -87,6 +87,11 @@
bool isRunning();
int getRepetitionCount() const { return mSkAnimatedImage->getRepetitionCount(); }
void setRepetitionCount(int count) { mSkAnimatedImage->setRepetitionCount(count); }
+ // Returns true if the filter mode is set to linear sampling; false if it is
+ // set to nearest neighbor sampling.
+ bool getFilterBitmap() const;
+ // Returns true if the filter mode was changed; false otherwise.
+ bool setFilterBitmap(bool filterBitmap);
void setOnAnimationEndListener(std::unique_ptr<OnAnimationEndListener> listener) {
mEndListener = std::move(listener);
diff --git a/libs/hwui/jni/AnimatedImageDrawable.cpp b/libs/hwui/jni/AnimatedImageDrawable.cpp
index b01e38d..2c8530d 100644
--- a/libs/hwui/jni/AnimatedImageDrawable.cpp
+++ b/libs/hwui/jni/AnimatedImageDrawable.cpp
@@ -276,6 +276,18 @@
drawable->setStagingBounds(rect);
}
+static jboolean AnimatedImageDrawable_nSetFilterBitmap(JNIEnv* env, jobject /*clazz*/,
+ jlong nativePtr, jboolean filterBitmap) {
+ auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
+ return drawable->setFilterBitmap(filterBitmap);
+}
+
+static jboolean AnimatedImageDrawable_nGetFilterBitmap(JNIEnv* env, jobject /*clazz*/,
+ jlong nativePtr) {
+ auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
+ return drawable->getFilterBitmap();
+}
+
static const JNINativeMethod gAnimatedImageDrawableMethods[] = {
{"nCreate", "(JLandroid/graphics/ImageDecoder;IIJZLandroid/graphics/Rect;)J",
(void*)AnimatedImageDrawable_nCreate},
@@ -294,6 +306,8 @@
{"nNativeByteSize", "(J)J", (void*)AnimatedImageDrawable_nNativeByteSize},
{"nSetMirrored", "(JZ)V", (void*)AnimatedImageDrawable_nSetMirrored},
{"nSetBounds", "(JLandroid/graphics/Rect;)V", (void*)AnimatedImageDrawable_nSetBounds},
+ {"nSetFilterBitmap", "(JZ)Z", (void*)AnimatedImageDrawable_nSetFilterBitmap},
+ {"nGetFilterBitmap", "(J)Z", (void*)AnimatedImageDrawable_nGetFilterBitmap},
};
int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) {