aboutsummaryrefslogtreecommitdiff
path: root/library/src/main/java/com/bumptech
diff options
context:
space:
mode:
authorSam Judd <judds@google.com>2014-09-26 18:55:41 -0700
committerSam Judd <judds@google.com>2014-09-28 11:26:26 -0700
commit4185efc3779f8caf2dd8103aa84705e7a88f679c (patch)
treea9ce24e71701d788375af1976619d6b285145c96 /library/src/main/java/com/bumptech
parente08fca6797945ad47b4d895a63bf5b3edc08c3be (diff)
downloadglide-4185efc3779f8caf2dd8103aa84705e7a88f679c.tar.gz
Decode first gif frame before returning resource.
Fixes #159.
Diffstat (limited to 'library/src/main/java/com/bumptech')
-rw-r--r--library/src/main/java/com/bumptech/glide/load/resource/gif/GifDrawable.java59
-rw-r--r--library/src/main/java/com/bumptech/glide/load/resource/gif/GifDrawableResource.java3
-rw-r--r--library/src/main/java/com/bumptech/glide/load/resource/gif/GifDrawableTransformation.java26
-rw-r--r--library/src/main/java/com/bumptech/glide/load/resource/gif/GifResourceDecoder.java82
4 files changed, 91 insertions, 79 deletions
diff --git a/library/src/main/java/com/bumptech/glide/load/resource/gif/GifDrawable.java b/library/src/main/java/com/bumptech/glide/load/resource/gif/GifDrawable.java
index 146a8a4b..3014c4d0 100644
--- a/library/src/main/java/com/bumptech/glide/load/resource/gif/GifDrawable.java
+++ b/library/src/main/java/com/bumptech/glide/load/resource/gif/GifDrawable.java
@@ -14,6 +14,7 @@ import android.os.Build;
import com.bumptech.glide.gifdecoder.GifDecoder;
import com.bumptech.glide.gifdecoder.GifHeader;
import com.bumptech.glide.load.Transformation;
+import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.load.resource.drawable.GlideDrawable;
/**
@@ -47,11 +48,13 @@ public class GifDrawable extends GlideDrawable implements GifFrameManager.FrameC
/**
* Constructor for GifDrawable.
*
- * @see #setFrameTransformation(com.bumptech.glide.load.Transformation, int, int)
+ * @see #setFrameTransformation(com.bumptech.glide.load.Transformation, android.graphics.Bitmap)
*
* @param context A context.
* @param bitmapProvider An {@link com.bumptech.glide.gifdecoder.GifDecoder.BitmapProvider} that can be used to
* retrieve re-usable {@link android.graphics.Bitmap}s.
+ * @param bitmapPool A {@link com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool} that can be used to return
+ * the first frame when this drawable is recycled.
* @param frameTransformation An {@link com.bumptech.glide.load.Transformation} that can be applied to each frame.
* @param targetFrameWidth The desired width of the frames displayed by this drawable (the width of the view or
* {@link com.bumptech.glide.request.target.Target} this drawable is being loaded into).
@@ -60,15 +63,13 @@ public class GifDrawable extends GlideDrawable implements GifFrameManager.FrameC
* @param id An id that uniquely identifies this particular gif.
* @param gifHeader The header data for this gif.
* @param data The full bytes of the gif.
- * @param finalFrameWidth The final width of the frames displayed by this drawable after they have been transformed.
- * @param finalFrameHeight The final height of the frames displayed by this drwaable after they have been
- * transformed.
+ * @param firstFrame The decoded and transformed first frame of this gif.
*/
- public GifDrawable(Context context, GifDecoder.BitmapProvider bitmapProvider,
+ public GifDrawable(Context context, GifDecoder.BitmapProvider bitmapProvider, BitmapPool bitmapPool,
Transformation<Bitmap> frameTransformation, int targetFrameWidth, int targetFrameHeight, String id,
- GifHeader gifHeader, byte[] data, int finalFrameWidth, int finalFrameHeight) {
+ GifHeader gifHeader, byte[] data, Bitmap firstFrame) {
this(new GifState(id, gifHeader, data, context, frameTransformation, targetFrameWidth, targetFrameHeight,
- bitmapProvider, finalFrameWidth, finalFrameHeight));
+ bitmapProvider, bitmapPool, firstFrame));
}
private GifDrawable(GifState state) {
@@ -76,23 +77,25 @@ public class GifDrawable extends GlideDrawable implements GifFrameManager.FrameC
this.decoder = new GifDecoder(state.bitmapProvider);
decoder.setData(state.id, state.gifHeader, state.data);
frameManager = new GifFrameManager(state.context, decoder, state.frameTransformation, state.targetWidth,
- state.targetHeight, state.finalFrameWidth, state.finalFrameHeight);
+ state.targetHeight, state.firstFrame.getWidth(), state.firstFrame.getHeight());
}
- // For testing.
- GifDrawable(GifDecoder decoder, GifFrameManager frameManager, int finalFrameWidth, int finalFrameHeight) {
+ // Visible for testing.
+ GifDrawable(GifDecoder decoder, GifFrameManager frameManager, Bitmap firstFrame, BitmapPool bitmapPool) {
this.decoder = decoder;
this.frameManager = frameManager;
this.state = new GifState(null);
- state.finalFrameWidth = finalFrameWidth;
- state.finalFrameHeight = finalFrameHeight;
+ state.bitmapPool = bitmapPool;
+ state.firstFrame = firstFrame;
+ }
+
+ public Bitmap getFirstFrame() {
+ return state.firstFrame;
}
- public void setFrameTransformation(Transformation<Bitmap> frameTransformation, int finalFrameWidth,
- int finalFrameHeight) {
+ public void setFrameTransformation(Transformation<Bitmap> frameTransformation, Bitmap firstFrame) {
state.frameTransformation = frameTransformation;
- state.finalFrameWidth = finalFrameWidth;
- state.finalFrameHeight = finalFrameHeight;
+ state.firstFrame = firstFrame;
}
public Transformation<Bitmap> getFrameTransformation() {
@@ -147,12 +150,12 @@ public class GifDrawable extends GlideDrawable implements GifFrameManager.FrameC
@Override
public int getIntrinsicWidth() {
- return state.finalFrameWidth;
+ return state.firstFrame.getWidth();
}
@Override
public int getIntrinsicHeight() {
- return state.finalFrameHeight;
+ return state.firstFrame.getHeight();
}
@Override
@@ -167,9 +170,8 @@ public class GifDrawable extends GlideDrawable implements GifFrameManager.FrameC
@Override
public void draw(Canvas canvas) {
- if (currentFrame != null) {
- canvas.drawBitmap(currentFrame, 0, 0, paint);
- }
+ Bitmap toDraw = currentFrame != null ? currentFrame : state.firstFrame;
+ canvas.drawBitmap(toDraw, 0, 0, paint);
}
@Override
@@ -224,6 +226,7 @@ public class GifDrawable extends GlideDrawable implements GifFrameManager.FrameC
*/
public void recycle() {
isRecycled = true;
+ state.bitmapPool.put(state.firstFrame);
frameManager.clear();
}
@@ -255,22 +258,22 @@ public class GifDrawable extends GlideDrawable implements GifFrameManager.FrameC
String id;
GifHeader gifHeader;
byte[] data;
- int finalFrameWidth;
- int finalFrameHeight;
Context context;
Transformation<Bitmap> frameTransformation;
int targetWidth;
int targetHeight;
GifDecoder.BitmapProvider bitmapProvider;
+ BitmapPool bitmapPool;
+ Bitmap firstFrame;
public GifState(String id, GifHeader header, byte[] data, Context context,
Transformation<Bitmap> frameTransformation, int targetWidth, int targetHeight,
- GifDecoder.BitmapProvider provider, int finalFrameWidth, int finalFrameHeight) {
+ GifDecoder.BitmapProvider provider, BitmapPool bitmapPool, Bitmap firstFrame) {
this.id = id;
gifHeader = header;
this.data = data;
- this.finalFrameWidth = finalFrameWidth;
- this.finalFrameHeight = finalFrameHeight;
+ this.bitmapPool = bitmapPool;
+ this.firstFrame = firstFrame;
this.context = context.getApplicationContext();
this.frameTransformation = frameTransformation;
this.targetWidth = targetWidth;
@@ -288,8 +291,8 @@ public class GifDrawable extends GlideDrawable implements GifFrameManager.FrameC
targetWidth = original.targetWidth;
targetHeight = original.targetHeight;
bitmapProvider = original.bitmapProvider;
- finalFrameWidth = original.finalFrameWidth;
- finalFrameHeight = original.finalFrameHeight;
+ bitmapPool = original.bitmapPool;
+ firstFrame = original.firstFrame;
}
}
diff --git a/library/src/main/java/com/bumptech/glide/load/resource/gif/GifDrawableResource.java b/library/src/main/java/com/bumptech/glide/load/resource/gif/GifDrawableResource.java
index 7123834c..7d2b67f2 100644
--- a/library/src/main/java/com/bumptech/glide/load/resource/gif/GifDrawableResource.java
+++ b/library/src/main/java/com/bumptech/glide/load/resource/gif/GifDrawableResource.java
@@ -1,6 +1,7 @@
package com.bumptech.glide.load.resource.gif;
import com.bumptech.glide.load.resource.drawable.DrawableResource;
+import com.bumptech.glide.util.Util;
/**
* A resource wrapping an {@link com.bumptech.glide.load.resource.gif.GifDrawable}.
@@ -12,7 +13,7 @@ public class GifDrawableResource extends DrawableResource<GifDrawable> {
@Override
public int getSize() {
- return drawable.getData().length;
+ return drawable.getData().length + Util.getBitmapByteSize(drawable.getFirstFrame());
}
@Override
diff --git a/library/src/main/java/com/bumptech/glide/load/resource/gif/GifDrawableTransformation.java b/library/src/main/java/com/bumptech/glide/load/resource/gif/GifDrawableTransformation.java
index db1c48d3..5f7b21f6 100644
--- a/library/src/main/java/com/bumptech/glide/load/resource/gif/GifDrawableTransformation.java
+++ b/library/src/main/java/com/bumptech/glide/load/resource/gif/GifDrawableTransformation.java
@@ -1,8 +1,6 @@
package com.bumptech.glide.load.resource.gif;
import android.graphics.Bitmap;
-
-import com.bumptech.glide.load.MultiTransformation;
import com.bumptech.glide.load.Transformation;
import com.bumptech.glide.load.engine.Resource;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
@@ -24,32 +22,20 @@ public class GifDrawableTransformation implements Transformation<GifDrawable> {
@Override
public Resource<GifDrawable> transform(Resource<GifDrawable> resource, int outWidth, int outHeight) {
GifDrawable drawable = resource.get();
- @SuppressWarnings("unchecked")
- Transformation<Bitmap> newTransformation =
- new MultiTransformation<Bitmap>(drawable.getFrameTransformation(), wrapped);
// The drawable needs to be initialized with the correct width and height in order for a view displaying it
// to end up with the right dimensions. Since our transformations may arbitrarily modify the dimensions of
// our gif, here we create a stand in for a frame and pass it to the transformation to see what the final
- // transformed dimensions will be so that our drawable can report the correct intrinsict width and height.
- Bitmap toTest = bitmapPool.get(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(),
- Bitmap.Config.RGB_565);
- if (toTest == null) {
- toTest = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(),
- Bitmap.Config.RGB_565);
- }
-
- Resource<Bitmap> bitmapResource = new BitmapResource(toTest, bitmapPool);
- Resource<Bitmap> transformed = newTransformation.transform(bitmapResource, outWidth, outHeight);
+ // transformed dimensions will be so that our drawable can report the correct intrinsic width and height.
+ Bitmap firstFrame = resource.get().getFirstFrame();
+ Resource<Bitmap> bitmapResource = new BitmapResource(firstFrame, bitmapPool);
+ Resource<Bitmap> transformed = wrapped.transform(bitmapResource, outWidth, outHeight);
if (bitmapResource != transformed) {
bitmapResource.recycle();
}
- Bitmap bitmap = transformed.get();
- final int transformedWidth = bitmap.getWidth();
- final int transformedHeight = bitmap.getHeight();
- transformed.recycle();
+ Bitmap transformedFrame = transformed.get();
- drawable.setFrameTransformation(newTransformation, transformedWidth, transformedHeight);
+ drawable.setFrameTransformation(wrapped, transformedFrame);
return resource;
}
diff --git a/library/src/main/java/com/bumptech/glide/load/resource/gif/GifResourceDecoder.java b/library/src/main/java/com/bumptech/glide/load/resource/gif/GifResourceDecoder.java
index f2725efb..cce52419 100644
--- a/library/src/main/java/com/bumptech/glide/load/resource/gif/GifResourceDecoder.java
+++ b/library/src/main/java/com/bumptech/glide/load/resource/gif/GifResourceDecoder.java
@@ -3,7 +3,6 @@ package com.bumptech.glide.load.resource.gif;
import android.content.Context;
import android.graphics.Bitmap;
import android.util.Log;
-
import com.bumptech.glide.Glide;
import com.bumptech.glide.gifdecoder.GifDecoder;
import com.bumptech.glide.gifdecoder.GifHeader;
@@ -28,22 +27,30 @@ import java.util.UUID;
*/
public class GifResourceDecoder implements ResourceDecoder<InputStream, GifDrawable> {
private static final String TAG = "GifResourceDecoder";
- private static final GifHeaderParserPool PARSER_POOL = new DefaultGifHeaderParserPool();
+ private static final GifHeaderParserPool PARSER_POOL = new GifHeaderParserPool();
+ private static final GifDecoderPool DECODER_POOL = new GifDecoderPool();
+
private final Context context;
- private final BitmapPool bitmapPool;
private final GifHeaderParserPool parserPool;
+ private BitmapPool bitmapPool;
+ private final GifDecoderPool decoderPool;
+ private final GifBitmapProvider provider;
public GifResourceDecoder(Context context) {
this(context, Glide.get(context).getBitmapPool());
}
public GifResourceDecoder(Context context, BitmapPool bitmapPool) {
- this(context, bitmapPool, PARSER_POOL);
+ this(context, bitmapPool, PARSER_POOL, DECODER_POOL);
}
- GifResourceDecoder(Context context, BitmapPool bitmapPool, GifHeaderParserPool parserPool) {
+ // Visible for testing.
+ GifResourceDecoder(Context context, BitmapPool bitmapPool, GifHeaderParserPool parserPool,
+ GifDecoderPool decoderPool) {
this.context = context;
this.bitmapPool = bitmapPool;
+ this.decoderPool = decoderPool;
+ this.provider = new GifBitmapProvider(bitmapPool);
this.parserPool = parserPool;
}
@@ -51,14 +58,16 @@ public class GifResourceDecoder implements ResourceDecoder<InputStream, GifDrawa
public GifDrawableResource decode(InputStream source, int width, int height) {
byte[] data = inputStreamToBytes(source);
final GifHeaderParser parser = parserPool.obtain(data);
+ final GifDecoder decoder = decoderPool.obtain(provider);
try {
- return decode(data, width, height, parser);
+ return decode(data, width, height, parser, decoder);
} finally {
parserPool.release(parser);
+ decoderPool.release(decoder);
}
}
- private GifDrawableResource decode(byte[] data, int width, int height, GifHeaderParser parser) {
+ private GifDrawableResource decode(byte[] data, int width, int height, GifHeaderParser parser, GifDecoder decoder) {
final GifHeader header = parser.parseHeader();
if (header.getNumFrames() <= 0 || header.getStatus() != GifDecoder.STATUS_OK) {
// If we couldn't decode the GIF, we will end up with a frame count of 0.
@@ -66,14 +75,21 @@ public class GifResourceDecoder implements ResourceDecoder<InputStream, GifDrawa
}
String id = getGifId(data);
+ Bitmap firstFrame = decodeFirstFrame(decoder, id, header, data);
+ Transformation<Bitmap> unitTransformation = UnitTransformation.get();
- Transformation<Bitmap> transformation = UnitTransformation.get();
- GifDrawable gifDrawable = new GifDrawable(context, new GifBitmapProvider(bitmapPool), transformation, width,
- height, id, header, data, header.getWidth(), header.getHeight());
+ GifDrawable gifDrawable = new GifDrawable(context, provider, bitmapPool, unitTransformation, width, height, id,
+ header, data, firstFrame);
return new GifDrawableResource(gifDrawable);
}
+ private Bitmap decodeFirstFrame(GifDecoder decoder, String id, GifHeader header, byte[] data) {
+ decoder.setData(id, header, data);
+ decoder.advance();
+ return decoder.getNextFrame();
+ }
+
@Override
public String getId() {
return "";
@@ -94,12 +110,12 @@ public class GifResourceDecoder implements ResourceDecoder<InputStream, GifDrawa
}
private static byte[] inputStreamToBytes(InputStream is) {
- final int bufferSize = 16384, initialCapacity = bufferSize;
- ByteArrayOutputStream buffer = new ByteArrayOutputStream(initialCapacity);
+ final int bufferSize = 16384;
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream(bufferSize);
try {
int nRead;
byte[] data = new byte[bufferSize];
- while ((nRead = is.read(data, 0, data.length)) != -1) {
+ while ((nRead = is.read(data)) != -1) {
buffer.write(data, 0, nRead);
}
buffer.flush();
@@ -110,32 +126,38 @@ public class GifResourceDecoder implements ResourceDecoder<InputStream, GifDrawa
return buffer.toByteArray();
}
- interface GifHeaderParserPool {
- public GifHeaderParser obtain(byte[] data);
- public void release(GifHeaderParser parser);
+ // Visible for testing.
+ static class GifDecoderPool {
+ private final Queue<GifDecoder> pool = Util.createQueue(0);
+
+ public synchronized GifDecoder obtain(GifDecoder.BitmapProvider bitmapProvider) {
+ GifDecoder result = pool.poll();
+ if (result == null) {
+ result = new GifDecoder(bitmapProvider);
+ }
+ return result;
+ }
+
+ public synchronized void release(GifDecoder decoder) {
+ decoder.clear();
+ pool.offer(decoder);
+ }
}
- private static class DefaultGifHeaderParserPool implements GifHeaderParserPool {
- private static final Queue<GifHeaderParser> POOL = Util.createQueue(0);
+ // Visible for testing.
+ static class GifHeaderParserPool {
+ private final Queue<GifHeaderParser> pool = Util.createQueue(0);
- @Override
- public GifHeaderParser obtain(byte[] data) {
- GifHeaderParser result;
- synchronized (POOL) {
- result = POOL.poll();
- }
+ public synchronized GifHeaderParser obtain(byte[] data) {
+ GifHeaderParser result = pool.poll();
if (result == null) {
result = new GifHeaderParser();
}
-
return result.setData(data);
}
- @Override
- public void release(GifHeaderParser parser) {
- synchronized (POOL) {
- POOL.offer(parser);
- }
+ public synchronized void release(GifHeaderParser parser) {
+ pool.offer(parser);
}
}