From da4ce795221e0fd9a3d11c84f018b3b5d6a10421 Mon Sep 17 00:00:00 2001 From: Sam Judd Date: Sun, 22 Jun 2014 17:13:23 -0700 Subject: [PATCH] Improve thumbnail performance. --- .../target/DrawableImageViewTarget.java | 14 ++ .../request/target/SquaringDrawable.java | 149 ++++++++++++++++++ .../bumptech/glide/request/target/Target.java | 2 +- .../res/layout/flickr_photo_grid_item.xml | 1 - .../glide/samples/flickr/FlickrPhotoGrid.java | 8 +- .../glide/samples/flickr/FlickrPhotoList.java | 7 +- .../glide/samples/flickr/api/Api.java | 2 +- 7 files changed, 172 insertions(+), 11 deletions(-) create mode 100644 library/src/main/java/com/bumptech/glide/request/target/SquaringDrawable.java diff --git a/library/src/main/java/com/bumptech/glide/request/target/DrawableImageViewTarget.java b/library/src/main/java/com/bumptech/glide/request/target/DrawableImageViewTarget.java index 82db02bfb..8fbd0e484 100644 --- a/library/src/main/java/com/bumptech/glide/request/target/DrawableImageViewTarget.java +++ b/library/src/main/java/com/bumptech/glide/request/target/DrawableImageViewTarget.java @@ -5,6 +5,7 @@ import android.widget.ImageView; import com.bumptech.glide.request.GlideAnimation; public class DrawableImageViewTarget extends ViewTarget { + private static final float SQUARE_RATIO_MARGIN = 0.05f; private final ImageView view; public DrawableImageViewTarget(ImageView view) { @@ -14,6 +15,19 @@ public class DrawableImageViewTarget extends ViewTarget { @Override public void onResourceReady(Drawable resource, GlideAnimation animation) { + + //TODO: Try to generalize this to other sizes/shapes. + // This is a dirty hack that tries to make loading square thumbnails and then square full images less costly by + // forcing both the smaller thumb and the larger version to have exactly the same intrinsic dimensions. If a + // drawable is replaced in an ImageView by another drawable with different intrinsic dimensions, the ImageView + // requests a layout. Scrolling rapidly while replacing thumbs with larger images triggers lots of these calls + // and causes significant amounts of jank. + float viewRatio = view.getWidth() / (float) view.getHeight(); + float drawableRatio = resource.getIntrinsicWidth() / (float) resource.getIntrinsicHeight(); + if (Math.abs(viewRatio - 1f) <= SQUARE_RATIO_MARGIN && Math.abs(drawableRatio - 1f) <= SQUARE_RATIO_MARGIN) { + resource = new SquaringDrawable(resource, view.getWidth()); + } + if (animation == null || !animation.animate(view.getDrawable(), resource, view, this)) { view.setImageDrawable(resource); } diff --git a/library/src/main/java/com/bumptech/glide/request/target/SquaringDrawable.java b/library/src/main/java/com/bumptech/glide/request/target/SquaringDrawable.java new file mode 100644 index 000000000..b0c8b60c0 --- /dev/null +++ b/library/src/main/java/com/bumptech/glide/request/target/SquaringDrawable.java @@ -0,0 +1,149 @@ +package com.bumptech.glide.request.target; + +import android.annotation.TargetApi; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.PorterDuff; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; + +/** + * A wrapper drawable to square the wrapped drawable so that it expands to fill a square with exactly the given side + * length. The goal of this drawable is to ensure that square thumbnail drawables always match the size of the view + * they will be displayed in to avoid a costly requestLayout call. This class should not be used with views or drawables + * that are not square. + */ +public class SquaringDrawable extends Drawable { + private final Drawable wrapped; + private int side; + + public SquaringDrawable(Drawable wrapped, int side) { + this.wrapped = wrapped; + this.side = side; + } + + @Override + public void setBounds(int left, int top, int right, int bottom) { + super.setBounds(left, top, right, bottom); + wrapped.setBounds(left, top, right, bottom); + } + + @Override + public void setBounds(Rect bounds) { + super.setBounds(bounds); + wrapped.setBounds(bounds); + } + public void setChangingConfigurations(int configs) { + wrapped.setChangingConfigurations(configs); + } + + @Override + public int getChangingConfigurations() { + return wrapped.getChangingConfigurations(); + } + + @Override + public void setDither(boolean dither) { + wrapped.setDither(dither); + } + + @Override + public void setFilterBitmap(boolean filter) { + wrapped.setFilterBitmap(filter); + } + + @TargetApi(11) + @Override + public Callback getCallback() { + return wrapped.getCallback(); + } + + @TargetApi(19) + @Override + public int getAlpha() { + return wrapped.getAlpha(); + } + + @Override + public void setColorFilter(int color, PorterDuff.Mode mode) { + wrapped.setColorFilter(color, mode); + } + + @Override + public void clearColorFilter() { + wrapped.clearColorFilter(); + } + + @Override + public Drawable getCurrent() { + return wrapped.getCurrent(); + } + + @Override + public boolean setVisible(boolean visible, boolean restart) { + return wrapped.setVisible(visible, restart); + } + + @Override + public int getIntrinsicWidth() { + return side; + } + + @Override + public int getIntrinsicHeight() { + return side; + } + + @Override + public int getMinimumWidth() { + return wrapped.getMinimumWidth(); + } + + @Override + public int getMinimumHeight() { + return wrapped.getMinimumHeight(); + } + + @Override + public boolean getPadding(Rect padding) { + return wrapped.getPadding(padding); + } + + @Override + public void invalidateSelf() { + super.invalidateSelf(); //To change body of overridden methods use File | Settings | File Templates. + wrapped.invalidateSelf(); + } + + @Override + public void unscheduleSelf(Runnable what) { + super.unscheduleSelf(what); //To change body of overridden methods use File | Settings | File Templates. + wrapped.unscheduleSelf(what); + } + + @Override + public void scheduleSelf(Runnable what, long when) { + super.scheduleSelf(what, when); //To change body of overridden methods use File | Settings | File Templates. + wrapped.scheduleSelf(what, when); + } + + @Override + public void draw(Canvas canvas) { + wrapped.draw(canvas); + } + + @Override + public void setAlpha(int i) { + wrapped.setAlpha(i); + } + + @Override + public void setColorFilter(ColorFilter colorFilter) { + wrapped.setColorFilter(colorFilter); + } + + @Override + public int getOpacity() { + return wrapped.getOpacity(); + } +} diff --git a/library/src/main/java/com/bumptech/glide/request/target/Target.java b/library/src/main/java/com/bumptech/glide/request/target/Target.java index c42fae935..5ea9d82d2 100644 --- a/library/src/main/java/com/bumptech/glide/request/target/Target.java +++ b/library/src/main/java/com/bumptech/glide/request/target/Target.java @@ -7,7 +7,7 @@ import com.bumptech.glide.request.Request; /** * An interface that Glide can load an image into * - * @param The type of resource the target can display. + * @param The type of resource the target can display. */ public interface Target { diff --git a/samples/flickr/res/layout/flickr_photo_grid_item.xml b/samples/flickr/res/layout/flickr_photo_grid_item.xml index 4c2acfd2e..550a2f2d5 100644 --- a/samples/flickr/res/layout/flickr_photo_grid_item.xml +++ b/samples/flickr/res/layout/flickr_photo_grid_item.xml @@ -1,6 +1,5 @@ diff --git a/samples/flickr/src/main/java/com/bumptech/glide/samples/flickr/FlickrPhotoGrid.java b/samples/flickr/src/main/java/com/bumptech/glide/samples/flickr/FlickrPhotoGrid.java index ca75f623b..c75e218f1 100644 --- a/samples/flickr/src/main/java/com/bumptech/glide/samples/flickr/FlickrPhotoGrid.java +++ b/samples/flickr/src/main/java/com/bumptech/glide/samples/flickr/FlickrPhotoGrid.java @@ -99,9 +99,8 @@ public class FlickrPhotoGrid extends SherlockFragment implements PhotoViewer { protected GenericRequestBuilder getRequestBuilder(Photo item) { return Glide.with(FlickrPhotoGrid.this) .loadFromImage(item) - .override(Api.THUMB_SIZE, Api.THUMB_SIZE) - .priority(Priority.HIGH) - .centerCrop(); + .override(Api.SQUARE_THUMB_SIZE, Api.SQUARE_THUMB_SIZE) + .priority(Priority.HIGH); } } @@ -150,8 +149,7 @@ public class FlickrPhotoGrid extends SherlockFragment implements PhotoViewer { .loadFromImage(current) .thumbnail(Glide.with(FlickrPhotoGrid.this) .loadFromImage(current) - .override(Api.THUMB_SIZE, Api.THUMB_SIZE) - .centerCrop() + .override(Api.SQUARE_THUMB_SIZE, Api.SQUARE_THUMB_SIZE) ) .animate(R.anim.fade_in) .centerCrop() diff --git a/samples/flickr/src/main/java/com/bumptech/glide/samples/flickr/FlickrPhotoList.java b/samples/flickr/src/main/java/com/bumptech/glide/samples/flickr/FlickrPhotoList.java index 10d113318..e33b8b9d2 100644 --- a/samples/flickr/src/main/java/com/bumptech/glide/samples/flickr/FlickrPhotoList.java +++ b/samples/flickr/src/main/java/com/bumptech/glide/samples/flickr/FlickrPhotoList.java @@ -115,8 +115,9 @@ public class FlickrPhotoList extends SherlockFragment implements PhotoViewer { return Glide.with(FlickrPhotoList.this) .loadFromImage(item) .thumbnail(Glide.with(FlickrPhotoList.this) - .loadFromImage(item) - .override(Api.THUMB_SIZE, Api.THUMB_SIZE)) + .loadFromImage(item) + .override(Api.SQUARE_THUMB_SIZE, Api.SQUARE_THUMB_SIZE) + ) .centerCrop(); } } @@ -176,7 +177,7 @@ public class FlickrPhotoList extends SherlockFragment implements PhotoViewer { .placeholder(new ColorDrawable(Color.GRAY)) .thumbnail(Glide.with(FlickrPhotoList.this) .loadFromImage(current) - .override(Api.THUMB_SIZE, Api.THUMB_SIZE)) + .override(Api.SQUARE_THUMB_SIZE, Api.SQUARE_THUMB_SIZE)) .centerCrop() .crossFade(R.anim.fade_in, 150) .into(viewHolder.imageView); diff --git a/samples/flickr/src/main/java/com/bumptech/glide/samples/flickr/api/Api.java b/samples/flickr/src/main/java/com/bumptech/glide/samples/flickr/api/Api.java index 4725c6b3e..995fd7e2b 100644 --- a/samples/flickr/src/main/java/com/bumptech/glide/samples/flickr/api/Api.java +++ b/samples/flickr/src/main/java/com/bumptech/glide/samples/flickr/api/Api.java @@ -45,7 +45,7 @@ public class Api { Collections.sort(SORTED_SIZE_KEYS); } - public static final int THUMB_SIZE = SORTED_SIZE_KEYS.get(0); + public static final int SQUARE_THUMB_SIZE = SORTED_SIZE_KEYS.get(0); private static String getSizeKey(int width, int height) { final int largestEdge = Math.max(width, height); -- GitLab