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