diff --git a/library/src/main/java/com/bumptech/glide/load/resource/bitmap/StreamBitmapDecoder.java b/library/src/main/java/com/bumptech/glide/load/resource/bitmap/StreamBitmapDecoder.java index cb36cd7137046aac16c035b5c08897024d201bda..24877dbf87fa5f775c1dda4f122164e58c7640af 100644 --- a/library/src/main/java/com/bumptech/glide/load/resource/bitmap/StreamBitmapDecoder.java +++ b/library/src/main/java/com/bumptech/glide/load/resource/bitmap/StreamBitmapDecoder.java @@ -1,10 +1,12 @@ package com.bumptech.glide.load.resource.bitmap; +import android.content.Context; import android.graphics.Bitmap; +import com.bumptech.glide.Glide; import com.bumptech.glide.Resource; +import com.bumptech.glide.load.DecodeFormat; import com.bumptech.glide.load.ResourceDecoder; import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; -import com.bumptech.glide.load.DecodeFormat; import java.io.InputStream; @@ -16,6 +18,10 @@ public class StreamBitmapDecoder implements ResourceDecoder private DecodeFormat decodeFormat; private String id; + public StreamBitmapDecoder(Context context) { + this(Glide.get(context).getBitmapPool()); + } + public StreamBitmapDecoder(BitmapPool bitmapPool) { this(Downsampler.AT_LEAST, bitmapPool, DecodeFormat.PREFER_RGB_565); } diff --git a/library/src/main/java/com/bumptech/glide/load/resource/gif/GifData.java b/library/src/main/java/com/bumptech/glide/load/resource/gif/GifData.java index 71c073a84c94d25c5e5fcc720cbb90c21ed4f915..34e6537ab82868d23280d0d1c2954a058cf76ff1 100644 --- a/library/src/main/java/com/bumptech/glide/load/resource/gif/GifData.java +++ b/library/src/main/java/com/bumptech/glide/load/resource/gif/GifData.java @@ -47,7 +47,7 @@ public class GifData { public GifDrawable getDrawable() { GifDecoder gifDecoder = new GifDecoder(bitmapPool); gifDecoder.setData(gifId, header, data); - GifFrameManager frameManager = new GifFrameManager(context, getFrameTransformation()); + GifFrameManager frameManager = new GifFrameManager(context, gifDecoder, getFrameTransformation()); GifDrawable result = new GifDrawable(gifDecoder, frameManager); drawables.add(result); diff --git a/library/src/main/java/com/bumptech/glide/load/resource/gif/GifResource.java b/library/src/main/java/com/bumptech/glide/load/resource/gif/GifDataResource.java similarity index 79% rename from library/src/main/java/com/bumptech/glide/load/resource/gif/GifResource.java rename to library/src/main/java/com/bumptech/glide/load/resource/gif/GifDataResource.java index b2e4fe81fdb013985bf593fb79abc524af36ab2d..78a7ef200ce51c34d8b5bf2800ff15b43d43fa5c 100644 --- a/library/src/main/java/com/bumptech/glide/load/resource/gif/GifResource.java +++ b/library/src/main/java/com/bumptech/glide/load/resource/gif/GifDataResource.java @@ -2,10 +2,10 @@ package com.bumptech.glide.load.resource.gif; import com.bumptech.glide.Resource; -public class GifResource extends Resource { +public class GifDataResource extends Resource { private GifData gifData; - public GifResource(GifData gifData) { + public GifDataResource(GifData gifData) { this.gifData = gifData; } 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 3067f321acb9c7f5fa15438b09df5360be8d322a..636c1c3f063c1410eecc20bbe367115c8d874ca9 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 @@ -29,7 +29,7 @@ public class GifDrawable extends Drawable implements Animatable, GifFrameManager public void start() { if (!isRunning) { isRunning = true; - frameManager.getNextFrame(decoder, this); + frameManager.getNextFrame(this); invalidateSelf(); } } @@ -102,7 +102,7 @@ public class GifDrawable extends Drawable implements Animatable, GifFrameManager invalidateSelf(); } - frameManager.getNextFrame(decoder, this); + frameManager.getNextFrame(this); } public void recycle() { diff --git a/library/src/main/java/com/bumptech/glide/load/resource/gif/GifFrameManager.java b/library/src/main/java/com/bumptech/glide/load/resource/gif/GifFrameManager.java index fbca421ee7d29ac5de332a63bbc210113aaabdc4..eab88f49ac6d2742b0e4c5949f890628c837b7ac 100644 --- a/library/src/main/java/com/bumptech/glide/load/resource/gif/GifFrameManager.java +++ b/library/src/main/java/com/bumptech/glide/load/resource/gif/GifFrameManager.java @@ -6,21 +6,32 @@ import android.os.Handler; import android.os.Looper; import android.os.SystemClock; import com.bumptech.glide.Glide; +import com.bumptech.glide.load.ResourceDecoder; +import com.bumptech.glide.load.ResourceEncoder; +import com.bumptech.glide.load.SkipCache; import com.bumptech.glide.load.Transformation; import com.bumptech.glide.load.engine.cache.MemorySizeCalculator; import com.bumptech.glide.load.resource.NullCacheDecoder; +import com.bumptech.glide.load.resource.bitmap.BitmapEncoder; +import com.bumptech.glide.load.resource.bitmap.StreamBitmapDecoder; import com.bumptech.glide.load.resource.gif.decoder.GifDecoder; import com.bumptech.glide.request.target.SimpleTarget; +import java.io.InputStream; + class GifFrameManager { + // 16ms per frame = 60fps static final long MIN_FRAME_DELAY = 16; private final MemorySizeCalculator calculator; - private final GifFrameLoader frameLoader; + private final GifFrameModelLoader frameLoader; private final GifFrameResourceDecoder frameResourceDecoder; - private final NullCacheDecoder cacheDecoder; + private final ResourceDecoder cacheDecoder; + private final GifDecoder decoder; private final Handler mainHandler; + private final ResourceEncoder encoder; + private final Context context; + private Transformation transformation; - private Context context; private DelayTarget current; private DelayTarget next; @@ -28,31 +39,46 @@ class GifFrameManager { public void onFrameRead(Bitmap frame); } - public GifFrameManager(Context context, Transformation transformation) { - this(context, new Handler(Looper.getMainLooper()), transformation); + public GifFrameManager(Context context, GifDecoder decoder, Transformation transformation) { + this(context, decoder, new Handler(Looper.getMainLooper()), transformation); } - public GifFrameManager(Context context, Handler mainHandler, + public GifFrameManager(Context context, GifDecoder decoder, Handler mainHandler, Transformation transformation) { this.context = context; + this.decoder = decoder; this.mainHandler = mainHandler; this.transformation = transformation; calculator = new MemorySizeCalculator(context); - frameLoader = new GifFrameLoader(); + frameLoader = new GifFrameModelLoader(); frameResourceDecoder = new GifFrameResourceDecoder(); - cacheDecoder = NullCacheDecoder.get(); + + if (!decoder.isTransparent()) { + // For non transparent gifs, we can beat the performance of our gif decoder for each frame by decoding jpegs + // from disk. + cacheDecoder = new StreamBitmapDecoder(context); + encoder = new BitmapEncoder(Bitmap.CompressFormat.JPEG, 70); + } else { + // For transparent gifs, we would have to encode as pngs which is actually slower than our gif decoder so we + // avoid writing frames to the disk cache entirely. + cacheDecoder = NullCacheDecoder.get(); + encoder = SkipCache.get(); + } } Transformation getTransformation() { return transformation; } - public void getNextFrame(final GifDecoder decoder, FrameCallback cb) { + public void getNextFrame(FrameCallback cb) { decoder.advance(); - boolean skipCache = decoder.getDecodedFrameByteSize() > calculator.getMemoryCacheSize() / 2; + // We don't want to blow out the entire memory cache with frames of gifs, so try to set some + // maximum size beyond which we will always just decode one frame at a time. + boolean skipCache = decoder.getDecodedFramesByteSizeSum() > calculator.getMemoryCacheSize() / 2; long targetTime = SystemClock.uptimeMillis() + (Math.min(MIN_FRAME_DELAY, decoder.getNextDelay())); next = new DelayTarget(decoder, cb, targetTime, mainHandler); + Glide.with(context) .using(frameLoader, GifDecoder.class) .load(decoder) @@ -60,8 +86,8 @@ class GifFrameManager { .decoder(frameResourceDecoder) .cacheDecoder(cacheDecoder) .transform(transformation) + .encoder(encoder) .skipMemoryCache(skipCache) - .skipDiskCache(true) .into(next); } diff --git a/library/src/main/java/com/bumptech/glide/load/resource/gif/GifFrameLoader.java b/library/src/main/java/com/bumptech/glide/load/resource/gif/GifFrameModelLoader.java similarity index 92% rename from library/src/main/java/com/bumptech/glide/load/resource/gif/GifFrameLoader.java rename to library/src/main/java/com/bumptech/glide/load/resource/gif/GifFrameModelLoader.java index d573e8a2ee18cf4d61b6a70750414ae1afa75dac..b4d54e07b53f41947b0aa62cdb6ddb368bc0a7fa 100644 --- a/library/src/main/java/com/bumptech/glide/load/resource/gif/GifFrameLoader.java +++ b/library/src/main/java/com/bumptech/glide/load/resource/gif/GifFrameModelLoader.java @@ -5,7 +5,7 @@ import com.bumptech.glide.load.data.DataFetcher; import com.bumptech.glide.load.model.ModelLoader; import com.bumptech.glide.load.resource.gif.decoder.GifDecoder; -public class GifFrameLoader implements ModelLoader { +public class GifFrameModelLoader implements ModelLoader { @Override public DataFetcher getResourceFetcher(GifDecoder model, int width, int height) { 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 4d59f0e3655d14e85dfa47565a3a8b2983c30a15..423252cbbeb09cb353c27b34636fa8c774356098 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 @@ -7,6 +7,7 @@ import com.bumptech.glide.load.ResourceDecoder; import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; import com.bumptech.glide.load.resource.gif.decoder.GifHeader; import com.bumptech.glide.load.resource.gif.decoder.GifHeaderParser; +import com.bumptech.glide.util.Util; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -30,11 +31,11 @@ public class GifResourceDecoder implements ResourceDecoder } @Override - public GifResource decode(InputStream source, int width, int height) throws IOException { + public GifDataResource decode(InputStream source, int width, int height) throws IOException { byte[] data = inputStreamToBytes(source); GifHeader header = new GifHeaderParser(data).parseHeader(); String id = getGifId(data); - return new GifResource(new GifData(context, bitmapPool, id, header, data)); + return new GifDataResource(new GifData(context, bitmapPool, id, header, data)); } @Override @@ -46,7 +47,7 @@ public class GifResourceDecoder implements ResourceDecoder try { MessageDigest digest = MessageDigest.getInstance("SHA-1"); digest.update(data); - return new String(digest.digest()); + return Util.sha256BytesToHex(digest.digest()); } catch (NoSuchAlgorithmException e) { if (Log.isLoggable(TAG, Log.WARN)) { Log.w(TAG, "Missing sha1 algorithm?", e); diff --git a/library/src/main/java/com/bumptech/glide/load/resource/gif/decoder/GifDecoder.java b/library/src/main/java/com/bumptech/glide/load/resource/gif/decoder/GifDecoder.java index da4aeb5d68f297a4529f6b5eb5fe724abdbe6273..04abea5f5508d5d136fffa445917e60db0e6248f 100644 --- a/library/src/main/java/com/bumptech/glide/load/resource/gif/decoder/GifDecoder.java +++ b/library/src/main/java/com/bumptech/glide/load/resource/gif/decoder/GifDecoder.java @@ -137,7 +137,7 @@ public class GifDecoder { return data; } - public int getDecodedFrameByteSize() { + public int getDecodedFramesByteSizeSum() { // 4 == ARGB_8888, 2 == RGB_565 return header.frameCount * header.width * header.height * (header.isTransparent ? 4 : 2); } diff --git a/library/src/main/java/com/bumptech/glide/load/resource/gifbitmap/GifBitmapWrapperTransformation.java b/library/src/main/java/com/bumptech/glide/load/resource/gifbitmap/GifBitmapWrapperTransformation.java index 5b65c641dd5d271080dc4ddc8a0c9cea92303524..50d2fb71d5ce6594c2dd256e9d040616a35948f9 100644 --- a/library/src/main/java/com/bumptech/glide/load/resource/gifbitmap/GifBitmapWrapperTransformation.java +++ b/library/src/main/java/com/bumptech/glide/load/resource/gifbitmap/GifBitmapWrapperTransformation.java @@ -6,7 +6,7 @@ import com.bumptech.glide.Resource; import com.bumptech.glide.load.MultiTransformation; import com.bumptech.glide.load.Transformation; import com.bumptech.glide.load.resource.gif.GifData; -import com.bumptech.glide.load.resource.gif.GifResource; +import com.bumptech.glide.load.resource.gif.GifDataResource; public class GifBitmapWrapperTransformation implements Transformation { private Context context; @@ -33,7 +33,7 @@ public class GifBitmapWrapperTransformation implements Transformation newTransformation = new MultiTransformation(gifData.getFrameTransformation(), wrapped); gifData.setFrameTransformation(newTransformation); - return new GifBitmapWrapperResource(new GifBitmapWrapper(null, new GifResource(gifData))); + return new GifBitmapWrapperResource(new GifBitmapWrapper(null, new GifDataResource(gifData))); } return resource; } diff --git a/library/src/test/java/com/bumptech/glide/GlideTest.java b/library/src/test/java/com/bumptech/glide/GlideTest.java index e207c6ed11e4fec16e8b22eed1338a2a6c091ef0..d983f3b938d9416e2722731108a950e65644d860 100644 --- a/library/src/test/java/com/bumptech/glide/GlideTest.java +++ b/library/src/test/java/com/bumptech/glide/GlideTest.java @@ -495,10 +495,6 @@ public class GlideTest { Glide.with(getContext()).load(0.5f).into(target); } - @Test - public void testSomething() { - } - @Test public void testNullModelDoesNotThrow() { String nullString = null; diff --git a/library/src/test/java/com/bumptech/glide/load/resource/gif/GifResourceTest.java b/library/src/test/java/com/bumptech/glide/load/resource/gif/GifDataResourceTest.java similarity index 88% rename from library/src/test/java/com/bumptech/glide/load/resource/gif/GifResourceTest.java rename to library/src/test/java/com/bumptech/glide/load/resource/gif/GifDataResourceTest.java index c5f1b910531bbfc7dd3dfd849cac6a77cd524b7c..6cfc10fa332c82d08ce138951417531eb005acc2 100644 --- a/library/src/test/java/com/bumptech/glide/load/resource/gif/GifResourceTest.java +++ b/library/src/test/java/com/bumptech/glide/load/resource/gif/GifDataResourceTest.java @@ -12,14 +12,14 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @RunWith(RobolectricTestRunner.class) -public class GifResourceTest { - private GifResource resource; +public class GifDataResourceTest { + private GifDataResource resource; private GifData gifData; @Before public void setUp() { gifData = mock(GifData.class); - resource = new GifResource(gifData); + resource = new GifDataResource(gifData); } @Test diff --git a/library/src/test/java/com/bumptech/glide/load/resource/gif/GifDrawableTest.java b/library/src/test/java/com/bumptech/glide/load/resource/gif/GifDrawableTest.java index 79f94618df49b3511f2fe2c4f80755aa414cf60f..08745731f534bb3486fb2c5782768a92b0baf860 100644 --- a/library/src/test/java/com/bumptech/glide/load/resource/gif/GifDrawableTest.java +++ b/library/src/test/java/com/bumptech/glide/load/resource/gif/GifDrawableTest.java @@ -66,7 +66,7 @@ public class GifDrawableTest { public void testRequestsNextFrameOnStart() { drawable.start(); - verify(frameManager).getNextFrame(eq(gifDecoder), eq(drawable)); + verify(frameManager).getNextFrame(eq(drawable)); } @Test @@ -81,7 +81,7 @@ public class GifDrawableTest { drawable.start(); drawable.start(); - verify(frameManager, times(1)).getNextFrame(eq(gifDecoder), eq(drawable)); + verify(frameManager, times(1)).getNextFrame(eq(drawable)); } @Test @@ -101,7 +101,7 @@ public class GifDrawableTest { drawable.setIsRunning(true); drawable.onFrameRead(Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)); - verify(frameManager).getNextFrame(eq(gifDecoder), eq(drawable)); + verify(frameManager).getNextFrame(eq(drawable)); } @Test @@ -117,7 +117,7 @@ public class GifDrawableTest { drawable.setIsRunning(false); drawable.onFrameRead(Bitmap.createBitmap(10, 100, Bitmap.Config.ARGB_8888)); - verify(frameManager, never()).getNextFrame(eq(gifDecoder), eq(drawable)); + verify(frameManager, never()).getNextFrame(eq(drawable)); } @Test diff --git a/library/src/test/java/com/bumptech/glide/load/resource/gif/GifFrameModelLoaderTest.java b/library/src/test/java/com/bumptech/glide/load/resource/gif/GifFrameModelLoaderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a2049e9d925057564fa3b62d272a2ff4194efe79 --- /dev/null +++ b/library/src/test/java/com/bumptech/glide/load/resource/gif/GifFrameModelLoaderTest.java @@ -0,0 +1,40 @@ +package com.bumptech.glide.load.resource.gif; + +import com.bumptech.glide.Priority; +import com.bumptech.glide.load.resource.gif.decoder.GifDecoder; +import org.junit.Before; +import org.junit.Test; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class GifFrameModelLoaderTest { + private GifFrameModelLoader loader; + private GifDecoder decoder; + + @Before + public void setUp() { + loader = new GifFrameModelLoader(); + decoder = mock(GifDecoder.class); + } + + @Test + public void testIdIncludesGifDecoderIdAndFrameIndex() { + String id = "asdfasd"; + int frameIndex = 124; + when(decoder.getId()).thenReturn(id); + when(decoder.getCurrentFrameIndex()).thenReturn(frameIndex); + + String loaderId = loader.getId(decoder); + + assertTrue(loaderId.contains(id)); + assertTrue(loaderId.contains(String.valueOf(frameIndex))); + } + + @Test + public void testAlwaysReturnsGivenDecoderFromFetcher() throws Exception { + assertEquals(decoder, loader.getResourceFetcher(decoder, 100, 100).loadData(Priority.NORMAL)); + } +} diff --git a/samples/flickr/res/layout/flickr_search_activity.xml b/samples/flickr/res/layout/flickr_search_activity.xml index 85b2414469dff3fdf29b2a6f72562a882ab27c81..fe172fddccddcc6be5328f6f6b1d162613ab5e38 100644 --- a/samples/flickr/res/layout/flickr_search_activity.xml +++ b/samples/flickr/res/layout/flickr_search_activity.xml @@ -4,11 +4,6 @@ android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> -