Skip to content

Commit

Permalink
Rename AnimatedWebpDecoder to AnimatedImageDecoder
Browse files Browse the repository at this point in the history
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
  • Loading branch information
vigneshvg authored and glide-copybara-robot committed Feb 14, 2023
1 parent 895e2f7 commit d704c89
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 6 deletions.
6 changes: 3 additions & 3 deletions library/src/main/java/com/bumptech/glide/RegistryFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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}.
*
* <p>Supported formats: WebP on Android P+.
*/
@RequiresApi(Build.VERSION_CODES.P)
public final class AnimatedImageDecoder {
private final List<ImageHeaderParser> imageHeaderParsers;
private final ArrayPool arrayPool;

public static ResourceDecoder<InputStream, Drawable> streamDecoder(
List<ImageHeaderParser> imageHeaderParsers, ArrayPool arrayPool) {
return new StreamAnimatedImageDecoder(new AnimatedImageDecoder(imageHeaderParsers, arrayPool));
}

public static ResourceDecoder<ByteBuffer, Drawable> byteBufferDecoder(
List<ImageHeaderParser> imageHeaderParsers, ArrayPool arrayPool) {
return new ByteBufferAnimatedImageDecoder(
new AnimatedImageDecoder(imageHeaderParsers, arrayPool));
}

private AnimatedImageDecoder(List<ImageHeaderParser> 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<Drawable> 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<Drawable> {
/** 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<Drawable> 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<InputStream, Drawable> {

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<Drawable> 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<ByteBuffer, Drawable> {

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<Drawable> 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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@
import java.util.List;

/**
* Allows decoding animated webp images using {@link ImageDecoder} on Android P+.
*
* <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<ImageHeaderParser> imageHeaderParsers;
Expand Down

0 comments on commit d704c89

Please sign in to comment.