Prefer animation in ImageDecoder.decodeDrawable
Pass down a flag to prefer animation over still images
when decodeDrawble is used. Pass the flag via source
to avoid adding this flag to all create/createFrom/
nCreate method variants. Check the flag only in
native_create when we actually create the decoder.
bug: 120414514
bug: 78868457
Change-Id: I84b4edd0225df9eaa59df8a787bd5902146ab500
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
index 98162af..ec91cbf 100644
--- a/core/jni/android/graphics/ImageDecoder.cpp
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -78,14 +78,18 @@
return nullptr;
}
-static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream, jobject source) {
+static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream,
+ jobject source, jboolean preferAnimation) {
if (!stream.get()) {
return throw_exception(env, ImageDecoder::kSourceMalformedData, "Failed to create a stream",
nullptr, source);
}
std::unique_ptr<ImageDecoder> decoder(new ImageDecoder);
SkCodec::Result result;
- auto codec = SkCodec::MakeFromStream(std::move(stream), &result, decoder->mPeeker.get());
+ auto codec = SkCodec::MakeFromStream(
+ std::move(stream), &result, decoder->mPeeker.get(),
+ preferAnimation ? SkCodec::SelectionPolicy::kPreferAnimation
+ : SkCodec::SelectionPolicy::kPreferStillImage);
if (jthrowable jexception = get_and_clear_exception(env)) {
return throw_exception(env, ImageDecoder::kSourceException, "", jexception, source);
}
@@ -124,7 +128,7 @@
}
static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/,
- jobject fileDescriptor, jobject source) {
+ jobject fileDescriptor, jboolean preferAnimation, jobject source) {
int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
struct stat fdStat;
@@ -142,11 +146,11 @@
}
std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file));
- return native_create(env, std::move(fileStream), source);
+ return native_create(env, std::move(fileStream), source, preferAnimation);
}
static jobject ImageDecoder_nCreateInputStream(JNIEnv* env, jobject /*clazz*/,
- jobject is, jbyteArray storage, jobject source) {
+ jobject is, jbyteArray storage, jboolean preferAnimation, jobject source) {
std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage, false));
if (!stream.get()) {
@@ -157,31 +161,33 @@
std::unique_ptr<SkStream> bufferedStream(
SkFrontBufferedStream::Make(std::move(stream),
SkCodec::MinBufferedBytesNeeded()));
- return native_create(env, std::move(bufferedStream), source);
+ return native_create(env, std::move(bufferedStream), source, preferAnimation);
}
-static jobject ImageDecoder_nCreateAsset(JNIEnv* env, jobject /*clazz*/, jlong assetPtr,
- jobject source) {
+static jobject ImageDecoder_nCreateAsset(JNIEnv* env, jobject /*clazz*/,
+ jlong assetPtr, jboolean preferAnimation, jobject source) {
Asset* asset = reinterpret_cast<Asset*>(assetPtr);
std::unique_ptr<SkStream> stream(new AssetStreamAdaptor(asset));
- return native_create(env, std::move(stream), source);
+ return native_create(env, std::move(stream), source, preferAnimation);
}
-static jobject ImageDecoder_nCreateByteBuffer(JNIEnv* env, jobject /*clazz*/, jobject jbyteBuffer,
- jint initialPosition, jint limit, jobject source) {
+static jobject ImageDecoder_nCreateByteBuffer(JNIEnv* env, jobject /*clazz*/,
+ jobject jbyteBuffer, jint initialPosition, jint limit,
+ jboolean preferAnimation, jobject source) {
std::unique_ptr<SkStream> stream = CreateByteBufferStreamAdaptor(env, jbyteBuffer,
initialPosition, limit);
if (!stream) {
return throw_exception(env, ImageDecoder::kSourceMalformedData, "Failed to read ByteBuffer",
nullptr, source);
}
- return native_create(env, std::move(stream), source);
+ return native_create(env, std::move(stream), source, preferAnimation);
}
-static jobject ImageDecoder_nCreateByteArray(JNIEnv* env, jobject /*clazz*/, jbyteArray byteArray,
- jint offset, jint length, jobject source) {
+static jobject ImageDecoder_nCreateByteArray(JNIEnv* env, jobject /*clazz*/,
+ jbyteArray byteArray, jint offset, jint length,
+ jboolean preferAnimation, jobject source) {
std::unique_ptr<SkStream> stream(CreateByteArrayStreamAdaptor(env, byteArray, offset, length));
- return native_create(env, std::move(stream), source);
+ return native_create(env, std::move(stream), source, preferAnimation);
}
jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas) {
@@ -514,11 +520,11 @@
}
static const JNINativeMethod gImageDecoderMethods[] = {
- { "nCreate", "(JLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateAsset },
- { "nCreate", "(Ljava/nio/ByteBuffer;IILandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer },
- { "nCreate", "([BIILandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray },
- { "nCreate", "(Ljava/io/InputStream;[BLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream },
- { "nCreate", "(Ljava/io/FileDescriptor;Landroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd },
+ { "nCreate", "(JZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateAsset },
+ { "nCreate", "(Ljava/nio/ByteBuffer;IIZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer },
+ { "nCreate", "([BIIZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray },
+ { "nCreate", "(Ljava/io/InputStream;[BZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream },
+ { "nCreate", "(Ljava/io/FileDescriptor;ZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd },
{ "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder;ZIILandroid/graphics/Rect;ZIZZZJZ)Landroid/graphics/Bitmap;",
(void*) ImageDecoder_nDecodeBitmap },
{ "nGetSampledSize","(JI)Landroid/util/Size;", (void*) ImageDecoder_nGetSampledSize },
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 2d5babc..150a941 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -214,7 +214,7 @@
/* @hide */
@NonNull
- abstract ImageDecoder createImageDecoder() throws IOException;
+ abstract ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException;
};
private static class ByteArraySource extends Source {
@@ -228,8 +228,8 @@
private final int mLength;
@Override
- public ImageDecoder createImageDecoder() throws IOException {
- return nCreate(mData, mOffset, mLength, this);
+ public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
+ return nCreate(mData, mOffset, mLength, preferAnimation, this);
}
}
@@ -240,14 +240,14 @@
private final ByteBuffer mBuffer;
@Override
- public ImageDecoder createImageDecoder() throws IOException {
+ public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
if (!mBuffer.isDirect() && mBuffer.hasArray()) {
int offset = mBuffer.arrayOffset() + mBuffer.position();
int length = mBuffer.limit() - mBuffer.position();
- return nCreate(mBuffer.array(), offset, length, this);
+ return nCreate(mBuffer.array(), offset, length, preferAnimation, this);
}
ByteBuffer buffer = mBuffer.slice();
- return nCreate(buffer, buffer.position(), buffer.limit(), this);
+ return nCreate(buffer, buffer.position(), buffer.limit(), preferAnimation, this);
}
}
@@ -267,7 +267,7 @@
Resources getResources() { return mResources; }
@Override
- public ImageDecoder createImageDecoder() throws IOException {
+ public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
AssetFileDescriptor assetFd = null;
try {
if (mUri.getScheme() == ContentResolver.SCHEME_CONTENT) {
@@ -284,26 +284,26 @@
throw new FileNotFoundException(mUri.toString());
}
- return createFromStream(is, true, this);
+ return createFromStream(is, true, preferAnimation, this);
}
- return createFromAssetFileDescriptor(assetFd, this);
+ return createFromAssetFileDescriptor(assetFd, preferAnimation, this);
}
}
@NonNull
private static ImageDecoder createFromFile(@NonNull File file,
- @NonNull Source source) throws IOException {
+ boolean preferAnimation, @NonNull Source source) throws IOException {
FileInputStream stream = new FileInputStream(file);
FileDescriptor fd = stream.getFD();
try {
Os.lseek(fd, 0, SEEK_CUR);
} catch (ErrnoException e) {
- return createFromStream(stream, true, source);
+ return createFromStream(stream, true, preferAnimation, source);
}
ImageDecoder decoder = null;
try {
- decoder = nCreate(fd, source);
+ decoder = nCreate(fd, preferAnimation, source);
} finally {
if (decoder == null) {
IoUtils.closeQuietly(stream);
@@ -317,12 +317,12 @@
@NonNull
private static ImageDecoder createFromStream(@NonNull InputStream is,
- boolean closeInputStream, Source source) throws IOException {
+ boolean closeInputStream, boolean preferAnimation, Source source) throws IOException {
// Arbitrary size matches BitmapFactory.
byte[] storage = new byte[16 * 1024];
ImageDecoder decoder = null;
try {
- decoder = nCreate(is, storage, source);
+ decoder = nCreate(is, storage, preferAnimation, source);
} finally {
if (decoder == null) {
if (closeInputStream) {
@@ -340,7 +340,7 @@
@NonNull
private static ImageDecoder createFromAssetFileDescriptor(@NonNull AssetFileDescriptor assetFd,
- Source source) throws IOException {
+ boolean preferAnimation, Source source) throws IOException {
final FileDescriptor fd = assetFd.getFileDescriptor();
final long offset = assetFd.getStartOffset();
@@ -348,9 +348,9 @@
try {
try {
Os.lseek(fd, offset, SEEK_SET);
- decoder = nCreate(fd, source);
+ decoder = nCreate(fd, preferAnimation, source);
} catch (ErrnoException e) {
- decoder = createFromStream(new FileInputStream(fd), true, source);
+ decoder = createFromStream(new FileInputStream(fd), true, preferAnimation, source);
}
} finally {
if (decoder == null) {
@@ -388,7 +388,7 @@
public int getDensity() { return mInputDensity; }
@Override
- public ImageDecoder createImageDecoder() throws IOException {
+ public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
synchronized (this) {
if (mInputStream == null) {
@@ -396,7 +396,7 @@
}
InputStream is = mInputStream;
mInputStream = null;
- return createFromStream(is, false, this);
+ return createFromStream(is, false, preferAnimation, this);
}
}
}
@@ -434,14 +434,14 @@
}
@Override
- public ImageDecoder createImageDecoder() throws IOException {
+ public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
synchronized (this) {
if (mAssetInputStream == null) {
throw new IOException("Cannot reuse AssetInputStreamSource");
}
AssetInputStream ais = mAssetInputStream;
mAssetInputStream = null;
- return createFromAsset(ais, this);
+ return createFromAsset(ais, preferAnimation, this);
}
}
}
@@ -469,7 +469,7 @@
}
@Override
- public ImageDecoder createImageDecoder() throws IOException {
+ public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
TypedValue value = new TypedValue();
// This is just used in order to access the underlying Asset and
// keep it alive.
@@ -483,7 +483,7 @@
}
}
- return createFromAsset((AssetInputStream) is, this);
+ return createFromAsset((AssetInputStream) is, preferAnimation, this);
}
}
@@ -491,11 +491,11 @@
* ImageDecoder will own the AssetInputStream.
*/
private static ImageDecoder createFromAsset(AssetInputStream ais,
- Source source) throws IOException {
+ boolean preferAnimation, Source source) throws IOException {
ImageDecoder decoder = null;
try {
long asset = ais.getNativeAsset();
- decoder = nCreate(asset, source);
+ decoder = nCreate(asset, preferAnimation, source);
} finally {
if (decoder == null) {
IoUtils.closeQuietly(ais);
@@ -517,9 +517,9 @@
private final String mFileName;
@Override
- public ImageDecoder createImageDecoder() throws IOException {
+ public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
InputStream is = mAssets.open(mFileName);
- return createFromAsset((AssetInputStream) is, this);
+ return createFromAsset((AssetInputStream) is, preferAnimation, this);
}
}
@@ -531,8 +531,8 @@
private final File mFile;
@Override
- public ImageDecoder createImageDecoder() throws IOException {
- return createFromFile(mFile, this);
+ public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
+ return createFromFile(mFile, preferAnimation, this);
}
}
@@ -544,7 +544,7 @@
private final Callable<AssetFileDescriptor> mCallable;
@Override
- public ImageDecoder createImageDecoder() throws IOException {
+ public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
AssetFileDescriptor assetFd = null;
try {
assetFd = mCallable.call();
@@ -555,7 +555,7 @@
throw new IOException(e);
}
}
- return createFromAssetFileDescriptor(assetFd, this);
+ return createFromAssetFileDescriptor(assetFd, preferAnimation, this);
}
}
@@ -1740,7 +1740,7 @@
@NonNull
private static Drawable decodeDrawableImpl(@NonNull Source src,
@Nullable OnHeaderDecodedListener listener) throws IOException {
- try (ImageDecoder decoder = src.createImageDecoder()) {
+ try (ImageDecoder decoder = src.createImageDecoder(true /*preferAnimation*/)) {
decoder.mSource = src;
decoder.callHeaderDecoded(listener, src);
@@ -1844,7 +1844,7 @@
@NonNull
private static Bitmap decodeBitmapImpl(@NonNull Source src,
@Nullable OnHeaderDecodedListener listener) throws IOException {
- try (ImageDecoder decoder = src.createImageDecoder()) {
+ try (ImageDecoder decoder = src.createImageDecoder(false /*preferAnimation*/)) {
decoder.mSource = src;
decoder.callHeaderDecoded(listener, src);
@@ -1971,15 +1971,17 @@
}
}
- private static native ImageDecoder nCreate(long asset, Source src) throws IOException;
- private static native ImageDecoder nCreate(ByteBuffer buffer, int position,
- int limit, Source src) throws IOException;
+ private static native ImageDecoder nCreate(long asset,
+ boolean preferAnimation, Source src) throws IOException;
+ private static native ImageDecoder nCreate(ByteBuffer buffer, int position, int limit,
+ boolean preferAnimation, Source src) throws IOException;
private static native ImageDecoder nCreate(byte[] data, int offset, int length,
- Source src) throws IOException;
+ boolean preferAnimation, Source src) throws IOException;
private static native ImageDecoder nCreate(InputStream is, byte[] storage,
- Source src) throws IOException;
+ boolean preferAnimation, Source src) throws IOException;
// The fd must be seekable.
- private static native ImageDecoder nCreate(FileDescriptor fd, Source src) throws IOException;
+ private static native ImageDecoder nCreate(FileDescriptor fd,
+ boolean preferAnimation, Source src) throws IOException;
@NonNull
private static native Bitmap nDecodeBitmap(long nativePtr,
@NonNull ImageDecoder decoder,