提交 a37592fb 编写于 作者: S Sam Judd

Fix GIF drawable transformations.

上级 da7bc32c
......@@ -92,7 +92,7 @@ public class DrawableRequestBuilder<ModelType> extends
}
public DrawableRequestBuilder<ModelType> bitmapTransform(Transformation<Bitmap> bitmapTransformation) {
return transform(new GifBitmapWrapperTransformation(context, bitmapTransformation));
return transform(new GifBitmapWrapperTransformation(bitmapTransformation));
}
@Override
......
......@@ -16,15 +16,20 @@ public class GifData {
private final GifHeader header;
private final byte[] data;
private String gifId;
private final int targetWidth;
private final int targetHeight;
private final List<GifDrawable> drawables = new ArrayList<GifDrawable>();
private Transformation<Bitmap> frameTransformation;
public GifData(Context context, BitmapPool bitmapPool, String gifId, GifHeader header, byte[] data) {
public GifData(Context context, BitmapPool bitmapPool, String gifId, GifHeader header, byte[] data,
int targetWidth, int targetHeight) {
this.context = context;
this.bitmapPool = bitmapPool;
this.header = header;
this.data = data;
this.gifId = gifId;
this.targetWidth = targetWidth;
this.targetHeight = targetHeight;
}
@SuppressWarnings("unchecked")
......@@ -47,7 +52,8 @@ public class GifData {
public GifDrawable getDrawable() {
GifDecoder gifDecoder = new GifDecoder(bitmapPool);
gifDecoder.setData(gifId, header, data);
GifFrameManager frameManager = new GifFrameManager(context, gifDecoder, getFrameTransformation());
GifFrameManager frameManager = new GifFrameManager(context, gifDecoder, getFrameTransformation(),
targetWidth, targetHeight);
GifDrawable result = new GifDrawable(gifDecoder, frameManager);
drawables.add(result);
......
package com.bumptech.glide.load.resource.gif;
import android.graphics.Bitmap;
import com.bumptech.glide.Resource;
import com.bumptech.glide.load.MultiTransformation;
import com.bumptech.glide.load.Transformation;
public class GifDataTransformation implements Transformation<GifData> {
private Transformation<Bitmap> wrapped;
public GifDataTransformation(Transformation<Bitmap> wrapped) {
this.wrapped = wrapped;
}
@Override
public Resource<GifData> transform(Resource<GifData> resource, int outWidth, int outHeight) {
GifData data = resource.get();
Transformation<Bitmap> newTransformation =
new MultiTransformation<Bitmap>(data.getFrameTransformation(), wrapped);
data.setFrameTransformation(newTransformation);
return resource;
}
@Override
public String getId() {
return wrapped.getId();
}
}
......@@ -13,6 +13,8 @@ public class GifDrawable extends Drawable implements Animatable, GifFrameManager
private final Paint paint;
private final GifFrameManager frameManager;
private int width;
private int height;
private GifDecoder decoder;
private boolean isRunning;
private Bitmap currentFrame;
......@@ -21,6 +23,8 @@ public class GifDrawable extends Drawable implements Animatable, GifFrameManager
public GifDrawable(GifDecoder decoder, GifFrameManager frameManager) {
this.decoder = decoder;
this.frameManager = frameManager;
width = -1;
height = -1;
paint = new Paint();
}
......@@ -44,14 +48,15 @@ public class GifDrawable extends Drawable implements Animatable, GifFrameManager
return super.setVisible(visible, restart);
}
@Override
public int getIntrinsicWidth() {
return decoder.getWidth();
return width;
}
@Override
public int getIntrinsicHeight() {
return decoder.getHeight();
return height;
}
@Override
......@@ -96,6 +101,12 @@ public class GifDrawable extends Drawable implements Animatable, GifFrameManager
if (!isRunning) {
return;
}
if (width == -1) {
width = frame.getWidth();
}
if (height == -1) {
height = frame.getHeight();
}
if (frame != null) {
currentFrame = frame;
......
......@@ -32,23 +32,29 @@ class GifFrameManager {
private final Context context;
private Transformation<Bitmap> transformation;
private final int targetWidth;
private final int targetHeight;
private DelayTarget current;
private DelayTarget next;
private int frameSize = -1;
public interface FrameCallback {
public void onFrameRead(Bitmap frame);
}
public GifFrameManager(Context context, GifDecoder decoder, Transformation<Bitmap> transformation) {
this(context, decoder, new Handler(Looper.getMainLooper()), transformation);
public GifFrameManager(Context context, GifDecoder decoder, Transformation<Bitmap> transformation, int targetWidth,
int targetHeight) {
this(context, decoder, new Handler(Looper.getMainLooper()), transformation, targetWidth, targetHeight);
}
public GifFrameManager(Context context, GifDecoder decoder, Handler mainHandler,
Transformation<Bitmap> transformation) {
Transformation<Bitmap> transformation, int targetWidth, int targetHeight) {
this.context = context;
this.decoder = decoder;
this.mainHandler = mainHandler;
this.transformation = transformation;
this.targetWidth = targetWidth;
this.targetHeight = targetHeight;
calculator = new MemorySizeCalculator(context);
frameLoader = new GifFrameModelLoader();
frameResourceDecoder = new GifFrameResourceDecoder();
......@@ -70,14 +76,22 @@ class GifFrameManager {
return transformation;
}
private int getEstimatedTotalFrameSize() {
if (frameSize == -1) {
return decoder.getDecodedFramesByteSizeSum();
} else {
return frameSize * decoder.getFrameCount();
}
}
public void getNextFrame(FrameCallback cb) {
decoder.advance();
// 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;
boolean skipCache = getEstimatedTotalFrameSize() > calculator.getMemoryCacheSize() / 2;
long targetTime = SystemClock.uptimeMillis() + (Math.min(MIN_FRAME_DELAY, decoder.getNextDelay()));
next = new DelayTarget(decoder, cb, targetTime, mainHandler);
next = new DelayTarget(cb, targetTime);
Glide.with(context)
.using(frameLoader, GifDecoder.class)
......@@ -105,18 +119,18 @@ class GifFrameManager {
class DelayTarget extends SimpleTarget<Bitmap> implements Runnable {
private FrameCallback cb;
private long targetTime;
private Handler mainHandler;
private Bitmap resource;
public DelayTarget(GifDecoder decoder, FrameCallback cb, long targetTime, Handler mainHandler) {
super(decoder.getWidth(), decoder.getHeight());
public DelayTarget(FrameCallback cb, long targetTime) {
super(targetWidth, targetHeight);
this.cb = cb;
this.targetTime = targetTime;
this.mainHandler = mainHandler;
}
@Override
public void onResourceReady(final Bitmap resource) {
// Ignore allocationByteSize, we only want the minimum frame size.
frameSize = resource.getHeight() * resource.getRowBytes();
this.resource = resource;
mainHandler.postAtTime(this, targetTime);
if (current != null) {
......
......@@ -35,7 +35,7 @@ public class GifResourceDecoder implements ResourceDecoder<InputStream, GifData>
byte[] data = inputStreamToBytes(source);
GifHeader header = new GifHeaderParser(data).parseHeader();
String id = getGifId(data);
return new GifDataResource(new GifData(context, bitmapPool, id, header, data));
return new GifDataResource(new GifData(context, bitmapPool, id, header, data, width, height));
}
@Override
......
package com.bumptech.glide.load.resource.gifbitmap;
import android.content.Context;
import android.graphics.Bitmap;
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.GifDataResource;
import com.bumptech.glide.load.resource.gif.GifDataTransformation;
public class GifBitmapWrapperTransformation implements Transformation<GifBitmapWrapper> {
private Context context;
private Transformation<Bitmap> wrapped;
private Transformation<Bitmap> bitmapTransformation;
private Transformation<GifData> gifDataTransformation;
public GifBitmapWrapperTransformation(Context context, Transformation<Bitmap> wrapped) {
this.context = context;
this.wrapped = wrapped;
public GifBitmapWrapperTransformation(Transformation<Bitmap> bitmapTransformation) {
this(bitmapTransformation, new GifDataTransformation(bitmapTransformation));
}
GifBitmapWrapperTransformation(Transformation<Bitmap> bitmapTransformation,
Transformation<GifData> gifDataTransformation) {
this.bitmapTransformation = bitmapTransformation;
this.gifDataTransformation = gifDataTransformation;
}
@Override
public Resource<GifBitmapWrapper> transform(Resource<GifBitmapWrapper> resource, int outWidth, int outHeight) {
Resource<Bitmap> bitmapResource = resource.get().getBitmapResource();
if (bitmapResource != null) {
Resource<Bitmap> transformed = wrapped.transform(bitmapResource, outWidth, outHeight);
Resource<GifData> gifResource = resource.get().getGifResource();
if (bitmapResource != null && bitmapTransformation != null) {
Resource<Bitmap> transformed = bitmapTransformation.transform(bitmapResource, outWidth, outHeight);
if (transformed != bitmapResource) {
GifBitmapWrapper gifBitmap = new GifBitmapWrapper(transformed, null);
GifBitmapWrapper gifBitmap = new GifBitmapWrapper(transformed, resource.get().getGifResource());
return new GifBitmapWrapperResource(gifBitmap);
}
} else if (gifResource != null && gifDataTransformation != null) {
Resource<GifData> transformed = gifDataTransformation.transform(gifResource, outWidth, outHeight);
if (transformed != gifResource) {
GifBitmapWrapper gifBitmap = new GifBitmapWrapper(resource.get().getBitmapResource(), transformed);
return new GifBitmapWrapperResource(gifBitmap);
}
} else {
//TODO: this should be pushed down into a GifData transformation?
Resource<GifData> gifResource = resource.get().getGifResource();
GifData gifData = gifResource.get();
Transformation<Bitmap> newTransformation =
new MultiTransformation<Bitmap>(gifData.getFrameTransformation(), wrapped);
gifData.setFrameTransformation(newTransformation);
return new GifBitmapWrapperResource(new GifBitmapWrapper(null, new GifDataResource(gifData)));
}
return resource;
}
@Override
public String getId() {
return wrapped.getId();
return bitmapTransformation.getId();
}
}
......@@ -29,7 +29,7 @@ public class GifDataTest {
BitmapPool bitmapPool = mock(BitmapPool.class);
GifHeader header = mock(GifHeader.class);
bytes = new byte[] { 'G', 'I', 'F' };
data = new GifData(Robolectric.application, bitmapPool, "gifId", header, bytes);
data = new GifData(Robolectric.application, bitmapPool, "gifId", header, bytes, 123, 456);
}
@Test
......
package com.bumptech.glide.load.resource.gif;
import android.graphics.Bitmap;
import com.bumptech.glide.Resource;
import com.bumptech.glide.load.Transformation;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.robolectric.RobolectricTestRunner;
import static junit.framework.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(RobolectricTestRunner.class)
public class GifDataTransformationTest {
Transformation<Bitmap> wrapped;
GifDataTransformation transformation;
@SuppressWarnings("unchecked")
@Before
public void setUp() {
wrapped = mock(Transformation.class);
transformation = new GifDataTransformation(wrapped);
}
@Test
public void testReturnsWrappedTransformationId() {
final String id = "testId";
when(wrapped.getId()).thenReturn(id);
assertEquals(id, transformation.getId());
}
@Test
public void testSetsTransformationAsFrameTransformation() {
Resource<GifData> resource = mock(Resource.class);
GifData gifData = mock(GifData.class);
when(gifData.getFrameTransformation()).thenReturn(Transformation.NONE);
when(resource.get()).thenReturn(gifData);
final Resource<Bitmap> toTransform = mock(Resource.class);
final int width = 123;
final int height = 456;
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
Transformation<Bitmap> transformation = (Transformation<Bitmap>) invocation.getArguments()[0];
transformation.transform(toTransform, width, height);
return null;
}
}).when(gifData).setFrameTransformation(any(Transformation.class));
transformation.transform(resource, width, height);
verify(gifData).setFrameTransformation(any(Transformation.class));
verify(wrapped).transform(eq(toTransform), eq(width), eq(height));
}
}
......@@ -39,17 +39,33 @@ public class GifDrawableTest {
}
@Test
public void testReturnsDecoderWidth() {
public void testReturnsInvalidWidthBeforeFirstFrame() {
assertEquals(-1, drawable.getIntrinsicWidth());
}
@Test
public void testReturnsInvalidHeightBeforeFirstFrame() {
assertEquals(-1, drawable.getIntrinsicHeight());
}
@Test
public void testReturnsFrameWidthAfterFirstFrame() {
int width = 123;
when(gifDecoder.getWidth()).thenReturn(width);
Bitmap bitmap = Bitmap.createBitmap(width, 1231, Bitmap.Config.ARGB_8888);
drawable.setIsRunning(true);
drawable.onFrameRead(bitmap);
assertEquals(width, drawable.getIntrinsicWidth());
}
@Test
public void testReturnsDecoderHeight() {
int height = 321;
when(gifDecoder.getHeight()).thenReturn(height);
public void testReturnsFrameHeightAfterFirstFrame() {
int height = 456;
Bitmap bitmap = Bitmap.createBitmap(1, height, Bitmap.Config.RGB_565);
drawable.setIsRunning(true);
drawable.onFrameRead(bitmap);
assertEquals(height, drawable.getIntrinsicHeight());
}
......
......@@ -7,11 +7,11 @@ import com.bumptech.glide.load.resource.gif.GifData;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotSame;
import static junit.framework.Assert.assertSame;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
......@@ -19,13 +19,43 @@ import static org.mockito.Mockito.when;
@RunWith(RobolectricTestRunner.class)
public class GifBitmapWrapperTransformationTest {
private Transformation<Bitmap> bitmapTransformation;
private GifBitmapWrapperTransformation transformation;
private Transformation<GifData> gifTransformation;
@SuppressWarnings("unchecked")
@Before
public void setUp() {
bitmapTransformation = mock(Transformation.class);
transformation = new GifBitmapWrapperTransformation(Robolectric.application, bitmapTransformation);
gifTransformation = mock(Transformation.class);
}
private class BitmapResourceHarness {
Resource<Bitmap> bitmapResource = mock(Resource.class);
GifBitmapWrapper gifBitmapWrapper = mock(GifBitmapWrapper.class);
Resource<GifBitmapWrapper> resource = mock(Resource.class);
GifBitmapWrapperTransformation transformation = new GifBitmapWrapperTransformation(bitmapTransformation);
int width = 123;
int height = 456;
public BitmapResourceHarness() {
when(gifBitmapWrapper.getBitmapResource()).thenReturn(bitmapResource);
when(resource.get()).thenReturn(gifBitmapWrapper);
}
}
private class GifResourceHarness {
GifData gifData = mock(GifData.class);
Resource<GifData> gifResource = mock(Resource.class);
GifBitmapWrapper gifBitmapWrapper = mock(GifBitmapWrapper.class);
Resource<GifBitmapWrapper> resource = mock(Resource.class);
GifBitmapWrapperTransformation transformation = new GifBitmapWrapperTransformation(null, gifTransformation);
int width = 123;
int height = 456;
public GifResourceHarness() {
when(gifResource.get()).thenReturn(gifData);
when(gifBitmapWrapper.getGifResource()).thenReturn(gifResource);
when(resource.get()).thenReturn(gifBitmapWrapper);
}
}
@Test
......@@ -33,51 +63,80 @@ public class GifBitmapWrapperTransformationTest {
String expectedId = "asdfas";
when(bitmapTransformation.getId()).thenReturn(expectedId);
assertEquals(expectedId, transformation.getId());
assertEquals(expectedId, new GifBitmapWrapperTransformation(bitmapTransformation).getId());
}
@Test
public void testAppliesTransformationToBitmapResourceAndReturnsNewGifBitmapResource() {
int dimens = 123;
Resource<Bitmap> initial = mock(Resource.class);
public void testAppliesBitmapTransformationIfBitmapTransformationIsGivenAndResourceHasBitmapResource() {
BitmapResourceHarness harness = new BitmapResourceHarness();
Resource<Bitmap> transformed = mock(Resource.class);
when(bitmapTransformation.transform(eq(initial), eq(dimens), eq(dimens))).thenReturn(transformed);
Resource<Bitmap> transformedBitmapResource = mock(Resource.class);
when(bitmapTransformation.transform(eq(harness.bitmapResource), eq(harness.width), eq(harness.height)))
.thenReturn(transformedBitmapResource);
Resource<GifBitmapWrapper> transformed = harness.transformation.transform(harness.resource, harness.width,
harness.height);
GifBitmapWrapper gifBitmap = mock(GifBitmapWrapper.class);
when(gifBitmap.getBitmapResource()).thenReturn(initial);
Resource<GifBitmapWrapper> gifBitmapResource = mock(Resource.class);
when(gifBitmapResource.get()).thenReturn(gifBitmap);
assertNotSame(harness.resource, transformed);
assertEquals(transformedBitmapResource, transformed.get().getBitmapResource());
}
@Test
public void testReturnsOriginalResourceIfTransformationDoesNotTransformGivenBitmapResource() {
BitmapResourceHarness harness = new BitmapResourceHarness();
assertEquals(transformed, transformation.transform(gifBitmapResource, dimens, dimens).get()
.getBitmapResource());
when(bitmapTransformation.transform(eq(harness.bitmapResource), eq(harness.width), eq(harness.height)))
.thenReturn(harness.bitmapResource);
Resource<GifBitmapWrapper> transformed = harness.transformation.transform(harness.resource, harness.width,
harness.height);
assertSame(harness.resource, transformed);
}
@Test
public void testReturnsNewGifBitmapResourceIfNoBitmapResource() {
GifBitmapWrapper gifBitmap = mock(GifBitmapWrapper.class);
Resource<GifBitmapWrapper> gifBitmapResource = mock(Resource.class);
when(gifBitmapResource.get()).thenReturn(gifBitmap);
public void testReturnsOriginalResourceIfBitmapTransformationIsGivenButResourceHasNoBitmapResource() {
BitmapResourceHarness harness = new BitmapResourceHarness();
when(harness.gifBitmapWrapper.getBitmapResource()).thenReturn(null);
GifData gifData = mock(GifData.class);
Resource<GifData> gifDataResource = mock(Resource.class);
when(gifDataResource.get()).thenReturn(gifData);
when(gifBitmap.getGifResource()).thenReturn(gifDataResource);
Resource<GifBitmapWrapper> transformed = harness.transformation.transform(harness.resource, harness.width,
harness.height);
assertNotSame(gifBitmapResource, transformation.transform(gifBitmapResource, 100, 100));
assertSame(harness.resource, transformed);
}
@Test
public void testReturnsGivenResourceIfWrappedTransformationDoesNotTransformBitmapResource() {
int dimens = 321;
Resource<Bitmap> initial = mock(Resource.class);
GifBitmapWrapper gifBitmap = mock(GifBitmapWrapper.class);
when(gifBitmap.getBitmapResource()).thenReturn(initial);
Resource<GifBitmapWrapper> gifBitmapResource = mock(Resource.class);
when(gifBitmapResource.get()).thenReturn(gifBitmap);
public void testAppliesGifTransformationIfGifTransformationGivenAndResourceHasGifResource() {
GifResourceHarness harness = new GifResourceHarness();
Resource<GifData> transformedGifResource = mock(Resource.class);
when(gifTransformation.transform(eq(harness.gifResource), eq(harness.width), eq(harness.height)))
.thenReturn(transformedGifResource);
Resource<GifBitmapWrapper> transformed = harness.transformation.transform(harness.resource, harness.width,
harness.height);
assertNotSame(harness.resource, transformed);
assertEquals(transformedGifResource, transformed.get().getGifResource());
}
@Test
public void testReturnsOriginalresourceIfTransformationDoesNotTransformGivenGifResource() {
GifResourceHarness harness = new GifResourceHarness();
when(gifTransformation.transform(eq(harness.gifResource), eq(harness.width), eq(harness.height)))
.thenReturn(harness.gifResource);
when(bitmapTransformation.transform(eq(initial), eq(dimens), eq(dimens))).thenReturn(initial);
Resource<GifBitmapWrapper> transformed = harness.transformation.transform(harness.resource, harness.width,
harness.height);
assertEquals(gifBitmapResource, transformation.transform(gifBitmapResource, dimens, dimens));
assertSame(harness.resource, transformed);
}
@Test
public void testReturnsOriginalResourceIfGifTransformationIsGivenButResourceHasNoGifResource() {
GifResourceHarness harness = new GifResourceHarness();
when(harness.gifBitmapWrapper.getGifResource()).thenReturn(null);
Resource<GifBitmapWrapper> transformed = harness.transformation.transform(harness.resource, harness.width,
harness.height);
assertSame(harness.resource, transformed);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册