diff --git a/library/src/com/bumptech/glide/presenter/ImagePresenter.java b/library/src/com/bumptech/glide/presenter/ImagePresenter.java index 68a701aac1..9d333436cd 100644 --- a/library/src/com/bumptech/glide/presenter/ImagePresenter.java +++ b/library/src/com/bumptech/glide/presenter/ImagePresenter.java @@ -56,10 +56,9 @@ public static class Builder { private ExceptionHandler exceptionHandler = new ExceptionHandler() { @Override public void onException(Exception e, T model, boolean isCurrent) { + Log.e("IP: onImageLoadException model= " + model); if (e != null) { e.printStackTrace(); - } else { - Log.e("IP: onImageLoadException model= " + model); } } }; diff --git a/library/src/com/bumptech/glide/resize/ImageManager.java b/library/src/com/bumptech/glide/resize/ImageManager.java index 9f9f5c13ce..12f67ef67f 100644 --- a/library/src/com/bumptech/glide/resize/ImageManager.java +++ b/library/src/com/bumptech/glide/resize/ImageManager.java @@ -599,7 +599,7 @@ private void putInDiskCache(String key, final Bitmap bitmap) { diskCache.put(key, new DiskCache.Writer() { @Override public void write(OutputStream os) { - bitmap.compress(bitmapCompressFormat, bitmapCompressQuality, os); + bitmap.compress(bitmap.getConfig() == Bitmap.Config.ARGB_8888 ? Bitmap.CompressFormat.PNG : Bitmap.CompressFormat.JPEG, bitmapCompressQuality, os); } }); diff --git a/library/src/com/bumptech/glide/resize/bitmap_recycle/BitmapPool.java b/library/src/com/bumptech/glide/resize/bitmap_recycle/BitmapPool.java index 201b1fd71c..3aff6c698b 100644 --- a/library/src/com/bumptech/glide/resize/bitmap_recycle/BitmapPool.java +++ b/library/src/com/bumptech/glide/resize/bitmap_recycle/BitmapPool.java @@ -4,5 +4,5 @@ public interface BitmapPool { public boolean put(Bitmap bitmap); - public Bitmap get(int width, int height); + public Bitmap get(int width, int height, Bitmap.Config config); } diff --git a/library/src/com/bumptech/glide/resize/bitmap_recycle/BitmapPoolAdapter.java b/library/src/com/bumptech/glide/resize/bitmap_recycle/BitmapPoolAdapter.java index 01afaab47e..1725715551 100644 --- a/library/src/com/bumptech/glide/resize/bitmap_recycle/BitmapPoolAdapter.java +++ b/library/src/com/bumptech/glide/resize/bitmap_recycle/BitmapPoolAdapter.java @@ -3,13 +3,15 @@ import android.graphics.Bitmap; public class BitmapPoolAdapter implements BitmapPool { + @Override public boolean put(Bitmap bitmap) { return false; } @Override - public Bitmap get(int width, int height) { + public Bitmap get(int width, int height, Bitmap.Config config) { return null; } + } diff --git a/library/src/com/bumptech/glide/resize/bitmap_recycle/LruBitmapPool.java b/library/src/com/bumptech/glide/resize/bitmap_recycle/LruBitmapPool.java index c2e166ece8..07bfde2d66 100644 --- a/library/src/com/bumptech/glide/resize/bitmap_recycle/LruBitmapPool.java +++ b/library/src/com/bumptech/glide/resize/bitmap_recycle/LruBitmapPool.java @@ -22,12 +22,6 @@ public LruBitmapPool(int maxSize) { @Override public synchronized boolean put(Bitmap bitmap) { - //BitmapFactory.decodeStream can sometimes return bitmaps with null configs, which can't generally be - //reused - if (bitmap.getConfig() == null) { - return false; - } - final int size = getSize(bitmap); pool.put(bitmap); @@ -45,10 +39,10 @@ private void evict() { } @Override - public synchronized Bitmap get(int width, int height) { - final Bitmap result = pool.get(width, height); + public synchronized Bitmap get(int width, int height, Bitmap.Config config) { + final Bitmap result = pool.get(width, height, config); if (result == null) { - Log.d("LBP: missing bitmap for width=" + width + " height=" + height); + Log.d("LBP: missing bitmap for width=" + width + " height=" + height + " config=" + config); } else { currentSize -= getSize(result); } @@ -77,14 +71,14 @@ private static class GroupedBitmapLinkedMap { private static class KeyPool { private static final int MAX_SIZE = 20; - private Queue keyPool = new LinkedList(); + private final Queue keyPool = new LinkedList(); - public Key get(int width, int height) { + public Key get(int width, int height, Bitmap.Config config) { Key result = keyPool.poll(); if (result == null) { result = new Key(); } - result.init(width, height); + result.init(width, height, config); return result; } @@ -98,10 +92,12 @@ public void offer(Key key) { private static class Key { private int width; private int height; + private Bitmap.Config config; //this can be null :( - public void init(int width, int height) { + public void init(int width, int height, Bitmap.Config config) { this.width = width; this.height = height; + this.config = config; } @Override @@ -113,6 +109,7 @@ public boolean equals(Object o) { if (height != key.height) return false; if (width != key.width) return false; + if (config != key.config) return false; return true; } @@ -121,12 +118,13 @@ public boolean equals(Object o) { public int hashCode() { int result = width; result = 31 * result + height; + result = 31 * result + (config != null ? config.hashCode() : 0); return result; } } public void put(Bitmap bitmap) { - final Key key = keyPool.get(bitmap.getWidth(), bitmap.getHeight()); + final Key key = keyPool.get(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig()); LinkedEntry entry = keyToEntry.get(key); if (entry == null) { @@ -140,8 +138,8 @@ public void put(Bitmap bitmap) { entry.add(bitmap); } - public Bitmap get(int width, int height) { - final Key key = keyPool.get(width, height); + public Bitmap get(int width, int height, Bitmap.Config config) { + final Key key = keyPool.get(width, height, config); LinkedEntry entry = keyToEntry.get(key); if (entry == null) { diff --git a/library/src/com/bumptech/glide/resize/load/Downsampler.java b/library/src/com/bumptech/glide/resize/load/Downsampler.java index cb921153b2..83f7522e8e 100644 --- a/library/src/com/bumptech/glide/resize/load/Downsampler.java +++ b/library/src/com/bumptech/glide/resize/load/Downsampler.java @@ -6,6 +6,7 @@ import android.os.Build; import com.bumptech.glide.resize.RecyclableBufferedInputStream; import com.bumptech.glide.resize.bitmap_recycle.BitmapPool; +import com.bumptech.glide.util.Log; import java.io.IOException; @@ -39,7 +40,6 @@ protected int getSampleSize(int inWidth, int inHeight, int outWidth, int outHeig } }; - /** * Load the image at its original size * @@ -71,7 +71,7 @@ public Bitmap downsample(RecyclableBufferedInputStream bis, BitmapFactory.Option bis.mark(MARK_POSITION); int orientation = 0; try { - orientation = new ExifOrientationParser(bis).getOrientation();//ImageResizer.getOrientation(bis); + orientation = new ExifOrientationParser(bis).getOrientation(); } catch (IOException e) { e.printStackTrace(); } @@ -109,11 +109,28 @@ protected Bitmap downsampleWithSize(RecyclableBufferedInputStream bis, BitmapFac if (sampleSize > 1) { options.inSampleSize = sampleSize; } else { - setInBitmap(options, pool.get(inWidth, inHeight)); + setInBitmap(options, pool.get(inWidth, inHeight, getConfig(bis))); } return decodeStream(bis, options); } + private Bitmap.Config getConfig(RecyclableBufferedInputStream bis) { + Bitmap.Config result = Bitmap.Config.RGB_565; + bis.mark(1024); //we probably only need 25, but this is safer (particularly since the buffer size is > 1024) + try { + result = new ExifOrientationParser(bis).hasAlpha() ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + bis.reset(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return result; + } + /** * Get some id that uniquely identifies the downsample for use as part of a cache key * @return A unique String @@ -163,6 +180,9 @@ private Bitmap decodeStream(RecyclableBufferedInputStream bis, BitmapFactory.Opt } final Bitmap result = BitmapFactory.decodeStream(bis, null, options); + if (result == null && !options.inJustDecodeBounds) { + throw new IllegalArgumentException("IP: null result, sampleSize=" + options.inSampleSize); + } try { if (options.inJustDecodeBounds) { @@ -172,6 +192,7 @@ private Bitmap decodeStream(RecyclableBufferedInputStream bis, BitmapFactory.Opt bis.close(); } } catch (IOException e) { + Log.d("Downsampler: exception loading inDecodeBounds=" + options.inJustDecodeBounds + " sample=" + options.inSampleSize); e.printStackTrace(); } diff --git a/library/src/com/bumptech/glide/resize/load/ExifOrientationParser.java b/library/src/com/bumptech/glide/resize/load/ExifOrientationParser.java index 40949f41ae..78d4f65a00 100644 --- a/library/src/com/bumptech/glide/resize/load/ExifOrientationParser.java +++ b/library/src/com/bumptech/glide/resize/load/ExifOrientationParser.java @@ -11,6 +11,8 @@ * A class for parsing the exif orientation from an InputStream for an image. Handles jpegs and tiffs. */ public class ExifOrientationParser { + private static final int GIF_HEADER = 0x474946; + private static final int PNG_HEADER = 0x89504E47; private static final int EXIF_MAGIC_NUMBER = 0xFFD8; private static final int MOTOROLA_TIFF_MAGIC_NUMBER = 0x4D4D; // "MM" private static final int INTEL_TIFF_MAGIC_NUMBER = 0x4949; // "II" @@ -32,6 +34,32 @@ public ExifOrientationParser(InputStream is) { streamReader = new StreamReader(is); } + + // 0xD0A3C68 -> > 8) { //JPEG + return false; + } + + final int firstTwoBytes = firstByte << 8 & 0xFF00 | streamReader.getUInt8() & 0xFF; + final int firstFourBytes = firstTwoBytes << 16 & 0xFFFF0000 | streamReader.getUInt16() & 0xFFFF; + if (firstFourBytes == PNG_HEADER) { //PNG + //see: http://stackoverflow.com/questions/2057923/how-to-check-a-png-for-grayscale-alpha-color-type + streamReader.skip(25 - 4); + int alpha = streamReader.getByte(); + return alpha > 3; + } + + if (firstFourBytes >> 8 == GIF_HEADER) { //GIF from first 3 bytes + return true; + } + + return false; + } + /** * Parse the orientation from the image header. If it doesn't handle this image type (or this is not an image) * it will return a default value rather than throwing an exception. @@ -225,6 +253,10 @@ public long skip(long total) throws IOException { public int read(byte[] buffer) throws IOException { return is.read(buffer); } + + public int getByte() throws IOException { + return is.read(); + } } } diff --git a/library/src/com/bumptech/glide/resize/load/ImageResizer.java b/library/src/com/bumptech/glide/resize/load/ImageResizer.java index 61d53aeeec..a96b047860 100644 --- a/library/src/com/bumptech/glide/resize/load/ImageResizer.java +++ b/library/src/com/bumptech/glide/resize/load/ImageResizer.java @@ -437,7 +437,7 @@ public static Bitmap rotateImageExif(Bitmap toOrient, BitmapPool pool, int exifO final int newWidth = Math.round(newRect.width()); final int newHeight = Math.round(newRect.height()); - Bitmap result = pool.get(newWidth, newHeight); + Bitmap result = pool.get(newWidth, newHeight, toOrient.getConfig()); if (result == null) { result = Bitmap.createBitmap(newWidth, newHeight, toOrient.getConfig()); } diff --git a/library/src/com/bumptech/glide/resize/load/Transformation.java b/library/src/com/bumptech/glide/resize/load/Transformation.java index e38a413972..1845fea315 100644 --- a/library/src/com/bumptech/glide/resize/load/Transformation.java +++ b/library/src/com/bumptech/glide/resize/load/Transformation.java @@ -20,7 +20,7 @@ public Bitmap transform(Bitmap bitmap, BitmapPool pool, int outWidth, int outHei throw new IllegalArgumentException("Cannot center crop image to width=" + outWidth + " and height=" + outHeight); } - return ImageResizer.centerCrop(pool.get(outWidth, outHeight), bitmap, outWidth, outHeight); + return ImageResizer.centerCrop(pool.get(outWidth, outHeight, bitmap.getConfig()), bitmap, outWidth, outHeight); } };