Downsampler.java revision 53c16e03081b659c2c9009721b1a50728d4fae80
1bcf4a0dae04a4ad14287eeb34069a97c96fe9bb1Sam Juddpackage com.bumptech.glide.load.resource.bitmap;
2c27229a159bfc992874609270ab8b57981fef339Sam Judd
3c27229a159bfc992874609270ab8b57981fef339Sam Juddimport android.annotation.TargetApi;
4c27229a159bfc992874609270ab8b57981fef339Sam Juddimport android.graphics.Bitmap;
5c27229a159bfc992874609270ab8b57981fef339Sam Juddimport android.graphics.BitmapFactory;
6c27229a159bfc992874609270ab8b57981fef339Sam Juddimport android.os.Build;
7e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Juddimport android.util.Log;
89fc12334a7d14347cd6951d0653264b2597bd3a0Sam Juddimport com.bumptech.glide.load.DecodeFormat;
9aed5a1923b6add5101689ca462107cc16877b05eSam Juddimport com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
100ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Juddimport com.bumptech.glide.util.ByteArrayPool;
110ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd
12c27229a159bfc992874609270ab8b57981fef339Sam Juddimport java.io.IOException;
130ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Juddimport java.io.InputStream;
14aed5a1923b6add5101689ca462107cc16877b05eSam Juddimport java.util.ArrayDeque;
15c24284108320cf3d613497ddd67ba4e1b232ce74Savvas Dalkitsisimport java.util.EnumSet;
16aed5a1923b6add5101689ca462107cc16877b05eSam Juddimport java.util.Queue;
17c24284108320cf3d613497ddd67ba4e1b232ce74Savvas Dalkitsisimport java.util.Set;
18c27229a159bfc992874609270ab8b57981fef339Sam Judd
19c27229a159bfc992874609270ab8b57981fef339Sam Judd/**
20c27229a159bfc992874609270ab8b57981fef339Sam Judd * A base class with methods for loading and decoding images from InputStreams.
21c27229a159bfc992874609270ab8b57981fef339Sam Judd */
220ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Juddpublic abstract class Downsampler implements BitmapDecoder<InputStream> {
23e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd    private static final String TAG = "Downsampler";
24c24284108320cf3d613497ddd67ba4e1b232ce74Savvas Dalkitsis
250ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd    private static final boolean CAN_RECYCLE = Build.VERSION.SDK_INT >= 11;
269fc12334a7d14347cd6951d0653264b2597bd3a0Sam Judd    private static final Set<ImageHeaderParser.ImageType> TYPES_THAT_USE_POOL = EnumSet.of(
279fc12334a7d14347cd6951d0653264b2597bd3a0Sam Judd            ImageHeaderParser.ImageType.JPEG, ImageHeaderParser.ImageType.PNG_A, ImageHeaderParser.ImageType.PNG);
280ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd
29aed5a1923b6add5101689ca462107cc16877b05eSam Judd    private static final Queue<BitmapFactory.Options> OPTIONS_QUEUE = new ArrayDeque<BitmapFactory.Options>();
30aed5a1923b6add5101689ca462107cc16877b05eSam Judd
310ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd    @TargetApi(11)
32aed5a1923b6add5101689ca462107cc16877b05eSam Judd    private static synchronized BitmapFactory.Options getDefaultOptions() {
33aed5a1923b6add5101689ca462107cc16877b05eSam Judd        BitmapFactory.Options decodeBitmapOptions = OPTIONS_QUEUE.poll();
34aed5a1923b6add5101689ca462107cc16877b05eSam Judd        if (decodeBitmapOptions == null) {
35aed5a1923b6add5101689ca462107cc16877b05eSam Judd            decodeBitmapOptions = new BitmapFactory.Options();
36aed5a1923b6add5101689ca462107cc16877b05eSam Judd            resetOptions(decodeBitmapOptions);
37aed5a1923b6add5101689ca462107cc16877b05eSam Judd        }
38aed5a1923b6add5101689ca462107cc16877b05eSam Judd
39aed5a1923b6add5101689ca462107cc16877b05eSam Judd        return decodeBitmapOptions;
40aed5a1923b6add5101689ca462107cc16877b05eSam Judd    }
41aed5a1923b6add5101689ca462107cc16877b05eSam Judd
42aed5a1923b6add5101689ca462107cc16877b05eSam Judd    private static void releaseOptions(BitmapFactory.Options decodeBitmapOptions) {
43aed5a1923b6add5101689ca462107cc16877b05eSam Judd        resetOptions(decodeBitmapOptions);
44aed5a1923b6add5101689ca462107cc16877b05eSam Judd        OPTIONS_QUEUE.offer(decodeBitmapOptions);
45aed5a1923b6add5101689ca462107cc16877b05eSam Judd    }
46aed5a1923b6add5101689ca462107cc16877b05eSam Judd
47aed5a1923b6add5101689ca462107cc16877b05eSam Judd    @TargetApi(11)
48aed5a1923b6add5101689ca462107cc16877b05eSam Judd    private static void resetOptions(BitmapFactory.Options decodeBitmapOptions) {
49aed5a1923b6add5101689ca462107cc16877b05eSam Judd        decodeBitmapOptions.inTempStorage = null;
50aed5a1923b6add5101689ca462107cc16877b05eSam Judd        decodeBitmapOptions.inDither = false;
51aed5a1923b6add5101689ca462107cc16877b05eSam Judd        decodeBitmapOptions.inScaled = false;
52aed5a1923b6add5101689ca462107cc16877b05eSam Judd        decodeBitmapOptions.inSampleSize = 1;
53aed5a1923b6add5101689ca462107cc16877b05eSam Judd        decodeBitmapOptions.inPreferredConfig = null;
54aed5a1923b6add5101689ca462107cc16877b05eSam Judd        decodeBitmapOptions.inJustDecodeBounds = false;
55aed5a1923b6add5101689ca462107cc16877b05eSam Judd
56aed5a1923b6add5101689ca462107cc16877b05eSam Judd        if (CAN_RECYCLE)  {
57aed5a1923b6add5101689ca462107cc16877b05eSam Judd            decodeBitmapOptions.inBitmap = null;
58aed5a1923b6add5101689ca462107cc16877b05eSam Judd            decodeBitmapOptions.inMutable = true;
59aed5a1923b6add5101689ca462107cc16877b05eSam Judd        }
600ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd    }
610ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd
62c27229a159bfc992874609270ab8b57981fef339Sam Judd    /**
63c27229a159bfc992874609270ab8b57981fef339Sam Judd     * Load and scale the image uniformly (maintaining the image's aspect ratio) so that the dimensions of the image
64c27229a159bfc992874609270ab8b57981fef339Sam Judd     * will be greater than or equal to the given width and height.
65c27229a159bfc992874609270ab8b57981fef339Sam Judd     *
66c27229a159bfc992874609270ab8b57981fef339Sam Judd     */
67c27229a159bfc992874609270ab8b57981fef339Sam Judd    public static Downsampler AT_LEAST = new Downsampler() {
68c27229a159bfc992874609270ab8b57981fef339Sam Judd        @Override
69c27229a159bfc992874609270ab8b57981fef339Sam Judd        protected int getSampleSize(int inWidth, int inHeight, int outWidth, int outHeight) {
70c27229a159bfc992874609270ab8b57981fef339Sam Judd            return Math.min(inHeight / outHeight, inWidth / outWidth);
71c27229a159bfc992874609270ab8b57981fef339Sam Judd        }
72423bc54484b4be962955b2c194cf72edf705a935Sam Judd
73423bc54484b4be962955b2c194cf72edf705a935Sam Judd        @Override
74423bc54484b4be962955b2c194cf72edf705a935Sam Judd        public String getId() {
75423bc54484b4be962955b2c194cf72edf705a935Sam Judd            return "AT_LEAST.com.bumptech.glide.load.data.bitmap";
76423bc54484b4be962955b2c194cf72edf705a935Sam Judd        }
77c27229a159bfc992874609270ab8b57981fef339Sam Judd    };
78c27229a159bfc992874609270ab8b57981fef339Sam Judd
79c27229a159bfc992874609270ab8b57981fef339Sam Judd    /**
80c27229a159bfc992874609270ab8b57981fef339Sam Judd     * Load and scale the image uniformly (maintaining the image's aspect ratio) so that the dimensions of the image
81c27229a159bfc992874609270ab8b57981fef339Sam Judd     * will be less than or equal to the given width and height.
82c27229a159bfc992874609270ab8b57981fef339Sam Judd     *
83c27229a159bfc992874609270ab8b57981fef339Sam Judd     */
84c27229a159bfc992874609270ab8b57981fef339Sam Judd    public static Downsampler AT_MOST = new Downsampler() {
85c27229a159bfc992874609270ab8b57981fef339Sam Judd        @Override
86c27229a159bfc992874609270ab8b57981fef339Sam Judd        protected int getSampleSize(int inWidth, int inHeight, int outWidth, int outHeight) {
87c27229a159bfc992874609270ab8b57981fef339Sam Judd            return Math.max(inHeight / outHeight, inWidth / outWidth);
88c27229a159bfc992874609270ab8b57981fef339Sam Judd        }
89423bc54484b4be962955b2c194cf72edf705a935Sam Judd
90423bc54484b4be962955b2c194cf72edf705a935Sam Judd        @Override
91423bc54484b4be962955b2c194cf72edf705a935Sam Judd        public String getId() {
92423bc54484b4be962955b2c194cf72edf705a935Sam Judd            return "AT_MOST.com.bumptech.glide.load.data.bitmap";
93423bc54484b4be962955b2c194cf72edf705a935Sam Judd        }
94c27229a159bfc992874609270ab8b57981fef339Sam Judd    };
95c27229a159bfc992874609270ab8b57981fef339Sam Judd
96c27229a159bfc992874609270ab8b57981fef339Sam Judd    /**
97c27229a159bfc992874609270ab8b57981fef339Sam Judd     * Load the image at its original size
98c27229a159bfc992874609270ab8b57981fef339Sam Judd     *
99c27229a159bfc992874609270ab8b57981fef339Sam Judd     */
100c27229a159bfc992874609270ab8b57981fef339Sam Judd    public static Downsampler NONE = new Downsampler() {
101c27229a159bfc992874609270ab8b57981fef339Sam Judd        @Override
102c27229a159bfc992874609270ab8b57981fef339Sam Judd        protected int getSampleSize(int inWidth, int inHeight, int outWidth, int outHeight) {
103c27229a159bfc992874609270ab8b57981fef339Sam Judd            return 0;
104c27229a159bfc992874609270ab8b57981fef339Sam Judd        }
105423bc54484b4be962955b2c194cf72edf705a935Sam Judd
106423bc54484b4be962955b2c194cf72edf705a935Sam Judd        @Override
107423bc54484b4be962955b2c194cf72edf705a935Sam Judd        public String getId() {
108423bc54484b4be962955b2c194cf72edf705a935Sam Judd            return "NONE.com.bumptech.glide.load.data.bitmap";
109423bc54484b4be962955b2c194cf72edf705a935Sam Judd        }
110c27229a159bfc992874609270ab8b57981fef339Sam Judd    };
111c27229a159bfc992874609270ab8b57981fef339Sam Judd
1120ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd    // 5MB. This is the max image header size we can handle, we preallocate a much smaller buffer but will resize up to
1130ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd    // this amount if necessary.
1140ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd    private static final int MARK_POSITION = 5 * 1024 * 1024;
1150ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd
116c27229a159bfc992874609270ab8b57981fef339Sam Judd
117c27229a159bfc992874609270ab8b57981fef339Sam Judd    /**
118c27229a159bfc992874609270ab8b57981fef339Sam Judd     * Load the image for the given InputStream. If a recycled Bitmap whose dimensions exactly match those of the image
119c27229a159bfc992874609270ab8b57981fef339Sam Judd     * for the given InputStream is available, the operation is much less expensive in terms of memory.
120c27229a159bfc992874609270ab8b57981fef339Sam Judd     *
121c27229a159bfc992874609270ab8b57981fef339Sam Judd     * Note - this method will throw an exception of a Bitmap with dimensions not matching those of the image for the
122c27229a159bfc992874609270ab8b57981fef339Sam Judd     * given InputStream is provided.
123c27229a159bfc992874609270ab8b57981fef339Sam Judd     *
1240ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd     * @param is An InputStream to the data for the image
125c27229a159bfc992874609270ab8b57981fef339Sam Judd     * @param pool A pool of recycled bitmaps
126c27229a159bfc992874609270ab8b57981fef339Sam Judd     * @param outWidth The width the final image should be close to
127c27229a159bfc992874609270ab8b57981fef339Sam Judd     * @param outHeight The height the final image should be close to
128c27229a159bfc992874609270ab8b57981fef339Sam Judd     * @return A new bitmap containing the image from the given InputStream, or recycle if recycle is not null
129c27229a159bfc992874609270ab8b57981fef339Sam Judd     */
1300ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd    @Override
13176fbad3dbce72240e9f5b82c826e3229c1176fb6Sam Judd    public Bitmap decode(InputStream is, BitmapPool pool, int outWidth, int outHeight, DecodeFormat decodeFormat) {
1320ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd        final ByteArrayPool byteArrayPool = ByteArrayPool.get();
1330ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd        byte[] bytesForOptions = byteArrayPool.getBytes();
1340ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd        byte[] bytesForStream = byteArrayPool.getBytes();
1350ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd        RecyclableBufferedInputStream bis = new RecyclableBufferedInputStream(is, bytesForStream);
136d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd        bis.mark(MARK_POSITION);
137d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd        int orientation = 0;
138d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd        try {
139f243e2db2fc16ab84ec68995520544f6a469b90dSam Judd            orientation = new ImageHeaderParser(bis).getOrientation();
140d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd        } catch (IOException e) {
141d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd            e.printStackTrace();
142d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd        }
143d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd        try {
144d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd            bis.reset();
145d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd        } catch (IOException e) {
146293fdc0fc9203e2286beef092ac4a1fcec55cd0dSam Judd            e.printStackTrace();
147d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd        }
148d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd
1490ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd        final BitmapFactory.Options options = getDefaultOptions();
1500ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd        options.inTempStorage = bytesForOptions;
1510ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd
152c27229a159bfc992874609270ab8b57981fef339Sam Judd        final int[] inDimens = getDimensions(bis, options);
153293fdc0fc9203e2286beef092ac4a1fcec55cd0dSam Judd        final int inWidth = inDimens[0];
154293fdc0fc9203e2286beef092ac4a1fcec55cd0dSam Judd        final int inHeight = inDimens[1];
155d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd
1560ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd        final int degreesToRotate = TransformationUtils.getExifOrientationDegrees(orientation);
157d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd        final int sampleSize;
158d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd        if (degreesToRotate == 90 || degreesToRotate == 270) {
1590ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd            // If we're rotating the image +-90 degrees, we need to downsample accordingly so the image width is
1600ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd            // decreased to near our target's height and the image height is decreased to near our target width.
161293fdc0fc9203e2286beef092ac4a1fcec55cd0dSam Judd            sampleSize = getSampleSize(inHeight, inWidth, outWidth, outHeight);
162d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd        } else {
163d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd            sampleSize = getSampleSize(inWidth, inHeight, outWidth, outHeight);
164d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd        }
165c27229a159bfc992874609270ab8b57981fef339Sam Judd
16676fbad3dbce72240e9f5b82c826e3229c1176fb6Sam Judd        final Bitmap downsampled = downsampleWithSize(bis, options, pool, inWidth, inHeight, sampleSize, decodeFormat);
167d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd
168dbb67f826b0e76645c809be6d589e9dcb8271324Sam Judd        Bitmap rotated = null;
169dbb67f826b0e76645c809be6d589e9dcb8271324Sam Judd        if (downsampled != null) {
170dbb67f826b0e76645c809be6d589e9dcb8271324Sam Judd            rotated = TransformationUtils.rotateImageExif(downsampled, pool, orientation);
171dbb67f826b0e76645c809be6d589e9dcb8271324Sam Judd
172dbb67f826b0e76645c809be6d589e9dcb8271324Sam Judd            if (downsampled != rotated && !pool.put(downsampled)) {
173dbb67f826b0e76645c809be6d589e9dcb8271324Sam Judd                downsampled.recycle();
174dbb67f826b0e76645c809be6d589e9dcb8271324Sam Judd            }
175d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd        }
176d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd
1770ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd        byteArrayPool.releaseBytes(bytesForOptions);
1780ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd        byteArrayPool.releaseBytes(bytesForStream);
179aed5a1923b6add5101689ca462107cc16877b05eSam Judd        releaseOptions(options);
180d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd        return rotated;
181c27229a159bfc992874609270ab8b57981fef339Sam Judd    }
182c27229a159bfc992874609270ab8b57981fef339Sam Judd
183da25daffc2047b70f7cf3eb1f6e7ae53043d9337Sam Judd    protected Bitmap downsampleWithSize(RecyclableBufferedInputStream bis, BitmapFactory.Options options,
18476fbad3dbce72240e9f5b82c826e3229c1176fb6Sam Judd            BitmapPool pool, int inWidth, int inHeight, int sampleSize, DecodeFormat decodeFormat) {
185da25daffc2047b70f7cf3eb1f6e7ae53043d9337Sam Judd        // Prior to KitKat, the inBitmap size must exactly match the size of the bitmap we're decoding.
18676fbad3dbce72240e9f5b82c826e3229c1176fb6Sam Judd        Bitmap.Config config = getConfig(bis, decodeFormat);
187da25daffc2047b70f7cf3eb1f6e7ae53043d9337Sam Judd        options.inSampleSize = sampleSize;
18876fbad3dbce72240e9f5b82c826e3229c1176fb6Sam Judd        options.inPreferredConfig = config;
189da25daffc2047b70f7cf3eb1f6e7ae53043d9337Sam Judd        if (options.inSampleSize == 1 || Build.VERSION.SDK_INT >= 19) {
19082341a72e30da80de01c59303e72855d10b5f945Savvas Dalkitsis            if (shouldUsePool(bis)) {
19176fbad3dbce72240e9f5b82c826e3229c1176fb6Sam Judd                setInBitmap(options, pool.get(inWidth, inHeight, config));
1922310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis            }
193293fdc0fc9203e2286beef092ac4a1fcec55cd0dSam Judd        }
194293fdc0fc9203e2286beef092ac4a1fcec55cd0dSam Judd        return decodeStream(bis, options);
195293fdc0fc9203e2286beef092ac4a1fcec55cd0dSam Judd    }
196293fdc0fc9203e2286beef092ac4a1fcec55cd0dSam Judd
197c24284108320cf3d613497ddd67ba4e1b232ce74Savvas Dalkitsis    private boolean shouldUsePool(RecyclableBufferedInputStream bis) {
198da25daffc2047b70f7cf3eb1f6e7ae53043d9337Sam Judd        // On KitKat+, any bitmap can be used to decode any other bitmap.
199da25daffc2047b70f7cf3eb1f6e7ae53043d9337Sam Judd        if (Build.VERSION.SDK_INT >= 19) {
200da25daffc2047b70f7cf3eb1f6e7ae53043d9337Sam Judd            return true;
201da25daffc2047b70f7cf3eb1f6e7ae53043d9337Sam Judd        }
202da25daffc2047b70f7cf3eb1f6e7ae53043d9337Sam Judd
2032310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis        bis.mark(1024);
2042310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis        try {
2059fc12334a7d14347cd6951d0653264b2597bd3a0Sam Judd            final ImageHeaderParser.ImageType type = new ImageHeaderParser(bis).getType();
206da25daffc2047b70f7cf3eb1f6e7ae53043d9337Sam Judd            // cannot reuse bitmaps when decoding images that are not PNG or JPG.
207da25daffc2047b70f7cf3eb1f6e7ae53043d9337Sam Judd            // look at : https://groups.google.com/forum/#!msg/android-developers/Mp0MFVFi1Fo/e8ZQ9FGdWdEJ
208c24284108320cf3d613497ddd67ba4e1b232ce74Savvas Dalkitsis            return TYPES_THAT_USE_POOL.contains(type);
2092310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis        } catch (IOException e) {
2102310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis            e.printStackTrace();
2112310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis        } finally {
2122310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis            try {
2132310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis                bis.reset();
2142310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis            } catch (IOException e) {
2152310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis                e.printStackTrace();
2162310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis            }
2172310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis        }
2182310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis        return false;
2192310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis    }
2202310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis
22176fbad3dbce72240e9f5b82c826e3229c1176fb6Sam Judd    private Bitmap.Config getConfig(RecyclableBufferedInputStream bis, DecodeFormat format) {
22276fbad3dbce72240e9f5b82c826e3229c1176fb6Sam Judd        if (format == DecodeFormat.ALWAYS_ARGB_8888) {
22376fbad3dbce72240e9f5b82c826e3229c1176fb6Sam Judd            return Bitmap.Config.ARGB_8888;
22476fbad3dbce72240e9f5b82c826e3229c1176fb6Sam Judd        }
22576fbad3dbce72240e9f5b82c826e3229c1176fb6Sam Judd
22676fbad3dbce72240e9f5b82c826e3229c1176fb6Sam Judd        boolean hasAlpha = false;
227525d50359e27ca73eeeba96a994155c684d05292Sam Judd        bis.mark(1024); //we probably only need 25, but this is safer (particularly since the buffer size is > 1024)
228525d50359e27ca73eeeba96a994155c684d05292Sam Judd        try {
22976fbad3dbce72240e9f5b82c826e3229c1176fb6Sam Judd            hasAlpha = new ImageHeaderParser(bis).hasAlpha();
230525d50359e27ca73eeeba96a994155c684d05292Sam Judd        } catch (IOException e) {
231525d50359e27ca73eeeba96a994155c684d05292Sam Judd            e.printStackTrace();
232525d50359e27ca73eeeba96a994155c684d05292Sam Judd        } finally {
233525d50359e27ca73eeeba96a994155c684d05292Sam Judd            try {
234525d50359e27ca73eeeba96a994155c684d05292Sam Judd                bis.reset();
235525d50359e27ca73eeeba96a994155c684d05292Sam Judd            } catch (IOException e) {
236525d50359e27ca73eeeba96a994155c684d05292Sam Judd                e.printStackTrace();
237525d50359e27ca73eeeba96a994155c684d05292Sam Judd            }
238525d50359e27ca73eeeba96a994155c684d05292Sam Judd        }
23976fbad3dbce72240e9f5b82c826e3229c1176fb6Sam Judd
24076fbad3dbce72240e9f5b82c826e3229c1176fb6Sam Judd        return hasAlpha ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
241525d50359e27ca73eeeba96a994155c684d05292Sam Judd    }
242525d50359e27ca73eeeba96a994155c684d05292Sam Judd
243c27229a159bfc992874609270ab8b57981fef339Sam Judd    /**
244c27229a159bfc992874609270ab8b57981fef339Sam Judd     * Determine the amount of downsampling to use for a load given the dimensions of the image to be downsampled and
245c27229a159bfc992874609270ab8b57981fef339Sam Judd     * the dimensions of the view/target the image will be displayed in.
246c27229a159bfc992874609270ab8b57981fef339Sam Judd     *
24753c16e03081b659c2c9009721b1a50728d4fae80Sam Judd     * @see android.graphics.BitmapFactory.Options#inSampleSize
248c27229a159bfc992874609270ab8b57981fef339Sam Judd     *
249c27229a159bfc992874609270ab8b57981fef339Sam Judd     * @param inWidth The width of the image to be downsampled
250c27229a159bfc992874609270ab8b57981fef339Sam Judd     * @param inHeight The height of the image to be downsampled
251c27229a159bfc992874609270ab8b57981fef339Sam Judd     * @param outWidth The width of the view/target the image will be displayed in
252c27229a159bfc992874609270ab8b57981fef339Sam Judd     * @param outHeight The height of the view/target the imag will be displayed in
253c27229a159bfc992874609270ab8b57981fef339Sam Judd     * @return An integer to pass in to {@link BitmapFactory#decodeStream(java.io.InputStream, android.graphics.Rect, android.graphics.BitmapFactory.Options)}
254c27229a159bfc992874609270ab8b57981fef339Sam Judd     */
255c27229a159bfc992874609270ab8b57981fef339Sam Judd    protected abstract int getSampleSize(int inWidth, int inHeight, int outWidth, int outHeight);
256c27229a159bfc992874609270ab8b57981fef339Sam Judd
257c27229a159bfc992874609270ab8b57981fef339Sam Judd    /**
258c27229a159bfc992874609270ab8b57981fef339Sam Judd     * A method for getting the dimensions of an image from the given InputStream
259c27229a159bfc992874609270ab8b57981fef339Sam Judd     *
260c27229a159bfc992874609270ab8b57981fef339Sam Judd     * @param bis The InputStream representing the image
261c27229a159bfc992874609270ab8b57981fef339Sam Judd     * @param options The options to pass to {@link BitmapFactory#decodeStream(java.io.InputStream, android.graphics.Rect, android.graphics.BitmapFactory.Options)}
262c27229a159bfc992874609270ab8b57981fef339Sam Judd     * @return an array containing the dimensions of the image in the form {width, height}
263c27229a159bfc992874609270ab8b57981fef339Sam Judd     */
264c27229a159bfc992874609270ab8b57981fef339Sam Judd    public int[] getDimensions(RecyclableBufferedInputStream bis, BitmapFactory.Options options) {
265c27229a159bfc992874609270ab8b57981fef339Sam Judd        options.inJustDecodeBounds = true;
266c27229a159bfc992874609270ab8b57981fef339Sam Judd        decodeStream(bis, options);
267c27229a159bfc992874609270ab8b57981fef339Sam Judd        options.inJustDecodeBounds = false;
268c27229a159bfc992874609270ab8b57981fef339Sam Judd        return new int[] { options.outWidth, options.outHeight };
269c27229a159bfc992874609270ab8b57981fef339Sam Judd    }
270c27229a159bfc992874609270ab8b57981fef339Sam Judd
271c27229a159bfc992874609270ab8b57981fef339Sam Judd
272c27229a159bfc992874609270ab8b57981fef339Sam Judd    private Bitmap decodeStream(RecyclableBufferedInputStream bis, BitmapFactory.Options options) {
273c27229a159bfc992874609270ab8b57981fef339Sam Judd         if (options.inJustDecodeBounds) {
274c27229a159bfc992874609270ab8b57981fef339Sam Judd             bis.mark(MARK_POSITION); //this is large, but jpeg headers are not size bounded so we need
275c27229a159bfc992874609270ab8b57981fef339Sam Judd                         //something large enough to minimize the possibility of not being able to fit
276c27229a159bfc992874609270ab8b57981fef339Sam Judd                         //enough of the header in the buffer to get the image size so that we don't fail
277c27229a159bfc992874609270ab8b57981fef339Sam Judd                         //to load images. The BufferedInputStream will create a new buffer of 2x the
278c27229a159bfc992874609270ab8b57981fef339Sam Judd                         //original size each time we use up the buffer space without passing the mark so
279c27229a159bfc992874609270ab8b57981fef339Sam Judd                         //this is a maximum bound on the buffer size, not a default. Most of the time we
280c27229a159bfc992874609270ab8b57981fef339Sam Judd                         //won't go past our pre-allocated 16kb
281c27229a159bfc992874609270ab8b57981fef339Sam Judd         }
282c27229a159bfc992874609270ab8b57981fef339Sam Judd
283c27229a159bfc992874609270ab8b57981fef339Sam Judd        final Bitmap result = BitmapFactory.decodeStream(bis, null, options);
284c27229a159bfc992874609270ab8b57981fef339Sam Judd
285c27229a159bfc992874609270ab8b57981fef339Sam Judd        try {
286c27229a159bfc992874609270ab8b57981fef339Sam Judd            if (options.inJustDecodeBounds) {
287c27229a159bfc992874609270ab8b57981fef339Sam Judd                bis.reset();
288c27229a159bfc992874609270ab8b57981fef339Sam Judd                bis.clearMark();
289c27229a159bfc992874609270ab8b57981fef339Sam Judd            } else {
290c27229a159bfc992874609270ab8b57981fef339Sam Judd                bis.close();
291c27229a159bfc992874609270ab8b57981fef339Sam Judd            }
292c27229a159bfc992874609270ab8b57981fef339Sam Judd        } catch (IOException e) {
293e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd            if (Log.isLoggable(TAG, Log.ERROR)) {
294e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd                Log.e(TAG, "Exception loading inDecodeBounds=" + options.inJustDecodeBounds
295e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd                        + " sample=" + options.inSampleSize, e);
296e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd            }
297c27229a159bfc992874609270ab8b57981fef339Sam Judd        }
298c27229a159bfc992874609270ab8b57981fef339Sam Judd
299c27229a159bfc992874609270ab8b57981fef339Sam Judd        return result;
300c27229a159bfc992874609270ab8b57981fef339Sam Judd    }
301c27229a159bfc992874609270ab8b57981fef339Sam Judd
302c27229a159bfc992874609270ab8b57981fef339Sam Judd    @TargetApi(11)
303c27229a159bfc992874609270ab8b57981fef339Sam Judd    private static void setInBitmap(BitmapFactory.Options options, Bitmap recycled) {
304c27229a159bfc992874609270ab8b57981fef339Sam Judd        if (Build.VERSION.SDK_INT >= 11) {
305c27229a159bfc992874609270ab8b57981fef339Sam Judd            options.inBitmap = recycled;
306c27229a159bfc992874609270ab8b57981fef339Sam Judd        }
307c27229a159bfc992874609270ab8b57981fef339Sam Judd    }
308c27229a159bfc992874609270ab8b57981fef339Sam Judd}
309