在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 p_w_picpath, returning a smaller p_w_picpath 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等经过人家一阵加工而成。
效率好像比第二种方法高一点点。

下面是我的例子:

1. <?xml version="1.0" encoding="utf-8"?>

2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

3. android:orientation="vertical"

4. android:layout_;fill_parent"

5. android:layout_height="fill_parent"

6. >

7.

8. <ImageView

9. android:id="@+id/p_w_picpathShow"

10. android:layout_;wrap_content"

11. android:layout_height="wrap_content"

12. />

13. <ImageView

14. android:id="@+id/p_w_picpath2"

15. android:layout_;wrap_content"

16. android:layout_height="wrap_content"

17. />

18. <TextView

19. android:id="@+id/text"

20. android:layout_;fill_parent"

21. android:layout_height="wrap_content"

22. android:text="@string/hello"

23. />

24. </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 android.app.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 android.media.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(R.id.text); 

35.

36.         imView=(ImageView) findViewById(R.id.p_w_picpathShow); 

37.         imView2=(ImageView) findViewById(R.id.p_w_picpath2); 

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 android.media; 

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 android.media.MediaMetadataRetriever; 

29. import android.media.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 p_w_picpath. 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 p_w_picpath 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 p_w_picpath 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 &lt;= 8 ) { 

237.             roundedSize = 1; 

238. while (roundedSize &lt; initialSize) { 

239.                 roundedSize &lt;&lt;= 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 &lt; 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 p_w_picpath 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 &lt; 0 || deltaY &lt; 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 p_w_picpath

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 p_w_picpath.

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 p_w_picpath.

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 &gt;= 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. }