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;
8e7319b67364bd0ac6306bc7470a43d4a31600c1aRobert Papp
99fc12334a7d14347cd6951d0653264b2597bd3a0Sam Juddimport com.bumptech.glide.load.DecodeFormat;
10aed5a1923b6add5101689ca462107cc16877b05eSam Juddimport com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
110ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Juddimport com.bumptech.glide.util.ByteArrayPool;
12526ae88675e01956aa14415b0cba527cf4f1cb0aSam Juddimport com.bumptech.glide.util.ExceptionCatchingInputStream;
13e8b5d67302f343bfa262c8c90fda5145813f292aRobert Pappimport com.bumptech.glide.util.Util;
140ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd
15c27229a159bfc992874609270ab8b57981fef339Sam Juddimport java.io.IOException;
160ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Juddimport java.io.InputStream;
17c24284108320cf3d613497ddd67ba4e1b232ce74Savvas Dalkitsisimport java.util.EnumSet;
18aed5a1923b6add5101689ca462107cc16877b05eSam Juddimport java.util.Queue;
19c24284108320cf3d613497ddd67ba4e1b232ce74Savvas Dalkitsisimport java.util.Set;
20c27229a159bfc992874609270ab8b57981fef339Sam Judd
21c27229a159bfc992874609270ab8b57981fef339Sam Judd/**
22c27229a159bfc992874609270ab8b57981fef339Sam Judd * A base class with methods for loading and decoding images from InputStreams.
23c27229a159bfc992874609270ab8b57981fef339Sam Judd */
240ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Juddpublic abstract class Downsampler implements BitmapDecoder<InputStream> {
25e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd    private static final String TAG = "Downsampler";
26c24284108320cf3d613497ddd67ba4e1b232ce74Savvas Dalkitsis
279fc12334a7d14347cd6951d0653264b2597bd3a0Sam Judd    private static final Set<ImageHeaderParser.ImageType> TYPES_THAT_USE_POOL = EnumSet.of(
289fc12334a7d14347cd6951d0653264b2597bd3a0Sam Judd            ImageHeaderParser.ImageType.JPEG, ImageHeaderParser.ImageType.PNG_A, ImageHeaderParser.ImageType.PNG);
290ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd
30e8b5d67302f343bfa262c8c90fda5145813f292aRobert Papp    private static final Queue<BitmapFactory.Options> OPTIONS_QUEUE = Util.createQueue(0);
31aed5a1923b6add5101689ca462107cc16877b05eSam Judd
32c27229a159bfc992874609270ab8b57981fef339Sam Judd    /**
33c27229a159bfc992874609270ab8b57981fef339Sam Judd     * Load and scale the image uniformly (maintaining the image's aspect ratio) so that the dimensions of the image
34c27229a159bfc992874609270ab8b57981fef339Sam Judd     * will be greater than or equal to the given width and height.
35c27229a159bfc992874609270ab8b57981fef339Sam Judd     */
36fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd    public static final Downsampler AT_LEAST = new Downsampler() {
37c27229a159bfc992874609270ab8b57981fef339Sam Judd        @Override
38c27229a159bfc992874609270ab8b57981fef339Sam Judd        protected int getSampleSize(int inWidth, int inHeight, int outWidth, int outHeight) {
39c27229a159bfc992874609270ab8b57981fef339Sam Judd            return Math.min(inHeight / outHeight, inWidth / outWidth);
40c27229a159bfc992874609270ab8b57981fef339Sam Judd        }
41423bc54484b4be962955b2c194cf72edf705a935Sam Judd
42423bc54484b4be962955b2c194cf72edf705a935Sam Judd        @Override
43423bc54484b4be962955b2c194cf72edf705a935Sam Judd        public String getId() {
44423bc54484b4be962955b2c194cf72edf705a935Sam Judd            return "AT_LEAST.com.bumptech.glide.load.data.bitmap";
45423bc54484b4be962955b2c194cf72edf705a935Sam Judd        }
46c27229a159bfc992874609270ab8b57981fef339Sam Judd    };
47c27229a159bfc992874609270ab8b57981fef339Sam Judd
48c27229a159bfc992874609270ab8b57981fef339Sam Judd    /**
49c27229a159bfc992874609270ab8b57981fef339Sam Judd     * Load and scale the image uniformly (maintaining the image's aspect ratio) so that the dimensions of the image
50c27229a159bfc992874609270ab8b57981fef339Sam Judd     * will be less than or equal to the given width and height.
51c27229a159bfc992874609270ab8b57981fef339Sam Judd     *
52c27229a159bfc992874609270ab8b57981fef339Sam Judd     */
53fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd    public static final Downsampler AT_MOST = new Downsampler() {
54c27229a159bfc992874609270ab8b57981fef339Sam Judd        @Override
55c27229a159bfc992874609270ab8b57981fef339Sam Judd        protected int getSampleSize(int inWidth, int inHeight, int outWidth, int outHeight) {
56c27229a159bfc992874609270ab8b57981fef339Sam Judd            return Math.max(inHeight / outHeight, inWidth / outWidth);
57c27229a159bfc992874609270ab8b57981fef339Sam Judd        }
58423bc54484b4be962955b2c194cf72edf705a935Sam Judd
59423bc54484b4be962955b2c194cf72edf705a935Sam Judd        @Override
60423bc54484b4be962955b2c194cf72edf705a935Sam Judd        public String getId() {
61423bc54484b4be962955b2c194cf72edf705a935Sam Judd            return "AT_MOST.com.bumptech.glide.load.data.bitmap";
62423bc54484b4be962955b2c194cf72edf705a935Sam Judd        }
63c27229a159bfc992874609270ab8b57981fef339Sam Judd    };
64c27229a159bfc992874609270ab8b57981fef339Sam Judd
65c27229a159bfc992874609270ab8b57981fef339Sam Judd    /**
66fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd     * Load the image at its original size.
67c27229a159bfc992874609270ab8b57981fef339Sam Judd     */
68fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd    public static final Downsampler NONE = new Downsampler() {
69c27229a159bfc992874609270ab8b57981fef339Sam Judd        @Override
70c27229a159bfc992874609270ab8b57981fef339Sam Judd        protected int getSampleSize(int inWidth, int inHeight, int outWidth, int outHeight) {
71c27229a159bfc992874609270ab8b57981fef339Sam Judd            return 0;
72c27229a159bfc992874609270ab8b57981fef339Sam Judd        }
73423bc54484b4be962955b2c194cf72edf705a935Sam Judd
74423bc54484b4be962955b2c194cf72edf705a935Sam Judd        @Override
75423bc54484b4be962955b2c194cf72edf705a935Sam Judd        public String getId() {
76423bc54484b4be962955b2c194cf72edf705a935Sam Judd            return "NONE.com.bumptech.glide.load.data.bitmap";
77423bc54484b4be962955b2c194cf72edf705a935Sam Judd        }
78c27229a159bfc992874609270ab8b57981fef339Sam Judd    };
79c27229a159bfc992874609270ab8b57981fef339Sam Judd
800ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd    // 5MB. This is the max image header size we can handle, we preallocate a much smaller buffer but will resize up to
810ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd    // this amount if necessary.
820ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd    private static final int MARK_POSITION = 5 * 1024 * 1024;
830ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd
84c27229a159bfc992874609270ab8b57981fef339Sam Judd
85c27229a159bfc992874609270ab8b57981fef339Sam Judd    /**
86c27229a159bfc992874609270ab8b57981fef339Sam Judd     * Load the image for the given InputStream. If a recycled Bitmap whose dimensions exactly match those of the image
87c27229a159bfc992874609270ab8b57981fef339Sam Judd     * for the given InputStream is available, the operation is much less expensive in terms of memory.
88c27229a159bfc992874609270ab8b57981fef339Sam Judd     *
89ae05603c3800df74390089d6a6c1562708cbca12Robert Papp     * <p>
90ae05603c3800df74390089d6a6c1562708cbca12Robert Papp     *     Note - this method will throw an exception of a Bitmap with dimensions not matching
91ae05603c3800df74390089d6a6c1562708cbca12Robert Papp     *     those of the image for the given InputStream is provided.
92ae05603c3800df74390089d6a6c1562708cbca12Robert Papp     * </p>
93c27229a159bfc992874609270ab8b57981fef339Sam Judd     *
94051069671413f8b66a1b237a8f682aa6c1b50275Sam Judd     * @param is An {@link InputStream} to the data for the image.
95051069671413f8b66a1b237a8f682aa6c1b50275Sam Judd     * @param pool A pool of recycled bitmaps.
96051069671413f8b66a1b237a8f682aa6c1b50275Sam Judd     * @param outWidth The width the final image should be close to.
97051069671413f8b66a1b237a8f682aa6c1b50275Sam Judd     * @param outHeight The height the final image should be close to.
98051069671413f8b66a1b237a8f682aa6c1b50275Sam Judd     * @return A new bitmap containing the image from the given InputStream, or recycle if recycle is not null.
99c27229a159bfc992874609270ab8b57981fef339Sam Judd     */
100ae05603c3800df74390089d6a6c1562708cbca12Robert Papp    @SuppressWarnings("resource")
101ae05603c3800df74390089d6a6c1562708cbca12Robert Papp    // see BitmapDecoder.decode
1020ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd    @Override
10376fbad3dbce72240e9f5b82c826e3229c1176fb6Sam Judd    public Bitmap decode(InputStream is, BitmapPool pool, int outWidth, int outHeight, DecodeFormat decodeFormat) {
1040ae32dc10d668a04f9f0484d587aefe5a7210e1cSam Judd        final ByteArrayPool byteArrayPool = ByteArrayPool.get();
105ae05603c3800df74390089d6a6c1562708cbca12Robert Papp        final byte[] bytesForOptions = byteArrayPool.getBytes();
106ae05603c3800df74390089d6a6c1562708cbca12Robert Papp        final byte[] bytesForStream = byteArrayPool.getBytes();
107ae05603c3800df74390089d6a6c1562708cbca12Robert Papp        final BitmapFactory.Options options = getDefaultOptions();
1082007440c380df454db15f83b019a5b4c55ca4b72Sam Judd
109526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd        // TODO(#126): when the framework handles exceptions better, consider removing.
110526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd        final ExceptionCatchingInputStream stream =
111526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd                ExceptionCatchingInputStream.obtain(new RecyclableBufferedInputStream(is, bytesForStream));
112d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd        try {
113526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd            stream.mark(MARK_POSITION);
114ae05603c3800df74390089d6a6c1562708cbca12Robert Papp            int orientation = 0;
115ae05603c3800df74390089d6a6c1562708cbca12Robert Papp            try {
116526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd                orientation = new ImageHeaderParser(stream).getOrientation();
117ae05603c3800df74390089d6a6c1562708cbca12Robert Papp            } catch (IOException e) {
118ae05603c3800df74390089d6a6c1562708cbca12Robert Papp                if (Log.isLoggable(TAG, Log.WARN)) {
119ae05603c3800df74390089d6a6c1562708cbca12Robert Papp                    Log.w(TAG, "Cannot determine the image orientation from header", e);
120ae05603c3800df74390089d6a6c1562708cbca12Robert Papp                }
121ae05603c3800df74390089d6a6c1562708cbca12Robert Papp            } finally {
122ae05603c3800df74390089d6a6c1562708cbca12Robert Papp                try {
123526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd                    stream.reset();
124ae05603c3800df74390089d6a6c1562708cbca12Robert Papp                } catch (IOException e) {
125ae05603c3800df74390089d6a6c1562708cbca12Robert Papp                    if (Log.isLoggable(TAG, Log.WARN)) {
126ae05603c3800df74390089d6a6c1562708cbca12Robert Papp                        Log.w(TAG, "Cannot reset the input stream", e);
127ae05603c3800df74390089d6a6c1562708cbca12Robert Papp                    }
128ae05603c3800df74390089d6a6c1562708cbca12Robert Papp                }
129ae05603c3800df74390089d6a6c1562708cbca12Robert Papp            }
130d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd
131ae05603c3800df74390089d6a6c1562708cbca12Robert Papp            options.inTempStorage = bytesForOptions;
132c27229a159bfc992874609270ab8b57981fef339Sam Judd
133526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd            final int[] inDimens = getDimensions(stream, options);
134ae05603c3800df74390089d6a6c1562708cbca12Robert Papp            final int inWidth = inDimens[0];
135ae05603c3800df74390089d6a6c1562708cbca12Robert Papp            final int inHeight = inDimens[1];
136d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd
137ae05603c3800df74390089d6a6c1562708cbca12Robert Papp            final int degreesToRotate = TransformationUtils.getExifOrientationDegrees(orientation);
13888868380fb22a5dae89d8664f2daa8c99522bc74Sam Judd            final int sampleSize = getRoundedSampleSize(degreesToRotate, inWidth, inHeight, outWidth, outHeight);
139ae05603c3800df74390089d6a6c1562708cbca12Robert Papp
140ae05603c3800df74390089d6a6c1562708cbca12Robert Papp            final Bitmap downsampled =
1412007440c380df454db15f83b019a5b4c55ca4b72Sam Judd                    downsampleWithSize(stream, options, pool, inWidth, inHeight, sampleSize,
1422007440c380df454db15f83b019a5b4c55ca4b72Sam Judd                            decodeFormat);
143526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd
1442007440c380df454db15f83b019a5b4c55ca4b72Sam Judd            // BitmapFactory swallows exceptions during decodes and in some cases when inBitmap is non null, may catch
145526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd            // and log a stack trace but still return a non null bitmap. To avoid displaying partially decoded bitmaps,
146526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd            // we catch exceptions reading from the stream in our ExceptionCatchingInputStream and throw them here.
147526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd            final Exception streamException = stream.getException();
148526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd            if (streamException != null) {
149526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd                throw new RuntimeException(streamException);
150526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd            }
151dbb67f826b0e76645c809be6d589e9dcb8271324Sam Judd
152ae05603c3800df74390089d6a6c1562708cbca12Robert Papp            Bitmap rotated = null;
153ae05603c3800df74390089d6a6c1562708cbca12Robert Papp            if (downsampled != null) {
154ae05603c3800df74390089d6a6c1562708cbca12Robert Papp                rotated = TransformationUtils.rotateImageExif(downsampled, pool, orientation);
155ae05603c3800df74390089d6a6c1562708cbca12Robert Papp
1565ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd                if (!downsampled.equals(rotated) && !pool.put(downsampled)) {
157ae05603c3800df74390089d6a6c1562708cbca12Robert Papp                    downsampled.recycle();
158ae05603c3800df74390089d6a6c1562708cbca12Robert Papp                }
159dbb67f826b0e76645c809be6d589e9dcb8271324Sam Judd            }
160d7efd9c46ebd8872640bb4b4efb564968905507dSam Judd
161ae05603c3800df74390089d6a6c1562708cbca12Robert Papp            return rotated;
162ae05603c3800df74390089d6a6c1562708cbca12Robert Papp        } finally {
163ae05603c3800df74390089d6a6c1562708cbca12Robert Papp            byteArrayPool.releaseBytes(bytesForOptions);
164ae05603c3800df74390089d6a6c1562708cbca12Robert Papp            byteArrayPool.releaseBytes(bytesForStream);
165526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd            stream.release();
166ae05603c3800df74390089d6a6c1562708cbca12Robert Papp            releaseOptions(options);
167ae05603c3800df74390089d6a6c1562708cbca12Robert Papp        }
168c27229a159bfc992874609270ab8b57981fef339Sam Judd    }
169c27229a159bfc992874609270ab8b57981fef339Sam Judd
17088868380fb22a5dae89d8664f2daa8c99522bc74Sam Judd    private int getRoundedSampleSize(int degreesToRotate, int inWidth, int inHeight, int outWidth, int outHeight) {
17188868380fb22a5dae89d8664f2daa8c99522bc74Sam Judd        final int exactSampleSize;
17288868380fb22a5dae89d8664f2daa8c99522bc74Sam Judd        if (degreesToRotate == 90 || degreesToRotate == 270) {
17388868380fb22a5dae89d8664f2daa8c99522bc74Sam Judd            // If we're rotating the image +-90 degrees, we need to downsample accordingly so the image width is
17488868380fb22a5dae89d8664f2daa8c99522bc74Sam Judd            // decreased to near our target's height and the image height is decreased to near our target width.
17588868380fb22a5dae89d8664f2daa8c99522bc74Sam Judd            exactSampleSize = getSampleSize(inHeight, inWidth, outWidth, outHeight);
17688868380fb22a5dae89d8664f2daa8c99522bc74Sam Judd        } else {
17788868380fb22a5dae89d8664f2daa8c99522bc74Sam Judd            exactSampleSize = getSampleSize(inWidth, inHeight, outWidth, outHeight);
17888868380fb22a5dae89d8664f2daa8c99522bc74Sam Judd        }
17988868380fb22a5dae89d8664f2daa8c99522bc74Sam Judd
18088868380fb22a5dae89d8664f2daa8c99522bc74Sam Judd        // BitmapFactory only accepts powers of 2, so it will round down to the nearest power of two that is less than
18188868380fb22a5dae89d8664f2daa8c99522bc74Sam Judd        // or equal to the sample size we provide. Because we need to estimate the final image width and height to
18288868380fb22a5dae89d8664f2daa8c99522bc74Sam Judd        // re-use Bitmaps, we mirror BitmapFactory's calculation here. For bug, see issue #224. For algorithm see
18388868380fb22a5dae89d8664f2daa8c99522bc74Sam Judd        // http://stackoverflow.com/a/17379704/800716.
18488868380fb22a5dae89d8664f2daa8c99522bc74Sam Judd        final int powerOfTwoSampleSize = exactSampleSize == 0 ? 0 : Integer.highestOneBit(exactSampleSize - 1);
18588868380fb22a5dae89d8664f2daa8c99522bc74Sam Judd
18688868380fb22a5dae89d8664f2daa8c99522bc74Sam Judd        // Although functionally equivalent to 0 for BitmapFactory, 1 is a safer default for our code than 0.
18788868380fb22a5dae89d8664f2daa8c99522bc74Sam Judd        return Math.max(1, powerOfTwoSampleSize);
18888868380fb22a5dae89d8664f2daa8c99522bc74Sam Judd    }
18988868380fb22a5dae89d8664f2daa8c99522bc74Sam Judd
1902007440c380df454db15f83b019a5b4c55ca4b72Sam Judd    private Bitmap downsampleWithSize(ExceptionCatchingInputStream is, BitmapFactory.Options options, BitmapPool pool,
1912007440c380df454db15f83b019a5b4c55ca4b72Sam Judd            int inWidth, int inHeight, int sampleSize, DecodeFormat decodeFormat) {
192da25daffc2047b70f7cf3eb1f6e7ae53043d9337Sam Judd        // Prior to KitKat, the inBitmap size must exactly match the size of the bitmap we're decoding.
193526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd        Bitmap.Config config = getConfig(is, decodeFormat);
194da25daffc2047b70f7cf3eb1f6e7ae53043d9337Sam Judd        options.inSampleSize = sampleSize;
19576fbad3dbce72240e9f5b82c826e3229c1176fb6Sam Judd        options.inPreferredConfig = config;
1965ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd        if ((options.inSampleSize == 1 || Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT) && shouldUsePool(is)) {
19788868380fb22a5dae89d8664f2daa8c99522bc74Sam Judd            int targetWidth = (int) Math.ceil(inWidth / (double) sampleSize);
19888868380fb22a5dae89d8664f2daa8c99522bc74Sam Judd            int targetHeight = (int) Math.ceil(inHeight / (double) sampleSize);
1995ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd            // BitmapFactory will clear out the Bitmap before writing to it, so getDirty is safe.
20088868380fb22a5dae89d8664f2daa8c99522bc74Sam Judd            setInBitmap(options, pool.getDirty(targetWidth, targetHeight, config));
201293fdc0fc9203e2286beef092ac4a1fcec55cd0dSam Judd        }
202526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd        return decodeStream(is, options);
203293fdc0fc9203e2286beef092ac4a1fcec55cd0dSam Judd    }
204293fdc0fc9203e2286beef092ac4a1fcec55cd0dSam Judd
205526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd    private static boolean shouldUsePool(InputStream is) {
206da25daffc2047b70f7cf3eb1f6e7ae53043d9337Sam Judd        // On KitKat+, any bitmap can be used to decode any other bitmap.
207e7319b67364bd0ac6306bc7470a43d4a31600c1aRobert Papp        if (Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT) {
208da25daffc2047b70f7cf3eb1f6e7ae53043d9337Sam Judd            return true;
209da25daffc2047b70f7cf3eb1f6e7ae53043d9337Sam Judd        }
210da25daffc2047b70f7cf3eb1f6e7ae53043d9337Sam Judd
211526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd        is.mark(1024);
2122310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis        try {
213526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd            final ImageHeaderParser.ImageType type = new ImageHeaderParser(is).getType();
214da25daffc2047b70f7cf3eb1f6e7ae53043d9337Sam Judd            // cannot reuse bitmaps when decoding images that are not PNG or JPG.
215da25daffc2047b70f7cf3eb1f6e7ae53043d9337Sam Judd            // look at : https://groups.google.com/forum/#!msg/android-developers/Mp0MFVFi1Fo/e8ZQ9FGdWdEJ
216c24284108320cf3d613497ddd67ba4e1b232ce74Savvas Dalkitsis            return TYPES_THAT_USE_POOL.contains(type);
2172310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis        } catch (IOException e) {
218ae05603c3800df74390089d6a6c1562708cbca12Robert Papp            if (Log.isLoggable(TAG, Log.WARN)) {
219ae05603c3800df74390089d6a6c1562708cbca12Robert Papp                Log.w(TAG, "Cannot determine the image type from header", e);
220ae05603c3800df74390089d6a6c1562708cbca12Robert Papp            }
2212310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis        } finally {
2222310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis            try {
223526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd                is.reset();
2242310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis            } catch (IOException e) {
225ae05603c3800df74390089d6a6c1562708cbca12Robert Papp                if (Log.isLoggable(TAG, Log.WARN)) {
226ae05603c3800df74390089d6a6c1562708cbca12Robert Papp                    Log.w(TAG, "Cannot reset the input stream", e);
227ae05603c3800df74390089d6a6c1562708cbca12Robert Papp                }
2282310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis            }
2292310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis        }
2302310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis        return false;
2312310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis    }
2322310318f0ff9ad1919ec61878a43cb8dc3bd5086Savvas Dalkitsis
233526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd    private static Bitmap.Config getConfig(InputStream is, DecodeFormat format) {
234b007bfcc492551550c1566a21c2eb7a402514776Sam Judd        // Changing configs can cause skewing on 4.1, see issue #128.
235b007bfcc492551550c1566a21c2eb7a402514776Sam Judd        if (format == DecodeFormat.ALWAYS_ARGB_8888 || Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN) {
23676fbad3dbce72240e9f5b82c826e3229c1176fb6Sam Judd            return Bitmap.Config.ARGB_8888;
23776fbad3dbce72240e9f5b82c826e3229c1176fb6Sam Judd        }
23876fbad3dbce72240e9f5b82c826e3229c1176fb6Sam Judd
23976fbad3dbce72240e9f5b82c826e3229c1176fb6Sam Judd        boolean hasAlpha = false;
240fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd        // We probably only need 25, but this is safer (particularly since the buffer size is > 1024).
241526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd        is.mark(1024);
242525d50359e27ca73eeeba96a994155c684d05292Sam Judd        try {
243526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd            hasAlpha = new ImageHeaderParser(is).hasAlpha();
244525d50359e27ca73eeeba96a994155c684d05292Sam Judd        } catch (IOException e) {
245ae05603c3800df74390089d6a6c1562708cbca12Robert Papp            if (Log.isLoggable(TAG, Log.WARN)) {
246ae05603c3800df74390089d6a6c1562708cbca12Robert Papp                Log.w(TAG, "Cannot determine whether the image has alpha or not from header for format " + format, e);
247ae05603c3800df74390089d6a6c1562708cbca12Robert Papp            }
248525d50359e27ca73eeeba96a994155c684d05292Sam Judd        } finally {
249525d50359e27ca73eeeba96a994155c684d05292Sam Judd            try {
250526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd                is.reset();
251525d50359e27ca73eeeba96a994155c684d05292Sam Judd            } catch (IOException e) {
252ae05603c3800df74390089d6a6c1562708cbca12Robert Papp                if (Log.isLoggable(TAG, Log.WARN)) {
253ae05603c3800df74390089d6a6c1562708cbca12Robert Papp                    Log.w(TAG, "Cannot reset the input stream", e);
254ae05603c3800df74390089d6a6c1562708cbca12Robert Papp                }
255525d50359e27ca73eeeba96a994155c684d05292Sam Judd            }
256525d50359e27ca73eeeba96a994155c684d05292Sam Judd        }
25776fbad3dbce72240e9f5b82c826e3229c1176fb6Sam Judd
25876fbad3dbce72240e9f5b82c826e3229c1176fb6Sam Judd        return hasAlpha ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
259525d50359e27ca73eeeba96a994155c684d05292Sam Judd    }
260525d50359e27ca73eeeba96a994155c684d05292Sam Judd
261c27229a159bfc992874609270ab8b57981fef339Sam Judd    /**
262c27229a159bfc992874609270ab8b57981fef339Sam Judd     * Determine the amount of downsampling to use for a load given the dimensions of the image to be downsampled and
263c27229a159bfc992874609270ab8b57981fef339Sam Judd     * the dimensions of the view/target the image will be displayed in.
264c27229a159bfc992874609270ab8b57981fef339Sam Judd     *
26553c16e03081b659c2c9009721b1a50728d4fae80Sam Judd     * @see android.graphics.BitmapFactory.Options#inSampleSize
266c27229a159bfc992874609270ab8b57981fef339Sam Judd     *
267fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd     * @param inWidth The width of the image to be downsampled.
268fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd     * @param inHeight The height of the image to be downsampled.
269fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd     * @param outWidth The width of the view/target the image will be displayed in.
270fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd     * @param outHeight The height of the view/target the imag will be displayed in.
271fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd     * @return An integer to pass in to {@link BitmapFactory#decodeStream(java.io.InputStream, android.graphics.Rect,
272fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd     *          android.graphics.BitmapFactory.Options)}.
273c27229a159bfc992874609270ab8b57981fef339Sam Judd     */
274c27229a159bfc992874609270ab8b57981fef339Sam Judd    protected abstract int getSampleSize(int inWidth, int inHeight, int outWidth, int outHeight);
275c27229a159bfc992874609270ab8b57981fef339Sam Judd
276c27229a159bfc992874609270ab8b57981fef339Sam Judd    /**
277fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd     * A method for getting the dimensions of an image from the given InputStream.
278c27229a159bfc992874609270ab8b57981fef339Sam Judd     *
279526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd     * @param is The InputStream representing the image.
280fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd     * @param options The options to pass to
281fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd     *          {@link BitmapFactory#decodeStream(java.io.InputStream, android.graphics.Rect,
282fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd     *              android.graphics.BitmapFactory.Options)}.
283fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd     * @return an array containing the dimensions of the image in the form {width, height}.
284c27229a159bfc992874609270ab8b57981fef339Sam Judd     */
2852007440c380df454db15f83b019a5b4c55ca4b72Sam Judd    public int[] getDimensions(ExceptionCatchingInputStream is, BitmapFactory.Options options) {
286c27229a159bfc992874609270ab8b57981fef339Sam Judd        options.inJustDecodeBounds = true;
287526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd        decodeStream(is, options);
288c27229a159bfc992874609270ab8b57981fef339Sam Judd        options.inJustDecodeBounds = false;
289c27229a159bfc992874609270ab8b57981fef339Sam Judd        return new int[] { options.outWidth, options.outHeight };
290c27229a159bfc992874609270ab8b57981fef339Sam Judd    }
291c27229a159bfc992874609270ab8b57981fef339Sam Judd
2922007440c380df454db15f83b019a5b4c55ca4b72Sam Judd    private static Bitmap decodeStream(ExceptionCatchingInputStream is, BitmapFactory.Options options) {
293c27229a159bfc992874609270ab8b57981fef339Sam Judd         if (options.inJustDecodeBounds) {
294fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd             // This is large, but jpeg headers are not size bounded so we need something large enough to minimize
295fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd             // the possibility of not being able to fit enough of the header in the buffer to get the image size so
296fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd             // that we don't fail to load images. The BufferedInputStream will create a new buffer of 2x the
297fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd             // original size each time we use up the buffer space without passing the mark so this is a maximum
298fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd             // bound on the buffer size, not a default. Most of the time we won't go past our pre-allocated 16kb.
299526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd             is.mark(MARK_POSITION);
3002007440c380df454db15f83b019a5b4c55ca4b72Sam Judd         } else {
3012007440c380df454db15f83b019a5b4c55ca4b72Sam Judd             // Once we've read the image header, we no longer need to allow the buffer to expand in size. To avoid
3022007440c380df454db15f83b019a5b4c55ca4b72Sam Judd             // unnecessary allocations reading image data, we fix the mark limit so that it is no larger than our
3032007440c380df454db15f83b019a5b4c55ca4b72Sam Judd             // current buffer size here. See issue #225.
3042007440c380df454db15f83b019a5b4c55ca4b72Sam Judd             is.fixMarkLimit();
305c27229a159bfc992874609270ab8b57981fef339Sam Judd         }
306c27229a159bfc992874609270ab8b57981fef339Sam Judd
307526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd        final Bitmap result = BitmapFactory.decodeStream(is, null, options);
308c27229a159bfc992874609270ab8b57981fef339Sam Judd
309c27229a159bfc992874609270ab8b57981fef339Sam Judd        try {
310c27229a159bfc992874609270ab8b57981fef339Sam Judd            if (options.inJustDecodeBounds) {
311526ae88675e01956aa14415b0cba527cf4f1cb0aSam Judd                is.reset();
312c27229a159bfc992874609270ab8b57981fef339Sam Judd            }
313c27229a159bfc992874609270ab8b57981fef339Sam Judd        } catch (IOException e) {
314e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd            if (Log.isLoggable(TAG, Log.ERROR)) {
315e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd                Log.e(TAG, "Exception loading inDecodeBounds=" + options.inJustDecodeBounds
316e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd                        + " sample=" + options.inSampleSize, e);
317e81c2c3c8fe1533de8ba8137ea831a42378b35a9Sam Judd            }
318c27229a159bfc992874609270ab8b57981fef339Sam Judd        }
319c27229a159bfc992874609270ab8b57981fef339Sam Judd
320c27229a159bfc992874609270ab8b57981fef339Sam Judd        return result;
321c27229a159bfc992874609270ab8b57981fef339Sam Judd    }
322c27229a159bfc992874609270ab8b57981fef339Sam Judd
323e7319b67364bd0ac6306bc7470a43d4a31600c1aRobert Papp    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
324c27229a159bfc992874609270ab8b57981fef339Sam Judd    private static void setInBitmap(BitmapFactory.Options options, Bitmap recycled) {
325e7319b67364bd0ac6306bc7470a43d4a31600c1aRobert Papp        if (Build.VERSION_CODES.HONEYCOMB <= Build.VERSION.SDK_INT) {
326c27229a159bfc992874609270ab8b57981fef339Sam Judd            options.inBitmap = recycled;
327c27229a159bfc992874609270ab8b57981fef339Sam Judd        }
328c27229a159bfc992874609270ab8b57981fef339Sam Judd    }
3295ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd
3305ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
3315ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd    private static synchronized BitmapFactory.Options getDefaultOptions() {
3325ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd        BitmapFactory.Options decodeBitmapOptions;
3335ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd        synchronized (OPTIONS_QUEUE) {
3345ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd            decodeBitmapOptions = OPTIONS_QUEUE.poll();
3355ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd        }
3365ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd        if (decodeBitmapOptions == null) {
3375ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd            decodeBitmapOptions = new BitmapFactory.Options();
3385ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd            resetOptions(decodeBitmapOptions);
3395ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd        }
3405ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd
3415ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd        return decodeBitmapOptions;
3425ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd    }
3435ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd
3445ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd    private static void releaseOptions(BitmapFactory.Options decodeBitmapOptions) {
3455ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd        resetOptions(decodeBitmapOptions);
3465ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd        synchronized (OPTIONS_QUEUE) {
3475ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd            OPTIONS_QUEUE.offer(decodeBitmapOptions);
3485ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd        }
3495ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd    }
3505ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd
3515ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
3525ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd    private static void resetOptions(BitmapFactory.Options decodeBitmapOptions) {
3535ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd        decodeBitmapOptions.inTempStorage = null;
3545ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd        decodeBitmapOptions.inDither = false;
3555ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd        decodeBitmapOptions.inScaled = false;
3565ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd        decodeBitmapOptions.inSampleSize = 1;
3575ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd        decodeBitmapOptions.inPreferredConfig = null;
3585ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd        decodeBitmapOptions.inJustDecodeBounds = false;
3595ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd
3605ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd        if (Build.VERSION_CODES.HONEYCOMB <= Build.VERSION.SDK_INT)  {
3615ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd            decodeBitmapOptions.inBitmap = null;
3625ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd            decodeBitmapOptions.inMutable = true;
3635ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd        }
3645ba19a0e69ad3a651b8f13ba45de48a56b56ce36Sam Judd    }
365c27229a159bfc992874609270ab8b57981fef339Sam Judd}
366