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