在Android中对大图片进行缩放真的很不尽如人意,不知道是不是我的方法不对。下面我列出3种对图片缩放的方法,并给出相应速度。请高人指教。 
第一种是BitmapFactory和BitmapFactory.Options。 
首先,BitmapFactory.Options有几个Fields很有用: 
inJustDecodeBounds:If set to true, the decoder will return null (no bitmap), but the out... 
也就是说,当inJustDecodeBounds设成true时,bitmap并不加载到内存,这样效率很高哦。而这时,你可以获得bitmap的高、宽等信息。 
outHeight:The resulting height of the bitmap, set independent of the state of inJustDecodeBounds. 
outWidth:The resulting width of the bitmap, set independent of the state of inJustDecodeBounds.  
看到了吧,上面3个变量是相关联的哦。 
inSampleSize : If set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory. 
这就是用来做缩放比的。这里有个技巧: 
inSampleSize=(outHeight/Height+outWidth/Width)/2 
实践证明,这样缩放出来的图片还是很好的。 
最后用BitmapFactory.decodeFile(path, options)生成。 
由于只是对bitmap加载到内存一次,所以效率比较高。解析速度快。 
第二种是使用Bitmap加Matrix来缩放。

首先要获得原bitmap,再从原bitmap的基础上生成新图片。这样效率很低。

第三种是用2.2新加的类ThumbnailUtils来做。 
让我们新看看这个类,从API中来看,此类就三个静态方法:createVideoThumbnail、extractThumbnail(Bitmap source, int width, int height, int options)、extractThumbnail(Bitmap source, int width, int height)。 
我这里使用了第三个方法。再看看它的源码,下面会附上。是上面我们用到的BitmapFactory.Options和Matrix等经过人家一阵加工而成。 
效率好像比第二种方法高一点点。

下面是我的例子:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
 3. android:orientation="vertical"
 4. android:layout_;fill_parent"
 5. android:layout_height="fill_parent"
>
 7.
<ImageView
 9. android:id="@+id/imageShow"
 10. android:layout_;wrap_content"
 11. android:layout_height="wrap_content"
/>
<ImageView
 14. android:id="@+id/image2"
 15. android:layout_;wrap_content"
 16. android:layout_height="wrap_content"
/>
<TextView
 19. android:id="@+id/text"
 20. android:layout_;fill_parent"
 21. android:layout_height="wrap_content"
 22. android:text="@string/hello"
/>
</LinearLayout>
 1. package com.linc.ResolvePicture; 
 2.
 3. import java.io.File; 
 4. import java.io.FileNotFoundException; 
 5. import java.io.FileOutputStream; 
 6. import java.io.IOException; 
 7.
 8. import .Activity; 
 9. import android.graphics.Bitmap; 
 10. import android.graphics.BitmapFactory; 
 11. import android.graphics.Matrix; 
 12. import android.graphics.drawable.BitmapDrawable; 
 13. import android.graphics.drawable.Drawable; 
 14. import .ThumbnailUtils; 
 15. import android.os.Bundle; 
 16. import android.util.Log; 
 17. import android.widget.ImageView; 
 18. import android.widget.TextView; 
 19.
 20. public class ResolvePicture extends Activity { 
 21. private static String tag="ResolvePicture"; 
 22.     Drawable bmImg;   
 23.     ImageView imView;  
 24.     ImageView imView2;  
 25.     TextView text; 
 26.     String theTime; 
 27. long start, stop;  
 28. /** Called when the activity is first created. */
 29. @Override
 30. public void onCreate(Bundle savedInstanceState) { 
 31. super.onCreate(savedInstanceState); 
 32.         setContentView(R.layout.main); 
 33.
 34.         text=(TextView)findViewById(.text); 
 35.
 36.         imView=(ImageView) findViewById(.imageShow); 
 37.         imView2=(ImageView) findViewById(.image2); 
 38.
 39.         Bitmap bitmap = BitmapFactory.decodeResource(getResources(),    
 40.                 R.drawable.pic); 
 41.
 42.         start=System.currentTimeMillis(); 
 43.
 44. //        imView.setImageDrawable(resizeImage(bitmap, 300, 100)); 
 45.
 46.         imView2.setImageDrawable(resizeImage2("/sdcard/2.jpeg", 200, 100));  
 47.
 48.         stop=System.currentTimeMillis(); 
 49.
 50.         String theTime= String.format("\n1 iterative: (%d msec)",   
 51.                 stop - start);   
 52.
 53.         start=System.currentTimeMillis(); 
 54.         imView.setImageBitmap(ThumbnailUtils.extractThumbnail(bitmap,200,100));//2.2才加进来的新类,简单易用
 55. //        imView.setImageDrawable(resizeImage(bitmap, 30, 30)); 
 56.         stop=System.currentTimeMillis(); 
 57.
 58.          theTime+= String.format("\n2 iterative: (%d msec)",   
 59.                 stop - start);  
 60.
 61.         text.setText(theTime); 
 62.     } 
 63.
 64. //使用Bitmap加Matrix来缩放
 65. public static Drawable resizeImage(Bitmap bitmap, int w, int h)  
 66.     {   
 67.         Bitmap BitmapOrg = bitmap;   
 68. int width = BitmapOrg.getWidth();   
 69. int height = BitmapOrg.getHeight();   
 70. int newWidth = w;   
 71. int newHeight = h;   
 72.
 73. float scaleWidth = ((float) newWidth) / width;   
 74. float scaleHeight = ((float) newHeight) / height;   
 75.
 76.         Matrix matrix = new Matrix();   
 77.         matrix.postScale(scaleWidth, scaleHeight);   
 78. // if you want to rotate the Bitmap   
 79. // matrix.postRotate(45);   
 80.         Bitmap resizedBitmap = Bitmap.createBitmap(BitmapOrg, 0, 0, width,   
 81.                         height, matrix, true);   
 82. return new BitmapDrawable(resizedBitmap);   
 83.     } 
 84.
 85. //使用BitmapFactory.Options的inSampleSize参数来缩放
 86. public static Drawable resizeImage2(String path, 
 87. int width,int height)  
 88.     { 
 89.         BitmapFactory.Options options = new BitmapFactory.Options(); 
 90.         options.inJustDecodeBounds = true;//不加载bitmap到内存中
 91.         BitmapFactory.decodeFile(path,options);  
 92. int outWidth = options.outWidth; 
 93. int outHeight = options.outHeight; 
 94.         options.inDither = false; 
 95.         options.inPreferredConfig = Bitmap.Config.ARGB_8888; 
 96.         options.inSampleSize = 1; 
 97.
 98. if (outWidth != 0 && outHeight != 0 && width != 0 && height != 0)  
 99.         { 
 100. int sampleSize=(outWidth/width+outHeight/height)/2; 
 101.             Log.d(tag, "sampleSize = " + sampleSize); 
 102.             options.inSampleSize = sampleSize; 
 103.         } 
 104.
 105.         options.inJustDecodeBounds = false; 
 106. return new BitmapDrawable(BitmapFactory.decodeFile(path, options));      
 107.     } 
 108.
 109. //图片保存
 110. private void saveThePicture(Bitmap bitmap) 
 111.     { 
 112.         File file=new File("/sdcard/2.jpeg"); 
 113. try
 114.         { 
 115.             FileOutputStream fos=new FileOutputStream(file); 
 116. if(bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos)) 
 117.             { 
 118.                 fos.flush(); 
 119.                 fos.close(); 
 120.             } 
 121.         } 
 122. catch(FileNotFoundException e1) 
 123.         { 
 124.             e1.printStackTrace(); 
 125.         } 
 126. catch(IOException e2) 
 127.         { 
 128.             e2.printStackTrace(); 
 129.         } 
 130.     } 
 131. } 
 ThumbnailUtils源码:
 1. /*
 2.  * Copyright (C) 2009 The Android Open Source Project
 3.  *
 4.  * Licensed under the Apache License, Version 2.0 (the "License");
 5.  * you may not use this file except in compliance with the License.
 6.  * You may obtain a copy of the License at
 7.  *
 8.  *      http://www.apache.org/licenses/LICENSE-2.0
 9.  *
 10.  * Unless required by applicable law or agreed to in writing, software
 11.  * distributed under the License is distributed on an "AS IS" BASIS,
 12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13.  * See the License for the specific language governing permissions and
 14.  * limitations under the License.
 15.  */
 16.
 17. package ; 
 18.
 19. import android.content.ContentResolver; 
 20. import android.content.ContentUris; 
 21. import android.content.ContentValues; 
 22. import android.database.Cursor; 
 23. import android.graphics.Bitmap; 
 24. import android.graphics.BitmapFactory; 
 25. import android.graphics.Canvas; 
 26. import android.graphics.Matrix; 
 27. import android.graphics.Rect; 
 28. import .MediaMetadataRetriever; 
 29. import .MediaFile.MediaFileType; 
 30. import android.net.Uri; 
 31. import android.os.ParcelFileDescriptor; 
 32. import android.provider.BaseColumns; 
 33. import android.provider.MediaStore.Images; 
 34. import android.provider.MediaStore.Images.Thumbnails; 
 35. import android.util.Log; 
 36.
 37. import java.io.FileInputStream; 
 38. import java.io.FileDescriptor; 
 39. import java.io.IOException; 
 40. import java.io.OutputStream; 
 41.
 42. /**
 43.  * Thumbnail generation routines for media provider.
 44.  */
 45.
 46. public class ThumbnailUtils { 
 47. private static final String TAG = "ThumbnailUtils"; 
 48.
 49. /* Maximum pixels size for created bitmap. */
 50. private static final int MAX_NUM_PIXELS_THUMBNAIL = 512 * 384; 
 51. private static final int MAX_NUM_PIXELS_MICRO_THUMBNAIL = 128 * 128; 
 52. private static final int UNCONSTRAINED = -1; 
 53.
 54. /* Options used internally. */
 55. private static final int OPTIONS_NONE = 0x0; 
 56. private static final int OPTIONS_SCALE_UP = 0x1; 
 57.
 58. /**
 59.      * Constant used to indicate we should recycle the input in
 60.      * {@link #extractThumbnail(Bitmap, int, int, int)} unless the output is the input.
 61.      */
 62. public static final int OPTIONS_RECYCLE_INPUT = 0x2; 
 63.
 64. /**
 65.      * Constant used to indicate the dimension of mini thumbnail.
 66.      * @hide Only used by media framework and media provider internally.
 67.      */
 68. public static final int TARGET_SIZE_MINI_THUMBNAIL = 320; 
 69.
 70. /**
 71.      * Constant used to indicate the dimension of micro thumbnail.
 72.      * @hide Only used by media framework and media provider internally.
 73.      */
 74. public static final int TARGET_SIZE_MICRO_THUMBNAIL = 96; 
 75.
 76. /**
 77.      * This method first examines if the thumbnail embedded in EXIF is bigger than our target
 78.      * size. If not, then it'll create a thumbnail from original image. Due to efficiency
 79.      * consideration, we want to let MediaThumbRequest avoid calling this method twice for
 80.      * both kinds, so it only requests for MICRO_KIND and set saveImage to true.
 81.      *
 82.      * This method always returns a "square thumbnail" for MICRO_KIND thumbnail.
 83.      *
 84.      * @param filePath the path of image file
 85.      * @param kind could be MINI_KIND or MICRO_KIND
 86.      * @return Bitmap
 87.      *
 88.      * @hide This method is only used by media framework and media provider internally.
 89.      */
 90. public static Bitmap createImageThumbnail(String filePath, int kind) { 
 91. boolean wantMini = (kind == Images.Thumbnails.MINI_KIND); 
 92. int targetSize = wantMini 
 93.                 ? TARGET_SIZE_MINI_THUMBNAIL 
 94.                 : TARGET_SIZE_MICRO_THUMBNAIL; 
 95. int maxPixels = wantMini 
 96.                 ? MAX_NUM_PIXELS_THUMBNAIL 
 97.                 : MAX_NUM_PIXELS_MICRO_THUMBNAIL; 
 98.         SizedThumbnailBitmap sizedThumbnailBitmap = new SizedThumbnailBitmap(); 
 99.         Bitmap bitmap = null; 
 100.         MediaFileType fileType = MediaFile.getFileType(filePath); 
 101. if (fileType != null && fileType.fileType == MediaFile.FILE_TYPE_JPEG) { 
 102.             createThumbnailFromEXIF(filePath, targetSize, maxPixels, sizedThumbnailBitmap); 
 103.             bitmap = sizedThumbnailBitmap.mBitmap; 
 104.         } 
 105.
 106. if (bitmap == null) { 
 107. try { 
 108.                 FileDescriptor fd = new FileInputStream(filePath).getFD(); 
 109.                 BitmapFactory.Options options = new BitmapFactory.Options(); 
 110.                 options.inSampleSize = 1; 
 111.                 options.inJustDecodeBounds = true; 
 112.                 BitmapFactory.decodeFileDescriptor(fd, null, options); 
 113. if (options.mCancel || options.outWidth == -1
 114.                         || options.outHeight == -1) { 
 115. return null; 
 116.                 } 
 117.                 options.inSampleSize = computeSampleSize( 
 118.                         options, targetSize, maxPixels); 
 119.                 options.inJustDecodeBounds = false; 
 120.
 121.                 options.inDither = false; 
 122.                 options.inPreferredConfig = Bitmap.Config.ARGB_8888; 
 123.                 bitmap = BitmapFactory.decodeFileDescriptor(fd, null, options); 
 124.             } catch (IOException ex) { 
 125.                 Log.e(TAG, "", ex); 
 126.             } 
 127.         } 
 128.
 129. if (kind == Images.Thumbnails.MICRO_KIND) { 
 130. // now we make it a "square thumbnail" for MICRO_KIND thumbnail
 131.             bitmap = extractThumbnail(bitmap, 
 132.                     TARGET_SIZE_MICRO_THUMBNAIL, 
 133.                     TARGET_SIZE_MICRO_THUMBNAIL, OPTIONS_RECYCLE_INPUT); 
 134.         } 
 135. return bitmap; 
 136.     } 
 137.
 138. /**
 139.      * Create a video thumbnail for a video. May return null if the video is
 140.      * corrupt or the format is not supported.
 141.      *
 142.      * @param filePath the path of video file
 143.      * @param kind could be MINI_KIND or MICRO_KIND
 144.      */
 145. public static Bitmap createVideoThumbnail(String filePath, int kind) { 
 146.         Bitmap bitmap = null; 
 147.         MediaMetadataRetriever retriever = new MediaMetadataRetriever(); 
 148. try { 
 149.             retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY); 
 150.             retriever.setDataSource(filePath); 
 151.             bitmap = retriever.captureFrame(); 
 152.         } catch (IllegalArgumentException ex) { 
 153. // Assume this is a corrupt video file
 154.         } catch (RuntimeException ex) { 
 155. // Assume this is a corrupt video file.
 156.         } finally { 
 157. try { 
 158.                 retriever.release(); 
 159.             } catch (RuntimeException ex) { 
 160. // Ignore failures while cleaning up.
 161.             } 
 162.         } 
 163. if (kind == Images.Thumbnails.MICRO_KIND && bitmap != null) { 
 164.             bitmap = extractThumbnail(bitmap, 
 165.                     TARGET_SIZE_MICRO_THUMBNAIL, 
 166.                     TARGET_SIZE_MICRO_THUMBNAIL, 
 167.                     OPTIONS_RECYCLE_INPUT); 
 168.         } 
 169. return bitmap; 
 170.     } 
 171.
 172. /**
 173.      * Creates a centered bitmap of the desired size.
 174.      *
 175.      * @param source original bitmap source
 176.      * @param width targeted width
 177.      * @param height targeted height
 178.      */
 179. public static Bitmap extractThumbnail( 
 180.             Bitmap source, int width, int height) { 
 181. return extractThumbnail(source, width, height, OPTIONS_NONE); 
 182.     } 
 183.
 184. /**
 185.      * Creates a centered bitmap of the desired size.
 186.      *
 187.      * @param source original bitmap source
 188.      * @param width targeted width
 189.      * @param height targeted height
 190.      * @param options options used during thumbnail extraction
 191.      */
 192. public static Bitmap extractThumbnail( 
 193.             Bitmap source, int width, int height, int options) { 
 194. if (source == null) { 
 195. return null; 
 196.         } 
 197.
 198. float scale; 
 199. if (source.getWidth() < source.getHeight()) { 
 200.             scale = width / (float) source.getWidth(); 
 201.         } else { 
 202.             scale = height / (float) source.getHeight(); 
 203.         } 
 204.         Matrix matrix = new Matrix(); 
 205.         matrix.setScale(scale, scale); 
 206.         Bitmap thumbnail = transform(matrix, source, width, height, 
 207.                 OPTIONS_SCALE_UP | options); 
 208. return thumbnail; 
 209.     } 
 210.
 211. /*
 212.      * Compute the sample size as a function of minSideLength
 213.      * and maxNumOfPixels.
 214.      * minSideLength is used to specify that minimal width or height of a
 215.      * bitmap.
 216.      * maxNumOfPixels is used to specify the maximal size in pixels that is
 217.      * tolerable in terms of memory usage.
 218.      *
 219.      * The function returns a sample size based on the constraints.
 220.      * Both size and minSideLength can be passed in as IImage.UNCONSTRAINED,
 221.      * which indicates no care of the corresponding constraint.
 222.      * The functions prefers returning a sample size that
 223.      * generates a smaller bitmap, unless minSideLength = IImage.UNCONSTRAINED.
 224.      *
 225.      * Also, the function rounds up the sample size to a power of 2 or multiple
 226.      * of 8 because BitmapFactory only honors sample size this way.
 227.      * For example, BitmapFactory downsamples an image by 2 even though the
 228.      * request is 3. So we round up the sample size to avoid OOM.
 229.      */
 230. private static int computeSampleSize(BitmapFactory.Options options, 
 231. int minSideLength, int maxNumOfPixels) { 
 232. int initialSize = computeInitialSampleSize(options, minSideLength, 
 233.                 maxNumOfPixels); 
 234.
 235. int roundedSize; 
 236. if (initialSize <= 8 ) { 
 237.             roundedSize = 1; 
 238. while (roundedSize < initialSize) { 
 239.                 roundedSize <<= 1; 
 240.             } 
 241.         } else { 
 242.             roundedSize = (initialSize + 7) / 8 * 8; 
 243.         } 
 244.
 245. return roundedSize; 
 246.     } 
 247.
 248. private static int computeInitialSampleSize(BitmapFactory.Options options, 
 249. int minSideLength, int maxNumOfPixels) { 
 250. double w = options.outWidth; 
 251. double h = options.outHeight; 
 252.
 253. int lowerBound = (maxNumOfPixels == UNCONSTRAINED) ? 1 : 
 254.                 (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels)); 
 255. int upperBound = (minSideLength == UNCONSTRAINED) ? 128 : 
 256.                 (int) Math.min(Math.floor(w / minSideLength), 
 257.                 Math.floor(h / minSideLength)); 
 258.
 259. if (upperBound < lowerBound) { 
 260. // return the larger one when there is no overlapping zone.
 261. return lowerBound; 
 262.         } 
 263.
 264. if ((maxNumOfPixels == UNCONSTRAINED) && 
 265.                 (minSideLength == UNCONSTRAINED)) { 
 266. return 1; 
 267.         } else if (minSideLength == UNCONSTRAINED) { 
 268. return lowerBound; 
 269.         } else { 
 270. return upperBound; 
 271.         } 
 272.     } 
 273.
 274. /**
 275.      * Make a bitmap from a given Uri, minimal side length, and maximum number of pixels.
 276.      * The image data will be read from specified pfd if it's not null, otherwise
 277.      * a new input stream will be created using specified ContentResolver.
 278.      *
 279.      * Clients are allowed to pass their own BitmapFactory.Options used for bitmap decoding. A
 280.      * new BitmapFactory.Options will be created if options is null.
 281.      */
 282. private static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels, 
 283.             Uri uri, ContentResolver cr, ParcelFileDescriptor pfd, 
 284.             BitmapFactory.Options options) { 
 285.             Bitmap b = null; 
 286. try { 
 287. if (pfd == null) pfd = makeInputStream(uri, cr); 
 288. if (pfd == null) return null; 
 289. if (options == null) options = new BitmapFactory.Options(); 
 290.
 291.             FileDescriptor fd = pfd.getFileDescriptor(); 
 292.             options.inSampleSize = 1; 
 293.             options.inJustDecodeBounds = true; 
 294.             BitmapFactory.decodeFileDescriptor(fd, null, options); 
 295. if (options.mCancel || options.outWidth == -1
 296.                     || options.outHeight == -1) { 
 297. return null; 
 298.             } 
 299.             options.inSampleSize = computeSampleSize( 
 300.                     options, minSideLength, maxNumOfPixels); 
 301.             options.inJustDecodeBounds = false; 
 302.
 303.             options.inDither = false; 
 304.             options.inPreferredConfig = Bitmap.Config.ARGB_8888; 
 305.             b = BitmapFactory.decodeFileDescriptor(fd, null, options); 
 306.         } catch (OutOfMemoryError ex) { 
 307.             Log.e(TAG, "Got oom exception ", ex); 
 308. return null; 
 309.         } finally { 
 310.             closeSilently(pfd); 
 311.         } 
 312. return b; 
 313.     } 
 314.
 315. private static void closeSilently(ParcelFileDescriptor c) { 
 316. if (c == null) return; 
 317. try { 
 318.           c.close(); 
 319.       } catch (Throwable t) { 
 320. // do nothing
 321.       } 
 322.     } 
 323.
 324. private static ParcelFileDescriptor makeInputStream( 
 325.             Uri uri, ContentResolver cr) { 
 326. try { 
 327. return cr.openFileDescriptor(uri, "r"); 
 328.         } catch (IOException ex) { 
 329. return null; 
 330.         } 
 331.     } 
 332.
 333. /**
 334.      * Transform source Bitmap to targeted width and height.
 335.      */
 336. private static Bitmap transform(Matrix scaler, 
 337.             Bitmap source, 
 338. int targetWidth, 
 339. int targetHeight, 
 340. int options) { 
 341. boolean scaleUp = (options & OPTIONS_SCALE_UP) != 0; 
 342. boolean recycle = (options & OPTIONS_RECYCLE_INPUT) != 0; 
 343.
 344. int deltaX = source.getWidth() - targetWidth; 
 345. int deltaY = source.getHeight() - targetHeight; 
 346. if (!scaleUp && (deltaX < 0 || deltaY < 0)) { 
 347. /*
 348.             * In this case the bitmap is smaller, at least in one dimension,
 349.             * than the target.  Transform it by placing as much of the image
 350.             * as possible into the target and leaving the top/bottom or
 351.             * left/right (or both) black.
 352.             */
 353.             Bitmap b2 = Bitmap.createBitmap(targetWidth, targetHeight, 
 354.             Bitmap.Config.ARGB_8888); 
 355.             Canvas c = new Canvas(b2); 
 356.
 357. int deltaXHalf = Math.max(0, deltaX / 2); 
 358. int deltaYHalf = Math.max(0, deltaY / 2); 
 359.             Rect src = new Rect( 
 360.             deltaXHalf, 
 361.             deltaYHalf, 
 362.             deltaXHalf + Math.min(targetWidth, source.getWidth()), 
 363.             deltaYHalf + Math.min(targetHeight, source.getHeight())); 
 364. int dstX = (targetWidth  - src.width())  / 2; 
 365. int dstY = (targetHeight - src.height()) / 2; 
 366.             Rect dst = new Rect( 
 367.                     dstX, 
 368.                     dstY, 
 369.                     targetWidth - dstX, 
 370.                     targetHeight - dstY); 
 371.             c.drawBitmap(source, src, dst, null); 
 372. if (recycle) { 
 373.                 source.recycle(); 
 374.             } 
 375. return b2; 
 376.         } 
 377. float bitmapWidthF = source.getWidth(); 
 378. float bitmapHeightF = source.getHeight(); 
 379.
 380. float bitmapAspect = bitmapWidthF / bitmapHeightF; 
 381. float viewAspect   = (float) targetWidth / targetHeight; 
 382.
 383. if (bitmapAspect > viewAspect) { 
 384. float scale = targetHeight / bitmapHeightF; 
 385. if (scale < .9F || scale > 1F) { 
 386.                 scaler.setScale(scale, scale); 
 387.             } else { 
 388.                 scaler = null; 
 389.             } 
 390.         } else { 
 391. float scale = targetWidth / bitmapWidthF; 
 392. if (scale < .9F || scale > 1F) { 
 393.                 scaler.setScale(scale, scale); 
 394.             } else { 
 395.                 scaler = null; 
 396.             } 
 397.         } 
 398.
 399.         Bitmap b1; 
 400. if (scaler != null) { 
 401. // this is used for minithumb and crop, so we want to filter here.
 402.             b1 = Bitmap.createBitmap(source, 0, 0, 
 403.             source.getWidth(), source.getHeight(), scaler, true); 
 404.         } else { 
 405.             b1 = source; 
 406.         } 
 407.
 408. if (recycle && b1 != source) { 
 409.             source.recycle(); 
 410.         } 
 411.
 412. int dx1 = Math.max(0, b1.getWidth() - targetWidth); 
 413. int dy1 = Math.max(0, b1.getHeight() - targetHeight); 
 414.
 415.         Bitmap b2 = Bitmap.createBitmap( 
 416.                 b1, 
 417.                 dx1 / 2, 
 418.                 dy1 / 2, 
 419.                 targetWidth, 
 420.                 targetHeight); 
 421.
 422. if (b2 != b1) { 
 423. if (recycle || b1 != source) { 
 424.                 b1.recycle(); 
 425.             } 
 426.         } 
 427.
 428. return b2; 
 429.     } 
 430.
 431. /**
 432.      * SizedThumbnailBitmap contains the bitmap, which is downsampled either from
 433.      * the thumbnail in exif or the full image.
 434.      * mThumbnailData, mThumbnailWidth and mThumbnailHeight are set together only if mThumbnail
 435.      * is not null.
 436.      *
 437.      * The width/height of the sized bitmap may be different from mThumbnailWidth/mThumbnailHeight.
 438.      */
 439. private static class SizedThumbnailBitmap { 
 440. public byte[] mThumbnailData; 
 441. public Bitmap mBitmap; 
 442. public int mThumbnailWidth; 
 443. public int mThumbnailHeight; 
 444.     } 
 445.
 446. /**
 447.      * Creates a bitmap by either downsampling from the thumbnail in EXIF or the full image.
 448.      * The functions returns a SizedThumbnailBitmap,
 449.      * which contains a downsampled bitmap and the thumbnail data in EXIF if exists.
 450.      */
 451. private static void createThumbnailFromEXIF(String filePath, int targetSize, 
 452. int maxPixels, SizedThumbnailBitmap sizedThumbBitmap) { 
 453. if (filePath == null) return; 
 454.
 455.         ExifInterface exif = null; 
 456. byte [] thumbData = null; 
 457. try { 
 458.             exif = new ExifInterface(filePath); 
 459. if (exif != null) { 
 460.                 thumbData = exif.getThumbnail(); 
 461.             } 
 462.         } catch (IOException ex) { 
 463.             Log.w(TAG, ex); 
 464.         } 
 465.
 466.         BitmapFactory.Options fullOptions = new BitmapFactory.Options(); 
 467.         BitmapFactory.Options exifOptions = new BitmapFactory.Options(); 
 468. int exifThumbWidth = 0; 
 469. int fullThumbWidth = 0; 
 470.
 471. // Compute exifThumbWidth.
 472. if (thumbData != null) { 
 473.             exifOptions.inJustDecodeBounds = true; 
 474.             BitmapFactory.decodeByteArray(thumbData, 0, thumbData.length, exifOptions); 
 475.             exifOptions.inSampleSize = computeSampleSize(exifOptions, targetSize, maxPixels); 
 476.             exifThumbWidth = exifOptions.outWidth / exifOptions.inSampleSize; 
 477.         } 
 478.
 479. // Compute fullThumbWidth.
 480.         fullOptions.inJustDecodeBounds = true; 
 481.         BitmapFactory.decodeFile(filePath, fullOptions); 
 482.         fullOptions.inSampleSize = computeSampleSize(fullOptions, targetSize, maxPixels); 
 483.         fullThumbWidth = fullOptions.outWidth / fullOptions.inSampleSize; 
 484.
 485. // Choose the larger thumbnail as the returning sizedThumbBitmap.
 486. if (thumbData != null && exifThumbWidth >= fullThumbWidth) { 
 487. int width = exifOptions.outWidth; 
 488. int height = exifOptions.outHeight; 
 489.             exifOptions.inJustDecodeBounds = false; 
 490.             sizedThumbBitmap.mBitmap = BitmapFactory.decodeByteArray(thumbData, 0, 
 491.                     thumbData.length, exifOptions); 
 492. if (sizedThumbBitmap.mBitmap != null) { 
 493.                 sizedThumbBitmap.mThumbnailData = thumbData; 
 494.                 sizedThumbBitmap.mThumbnailWidth = width; 
 495.                 sizedThumbBitmap.mThumbnailHeight = height; 
 496.             } 
 497.         } else { 
 498.             fullOptions.inJustDecodeBounds = false; 
 499.             sizedThumbBitmap.mBitmap = BitmapFactory.decodeFile(filePath, fullOptions); 
 500.         } 
 501.     } 
 502. }