1d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru/* 2d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Copyright (C) 2011 The Android Open Source Project 3d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * 4d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Licensed under the Apache License, Version 2.0 (the "License"); 5d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * you may not use this file except in compliance with the License. 6d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * You may obtain a copy of the License at 7d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * 8d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * http://www.apache.org/licenses/LICENSE-2.0 9d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * 10d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Unless required by applicable law or agreed to in writing, software 11d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * distributed under the License is distributed on an "AS IS" BASIS, 12d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * See the License for the specific language governing permissions and 14d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * limitations under the License. 15d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 16d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 17d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Querupackage com.android.volley.toolbox; 18d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 19d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.DefaultRetryPolicy; 20d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.NetworkResponse; 21d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.ParseError; 22d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.Request; 23d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.Response; 24d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport com.android.volley.VolleyLog; 25e48f4430bfd3030350aa5ba827b449c37e2fadc9Jean-Baptiste Queru 26d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport android.graphics.Bitmap; 27d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport android.graphics.Bitmap.Config; 28d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queruimport android.graphics.BitmapFactory; 29ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmannimport android.widget.ImageView.ScaleType; 30d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 31d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru/** 32d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * A canned request for getting an image at a given URL and calling 33d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * back with a decoded Bitmap. 34d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 35d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Querupublic class ImageRequest extends Request<Bitmap> { 36d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** Socket timeout in milliseconds for image requests */ 37d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private static final int IMAGE_TIMEOUT_MS = 1000; 38d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 39d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** Default number of retries for image requests */ 40d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private static final int IMAGE_MAX_RETRIES = 2; 41d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 42d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** Default backoff multiplier for image requests */ 43d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private static final float IMAGE_BACKOFF_MULT = 2f; 44d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 45d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private final Response.Listener<Bitmap> mListener; 46d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private final Config mDecodeConfig; 47d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private final int mMaxWidth; 48d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private final int mMaxHeight; 49ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann private ScaleType mScaleType; 50d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 51d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** Decoding lock so that we don't decode more than one image at a time (to avoid OOM's) */ 52d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private static final Object sDecodeLock = new Object(); 53d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 54d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** 55d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Creates a new image request, decoding to a maximum specified width and 56d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * height. If both width and height are zero, the image will be decoded to 57d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * its natural size. If one of the two is nonzero, that dimension will be 58d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * clamped and the other one will be set to preserve the image's aspect 59d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * ratio. If both width and height are nonzero, the image will be decoded to 60d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * be fit in the rectangle of dimensions width x height while keeping its 61d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * aspect ratio. 62d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * 63d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param url URL of the image 64d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param listener Listener to receive the decoded bitmap 65d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param maxWidth Maximum width to decode this bitmap to, or zero for none 66d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param maxHeight Maximum height to decode this bitmap to, or zero for 67d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * none 68ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann * @param scaleType The ImageViews ScaleType used to calculate the needed image size. 69d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param decodeConfig Format to decode the bitmap to 70d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param errorListener Error listener, or null to ignore errors 71d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 72d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru public ImageRequest(String url, Response.Listener<Bitmap> listener, int maxWidth, int maxHeight, 73ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann ScaleType scaleType, Config decodeConfig, Response.ErrorListener errorListener) { 74ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann super(Method.GET, url, errorListener); 75d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru setRetryPolicy( 76d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru new DefaultRetryPolicy(IMAGE_TIMEOUT_MS, IMAGE_MAX_RETRIES, IMAGE_BACKOFF_MULT)); 77d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru mListener = listener; 78d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru mDecodeConfig = decodeConfig; 79d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru mMaxWidth = maxWidth; 80d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru mMaxHeight = maxHeight; 81ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann mScaleType = scaleType; 82d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 83d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 84cf498d16c616989edb5e2791da801bd29f548628Ficus Kirkpatrick /** 85cf498d16c616989edb5e2791da801bd29f548628Ficus Kirkpatrick * For API compatibility with the pre-ScaleType variant of the constructor. Equivalent to 86cf498d16c616989edb5e2791da801bd29f548628Ficus Kirkpatrick * the normal constructor with {@code ScaleType.CENTER_INSIDE}. 87cf498d16c616989edb5e2791da801bd29f548628Ficus Kirkpatrick */ 88cf498d16c616989edb5e2791da801bd29f548628Ficus Kirkpatrick @Deprecated 89cf498d16c616989edb5e2791da801bd29f548628Ficus Kirkpatrick public ImageRequest(String url, Response.Listener<Bitmap> listener, int maxWidth, int maxHeight, 90cf498d16c616989edb5e2791da801bd29f548628Ficus Kirkpatrick Config decodeConfig, Response.ErrorListener errorListener) { 91cf498d16c616989edb5e2791da801bd29f548628Ficus Kirkpatrick this(url, listener, maxWidth, maxHeight, 92cf498d16c616989edb5e2791da801bd29f548628Ficus Kirkpatrick ScaleType.CENTER_INSIDE, decodeConfig, errorListener); 93cf498d16c616989edb5e2791da801bd29f548628Ficus Kirkpatrick } 94d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru @Override 95d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru public Priority getPriority() { 96d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru return Priority.LOW; 97d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 98d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 99d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** 100d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Scales one side of a rectangle to fit aspect ratio. 101d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * 102d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param maxPrimary Maximum size of the primary dimension (i.e. width for 103d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * max width), or zero to maintain aspect ratio with secondary 104d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * dimension 105d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param maxSecondary Maximum size of the secondary dimension, or zero to 106d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * maintain aspect ratio with primary dimension 107d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param actualPrimary Actual size of the primary dimension 108d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param actualSecondary Actual size of the secondary dimension 109ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann * @param scaleType The ScaleType used to calculate the needed image size. 110d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 111d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private static int getResizedDimension(int maxPrimary, int maxSecondary, int actualPrimary, 112ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann int actualSecondary, ScaleType scaleType) { 113ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann 114d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru // If no dominant value at all, just return the actual. 115ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann if ((maxPrimary == 0) && (maxSecondary == 0)) { 116d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru return actualPrimary; 117d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 118d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 119ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann // If ScaleType.FIT_XY fill the whole rectangle, ignore ratio. 120ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann if (scaleType == ScaleType.FIT_XY) { 121ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann if (maxPrimary == 0) { 122ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann return actualPrimary; 123ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann } 124ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann return maxPrimary; 125ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann } 126ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann 127d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru // If primary is unspecified, scale primary to match secondary's scaling ratio. 128d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if (maxPrimary == 0) { 129d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru double ratio = (double) maxSecondary / (double) actualSecondary; 130d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru return (int) (actualPrimary * ratio); 131d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 132d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 133d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if (maxSecondary == 0) { 134d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru return maxPrimary; 135d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 136d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 137d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru double ratio = (double) actualSecondary / (double) actualPrimary; 138d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru int resized = maxPrimary; 139ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann 140ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann // If ScaleType.CENTER_CROP fill the whole rectangle, preserve aspect ratio. 141ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann if (scaleType == ScaleType.CENTER_CROP) { 142ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann if ((resized * ratio) < maxSecondary) { 143ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann resized = (int) (maxSecondary / ratio); 144ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann } 145ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann return resized; 146ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann } 147ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann 148ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann if ((resized * ratio) > maxSecondary) { 149d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru resized = (int) (maxSecondary / ratio); 150d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 151d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru return resized; 152d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 153d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 154d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru @Override 155d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru protected Response<Bitmap> parseNetworkResponse(NetworkResponse response) { 156d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru // Serialize all decode on a global lock to reduce concurrent heap usage. 157d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru synchronized (sDecodeLock) { 158d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru try { 159d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru return doParse(response); 160d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } catch (OutOfMemoryError e) { 161d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru VolleyLog.e("Caught OOM for %d byte image, url=%s", response.data.length, getUrl()); 162d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru return Response.error(new ParseError(e)); 163d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 164d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 165d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 166d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 167d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** 168d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * The real guts of parseNetworkResponse. Broken out for readability. 169d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 170d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru private Response<Bitmap> doParse(NetworkResponse response) { 171d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru byte[] data = response.data; 172d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru BitmapFactory.Options decodeOptions = new BitmapFactory.Options(); 173d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru Bitmap bitmap = null; 174d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if (mMaxWidth == 0 && mMaxHeight == 0) { 175d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru decodeOptions.inPreferredConfig = mDecodeConfig; 176d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions); 177d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } else { 178d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru // If we have to resize this image, first get the natural bounds. 179d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru decodeOptions.inJustDecodeBounds = true; 180d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions); 181d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru int actualWidth = decodeOptions.outWidth; 182d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru int actualHeight = decodeOptions.outHeight; 183d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 184d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru // Then compute the dimensions we would ideally like to decode to. 185d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight, 186ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann actualWidth, actualHeight, mScaleType); 187d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth, 188ced8a98b9ffa3612656b7979f8933ae9cf19d657Ralph Bergmann actualHeight, actualWidth, mScaleType); 189d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 190d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru // Decode to the nearest power of two scaling factor. 191d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru decodeOptions.inJustDecodeBounds = false; 192d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru // TODO(ficus): Do we need this or is it okay since API 8 doesn't support it? 193d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru // decodeOptions.inPreferQualityOverSpeed = PREFER_QUALITY_OVER_SPEED; 194d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru decodeOptions.inSampleSize = 195d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru findBestSampleSize(actualWidth, actualHeight, desiredWidth, desiredHeight); 196d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru Bitmap tempBitmap = 197d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions); 198d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 1996772bce3d3322ccbcf6481545ffe895d5d401b39Jean-Baptiste Queru // If necessary, scale down to the maximal acceptable size. 2006772bce3d3322ccbcf6481545ffe895d5d401b39Jean-Baptiste Queru if (tempBitmap != null && (tempBitmap.getWidth() > desiredWidth || 2016772bce3d3322ccbcf6481545ffe895d5d401b39Jean-Baptiste Queru tempBitmap.getHeight() > desiredHeight)) { 202d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru bitmap = Bitmap.createScaledBitmap(tempBitmap, 203d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru desiredWidth, desiredHeight, true); 204d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru tempBitmap.recycle(); 205d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } else { 206d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru bitmap = tempBitmap; 207d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 208d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 209d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 210d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru if (bitmap == null) { 2117c8e29e4cc2219275042a228abea0657bb7633cdEmmanuel Rodriguez return Response.error(new ParseError(response)); 212d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } else { 213d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru return Response.success(bitmap, HttpHeaderParser.parseCacheHeaders(response)); 214d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 215d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 216d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 217d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru @Override 218d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru protected void deliverResponse(Bitmap response) { 219d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru mListener.onResponse(response); 220d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 221d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 222d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru /** 223d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * Returns the largest power-of-two divisor for use in downscaling a bitmap 224d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * that will not result in the scaling past the desired dimensions. 225d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * 226d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param actualWidth Actual width of the bitmap 227d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param actualHeight Actual height of the bitmap 228d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param desiredWidth Desired width of the bitmap 229d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru * @param desiredHeight Desired height of the bitmap 230d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru */ 231d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru // Visible for testing. 232d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru static int findBestSampleSize( 233d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru int actualWidth, int actualHeight, int desiredWidth, int desiredHeight) { 234d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru double wr = (double) actualWidth / desiredWidth; 235d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru double hr = (double) actualHeight / desiredHeight; 236d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru double ratio = Math.min(wr, hr); 237d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru float n = 1.0f; 238d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru while ((n * 2) <= ratio) { 239d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru n *= 2; 240d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 241d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru 242d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru return (int) n; 243d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru } 244d56b88ae161057e848e7410d1b9ce5b0b8c427fcJean-Baptiste Queru} 245