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;