13713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick/*
23713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * Copyright (C) 2011 The Android Open Source Project
33713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick *
43713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * Licensed under the Apache License, Version 2.0 (the "License");
53713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * you may not use this file except in compliance with the License.
63713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * You may obtain a copy of the License at
73713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick *
83713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick *      http://www.apache.org/licenses/LICENSE-2.0
93713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick *
103713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * Unless required by applicable law or agreed to in writing, software
113713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * distributed under the License is distributed on an "AS IS" BASIS,
123713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * See the License for the specific language governing permissions and
143713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * limitations under the License.
153713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick */
163713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
173713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickpackage com.android.volley.toolbox;
183713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
193713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport com.android.volley.DefaultRetryPolicy;
203713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport com.android.volley.NetworkResponse;
213713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport com.android.volley.ParseError;
223713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport com.android.volley.Request;
233713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport com.android.volley.Response;
243713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport com.android.volley.VolleyLog;
253713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport android.graphics.Bitmap;
263713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport android.graphics.Bitmap.Config;
273713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickimport android.graphics.BitmapFactory;
283713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
293713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick/**
303713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * A canned request for getting an image at a given URL and calling
313713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick * back with a decoded Bitmap.
323713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick */
333713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrickpublic class ImageRequest extends Request<Bitmap> {
343713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    /** Socket timeout in milliseconds for image requests */
353713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    private static final int IMAGE_TIMEOUT_MS = 1000;
363713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
373713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    /** Default number of retries for image requests */
383713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    private static final int IMAGE_MAX_RETRIES = 2;
393713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
403713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    /** Default backoff multiplier for image requests */
413713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    private static final float IMAGE_BACKOFF_MULT = 2f;
423713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
433713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    private final Response.Listener<Bitmap> mListener;
443713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    private final Config mDecodeConfig;
453713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    private final int mMaxWidth;
463713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    private final int mMaxHeight;
473713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
483713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    /** Decoding lock so that we don't decode more than one image at a time (to avoid OOM's) */
493713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    private static final Object sDecodeLock = new Object();
503713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
513713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    /**
523713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     * Creates a new image request, decoding to a maximum specified width and
533713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     * height. If both width and height are zero, the image will be decoded to
543713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     * its natural size. If one of the two is nonzero, that dimension will be
553713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     * clamped and the other one will be set to preserve the image's aspect
563713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     * ratio. If both width and height are nonzero, the image will be decoded to
573713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     * be fit in the rectangle of dimensions width x height while keeping its
583713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     * aspect ratio.
593713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     *
603713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     * @param url URL of the image
613713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     * @param listener Listener to receive the decoded bitmap
623713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     * @param maxWidth Maximum width to decode this bitmap to, or zero for none
633713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     * @param maxHeight Maximum height to decode this bitmap to, or zero for
643713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     *            none
653713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     * @param decodeConfig Format to decode the bitmap to
663713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     * @param errorListener Error listener, or null to ignore errors
673713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     */
683713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    public ImageRequest(String url, Response.Listener<Bitmap> listener, int maxWidth, int maxHeight,
693713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            Config decodeConfig, Response.ErrorListener errorListener) {
703713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        super(url, errorListener);
713713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        setRetryPolicy(
723713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick                new DefaultRetryPolicy(IMAGE_TIMEOUT_MS, IMAGE_MAX_RETRIES, IMAGE_BACKOFF_MULT));
733713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        mListener = listener;
743713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        mDecodeConfig = decodeConfig;
753713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        mMaxWidth = maxWidth;
763713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        mMaxHeight = maxHeight;
773713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    }
783713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
793713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    @Override
803713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    public Priority getPriority() {
813713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        return Priority.LOW;
823713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    }
833713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
843713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    /**
853713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     * Scales one side of a rectangle to fit aspect ratio.
863713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     *
873713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     * @param maxPrimary Maximum size of the primary dimension (i.e. width for
883713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     *        max width), or zero to maintain aspect ratio with secondary
893713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     *        dimension
903713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     * @param maxSecondary Maximum size of the secondary dimension, or zero to
913713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     *        maintain aspect ratio with primary dimension
923713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     * @param actualPrimary Actual size of the primary dimension
933713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     * @param actualSecondary Actual size of the secondary dimension
943713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     */
953713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    private static int getResizedDimension(int maxPrimary, int maxSecondary, int actualPrimary,
963713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            int actualSecondary) {
973713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        // If no dominant value at all, just return the actual.
983713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        if (maxPrimary == 0 && maxSecondary == 0) {
993713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            return actualPrimary;
1003713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        }
1013713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
1023713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        // If primary is unspecified, scale primary to match secondary's scaling ratio.
1033713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        if (maxPrimary == 0) {
1043713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            double ratio = (double) maxSecondary / (double) actualSecondary;
1053713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            return (int) (actualPrimary * ratio);
1063713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        }
1073713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
1083713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        if (maxSecondary == 0) {
1093713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            return maxPrimary;
1103713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        }
1113713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
1123713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        double ratio = (double) actualSecondary / (double) actualPrimary;
1133713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        int resized = maxPrimary;
1143713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        if (resized * ratio > maxSecondary) {
1153713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            resized = (int) (maxSecondary / ratio);
1163713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        }
1173713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        return resized;
1183713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    }
1193713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
1203713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    @Override
1213713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    protected Response<Bitmap> parseNetworkResponse(NetworkResponse response) {
1223713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        // Serialize all decode on a global lock to reduce concurrent heap usage.
1233713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        synchronized (sDecodeLock) {
1243713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            try {
1253713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick                return doParse(response);
1263713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            } catch (OutOfMemoryError e) {
1273713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick                VolleyLog.e("Caught OOM for %d byte image, url=%s", response.data.length, getUrl());
1283713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick                return Response.error(new ParseError(e));
1293713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            }
1303713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        }
1313713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    }
1323713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
1333713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    /**
1343713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     * The real guts of parseNetworkResponse. Broken out for readability.
1353713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     */
1363713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    private Response<Bitmap> doParse(NetworkResponse response) {
1373713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        byte[] data = response.data;
1383713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
1393713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        Bitmap bitmap = null;
1403713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        if (mMaxWidth == 0 && mMaxHeight == 0) {
1413713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            decodeOptions.inPreferredConfig = mDecodeConfig;
1423713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
1433713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        } else {
1443713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            // If we have to resize this image, first get the natural bounds.
1453713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            decodeOptions.inJustDecodeBounds = true;
1463713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
1473713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            int actualWidth = decodeOptions.outWidth;
1483713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            int actualHeight = decodeOptions.outHeight;
1493713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
1503713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            // Then compute the dimensions we would ideally like to decode to.
1513713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight,
1523713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick                    actualWidth, actualHeight);
1533713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth,
1543713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick                    actualHeight, actualWidth);
1553713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
1563713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            // Decode to the nearest power of two scaling factor.
1573713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            decodeOptions.inJustDecodeBounds = false;
1583713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            // TODO(ficus): Do we need this or is it okay since API 8 doesn't support it?
1593713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            // decodeOptions.inPreferQualityOverSpeed = PREFER_QUALITY_OVER_SPEED;
1603713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            decodeOptions.inSampleSize =
1613713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick                findBestSampleSize(actualWidth, actualHeight, desiredWidth, desiredHeight);
1623713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            Bitmap tempBitmap =
1633713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick                BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
1643713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
165c21893eac195508dab45d795322e3278ebb9fe1fJorim Jaggi            // If necessary, scale down to the maximal acceptable size.
166c21893eac195508dab45d795322e3278ebb9fe1fJorim Jaggi            if (tempBitmap != null && (tempBitmap.getWidth() > desiredWidth ||
167c21893eac195508dab45d795322e3278ebb9fe1fJorim Jaggi                    tempBitmap.getHeight() > desiredHeight)) {
1683713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick                bitmap = Bitmap.createScaledBitmap(tempBitmap,
1693713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick                        desiredWidth, desiredHeight, true);
1703713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick                tempBitmap.recycle();
1713713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            } else {
1723713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick                bitmap = tempBitmap;
1733713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            }
1743713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        }
1753713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
1763713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        if (bitmap == null) {
1773713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            return Response.error(new ParseError());
1783713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        } else {
1793713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            return Response.success(bitmap, HttpHeaderParser.parseCacheHeaders(response));
1803713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        }
1813713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    }
1823713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
1833713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    @Override
1843713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    protected void deliverResponse(Bitmap response) {
1853713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        mListener.onResponse(response);
1863713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    }
1873713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
1883713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    /**
1893713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     * Returns the largest power-of-two divisor for use in downscaling a bitmap
1903713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     * that will not result in the scaling past the desired dimensions.
1913713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     *
1923713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     * @param actualWidth Actual width of the bitmap
1933713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     * @param actualHeight Actual height of the bitmap
1943713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     * @param desiredWidth Desired width of the bitmap
1953713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     * @param desiredHeight Desired height of the bitmap
1963713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick     */
1973713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    // Visible for testing.
1983713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    static int findBestSampleSize(
1993713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            int actualWidth, int actualHeight, int desiredWidth, int desiredHeight) {
2003713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        double wr = (double) actualWidth / desiredWidth;
2013713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        double hr = (double) actualHeight / desiredHeight;
2023713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        double ratio = Math.min(wr, hr);
2033713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        float n = 1.0f;
2043713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        while ((n * 2) <= ratio) {
2053713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick            n *= 2;
2063713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        }
2073713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick
2083713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick        return (int) n;
2093713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick    }
2103713094c56d25e25df2a508dbee4aea869ffdea1Ficus Kirkpatrick}
211