1c68570335a64be66829727e121afe1622902a74bDoris Liu/*
2c68570335a64be66829727e121afe1622902a74bDoris Liu * Copyright (C) 2013 The Android Open Source Project
3c68570335a64be66829727e121afe1622902a74bDoris Liu *
4c68570335a64be66829727e121afe1622902a74bDoris Liu * Licensed under the Apache License, Version 2.0 (the "License");
5c68570335a64be66829727e121afe1622902a74bDoris Liu * you may not use this file except in compliance with the License.
6c68570335a64be66829727e121afe1622902a74bDoris Liu * You may obtain a copy of the License at
7c68570335a64be66829727e121afe1622902a74bDoris Liu *
8c68570335a64be66829727e121afe1622902a74bDoris Liu *      http://www.apache.org/licenses/LICENSE-2.0
9c68570335a64be66829727e121afe1622902a74bDoris Liu *
10c68570335a64be66829727e121afe1622902a74bDoris Liu * Unless required by applicable law or agreed to in writing, software
11c68570335a64be66829727e121afe1622902a74bDoris Liu * distributed under the License is distributed on an "AS IS" BASIS,
12c68570335a64be66829727e121afe1622902a74bDoris Liu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c68570335a64be66829727e121afe1622902a74bDoris Liu * See the License for the specific language governing permissions and
14c68570335a64be66829727e121afe1622902a74bDoris Liu * limitations under the License.
15c68570335a64be66829727e121afe1622902a74bDoris Liu */
16c68570335a64be66829727e121afe1622902a74bDoris Liu
17c68570335a64be66829727e121afe1622902a74bDoris Liupackage com.android.camera.ui;
18c68570335a64be66829727e121afe1622902a74bDoris Liu
19c68570335a64be66829727e121afe1622902a74bDoris Liuimport android.content.Context;
20c68570335a64be66829727e121afe1622902a74bDoris Liuimport android.graphics.Bitmap;
21c68570335a64be66829727e121afe1622902a74bDoris Liuimport android.graphics.BitmapFactory;
22c68570335a64be66829727e121afe1622902a74bDoris Liuimport android.graphics.BitmapRegionDecoder;
23c68570335a64be66829727e121afe1622902a74bDoris Liuimport android.graphics.Matrix;
242666dc8a1c23b7c2b7c80d7c09104f113fb7dbb2Sam Juddimport android.graphics.Point;
25c68570335a64be66829727e121afe1622902a74bDoris Liuimport android.graphics.Rect;
26c68570335a64be66829727e121afe1622902a74bDoris Liuimport android.graphics.RectF;
27c68570335a64be66829727e121afe1622902a74bDoris Liuimport android.net.Uri;
28c68570335a64be66829727e121afe1622902a74bDoris Liuimport android.os.AsyncTask;
29c68570335a64be66829727e121afe1622902a74bDoris Liuimport android.view.View;
30c68570335a64be66829727e121afe1622902a74bDoris Liuimport android.widget.ImageView;
312bca210e5fc8a77685775ffb403096167b017dceAngus Kong
328ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohdeimport com.android.camera.data.FilmstripItemUtils;
332bca210e5fc8a77685775ffb403096167b017dceAngus Kongimport com.android.camera.debug.Log;
34c68570335a64be66829727e121afe1622902a74bDoris Liu
35c68570335a64be66829727e121afe1622902a74bDoris Liuimport java.io.FileNotFoundException;
36c68570335a64be66829727e121afe1622902a74bDoris Liuimport java.io.IOException;
37c68570335a64be66829727e121afe1622902a74bDoris Liuimport java.io.InputStream;
38c68570335a64be66829727e121afe1622902a74bDoris Liu
398de13111cc4e62da3462ea321d18c7951282e0d0Doris Liupublic class ZoomView extends ImageView {
40c68570335a64be66829727e121afe1622902a74bDoris Liu
412bca210e5fc8a77685775ffb403096167b017dceAngus Kong    private static final Log.Tag TAG = new Log.Tag("ZoomView");
42c68570335a64be66829727e121afe1622902a74bDoris Liu
43c68570335a64be66829727e121afe1622902a74bDoris Liu    private int mViewportWidth = 0;
44c68570335a64be66829727e121afe1622902a74bDoris Liu    private int mViewportHeight = 0;
45c68570335a64be66829727e121afe1622902a74bDoris Liu
46c68570335a64be66829727e121afe1622902a74bDoris Liu    private BitmapRegionDecoder mRegionDecoder;
47d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong    // This is null when there's no decoding going on.
48c68570335a64be66829727e121afe1622902a74bDoris Liu    private DecodePartialBitmap mPartialDecodingTask;
49c68570335a64be66829727e121afe1622902a74bDoris Liu
50c68570335a64be66829727e121afe1622902a74bDoris Liu    private Uri mUri;
5187fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu    private int mOrientation;
52c68570335a64be66829727e121afe1622902a74bDoris Liu
53c68570335a64be66829727e121afe1622902a74bDoris Liu    private class DecodePartialBitmap extends AsyncTask<RectF, Void, Bitmap> {
54d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong        BitmapRegionDecoder mDecoder;
55d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong
56d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong        @Override
57d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong        protected void onPreExecute() {
58d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong            mDecoder = mRegionDecoder;
59d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong        }
60c68570335a64be66829727e121afe1622902a74bDoris Liu
61c68570335a64be66829727e121afe1622902a74bDoris Liu        @Override
62c68570335a64be66829727e121afe1622902a74bDoris Liu        protected Bitmap doInBackground(RectF... params) {
63c68570335a64be66829727e121afe1622902a74bDoris Liu            RectF endRect = params[0];
6487fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu
6587fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            // Calculate the rotation matrix to apply orientation on the original image
6687fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            // rect.
672666dc8a1c23b7c2b7c80d7c09104f113fb7dbb2Sam Judd            InputStream isForDimensions = getInputStream();
682666dc8a1c23b7c2b7c80d7c09104f113fb7dbb2Sam Judd            if (isForDimensions == null) {
692666dc8a1c23b7c2b7c80d7c09104f113fb7dbb2Sam Judd                return null;
702666dc8a1c23b7c2b7c80d7c09104f113fb7dbb2Sam Judd            }
712666dc8a1c23b7c2b7c80d7c09104f113fb7dbb2Sam Judd
728ee16b8a323ffa20e6fb1270d498ec445f64defcPaul Rohde            Point imageSize = FilmstripItemUtils.decodeBitmapDimension(isForDimensions);
732666dc8a1c23b7c2b7c80d7c09104f113fb7dbb2Sam Judd            try {
742666dc8a1c23b7c2b7c80d7c09104f113fb7dbb2Sam Judd                isForDimensions.close();
752666dc8a1c23b7c2b7c80d7c09104f113fb7dbb2Sam Judd            } catch (IOException e) {
76b9535998da7a02f22156b70615c3e211401d14b0Alan Newberger                Log.e(TAG, "exception closing dimensions inputstream", e);
772666dc8a1c23b7c2b7c80d7c09104f113fb7dbb2Sam Judd            }
782666dc8a1c23b7c2b7c80d7c09104f113fb7dbb2Sam Judd            if (imageSize == null) {
792666dc8a1c23b7c2b7c80d7c09104f113fb7dbb2Sam Judd                return null;
802666dc8a1c23b7c2b7c80d7c09104f113fb7dbb2Sam Judd            }
812666dc8a1c23b7c2b7c80d7c09104f113fb7dbb2Sam Judd
822666dc8a1c23b7c2b7c80d7c09104f113fb7dbb2Sam Judd            RectF fullResRect = new RectF(0, 0, imageSize.x - 1, imageSize.y - 1);
8387fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            Matrix rotationMatrix = new Matrix();
8487fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            rotationMatrix.setRotate(mOrientation, 0, 0);
8587fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            rotationMatrix.mapRect(fullResRect);
8687fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            // Set the translation of the matrix so that after rotation, the top left
8787fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            // of the image rect is at (0, 0)
8887fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            rotationMatrix.postTranslate(-fullResRect.left, -fullResRect.top);
892666dc8a1c23b7c2b7c80d7c09104f113fb7dbb2Sam Judd            rotationMatrix.mapRect(fullResRect, new RectF(0, 0, imageSize.x - 1,
902666dc8a1c23b7c2b7c80d7c09104f113fb7dbb2Sam Judd                    imageSize.y - 1));
9187fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu
92c68570335a64be66829727e121afe1622902a74bDoris Liu            // Find intersection with the screen
93c68570335a64be66829727e121afe1622902a74bDoris Liu            RectF visibleRect = new RectF(endRect);
94121022cb590955e37f1264f77190ce4711159976Doris Liu            visibleRect.intersect(0, 0, mViewportWidth - 1, mViewportHeight - 1);
9587fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            // Calculate the mapping (i.e. transform) between current low res rect
9687fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            // and full res image rect, and apply the mapping on current visible rect
9787fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            // to find out the partial region in the full res image that we need
9887fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            // to decode.
9987fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            Matrix mapping = new Matrix();
10087fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            mapping.setRectToRect(endRect, fullResRect, Matrix.ScaleToFit.CENTER);
10187fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            RectF visibleAfterRotation = new RectF();
10287fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            mapping.mapRect(visibleAfterRotation, visibleRect);
10387fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu
10487fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            // Now the visible region we have is rotated, we need to reverse the
10587fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            // rotation to find out the region in the original image
106c68570335a64be66829727e121afe1622902a74bDoris Liu            RectF visibleInImage = new RectF();
10787fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            Matrix invertRotation = new Matrix();
10887fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            rotationMatrix.invert(invertRotation);
10987fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            invertRotation.mapRect(visibleInImage, visibleAfterRotation);
110c68570335a64be66829727e121afe1622902a74bDoris Liu
111c68570335a64be66829727e121afe1622902a74bDoris Liu            // Decode region
11287fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            Rect region = new Rect();
11387fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            visibleInImage.round(region);
114121022cb590955e37f1264f77190ce4711159976Doris Liu
115121022cb590955e37f1264f77190ce4711159976Doris Liu            // Make sure region to decode is inside the image.
1162666dc8a1c23b7c2b7c80d7c09104f113fb7dbb2Sam Judd            region.intersect(0, 0, imageSize.x - 1, imageSize.y - 1);
117121022cb590955e37f1264f77190ce4711159976Doris Liu
1181f69e36bc45ccf0090cb1d8392d603cfe2a4e8bfDoris Liu            if (region.width() == 0 || region.height() == 0) {
1191f69e36bc45ccf0090cb1d8392d603cfe2a4e8bfDoris Liu                Log.e(TAG, "Invalid size for partial region. Region: " + region.toString());
1201f69e36bc45ccf0090cb1d8392d603cfe2a4e8bfDoris Liu                return null;
1211f69e36bc45ccf0090cb1d8392d603cfe2a4e8bfDoris Liu            }
1221f69e36bc45ccf0090cb1d8392d603cfe2a4e8bfDoris Liu
123c68570335a64be66829727e121afe1622902a74bDoris Liu            if (isCancelled()) {
124c68570335a64be66829727e121afe1622902a74bDoris Liu                return null;
125c68570335a64be66829727e121afe1622902a74bDoris Liu            }
126c68570335a64be66829727e121afe1622902a74bDoris Liu
127c68570335a64be66829727e121afe1622902a74bDoris Liu            BitmapFactory.Options options = new BitmapFactory.Options();
128c502bc9d50ded1f2eec28f4809c7193ec4ec5298Doris Liu            if ((mOrientation + 360) % 180 == 0) {
129c502bc9d50ded1f2eec28f4809c7193ec4ec5298Doris Liu                options.inSampleSize = getSampleFactor(region.width(), region.height());
130c502bc9d50ded1f2eec28f4809c7193ec4ec5298Doris Liu            } else {
131c502bc9d50ded1f2eec28f4809c7193ec4ec5298Doris Liu                // The decoded region will be rotated 90/270 degrees before showing
132c502bc9d50ded1f2eec28f4809c7193ec4ec5298Doris Liu                // on screen. In other words, the width and height will be swapped.
133c502bc9d50ded1f2eec28f4809c7193ec4ec5298Doris Liu                // Therefore, sample factor should be calculated using swapped width
134c502bc9d50ded1f2eec28f4809c7193ec4ec5298Doris Liu                // and height.
135c502bc9d50ded1f2eec28f4809c7193ec4ec5298Doris Liu                options.inSampleSize = getSampleFactor(region.height(), region.width());
136c502bc9d50ded1f2eec28f4809c7193ec4ec5298Doris Liu            }
137c502bc9d50ded1f2eec28f4809c7193ec4ec5298Doris Liu
138d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong            if (mDecoder == null) {
139121022cb590955e37f1264f77190ce4711159976Doris Liu                InputStream is = getInputStream();
1402666dc8a1c23b7c2b7c80d7c09104f113fb7dbb2Sam Judd                if (is == null) {
1412666dc8a1c23b7c2b7c80d7c09104f113fb7dbb2Sam Judd                    return null;
1422666dc8a1c23b7c2b7c80d7c09104f113fb7dbb2Sam Judd                }
1432666dc8a1c23b7c2b7c80d7c09104f113fb7dbb2Sam Judd
144121022cb590955e37f1264f77190ce4711159976Doris Liu                try {
145d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong                    mDecoder = BitmapRegionDecoder.newInstance(is, false);
146121022cb590955e37f1264f77190ce4711159976Doris Liu                    is.close();
147121022cb590955e37f1264f77190ce4711159976Doris Liu                } catch (IOException e) {
148121022cb590955e37f1264f77190ce4711159976Doris Liu                    Log.e(TAG, "Failed to instantiate region decoder");
149121022cb590955e37f1264f77190ce4711159976Doris Liu                }
150121022cb590955e37f1264f77190ce4711159976Doris Liu            }
151d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong            if (mDecoder == null) {
152121022cb590955e37f1264f77190ce4711159976Doris Liu                return null;
153121022cb590955e37f1264f77190ce4711159976Doris Liu            }
154d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong            Bitmap b = mDecoder.decodeRegion(region, options);
15587fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            if (isCancelled()) {
15687fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu                return null;
15787fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            }
15887fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            Matrix rotation = new Matrix();
15987fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            rotation.setRotate(mOrientation);
16087fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            return Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), rotation, false);
161c68570335a64be66829727e121afe1622902a74bDoris Liu        }
162c68570335a64be66829727e121afe1622902a74bDoris Liu
163c68570335a64be66829727e121afe1622902a74bDoris Liu        @Override
164c68570335a64be66829727e121afe1622902a74bDoris Liu        protected void onPostExecute(Bitmap b) {
165c68570335a64be66829727e121afe1622902a74bDoris Liu            mPartialDecodingTask = null;
166d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong            if (mDecoder != mRegionDecoder) {
167d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong                // This decoder will no longer be used, recycle it.
168d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong                mDecoder.recycle();
169d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong            }
170d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong            if (b != null) {
171d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong                setImageBitmap(b);
172d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong                showPartiallyDecodedImage(true);
173d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong            }
174c68570335a64be66829727e121afe1622902a74bDoris Liu        }
175c68570335a64be66829727e121afe1622902a74bDoris Liu    }
176c68570335a64be66829727e121afe1622902a74bDoris Liu
1778de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu    public ZoomView(Context context) {
178c68570335a64be66829727e121afe1622902a74bDoris Liu        super(context);
1793179f6a49516833761be5155c4e1244ac0aa17feDoris Liu        setScaleType(ScaleType.FIT_CENTER);
180c68570335a64be66829727e121afe1622902a74bDoris Liu        addOnLayoutChangeListener(new OnLayoutChangeListener() {
181c68570335a64be66829727e121afe1622902a74bDoris Liu            @Override
182c68570335a64be66829727e121afe1622902a74bDoris Liu            public void onLayoutChange(View v, int left, int top, int right, int bottom,
183c68570335a64be66829727e121afe1622902a74bDoris Liu                                       int oldLeft, int oldTop, int oldRight, int oldBottom) {
184c68570335a64be66829727e121afe1622902a74bDoris Liu                int w = right - left;
185c68570335a64be66829727e121afe1622902a74bDoris Liu                int h = bottom - top;
186c68570335a64be66829727e121afe1622902a74bDoris Liu                if (mViewportHeight != h || mViewportWidth != w) {
187c68570335a64be66829727e121afe1622902a74bDoris Liu                    mViewportWidth = w;
188c68570335a64be66829727e121afe1622902a74bDoris Liu                    mViewportHeight = h;
189c68570335a64be66829727e121afe1622902a74bDoris Liu                }
190c68570335a64be66829727e121afe1622902a74bDoris Liu            }
191c68570335a64be66829727e121afe1622902a74bDoris Liu        });
192c68570335a64be66829727e121afe1622902a74bDoris Liu    }
193c68570335a64be66829727e121afe1622902a74bDoris Liu
194d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong    public void resetDecoder() {
195d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong        if (mRegionDecoder != null) {
196d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong            cancelPartialDecodingTask();
197d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong            if (mPartialDecodingTask == null) {
198d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong                // No ongoing decoding task, safe to recycle the decoder.
199d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong                mRegionDecoder.recycle();
200d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong            }
201d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong            mRegionDecoder = null;
202d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong        }
203d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong    }
204d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong
20587fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu    public void loadBitmap(Uri uri, int orientation, RectF imageRect) {
206121022cb590955e37f1264f77190ce4711159976Doris Liu        if (!uri.equals(mUri)) {
207d1d28254c1413b4d7725df7d7b38e2a1f2f42e28Angus Kong            resetDecoder();
208121022cb590955e37f1264f77190ce4711159976Doris Liu            mUri = uri;
20987fc5e1568f737e03327770cb3dee03c4b939c59Doris Liu            mOrientation = orientation;
210c68570335a64be66829727e121afe1622902a74bDoris Liu        }
2118de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        startPartialDecodingTask(imageRect);
212c68570335a64be66829727e121afe1622902a74bDoris Liu    }
213c68570335a64be66829727e121afe1622902a74bDoris Liu
214c68570335a64be66829727e121afe1622902a74bDoris Liu    private void showPartiallyDecodedImage(boolean show) {
215c68570335a64be66829727e121afe1622902a74bDoris Liu        if (show) {
2168de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            setVisibility(View.VISIBLE);
217c68570335a64be66829727e121afe1622902a74bDoris Liu        } else {
2188de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            setVisibility(View.GONE);
219c68570335a64be66829727e121afe1622902a74bDoris Liu        }
2208de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu    }
2218de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu
2228de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu    public void cancelPartialDecodingTask() {
2238de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        if (mPartialDecodingTask != null && !mPartialDecodingTask.isCancelled()) {
2248de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            mPartialDecodingTask.cancel(true);
2258de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            setVisibility(GONE);
2268de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        }
227c68570335a64be66829727e121afe1622902a74bDoris Liu    }
228c68570335a64be66829727e121afe1622902a74bDoris Liu
229c68570335a64be66829727e121afe1622902a74bDoris Liu    /**
2308de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu     * If the given rect is smaller than viewport on x or y axis, center rect within
2318de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu     * viewport on the corresponding axis. Otherwise, make sure viewport is within
2328de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu     * the bounds of the rect.
2338de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu     */
234121022cb590955e37f1264f77190ce4711159976Doris Liu    public static RectF adjustToFitInBounds(RectF rect, int viewportWidth, int viewportHeight) {
235121022cb590955e37f1264f77190ce4711159976Doris Liu        float dx = 0, dy = 0;
236121022cb590955e37f1264f77190ce4711159976Doris Liu        RectF newRect = new RectF(rect);
2378de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        if (newRect.width() < viewportWidth) {
2388de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            dx = viewportWidth / 2 - (newRect.left + newRect.right) / 2;
2398de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        } else {
2408de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            if (newRect.left > 0) {
2418de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu                dx = -newRect.left;
2428de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            } else if (newRect.right < viewportWidth) {
2438de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu                dx = viewportWidth - newRect.right;
2448de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            }
2458de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        }
2468de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu
2478de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        if (newRect.height() < viewportHeight) {
2488de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            dy = viewportHeight / 2 - (newRect.top + newRect.bottom) / 2;
249c68570335a64be66829727e121afe1622902a74bDoris Liu        } else {
2508de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            if (newRect.top > 0) {
2518de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu                dy = -newRect.top;
2528de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            } else if (newRect.bottom < viewportHeight) {
2538de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu                dy = viewportHeight - newRect.bottom;
2548de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            }
255c68570335a64be66829727e121afe1622902a74bDoris Liu        }
2568de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu
2578de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        if (dx != 0 || dy != 0) {
2588de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu            newRect.offset(dx, dy);
2598de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        }
2608de13111cc4e62da3462ea321d18c7951282e0d0Doris Liu        return newRect;
261c68570335a64be66829727e121afe1622902a74bDoris Liu    }
262c68570335a64be66829727e121afe1622902a74bDoris Liu
263c68570335a64be66829727e121afe1622902a74bDoris Liu    private void startPartialDecodingTask(RectF endRect) {
264c68570335a64be66829727e121afe1622902a74bDoris Liu        // Cancel on-going partial decoding tasks
265c68570335a64be66829727e121afe1622902a74bDoris Liu        cancelPartialDecodingTask();
266c68570335a64be66829727e121afe1622902a74bDoris Liu        mPartialDecodingTask = new DecodePartialBitmap();
267c68570335a64be66829727e121afe1622902a74bDoris Liu        mPartialDecodingTask.execute(endRect);
268c68570335a64be66829727e121afe1622902a74bDoris Liu    }
269c68570335a64be66829727e121afe1622902a74bDoris Liu
270c68570335a64be66829727e121afe1622902a74bDoris Liu    // TODO: Cache the inputstream
271c68570335a64be66829727e121afe1622902a74bDoris Liu    private InputStream getInputStream() {
272c68570335a64be66829727e121afe1622902a74bDoris Liu        InputStream is = null;
273c68570335a64be66829727e121afe1622902a74bDoris Liu        try {
274c68570335a64be66829727e121afe1622902a74bDoris Liu            is = getContext().getContentResolver().openInputStream(mUri);
275c68570335a64be66829727e121afe1622902a74bDoris Liu        } catch (FileNotFoundException e) {
276c68570335a64be66829727e121afe1622902a74bDoris Liu            Log.e(TAG, "File not found at: " + mUri);
277c68570335a64be66829727e121afe1622902a74bDoris Liu        }
278c68570335a64be66829727e121afe1622902a74bDoris Liu        return is;
279c68570335a64be66829727e121afe1622902a74bDoris Liu    }
280c68570335a64be66829727e121afe1622902a74bDoris Liu
281c68570335a64be66829727e121afe1622902a74bDoris Liu    /**
282c68570335a64be66829727e121afe1622902a74bDoris Liu     * Find closest sample factor that is power of 2, based on the given width and height
283c68570335a64be66829727e121afe1622902a74bDoris Liu     *
284c68570335a64be66829727e121afe1622902a74bDoris Liu     * @param width width of the partial region to decode
285c68570335a64be66829727e121afe1622902a74bDoris Liu     * @param height height of the partial region to decode
286c68570335a64be66829727e121afe1622902a74bDoris Liu     * @return sample factor
287c68570335a64be66829727e121afe1622902a74bDoris Liu     */
288c68570335a64be66829727e121afe1622902a74bDoris Liu    private int getSampleFactor(int width, int height) {
289c68570335a64be66829727e121afe1622902a74bDoris Liu
290c68570335a64be66829727e121afe1622902a74bDoris Liu        float fitWidthScale = ((float) mViewportWidth) / ((float) width);
291c68570335a64be66829727e121afe1622902a74bDoris Liu        float fitHeightScale = ((float) mViewportHeight) / ((float) height);
292c68570335a64be66829727e121afe1622902a74bDoris Liu
293c68570335a64be66829727e121afe1622902a74bDoris Liu        float scale = Math.min(fitHeightScale, fitWidthScale);
294c68570335a64be66829727e121afe1622902a74bDoris Liu
295c68570335a64be66829727e121afe1622902a74bDoris Liu        // Find the closest sample factor that is power of 2
296c68570335a64be66829727e121afe1622902a74bDoris Liu        int sampleFactor = (int) (1f / scale);
297c68570335a64be66829727e121afe1622902a74bDoris Liu        if (sampleFactor <=1) {
298c68570335a64be66829727e121afe1622902a74bDoris Liu            return 1;
299c68570335a64be66829727e121afe1622902a74bDoris Liu        }
300c68570335a64be66829727e121afe1622902a74bDoris Liu        for (int i = 0; i < 32; i++) {
301c68570335a64be66829727e121afe1622902a74bDoris Liu            if ((1 << (i + 1)) > sampleFactor) {
302c68570335a64be66829727e121afe1622902a74bDoris Liu                sampleFactor = (1 << i);
303c68570335a64be66829727e121afe1622902a74bDoris Liu                break;
304c68570335a64be66829727e121afe1622902a74bDoris Liu            }
305c68570335a64be66829727e121afe1622902a74bDoris Liu        }
306c68570335a64be66829727e121afe1622902a74bDoris Liu        return sampleFactor;
307c68570335a64be66829727e121afe1622902a74bDoris Liu    }
308c68570335a64be66829727e121afe1622902a74bDoris Liu}
309