提交 1614d6b4 编写于 作者: S Sam Judd

Add simplified BitmapTransformation.

上级 09a5b320
......@@ -14,6 +14,7 @@ import com.bumptech.glide.load.Transformation;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.load.model.ImageVideoWrapper;
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
import com.bumptech.glide.load.resource.bitmap.Downsampler;
import com.bumptech.glide.load.resource.bitmap.FileDescriptorBitmapDecoder;
import com.bumptech.glide.load.resource.bitmap.ImageVideoBitmapDecoder;
......@@ -172,8 +173,7 @@ public class BitmapRequestBuilder<ModelType, TranscodeType>
* {@inheritDoc}
*/
@Override
public BitmapRequestBuilder<ModelType, TranscodeType> cacheDecoder(
ResourceDecoder<File, Bitmap> cacheDecoder) {
public BitmapRequestBuilder<ModelType, TranscodeType> cacheDecoder(ResourceDecoder<File, Bitmap> cacheDecoder) {
super.cacheDecoder(cacheDecoder);
return this;
}
......@@ -247,10 +247,26 @@ public class BitmapRequestBuilder<ModelType, TranscodeType>
return this;
}
/**
* Transform images using the given {@link com.bumptech.glide.load.resource.bitmap.BitmapTransformation}s.
*
* @see #centerCrop()
* @see #fitCenter()
* @see #transform(com.bumptech.glide.load.Transformation[])
*
* @param transformations The transformations to apply in order.
* @return This request builder.
*/
public BitmapRequestBuilder<ModelType, TranscodeType> transform(BitmapTransformation... transformations) {
super.transform(transformations);
return this;
}
/**
* Transform images using {@link com.bumptech.glide.load.resource.bitmap.CenterCrop}.
*
* @see #fitCenter()
* @see #transform(com.bumptech.glide.load.resource.bitmap.BitmapTransformation...)
* @see #transform(com.bumptech.glide.load.Transformation[])
*
* @return This request builder.
......@@ -263,6 +279,7 @@ public class BitmapRequestBuilder<ModelType, TranscodeType>
* Transform images using {@link com.bumptech.glide.load.resource.bitmap.FitCenter}.
*
* @see #centerCrop()
* @see #transform(com.bumptech.glide.load.resource.bitmap.BitmapTransformation...)
* @see #transform(com.bumptech.glide.load.Transformation[])
*
* @return This request builder.
......
......@@ -11,6 +11,7 @@ import com.bumptech.glide.load.ResourceEncoder;
import com.bumptech.glide.load.Transformation;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.model.ImageVideoWrapper;
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
import com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapper;
import com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperTransformation;
import com.bumptech.glide.load.resource.transcode.ResourceTranscoder;
......@@ -98,8 +99,7 @@ public class DrawableRequestBuilder<ModelType>
* {@inheritDoc}
*/
@Override
public DrawableRequestBuilder<ModelType> sizeMultiplier(
float sizeMultiplier) {
public DrawableRequestBuilder<ModelType> sizeMultiplier(float sizeMultiplier) {
super.sizeMultiplier(sizeMultiplier);
return this;
}
......@@ -108,8 +108,7 @@ public class DrawableRequestBuilder<ModelType>
* {@inheritDoc}
*/
@Override
public DrawableRequestBuilder<ModelType> decoder(
ResourceDecoder<ImageVideoWrapper, GifBitmapWrapper> decoder) {
public DrawableRequestBuilder<ModelType> decoder(ResourceDecoder<ImageVideoWrapper, GifBitmapWrapper> decoder) {
super.decoder(decoder);
return this;
}
......@@ -118,8 +117,7 @@ public class DrawableRequestBuilder<ModelType>
* {@inheritDoc}
*/
@Override
public DrawableRequestBuilder<ModelType> cacheDecoder(
ResourceDecoder<File, GifBitmapWrapper> cacheDecoder) {
public DrawableRequestBuilder<ModelType> cacheDecoder(ResourceDecoder<File, GifBitmapWrapper> cacheDecoder) {
super.cacheDecoder(cacheDecoder);
return this;
}
......@@ -128,8 +126,7 @@ public class DrawableRequestBuilder<ModelType>
* {@inheritDoc}
*/
@Override
public DrawableRequestBuilder<ModelType> encoder(
ResourceEncoder<GifBitmapWrapper> encoder) {
public DrawableRequestBuilder<ModelType> encoder(ResourceEncoder<GifBitmapWrapper> encoder) {
super.encoder(encoder);
return this;
}
......@@ -143,10 +140,32 @@ public class DrawableRequestBuilder<ModelType>
return this;
}
/**
* Transform {@link android.graphics.drawable.Drawable}s using the given
* {@link com.bumptech.glide.load.resource.bitmap.BitmapTransformation}s.
*
* <p>
* Note - Bitmap transformations will apply individually to each frame of animated GIF images and also to
* individual {@link Bitmap}s.
* </p>
*
* @see #centerCrop()
* @see #fitCenter()
* @see #bitmapTransform(com.bumptech.glide.load.Transformation[])
* @see #transform(com.bumptech.glide.load.Transformation[])
*
* @param transformations The transformations to apply in order.
* @return This request builder.
*/
public DrawableRequestBuilder<ModelType> transform(BitmapTransformation... transformations) {
return bitmapTransform(transformations);
}
/**
* Transform {@link Drawable}s using {@link com.bumptech.glide.load.resource.bitmap.CenterCrop}.
*
* @see #fitCenter()
* @see #transform(com.bumptech.glide.load.resource.bitmap.BitmapTransformation...)
* @see #bitmapTransform(com.bumptech.glide.load.Transformation[])
* @see #transform(com.bumptech.glide.load.Transformation[])
*
......@@ -161,6 +180,7 @@ public class DrawableRequestBuilder<ModelType>
* {@link com.bumptech.glide.load.resource.bitmap.FitCenter}.
*
* @see #centerCrop()
* @see #transform(com.bumptech.glide.load.resource.bitmap.BitmapTransformation...)
* @see #bitmapTransform(com.bumptech.glide.load.Transformation[])
* @see #transform(com.bumptech.glide.load.Transformation[])
*
......@@ -176,6 +196,7 @@ public class DrawableRequestBuilder<ModelType>
*
* @see #fitCenter()
* @see #centerCrop()
* @see #transform(com.bumptech.glide.load.resource.bitmap.BitmapTransformation...)
* @see #transform(com.bumptech.glide.load.Transformation[])
*
* @return This request builder.
......
......@@ -10,6 +10,7 @@ import com.bumptech.glide.load.ResourceDecoder;
import com.bumptech.glide.load.ResourceEncoder;
import com.bumptech.glide.load.Transformation;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
import com.bumptech.glide.load.resource.gif.GifData;
import com.bumptech.glide.load.resource.gif.GifDataTransformation;
import com.bumptech.glide.load.resource.gif.GifDrawable;
......@@ -142,8 +143,9 @@ public class GifRequestBuilder<ModelType> extends GenericRequestBuilder<ModelTyp
/**
* Transforms each frame of the GIF using {@link com.bumptech.glide.load.resource.bitmap.CenterCrop}.
*
* @see #transformFrame(com.bumptech.glide.load.Transformation[])
* @see #fitCenter()
* @see #transformFrame(com.bumptech.glide.load.resource.bitmap.BitmapTransformation...)
* @see #transformFrame(com.bumptech.glide.load.Transformation[])
* @see #transform(com.bumptech.glide.load.Transformation[])
*
* @return This request builder.
......@@ -155,8 +157,9 @@ public class GifRequestBuilder<ModelType> extends GenericRequestBuilder<ModelTyp
/**
* Transforms each frame of the GIF using {@link com.bumptech.glide.load.resource.bitmap.FitCenter}.
*
* @see #transformFrame(com.bumptech.glide.load.Transformation[])
* @see #centerCrop()
* @see #transformFrame(com.bumptech.glide.load.resource.bitmap.BitmapTransformation...)
* @see #transformFrame(com.bumptech.glide.load.Transformation[])
* @see #transform(com.bumptech.glide.load.Transformation[])
*
* @return This request builder..
......@@ -166,22 +169,41 @@ public class GifRequestBuilder<ModelType> extends GenericRequestBuilder<ModelTyp
}
/**
* Transforms each frame of the GIF using the given transformation.
* Transforms each frame of the GIF using the given transformations.
*
* @see #centerCrop()
* @see #fitCenter()
* @see #transformFrame(com.bumptech.glide.load.Transformation[])
* @see #transform(com.bumptech.glide.load.Transformation[])
*
* @param bitmapTransformations The transformations to apply in order to each frame.
* @return This request builder.
*/
public GifRequestBuilder<ModelType> transformFrame(BitmapTransformation... bitmapTransformations) {
return transform(toGifTransformations(bitmapTransformations));
}
/**
* Transforms each frame of the GIF using the given transformations.
*
* @see #fitCenter()
* @see #centerCrop()
* @see #transformFrame(com.bumptech.glide.load.resource.bitmap.BitmapTransformation...)
* @see #transform(com.bumptech.glide.load.Transformation[])
*
* @param bitmapTransformations The transformation to use.
* @param bitmapTransformations The transformations to apply in order to each frame.
* @return This request builder.
*/
public GifRequestBuilder<ModelType> transformFrame(Transformation<Bitmap>... bitmapTransformations) {
return transform(toGifTransformations(bitmapTransformations));
}
private static GifDataTransformation[] toGifTransformations(Transformation<Bitmap>[] bitmapTransformations) {
GifDataTransformation[] transformations = new GifDataTransformation[bitmapTransformations.length];
for (int i = 0; i < bitmapTransformations.length; i++) {
transformations[i] = new GifDataTransformation(bitmapTransformations[i]);
}
return transform(transformations);
return transformations;
}
/**
......@@ -189,6 +211,7 @@ public class GifRequestBuilder<ModelType> extends GenericRequestBuilder<ModelTyp
*
* @see #fitCenter()
* @see #centerCrop()
* @see #transformFrame(com.bumptech.glide.load.resource.bitmap.BitmapTransformation...)
* @see #transformFrame(com.bumptech.glide.load.Transformation[])
*
*/
......
package com.bumptech.glide.load.resource.bitmap;
import android.content.Context;
import android.graphics.Bitmap;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.Transformation;
import com.bumptech.glide.load.engine.Resource;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
/**
* A simple {@link com.bumptech.glide.load.Transformation} for transforming {@link android.graphics.Bitmap}s that
* abstracts away dealing with {@link com.bumptech.glide.load.engine.Resource} objects for subclasses.
*
* Use cases will look something like this:
* <pre>
* <code>
* public class FillSpace extends BaseBitmapTransformation {
* {@literal @Override}
* public Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
* if (toTransform.getWidth() == outWidth && toTransform.getHeight == outHeight) {
* return toTransform;
* }
*
* return Bitmap.createScaledBitmap(toTransform, outWidth, outHeight, true);
* }
* }
* </code>
* </pre>
*/
public abstract class BitmapTransformation implements Transformation<Bitmap> {
private BitmapPool bitmapPool;
public BitmapTransformation(Context context) {
this(Glide.get(context).getBitmapPool());
}
public BitmapTransformation(BitmapPool bitmapPool) {
this.bitmapPool = bitmapPool;
}
@Override
public final Resource<Bitmap> transform(Resource<Bitmap> resource, int outWidth, int outHeight) {
if (outWidth <= 0 || outHeight <= 0) {
throw new IllegalArgumentException("Cannot appy transformation on width: " + outWidth + " or height: "
+ outHeight + " less than or equal to zero");
}
Bitmap toTransform = resource.get();
Bitmap transformed = transform(bitmapPool, toTransform, outWidth, outHeight);
final Resource<Bitmap> result;
if (transformed == toTransform) {
result = resource;
} else {
result = new BitmapResource(transformed, bitmapPool);
}
return result;
}
/**
* Transforms the given {@link android.graphics.Bitmap} based on the given dimensions and returns the transformed
* result.
*
* <p>
* Note - As with all {@link com.bumptech.glide.load.Transformation}s, this method must be idempotent. Given
* bitmap A, and bitmap B == transform(A). transform(B) must always equal B.
*
* </p>
*
* @param pool A {@link com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool} that can be used to obtain and
* return intermediate {@link Bitmap}s used in this transformation. For every
* {@link android.graphics.Bitmap} obtained from the pool during this transformation, a
* {@link android.graphics.Bitmap} must also be returned.
* @param toTransform The {@link android.graphics.Bitmap} to transform.
* @param outWidth The ideal width of the transformed bitmap (does not need to match exactly).
* @param outHeight The ideal height of the transformed bitmap (does not need to match exactly).
*/
protected abstract Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight);
}
package com.bumptech.glide.load.resource.bitmap;
import android.content.Context;
import android.graphics.Bitmap;
import com.bumptech.glide.load.engine.Resource;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.load.Transformation;
/**
* Scale the image so that either the width of the image matches the given width and the height of the image is
......@@ -11,31 +11,24 @@ import com.bumptech.glide.load.Transformation;
*
* Does not maintain the image's aspect ratio
*/
public class CenterCrop implements Transformation<Bitmap> {
private BitmapPool pool;
public class CenterCrop extends BitmapTransformation {
public CenterCrop(BitmapPool pool) {
this.pool = pool;
public CenterCrop(Context context) {
super(context);
}
@Override
public Resource<Bitmap> transform(Resource<Bitmap> resource, int outWidth, int outHeight) {
if (outWidth <= 0 || outHeight <= 0) {
throw new IllegalArgumentException("Cannot center crop image to width=" + outWidth + " and height="
+ outHeight);
}
public CenterCrop(BitmapPool bitmapPool) {
super(bitmapPool);
}
final Bitmap toReuse = pool.get(outWidth, outHeight, resource.get().getConfig());
Bitmap transformed = TransformationUtils.centerCrop(toReuse, resource.get(), outWidth, outHeight);
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
final Bitmap toReuse = pool.get(outWidth, outHeight, toTransform.getConfig());
Bitmap transformed = TransformationUtils.centerCrop(toReuse, toTransform, outWidth, outHeight);
if (toReuse != null && toReuse != transformed && !pool.put(toReuse)) {
toReuse.recycle();
}
if (transformed == resource.get()) {
return resource;
} else {
return new BitmapResource(transformed, pool);
}
return transformed;
}
@Override
......
package com.bumptech.glide.load.resource.bitmap;
import android.content.Context;
import android.graphics.Bitmap;
import com.bumptech.glide.load.engine.Resource;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.load.Transformation;
/**
* Scales the image uniformly (maintaining the image's aspect ratio) so that one of the dimensions of the image
* will be equal to the given dimension and the other will be less than the given dimension.
*/
public class FitCenter implements Transformation<Bitmap> {
private BitmapPool pool;
public class FitCenter extends BitmapTransformation {
public FitCenter(Context context) {
super(context);
}
public FitCenter(BitmapPool pool) {
this.pool = pool;
public FitCenter(BitmapPool bitmapPool) {
super(bitmapPool);
}
@Override
public Resource<Bitmap> transform(Resource<Bitmap> resource, int outWidth, int outHeight) {
if (outWidth <= 0 || outHeight <= 0) {
throw new IllegalArgumentException("Cannot fit center image to within width=" + outWidth + " or height="
+ outHeight);
}
Bitmap transformed = TransformationUtils.fitCenter(resource.get(), pool, outWidth, outHeight);
if (transformed == resource.get()) {
return resource;
} else {
return new BitmapResource(transformed, pool);
}
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
return TransformationUtils.fitCenter(toTransform, pool, outWidth, outHeight);
}
@Override
......
package com.bumptech.glide.load.resource.bitmap;
import android.graphics.Bitmap;
import com.bumptech.glide.load.engine.Resource;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import static junit.framework.Assert.assertNotSame;
import static junit.framework.TestCase.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@RunWith(RobolectricTestRunner.class)
public class BitmapTransformationTest {
private BitmapPool bitmapPool;
@Before
public void setUp() {
bitmapPool = mock(BitmapPool.class);
}
@Test
public void testReturnsGivenResourceWhenBitmapNotTransformed() {
BitmapTransformation transformation = new BitmapTransformation(bitmapPool) {
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
return toTransform;
}
@Override
public String getId() {
return null;
}
};
Resource<Bitmap> resource = mock(Resource.class);
when(resource.get()).thenReturn(Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_4444));
assertEquals(resource, transformation.transform(resource, 1, 1));
}
@Test
public void testReturnsNewResourceWhenBitmapTransformed() {
final Bitmap toTransform = Bitmap.createBitmap(1, 2, Bitmap.Config.RGB_565);
final Bitmap transformed = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_4444);
BitmapTransformation transformation = new BitmapTransformation(bitmapPool) {
@Override
protected Bitmap transform(BitmapPool pool, Bitmap bitmap, int outWidth, int outHeight) {
return transformed;
}
@Override
public String getId() {
return null;
}
};
Resource<Bitmap> resource = mock(Resource.class);
when(resource.get()).thenReturn(toTransform);
assertNotSame(resource, transformation.transform(resource, 100, 100));
}
@Test
public void testPassesGivenArgumentsToTransform() {
final int expectedWidth = 13;
final int expectedHeight = 148;
final Bitmap expected = Bitmap.createBitmap(223, 4123, Bitmap.Config.RGB_565);
BitmapTransformation transformation = new BitmapTransformation(bitmapPool) {
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
assertEquals(bitmapPool, pool);
assertEquals(expected, toTransform);
assertEquals(expectedWidth, outWidth);
assertEquals(expectedHeight, outHeight);
return null;
}
@Override
public String getId() {
return null;
}
};
Resource<Bitmap> resource = mock(Resource.class);
when(resource.get()).thenReturn(expected);
transformation.transform(resource, expectedWidth, expectedHeight);
}
@Test(expected = IllegalArgumentException.class)
public void testThrowsIfGivenInvalidWidth() {
BitmapTransformation transformation = new BitmapTransformation(bitmapPool) {
@Override
protected Bitmap transform(BitmapPool bitmapPool, Bitmap toTransform, int outWidth, int outHeight) {
return null;
}
@Override
public String getId() {
return null;
}
};
transformation.transform(mock(Resource.class), -1, 100);
}
@Test(expected = IllegalArgumentException.class)
public void testThrowsIfGivenInvalidHeight() {
BitmapTransformation transformation = new BitmapTransformation(bitmapPool) {
@Override
protected Bitmap transform(BitmapPool bitmapPool, Bitmap toTransform, int outWidth, int outHeight) {
return null;
}
@Override
public String getId() {
return null;
}
};
transformation.transform(mock(Resource.class), 100, -1);
}
}
\ No newline at end of file
......@@ -46,7 +46,7 @@ public class CenterCropTest {
@Test
public void testDoesNotRecycleGivenResourceIfMatchesSizeExactly() {
Resource<Bitmap> result = harness.centerCrop.transform(harness.resource, harness.bitmapWidth,
harness.centerCrop.transform(harness.resource, harness.bitmapWidth,
harness.bitmapHeight);
verify(harness.resource, never()).recycle();
......@@ -70,6 +70,40 @@ public class CenterCropTest {
verify(harness.resource, never()).recycle();
}
@Test
public void testReturnsBitmapWithExactlyGivenDimensionsIfBitmapIsLargerThanTarget() {
int expectedWidth = 75;
int expectedHeight = 74;
Resource<Bitmap> resource = mock(Resource.class);
for (int[] dimens : new int[][] { new int[] { 800, 200}, new int[] { 450, 100 }, new int[] { 78, 78 }}) {
Bitmap toTransform = Bitmap.createBitmap(dimens[0], dimens[1], Bitmap.Config.ARGB_4444);
when(resource.get()).thenReturn(toTransform);
Resource<Bitmap> result = harness.centerCrop.transform(resource, expectedWidth, expectedHeight);
Bitmap transformed = result.get();
assertEquals(expectedWidth, transformed.getWidth());
assertEquals(expectedHeight, transformed.getHeight());
}
}
@Test
public void testReturnsBitmapWithExactlyGivenDimensionsIfBitmapIsSmallerThanTarget() {
int expectedWidth = 100;
int expectedHeight = 100;
Resource<Bitmap> resource = mock(Resource.class);
for (int[] dimens : new int[][] { new int[] { 50, 90}, new int[] { 150, 2 }, new int[] { 78, 78 }}) {
Bitmap toTransform = Bitmap.createBitmap(dimens[0], dimens[1], Bitmap.Config.ARGB_4444);
when(resource.get()).thenReturn(toTransform);
Resource<Bitmap> result = harness.centerCrop.transform(resource, expectedWidth, expectedHeight);
Bitmap transformed = result.get();
assertEquals(expectedWidth, transformed.getWidth());
assertEquals(expectedHeight, transformed.getHeight());
}
}
@Test
public void testHasValidId() {
Util.assertClassHasValidId(CenterCrop.class, harness.centerCrop.getId());
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册