From d704c8954ae667483afa9a8efdb8b0059e1e979e Mon Sep 17 00:00:00 2001 From: Vignesh Venkatasubramanian Date: Tue, 14 Feb 2023 15:38:48 -0800 Subject: [PATCH] Rename AnimatedWebpDecoder to AnimatedImageDecoder This sets up the class to be used in a generic manner for other animated image formats (like AVIF) that are supported by the android platform's ImageDecoder. PiperOrigin-RevId: 509657824 --- .../com/bumptech/glide/RegistryFactory.java | 6 +- .../drawable/AnimatedImageDecoder.java | 164 ++++++++++++++++++ .../drawable/AnimatedWebpDecoder.java | 7 +- 3 files changed, 171 insertions(+), 6 deletions(-) create mode 100644 library/src/main/java/com/bumptech/glide/load/resource/drawable/AnimatedImageDecoder.java diff --git a/library/src/main/java/com/bumptech/glide/RegistryFactory.java b/library/src/main/java/com/bumptech/glide/RegistryFactory.java index f281aa048a..26e238eb3b 100644 --- a/library/src/main/java/com/bumptech/glide/RegistryFactory.java +++ b/library/src/main/java/com/bumptech/glide/RegistryFactory.java @@ -58,7 +58,7 @@ import com.bumptech.glide.load.resource.bitmap.UnitBitmapDecoder; import com.bumptech.glide.load.resource.bitmap.VideoDecoder; import com.bumptech.glide.load.resource.bytes.ByteBufferRewinder; -import com.bumptech.glide.load.resource.drawable.AnimatedWebpDecoder; +import com.bumptech.glide.load.resource.drawable.AnimatedImageDecoder; import com.bumptech.glide.load.resource.drawable.ResourceDrawableDecoder; import com.bumptech.glide.load.resource.drawable.UnitDrawableDecoder; import com.bumptech.glide.load.resource.file.FileDecoder; @@ -174,12 +174,12 @@ private static void initializeDefaults( Registry.BUCKET_ANIMATION, InputStream.class, Drawable.class, - AnimatedWebpDecoder.streamDecoder(imageHeaderParsers, arrayPool)); + AnimatedImageDecoder.streamDecoder(imageHeaderParsers, arrayPool)); registry.append( Registry.BUCKET_ANIMATION, ByteBuffer.class, Drawable.class, - AnimatedWebpDecoder.byteBufferDecoder(imageHeaderParsers, arrayPool)); + AnimatedImageDecoder.byteBufferDecoder(imageHeaderParsers, arrayPool)); } ResourceDrawableDecoder resourceDrawableDecoder = new ResourceDrawableDecoder(context); diff --git a/library/src/main/java/com/bumptech/glide/load/resource/drawable/AnimatedImageDecoder.java b/library/src/main/java/com/bumptech/glide/load/resource/drawable/AnimatedImageDecoder.java new file mode 100644 index 0000000000..e09e4d9f84 --- /dev/null +++ b/library/src/main/java/com/bumptech/glide/load/resource/drawable/AnimatedImageDecoder.java @@ -0,0 +1,164 @@ +package com.bumptech.glide.load.resource.drawable; + +import android.graphics.Bitmap; +import android.graphics.ImageDecoder; +import android.graphics.ImageDecoder.Source; +import android.graphics.drawable.AnimatedImageDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; +import com.bumptech.glide.load.ImageHeaderParser; +import com.bumptech.glide.load.ImageHeaderParser.ImageType; +import com.bumptech.glide.load.ImageHeaderParserUtils; +import com.bumptech.glide.load.Options; +import com.bumptech.glide.load.ResourceDecoder; +import com.bumptech.glide.load.engine.Resource; +import com.bumptech.glide.load.engine.bitmap_recycle.ArrayPool; +import com.bumptech.glide.load.resource.DefaultOnHeaderDecodedListener; +import com.bumptech.glide.util.ByteBufferUtil; +import com.bumptech.glide.util.Synthetic; +import com.bumptech.glide.util.Util; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.util.List; + +/** + * Allows decoding animated images using {@link ImageDecoder}. + * + *

Supported formats: WebP on Android P+. + */ +@RequiresApi(Build.VERSION_CODES.P) +public final class AnimatedImageDecoder { + private final List imageHeaderParsers; + private final ArrayPool arrayPool; + + public static ResourceDecoder streamDecoder( + List imageHeaderParsers, ArrayPool arrayPool) { + return new StreamAnimatedImageDecoder(new AnimatedImageDecoder(imageHeaderParsers, arrayPool)); + } + + public static ResourceDecoder byteBufferDecoder( + List imageHeaderParsers, ArrayPool arrayPool) { + return new ByteBufferAnimatedImageDecoder( + new AnimatedImageDecoder(imageHeaderParsers, arrayPool)); + } + + private AnimatedImageDecoder(List imageHeaderParsers, ArrayPool arrayPool) { + this.imageHeaderParsers = imageHeaderParsers; + this.arrayPool = arrayPool; + } + + @Synthetic + boolean handles(ByteBuffer byteBuffer) throws IOException { + return isHandled(ImageHeaderParserUtils.getType(imageHeaderParsers, byteBuffer)); + } + + @Synthetic + boolean handles(InputStream is) throws IOException { + return isHandled(ImageHeaderParserUtils.getType(imageHeaderParsers, is, arrayPool)); + } + + private boolean isHandled(ImageType imageType) { + return imageType == ImageType.ANIMATED_WEBP; + } + + @Synthetic + Resource decode(@NonNull Source source, int width, int height, @NonNull Options options) + throws IOException { + Drawable decoded = + ImageDecoder.decodeDrawable( + source, new DefaultOnHeaderDecodedListener(width, height, options)); + if (!(decoded instanceof AnimatedImageDrawable)) { + throw new IOException( + "Received unexpected drawable type for animated webp, failing: " + decoded); + } + return new AnimatedImageDrawableResource((AnimatedImageDrawable) decoded); + } + + private static final class AnimatedImageDrawableResource implements Resource { + /** A totally made up number of the number of frames we think are held in memory at once... */ + private static final int ESTIMATED_NUMBER_OF_FRAMES = 2; + + private final AnimatedImageDrawable imageDrawable; + + AnimatedImageDrawableResource(AnimatedImageDrawable imageDrawable) { + this.imageDrawable = imageDrawable; + } + + @NonNull + @Override + public Class getResourceClass() { + return Drawable.class; + } + + @NonNull + @Override + public AnimatedImageDrawable get() { + return imageDrawable; + } + + @Override + public int getSize() { + return imageDrawable.getIntrinsicWidth() + * imageDrawable.getIntrinsicHeight() + * Util.getBytesPerPixel(Bitmap.Config.ARGB_8888) + * ESTIMATED_NUMBER_OF_FRAMES; + } + + @Override + public void recycle() { + imageDrawable.stop(); + imageDrawable.clearAnimationCallbacks(); + } + } + + private static final class StreamAnimatedImageDecoder + implements ResourceDecoder { + + private final AnimatedImageDecoder delegate; + + StreamAnimatedImageDecoder(AnimatedImageDecoder delegate) { + this.delegate = delegate; + } + + @Override + public boolean handles(@NonNull InputStream source, @NonNull Options options) + throws IOException { + return delegate.handles(source); + } + + @Override + public Resource decode( + @NonNull InputStream is, int width, int height, @NonNull Options options) + throws IOException { + Source source = ImageDecoder.createSource(ByteBufferUtil.fromStream(is)); + return delegate.decode(source, width, height, options); + } + } + + private static final class ByteBufferAnimatedImageDecoder + implements ResourceDecoder { + + private final AnimatedImageDecoder delegate; + + ByteBufferAnimatedImageDecoder(AnimatedImageDecoder delegate) { + this.delegate = delegate; + } + + @Override + public boolean handles(@NonNull ByteBuffer source, @NonNull Options options) + throws IOException { + return delegate.handles(source); + } + + @Override + public Resource decode( + @NonNull ByteBuffer byteBuffer, int width, int height, @NonNull Options options) + throws IOException { + Source source = ImageDecoder.createSource(byteBuffer); + return delegate.decode(source, width, height, options); + } + } +} diff --git a/library/src/main/java/com/bumptech/glide/load/resource/drawable/AnimatedWebpDecoder.java b/library/src/main/java/com/bumptech/glide/load/resource/drawable/AnimatedWebpDecoder.java index 371b5e5740..bc5497b453 100644 --- a/library/src/main/java/com/bumptech/glide/load/resource/drawable/AnimatedWebpDecoder.java +++ b/library/src/main/java/com/bumptech/glide/load/resource/drawable/AnimatedWebpDecoder.java @@ -25,10 +25,11 @@ import java.util.List; /** - * Allows decoding animated webp images using {@link ImageDecoder} on Android P+. - * - *

This class is experimental and may be removed at any time. + * Allows decoding animated webp images using {@link ImageDecoder} on Android P+. @Deprecated This + * class has been replaced by {@link AnimatedImageDecoder} and is not used in Glide by default. It + * will be removed in a future version. */ +@Deprecated @RequiresApi(Build.VERSION_CODES.P) public final class AnimatedWebpDecoder { private final List imageHeaderParsers;