193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein/*
293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * Copyright (C) 2013 The Android Open Source Project
393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein *
493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * Licensed under the Apache License, Version 2.0 (the "License");
593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * you may not use this file except in compliance with the License.
693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * You may obtain a copy of the License at
793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein *
893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein *      http://www.apache.org/licenses/LICENSE-2.0
993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein *
1093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * Unless required by applicable law or agreed to in writing, software
1193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * distributed under the License is distributed on an "AS IS" BASIS,
1293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * See the License for the specific language governing permissions and
1493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * limitations under the License.
1593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein */
1693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
1793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinpackage com.android.bitmap;
1893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
1993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.graphics.Bitmap;
2093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.graphics.BitmapFactory;
2193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.graphics.BitmapRegionDecoder;
2293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.graphics.Rect;
2393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.os.AsyncTask;
24cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Weiimport android.os.ParcelFileDescriptor;
25cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Weiimport android.os.ParcelFileDescriptor.AutoCloseInputStream;
2693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport android.util.Log;
2793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
289c6ac19d4a3d39b7c2992060957920118ff56a65Mark Weiimport com.android.bitmap.RequestKey.FileDescriptorFactory;
2993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport com.android.bitmap.util.BitmapUtils;
3093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport com.android.bitmap.util.Exif;
3193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport com.android.bitmap.util.RectUtils;
3293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport com.android.bitmap.util.Trace;
3393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
3493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport java.io.IOException;
3593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinimport java.io.InputStream;
3693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
3793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein/**
3893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * Decodes an image from either a file descriptor or input stream on a worker thread. After the
3993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * decode is complete, even if the task is cancelled, the result is placed in the given cache.
4093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * A {@link DecodeCallback} client may be notified on decode begin and completion.
4193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * <p>
4293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * This class uses {@link BitmapRegionDecoder} when possible to minimize unnecessary decoding
4393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * and allow bitmap reuse on Jellybean 4.1 and later.
4493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein * <p>
4593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein *  GIFs are supported, but their decode does not reuse bitmaps at all. The resulting
4693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein *  {@link ReusableBitmap} will be marked as not reusable
4793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein *  ({@link ReusableBitmap#isEligibleForPooling()} will return false).
4893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein */
4993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzsteinpublic class DecodeTask extends AsyncTask<Void, Void, ReusableBitmap> {
5093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
5140662f4b39e795d9c64502b13036e7c37fa2d373Sam Blitzstein    private final RequestKey mKey;
52cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei    private final DecodeOptions mDecodeOpts;
539c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei    private final FileDescriptorFactory mFactory;
5493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private final DecodeCallback mDecodeCallback;
5593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private final BitmapCache mCache;
5693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private final BitmapFactory.Options mOpts = new BitmapFactory.Options();
5793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
5893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private ReusableBitmap mInBitmap = null;
5993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
6093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private static final boolean CROP_DURING_DECODE = true;
6193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
6293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private static final String TAG = DecodeTask.class.getSimpleName();
63cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei    public static final boolean DEBUG = false;
6493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
6593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    /**
6693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * Callback interface for clients to be notified of decode state changes and completion.
6793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     */
6893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    public interface DecodeCallback {
6993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        /**
7093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein         * Notifies that the async task's work is about to begin. Up until this point, the task
7193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein         * may have been preempted by the scheduler or queued up by a bottlenecked executor.
7293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein         * <p>
7393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein         * N.B. this method runs on the UI thread.
7493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein         */
7540662f4b39e795d9c64502b13036e7c37fa2d373Sam Blitzstein        void onDecodeBegin(RequestKey key);
7693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        /**
7793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein         * The task is now complete and the ReusableBitmap is available for use. Clients should
7893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein         * double check that the request matches what the client is expecting.
7993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein         */
8040662f4b39e795d9c64502b13036e7c37fa2d373Sam Blitzstein        void onDecodeComplete(RequestKey key, ReusableBitmap result);
8193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        /**
8240662f4b39e795d9c64502b13036e7c37fa2d373Sam Blitzstein         * The task has been canceled, and {@link #onDecodeComplete(RequestKey, ReusableBitmap)}
8340662f4b39e795d9c64502b13036e7c37fa2d373Sam Blitzstein         * will not be called.
8493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein         */
8540662f4b39e795d9c64502b13036e7c37fa2d373Sam Blitzstein        void onDecodeCancel(RequestKey key);
8693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
8793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
88cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei    /**
899c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei   * Create new DecodeTask.
909c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei   *
919c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei   * @param requestKey The request to decode, also the key to use for the cache.
929c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei   * @param decodeOpts The decode options.
939c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei   * @param factory    The factory to obtain file descriptors to decode from. If this factory is
949c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei     *                 null, then we will decode from requestKey.createInputStream().
959c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei   * @param callback   The callback to notify of decode state changes.
969c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei   * @param cache      The cache and pool.
979c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei   */
98cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei    public DecodeTask(RequestKey requestKey, DecodeOptions decodeOpts,
999c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei            FileDescriptorFactory factory, DecodeCallback callback, BitmapCache cache) {
100cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei        mKey = requestKey;
101cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei        mDecodeOpts = decodeOpts;
1029c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei        mFactory = factory;
103cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei        mDecodeCallback = callback;
10493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        mCache = cache;
10593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
10693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
10793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    @Override
10893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    protected ReusableBitmap doInBackground(Void... params) {
10993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        // enqueue the 'onDecodeBegin' signal on the main thread
11093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        publishProgress();
11193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
11293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        return decode();
11393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
11493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
11593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    public ReusableBitmap decode() {
11693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (isCancelled()) {
11793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            return null;
11893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
11993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
12093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        ReusableBitmap result = null;
121cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei        ParcelFileDescriptor fd = null;
12293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        InputStream in = null;
12393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
1241ef4c6b7fdbc6e36c030e4a6fab3a8ccf8ba524bMark Wei        try {
1259c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei            if (mFactory != null) {
1262e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei                Trace.beginSection("create fd");
1279c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei                fd = mFactory.createFileDescriptor();
1282e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei                Trace.endSection();
1299c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei            } else {
13093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                in = reset(in);
13193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                if (in == null) {
13293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    return null;
13393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                }
1341ef4c6b7fdbc6e36c030e4a6fab3a8ccf8ba524bMark Wei                if (isCancelled()) {
1351ef4c6b7fdbc6e36c030e4a6fab3a8ccf8ba524bMark Wei                    return null;
1361ef4c6b7fdbc6e36c030e4a6fab3a8ccf8ba524bMark Wei                }
1371ef4c6b7fdbc6e36c030e4a6fab3a8ccf8ba524bMark Wei            }
1381ef4c6b7fdbc6e36c030e4a6fab3a8ccf8ba524bMark Wei
1391ef4c6b7fdbc6e36c030e4a6fab3a8ccf8ba524bMark Wei            final boolean isJellyBeanOrAbove = android.os.Build.VERSION.SDK_INT
1401ef4c6b7fdbc6e36c030e4a6fab3a8ccf8ba524bMark Wei                    >= android.os.Build.VERSION_CODES.JELLY_BEAN;
1411ef4c6b7fdbc6e36c030e4a6fab3a8ccf8ba524bMark Wei            // This blocks during fling when the pool is empty. We block early to avoid jank.
1421ef4c6b7fdbc6e36c030e4a6fab3a8ccf8ba524bMark Wei            if (isJellyBeanOrAbove) {
1431ef4c6b7fdbc6e36c030e4a6fab3a8ccf8ba524bMark Wei                Trace.beginSection("poll for reusable bitmap");
1441ef4c6b7fdbc6e36c030e4a6fab3a8ccf8ba524bMark Wei                mInBitmap = mCache.poll();
1451ef4c6b7fdbc6e36c030e4a6fab3a8ccf8ba524bMark Wei                Trace.endSection();
14693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
1472e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei
1482e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei            if (isCancelled()) {
1492e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei                return null;
1502e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei            }
15193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
15293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            Trace.beginSection("get bytesize");
15393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            final long byteSize;
15493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (fd != null) {
155cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei                byteSize = fd.getStatSize();
15693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            } else {
15793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                byteSize = -1;
15893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
15993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            Trace.endSection();
16093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
16193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            Trace.beginSection("get orientation");
16293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            final int orientation;
16393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (mKey.hasOrientationExif()) {
16493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                if (fd != null) {
16593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    // Creating an input stream from the file descriptor makes it useless
16693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    // afterwards.
1679c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei                    Trace.beginSection("create orientation fd and stream");
1689c6ac19d4a3d39b7c2992060957920118ff56a65Mark Wei                    final ParcelFileDescriptor orientationFd = mFactory.createFileDescriptor();
169cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei                    in = new AutoCloseInputStream(orientationFd);
17093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    Trace.endSection();
17193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                }
17293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                orientation = Exif.getOrientation(in, byteSize);
17393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                if (fd != null) {
17493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    try {
17593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                        // Close the temporary file descriptor.
17693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                        in.close();
17793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    } catch (IOException ignored) {
17893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    }
17993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                }
18093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            } else {
18193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                orientation = 0;
18293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
18393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            final boolean isNotRotatedOr180 = orientation == 0 || orientation == 180;
18493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            Trace.endSection();
18593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
18693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (orientation != 0) {
18793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                // disable inBitmap-- bitmap reuse doesn't work with different decode regions due
18893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                // to orientation
18993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                if (mInBitmap != null) {
19093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    mCache.offer(mInBitmap);
19193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    mInBitmap = null;
19293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    mOpts.inBitmap = null;
19393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                }
19493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
19593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
19693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (isCancelled()) {
19793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                return null;
19893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
19993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
20093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (fd == null) {
20193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                in = reset(in);
20293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                if (in == null) {
20393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    return null;
20493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                }
2052e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei                if (isCancelled()) {
2062e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei                    return null;
2072e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei                }
20893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
20993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
21093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            Trace.beginSection("decodeBounds");
21193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            mOpts.inJustDecodeBounds = true;
21293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (fd != null) {
21393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                BitmapFactory.decodeFileDescriptor(fd.getFileDescriptor(), null, mOpts);
21493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            } else {
21593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                BitmapFactory.decodeStream(in, null, mOpts);
21693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
21793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            Trace.endSection();
21893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
21993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (isCancelled()) {
22093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                return null;
22193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
22293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
22393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            // We want to calculate the sample size "as if" the orientation has been corrected.
22493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            final int srcW, srcH; // Orientation corrected.
22593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (isNotRotatedOr180) {
22693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                srcW = mOpts.outWidth;
22793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                srcH = mOpts.outHeight;
22893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            } else {
22993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                srcW = mOpts.outHeight;
23093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                srcH = mOpts.outWidth;
23193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
23222955165fab693684cc3614c84ee81883ae933c8Mark Wei
23322955165fab693684cc3614c84ee81883ae933c8Mark Wei            // BEGIN MANUAL-INLINE calculateSampleSize()
23422955165fab693684cc3614c84ee81883ae933c8Mark Wei
23522955165fab693684cc3614c84ee81883ae933c8Mark Wei            final float sz = Math
23622955165fab693684cc3614c84ee81883ae933c8Mark Wei                    .min((float) srcW / mDecodeOpts.destW, (float) srcH / mDecodeOpts.destH);
23722955165fab693684cc3614c84ee81883ae933c8Mark Wei
23822955165fab693684cc3614c84ee81883ae933c8Mark Wei            final int sampleSize;
23922955165fab693684cc3614c84ee81883ae933c8Mark Wei            switch (mDecodeOpts.sampleSizeStrategy) {
24022955165fab693684cc3614c84ee81883ae933c8Mark Wei                case DecodeOptions.STRATEGY_TRUNCATE:
24122955165fab693684cc3614c84ee81883ae933c8Mark Wei                    sampleSize = (int) sz;
24222955165fab693684cc3614c84ee81883ae933c8Mark Wei                    break;
24322955165fab693684cc3614c84ee81883ae933c8Mark Wei                case DecodeOptions.STRATEGY_ROUND_UP:
24422955165fab693684cc3614c84ee81883ae933c8Mark Wei                    sampleSize = (int) Math.ceil(sz);
24522955165fab693684cc3614c84ee81883ae933c8Mark Wei                    break;
24622955165fab693684cc3614c84ee81883ae933c8Mark Wei                case DecodeOptions.STRATEGY_ROUND_NEAREST:
24722955165fab693684cc3614c84ee81883ae933c8Mark Wei                default:
24822955165fab693684cc3614c84ee81883ae933c8Mark Wei                    sampleSize = (int) Math.pow(2, (int) (0.5 + (Math.log(sz) / Math.log(2))));
24922955165fab693684cc3614c84ee81883ae933c8Mark Wei                    break;
25022955165fab693684cc3614c84ee81883ae933c8Mark Wei            }
25122955165fab693684cc3614c84ee81883ae933c8Mark Wei            mOpts.inSampleSize = Math.max(1, sampleSize);
25222955165fab693684cc3614c84ee81883ae933c8Mark Wei
25322955165fab693684cc3614c84ee81883ae933c8Mark Wei            // END MANUAL-INLINE calculateSampleSize()
25422955165fab693684cc3614c84ee81883ae933c8Mark Wei
25593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            mOpts.inJustDecodeBounds = false;
25693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            mOpts.inMutable = true;
25793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (isJellyBeanOrAbove && orientation == 0) {
25893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                if (mInBitmap == null) {
25993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    if (DEBUG) {
26093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                        Log.e(TAG, "decode thread wants a bitmap. cache dump:\n"
26193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                                + mCache.toDebugString());
26293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    }
26393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    Trace.beginSection("create reusable bitmap");
264cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei                    mInBitmap = new ReusableBitmap(
265cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei                            Bitmap.createBitmap(mDecodeOpts.destW, mDecodeOpts.destH,
266cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei                                    Bitmap.Config.ARGB_8888));
26793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    Trace.endSection();
26893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
26993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    if (isCancelled()) {
27093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                        return null;
27193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    }
27293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
27393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    if (DEBUG) {
27493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                        Log.e(TAG, "*** allocated new bitmap in decode thread: "
27593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                                + mInBitmap + " key=" + mKey);
27693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    }
27793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                } else {
27893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    if (DEBUG) {
27993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                        Log.e(TAG, "*** reusing existing bitmap in decode thread: "
28093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                                + mInBitmap + " key=" + mKey);
28193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    }
28293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
28393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                }
28493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                mOpts.inBitmap = mInBitmap.bmp;
28593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
28693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
28793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (isCancelled()) {
28893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                return null;
28993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
29093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
29193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (fd == null) {
29293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                in = reset(in);
29393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                if (in == null) {
29493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    return null;
29593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                }
2962e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei                if (isCancelled()) {
2972e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei                    return null;
2982e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei                }
29993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
30093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
3012e4d0863dba53435372ec96538f2ef3e1c3675bfMark Wei
30293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            Bitmap decodeResult = null;
30393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            final Rect srcRect = new Rect(); // Not orientation corrected. True coordinates.
30493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (CROP_DURING_DECODE) {
30593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                try {
30693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    Trace.beginSection("decodeCropped" + mOpts.inSampleSize);
30722955165fab693684cc3614c84ee81883ae933c8Mark Wei
30822955165fab693684cc3614c84ee81883ae933c8Mark Wei                    // BEGIN MANUAL INLINE decodeCropped()
30922955165fab693684cc3614c84ee81883ae933c8Mark Wei
31022955165fab693684cc3614c84ee81883ae933c8Mark Wei                    final BitmapRegionDecoder brd;
31122955165fab693684cc3614c84ee81883ae933c8Mark Wei                    if (fd != null) {
31222955165fab693684cc3614c84ee81883ae933c8Mark Wei                        brd = BitmapRegionDecoder
31322955165fab693684cc3614c84ee81883ae933c8Mark Wei                                .newInstance(fd.getFileDescriptor(), true /* shareable */);
31422955165fab693684cc3614c84ee81883ae933c8Mark Wei                    } else {
31522955165fab693684cc3614c84ee81883ae933c8Mark Wei                        brd = BitmapRegionDecoder.newInstance(in, true /* shareable */);
31622955165fab693684cc3614c84ee81883ae933c8Mark Wei                    }
31722955165fab693684cc3614c84ee81883ae933c8Mark Wei
31822955165fab693684cc3614c84ee81883ae933c8Mark Wei                    final Bitmap bitmap;
31922955165fab693684cc3614c84ee81883ae933c8Mark Wei                    if (isCancelled()) {
32022955165fab693684cc3614c84ee81883ae933c8Mark Wei                        bitmap = null;
32122955165fab693684cc3614c84ee81883ae933c8Mark Wei                    } else {
32222955165fab693684cc3614c84ee81883ae933c8Mark Wei                        // We want to call calculateCroppedSrcRect() on the source rectangle "as
32322955165fab693684cc3614c84ee81883ae933c8Mark Wei                        // if" the orientation has been corrected.
32422955165fab693684cc3614c84ee81883ae933c8Mark Wei                        // Center the decode on the top 1/3.
32522955165fab693684cc3614c84ee81883ae933c8Mark Wei                        BitmapUtils.calculateCroppedSrcRect(srcW, srcH, mDecodeOpts.destW,
3263a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung                                mDecodeOpts.destH, mDecodeOpts.destH, mOpts.inSampleSize,
3273a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung                                mDecodeOpts.horizontalCenter, mDecodeOpts.verticalCenter,
32822955165fab693684cc3614c84ee81883ae933c8Mark Wei                                true /* absoluteFraction */,
32922955165fab693684cc3614c84ee81883ae933c8Mark Wei                                1f, srcRect);
33022955165fab693684cc3614c84ee81883ae933c8Mark Wei                        if (DEBUG) {
33122955165fab693684cc3614c84ee81883ae933c8Mark Wei                            System.out.println("rect for this decode is: " + srcRect
33222955165fab693684cc3614c84ee81883ae933c8Mark Wei                                    + " srcW/H=" + srcW + "/" + srcH
33322955165fab693684cc3614c84ee81883ae933c8Mark Wei                                    + " dstW/H=" + mDecodeOpts.destW + "/" + mDecodeOpts.destH);
33422955165fab693684cc3614c84ee81883ae933c8Mark Wei                        }
33522955165fab693684cc3614c84ee81883ae933c8Mark Wei
33622955165fab693684cc3614c84ee81883ae933c8Mark Wei                        // calculateCroppedSrcRect() gave us the source rectangle "as if" the
33722955165fab693684cc3614c84ee81883ae933c8Mark Wei                        // orientation has been corrected. We need to decode the uncorrected
33822955165fab693684cc3614c84ee81883ae933c8Mark Wei                        // source rectangle. Calculate true coordinates.
33922955165fab693684cc3614c84ee81883ae933c8Mark Wei                        RectUtils.rotateRectForOrientation(orientation, new Rect(0, 0, srcW, srcH),
34022955165fab693684cc3614c84ee81883ae933c8Mark Wei                                srcRect);
34122955165fab693684cc3614c84ee81883ae933c8Mark Wei
34222955165fab693684cc3614c84ee81883ae933c8Mark Wei                        bitmap = brd.decodeRegion(srcRect, mOpts);
34322955165fab693684cc3614c84ee81883ae933c8Mark Wei                    }
34422955165fab693684cc3614c84ee81883ae933c8Mark Wei                    brd.recycle();
34522955165fab693684cc3614c84ee81883ae933c8Mark Wei
34622955165fab693684cc3614c84ee81883ae933c8Mark Wei                    // END MANUAL INLINE decodeCropped()
34722955165fab693684cc3614c84ee81883ae933c8Mark Wei
34822955165fab693684cc3614c84ee81883ae933c8Mark Wei                    decodeResult = bitmap;
34993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                } catch (IOException e) {
35093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    // fall through to below and try again with the non-cropping decoder
351c7ad691a967e860a93d76bedb7c5d207dd083591Mark Wei                    if (fd == null) {
352c7ad691a967e860a93d76bedb7c5d207dd083591Mark Wei                        in = reset(in);
353c7ad691a967e860a93d76bedb7c5d207dd083591Mark Wei                        if (in == null) {
354c7ad691a967e860a93d76bedb7c5d207dd083591Mark Wei                            return null;
355c7ad691a967e860a93d76bedb7c5d207dd083591Mark Wei                        }
356c7ad691a967e860a93d76bedb7c5d207dd083591Mark Wei                        if (isCancelled()) {
357c7ad691a967e860a93d76bedb7c5d207dd083591Mark Wei                            return null;
358c7ad691a967e860a93d76bedb7c5d207dd083591Mark Wei                        }
359c7ad691a967e860a93d76bedb7c5d207dd083591Mark Wei                    }
360c7ad691a967e860a93d76bedb7c5d207dd083591Mark Wei
36193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    e.printStackTrace();
36293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                } finally {
36393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    Trace.endSection();
36493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                }
36593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
36693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                if (isCancelled()) {
36793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    return null;
36893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                }
36993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
37093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
37193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            //noinspection PointlessBooleanExpression
37293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (!CROP_DURING_DECODE || (decodeResult == null && !isCancelled())) {
37393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                try {
37493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    Trace.beginSection("decode" + mOpts.inSampleSize);
37593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    // disable inBitmap-- bitmap reuse doesn't work well below K
37693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    if (mInBitmap != null) {
37793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                        mCache.offer(mInBitmap);
37893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                        mInBitmap = null;
37993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                        mOpts.inBitmap = null;
38093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    }
38193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    decodeResult = decode(fd, in);
38293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                } catch (IllegalArgumentException e) {
38393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    Log.e(TAG, "decode failed: reason='" + e.getMessage() + "' ss="
38493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                            + mOpts.inSampleSize);
38593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
38693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    if (mOpts.inSampleSize > 1) {
38793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                        // try again with ss=1
38893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                        mOpts.inSampleSize = 1;
38993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                        decodeResult = decode(fd, in);
39093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    }
39193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                } finally {
39293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    Trace.endSection();
39393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                }
39493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
39593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                if (isCancelled()) {
39693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    return null;
39793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                }
39893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
39993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
40093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (decodeResult == null) {
40193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                return null;
40293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
40393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
40493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (mInBitmap != null) {
40593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                result = mInBitmap;
40693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                // srcRect is non-empty when using the cropping BitmapRegionDecoder codepath
40793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                if (!srcRect.isEmpty()) {
40893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    result.setLogicalWidth((srcRect.right - srcRect.left) / mOpts.inSampleSize);
40993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    result.setLogicalHeight(
41093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                            (srcRect.bottom - srcRect.top) / mOpts.inSampleSize);
41193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                } else {
41293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    result.setLogicalWidth(mOpts.outWidth);
41393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    result.setLogicalHeight(mOpts.outHeight);
41493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                }
41593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            } else {
41693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                // no mInBitmap means no pooling
41793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                result = new ReusableBitmap(decodeResult, false /* reusable */);
41893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                if (isNotRotatedOr180) {
41993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    result.setLogicalWidth(decodeResult.getWidth());
42093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    result.setLogicalHeight(decodeResult.getHeight());
42193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                } else {
42293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    result.setLogicalWidth(decodeResult.getHeight());
42393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    result.setLogicalHeight(decodeResult.getWidth());
42493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                }
42593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
42693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            result.setOrientation(orientation);
42793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        } catch (Exception e) {
42893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            e.printStackTrace();
42993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        } finally {
43093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (fd != null) {
43193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                try {
43293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    fd.close();
43393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                } catch (IOException ignored) {
43493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                }
43593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
43693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (in != null) {
43793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                try {
43893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    in.close();
43993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                } catch (IOException ignored) {
44093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                }
44193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
442a07e0af0f1997ce3d40df6a8a9f44cb0b2e4c07fJames Kung
4434309c1f708f469a5ab3ac52b6b22cc6ede1d50ffSam Blitzstein            // Cancellations can't be guaranteed to be correct, so skip the cache
4444309c1f708f469a5ab3ac52b6b22cc6ede1d50ffSam Blitzstein            if (!isCancelled()) {
4454309c1f708f469a5ab3ac52b6b22cc6ede1d50ffSam Blitzstein                // Put result in cache, regardless of null. The cache will handle null results.
4464309c1f708f469a5ab3ac52b6b22cc6ede1d50ffSam Blitzstein                mCache.put(mKey, result);
4474309c1f708f469a5ab3ac52b6b22cc6ede1d50ffSam Blitzstein            }
44893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            if (result != null) {
44993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                result.acquireReference();
45093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                if (DEBUG) {
45193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    Log.d(TAG, "placed result in cache: key=" + mKey + " bmp="
45293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                        + result + " cancelled=" + isCancelled());
45393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                }
45493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            } else if (mInBitmap != null) {
45593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                if (DEBUG) {
45693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                    Log.d(TAG, "placing failed/cancelled bitmap in pool: key="
45793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                        + mKey + " bmp=" + mInBitmap);
45893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                }
45993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                mCache.offer(mInBitmap);
46093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
46193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
46293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        return result;
46393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
46493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
46593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    /**
46693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * Return an input stream that can be read from the beginning using the most efficient way,
46793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * given an input stream that may or may not support reset(), or given null.
46893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     *
46993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     * The returned input stream may or may not be the same stream.
47093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein     */
47193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    private InputStream reset(InputStream in) throws IOException {
47293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        Trace.beginSection("create stream");
47393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (in == null) {
47493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            in = mKey.createInputStream();
47593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        } else if (in.markSupported()) {
47693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            in.reset();
47793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        } else {
47893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            try {
47993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein                in.close();
48093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            } catch (IOException ignored) {
48193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            }
48293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            in = mKey.createInputStream();
48393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
48493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        Trace.endSection();
48593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        return in;
48693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
48793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
488cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei    private Bitmap decode(ParcelFileDescriptor fd, InputStream in) {
48993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        final Bitmap result;
49093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (fd != null) {
49193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            result = BitmapFactory.decodeFileDescriptor(fd.getFileDescriptor(), null, mOpts);
49293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        } else {
49393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            result = BitmapFactory.decodeStream(in, null, mOpts);
49493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
49593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        return result;
49693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
49793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
49893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    public void cancel() {
49993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        cancel(true);
50093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        mOpts.requestCancelDecode();
50193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
50293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
50393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    @Override
50493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    protected void onProgressUpdate(Void... values) {
50593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        mDecodeCallback.onDecodeBegin(mKey);
50693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
50793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
50893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    @Override
50993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    public void onPostExecute(ReusableBitmap result) {
51093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        mDecodeCallback.onDecodeComplete(mKey, result);
51193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
51293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
51393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    @Override
51493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    protected void onCancelled(ReusableBitmap result) {
51593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        mDecodeCallback.onDecodeCancel(mKey);
51693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (result == null) {
51793a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            return;
51893a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
51993a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
52093a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        result.releaseReference();
52193a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        if (mInBitmap == null) {
52293a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            // not reusing bitmaps: can recycle immediately
52393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein            result.bmp.recycle();
52493a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein        }
52593a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein    }
52693a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein
5275030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei    /**
5285030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei     * Parameters to pass to the DecodeTask.
5295030ae34cd5978a8ab8a06f6c3b69b8645873122Mark Wei     */
530cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei    public static class DecodeOptions {
531cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei
532cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei        /**
533cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei         * Round sample size to the nearest power of 2. Depending on the source and destination
534cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei         * dimensions, we will either truncate, in which case we decode from a bigger region and
535cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei         * crop down, or we will round up, in which case we decode from a smaller region and scale
536cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei         * up.
537cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei         */
538cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei        public static final int STRATEGY_ROUND_NEAREST = 0;
539cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei        /**
540cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei         * Always decode from a bigger region and crop down.
541cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei         */
542cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei        public static final int STRATEGY_TRUNCATE = 1;
543cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei
544cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei        /**
545cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei         * Always decode from a smaller region and scale up.
546cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei         */
547cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei        public static final int STRATEGY_ROUND_UP = 2;
548cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei
549cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei        /**
550cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei         * The destination width to decode to.
551cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei         */
552cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei        public int destW;
553cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei        /**
554cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei         * The destination height to decode to.
555cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei         */
556cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei        public int destH;
557cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei        /**
558cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei         * If the destination dimensions are smaller than the source image provided by the request
5593a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung         * key, this will determine where horizontally the destination rect will be cropped from.
5603a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung         * Value from 0f for left-most crop to 1f for right-most crop.
5613a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung         */
5623a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung        public float horizontalCenter;
5633a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung        /**
5643a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung         * If the destination dimensions are smaller than the source image provided by the request
565cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei         * key, this will determine where vertically the destination rect will be cropped from.
566cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei         * Value from 0f for top-most crop to 1f for bottom-most crop.
567cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei         */
568cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei        public float verticalCenter;
569cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei        /**
570cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei         * One of the STRATEGY constants.
571cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei         */
572cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei        public int sampleSizeStrategy;
573cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei
574cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei        public DecodeOptions(final int destW, final int destH) {
5753a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung            this(destW, destH, 0.5f, 0.5f, STRATEGY_ROUND_NEAREST);
5763a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung        }
5773a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung
5783a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung        /**
5793a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung         * Create new DecodeOptions with horizontally-centered cropping if applicable.
5803a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung         * @param destW The destination width to decode to.
5813a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung         * @param destH The destination height to decode to.
5823a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung         * @param verticalCenter If the destination dimensions are smaller than the source image
5833a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung         *                       provided by the request key, this will determine where vertically
5843a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung         *                       the destination rect will be cropped from.
5853a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung         * @param sampleSizeStrategy One of the STRATEGY constants.
5863a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung         */
5873a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung        public DecodeOptions(final int destW, final int destH,
5883a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung                final float verticalCenter, final int sampleSizeStrategy) {
5893a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung            this(destW, destH, 0.5f, verticalCenter, sampleSizeStrategy);
590cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei        }
591cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei
592cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei        /**
593cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei         * Create new DecodeOptions.
594cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei         * @param destW The destination width to decode to.
595cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei         * @param destH The destination height to decode to.
5963a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung         * @param horizontalCenter If the destination dimensions are smaller than the source image
5973a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung         *                         provided by the request key, this will determine where
5983a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung         *                         horizontally the destination rect will be cropped from.
599cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei         * @param verticalCenter If the destination dimensions are smaller than the source image
600cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei         *                       provided by the request key, this will determine where vertically
601cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei         *                       the destination rect will be cropped from.
602cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei         * @param sampleSizeStrategy One of the STRATEGY constants.
603cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei         */
6043a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung        public DecodeOptions(final int destW, final int destH, final float horizontalCenter,
6053a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung                final float verticalCenter, final int sampleSizeStrategy) {
606cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei            this.destW = destW;
607cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei            this.destH = destH;
6083a79e2002f9f6114b549c4bc2cc08bb10e75a4d2James Kung            this.horizontalCenter = horizontalCenter;
609cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei            this.verticalCenter = verticalCenter;
610cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei            this.sampleSizeStrategy = sampleSizeStrategy;
611cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei        }
612cea0c012d538f11b3ee97d4b7e78f4c1ea73d5beMark Wei    }
61393a35b93dc582e38ff8ee5979754a16b4bf4da0cSam Blitzstein}
614