Android:调整一个大的位图文件到缩放的输出文件
我有一个大的位图(比如3888×2592)在一个文件中。 现在,我想调整位图为800×533并保存到另一个文件。 我通常会通过调用Bitmap.createBitmap
方法来缩放位图,但它需要一个源位图作为第一个参数,这是我无法提供的,因为将原始图像加载到Bitmap对象当然会超出内存(例如, )。
我也无法读取位图,例如, BitmapFactory.decodeFile(file, options)
,提供一个BitmapFactory.Options.inSampleSize
,因为我想调整它的大小和宽度。 使用inSampleSize
会调整位图到972×648(如果我使用inSampleSize=4
)或778×518(如果我使用inSampleSize=5
,甚至不是2的幂)。
我还想避免在第一步中使用inSampleSize(例如972×648)读取图像,然后在第二步中将其大小改为800×533,因为与原始图像的直接resize相比质量会差一些。
总结我的问题:有没有办法读取一个大的图像文件与10MP或更多,并将其保存到一个新的图像文件,调整到一个特定的新的宽度和高度,没有得到一个OutOfMemoryexception?
我也尝试了BitmapFactory.decodeFile(file, options)
,并手动设置Options.outHeight和Options.outWidth值为800和533,但它不这样工作。
不,我喜欢有人来纠正我,但我接受了你试图作为妥协的加载/调整方法。
以下是任何人浏览的步骤:
- 计算仍然产生比目标大的图像的最大可能
inSampleSize
。 - 使用
BitmapFactory.decodeFile(file, options)
加载图像,并传入inSampleSize作为选项。 - 使用
Bitmap.createScaledBitmap()
调整到所需的尺寸。
贾斯汀答案翻译成代码(完美的作品):
private Bitmap getBitmap(String path) { Uri uri = getImageUri(path); InputStream in = null; try { final int IMAGE_MAX_SIZE = 1200000; // 1.2MP in = mContentResolver.openInputStream(uri); // Decode image size BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(in, null, options); in.close(); int scale = 1; while ((options.outWidth * options.outHeight) * (1 / Math.pow(scale, 2)) > IMAGE_MAX_SIZE) { scale++; } Log.d(TAG, "scale = " + scale + ", orig-width: " + options.outWidth + ", orig-height: " + options.outHeight); Bitmap resultBitmap = null; in = mContentResolver.openInputStream(uri); if (scale > 1) { scale--; // scale to max possible inSampleSize that still yields an image // larger than target options = new BitmapFactory.Options(); options.inSampleSize = scale; resultBitmap = BitmapFactory.decodeStream(in, null, options); // resize to desired dimensions int height = resultBitmap.getHeight(); int width = resultBitmap.getWidth(); Log.d(TAG, "1th scale operation dimenions - width: " + width + ", height: " + height); double y = Math.sqrt(IMAGE_MAX_SIZE / (((double) width) / height)); double x = (y / height) * width; Bitmap scaledBitmap = Bitmap.createScaledBitmap(resultBitmap, (int) x, (int) y, true); resultBitmap.recycle(); resultBitmap = scaledBitmap; System.gc(); } else { resultBitmap = BitmapFactory.decodeStream(in); } in.close(); Log.d(TAG, "bitmap size - width: " +resultBitmap.getWidth() + ", height: " + resultBitmap.getHeight()); return resultBitmap; } catch (IOException e) { Log.e(TAG, e.getMessage(),e); return null; }
这是“Mojo Risin's”和“Ofir's解决scheme”的结合。 这会给你一个比例调整的图像与最大宽度和最大高度的边界。
- 它只读取元数据来获取原始大小(options.inJustDecodeBounds)
- 它使用一个可resize来节省内存(itmap.createScaledBitmap)
- 它使用基于前面创build的粗糙Bitamp的精确resize的图像。
对我而言,它已经在下面的5百万像素图像上performance良好。
try { int inWidth = 0; int inHeight = 0; InputStream in = new FileInputStream(pathOfInputImage); // decode image size (decode metadata only, not the whole image) BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(in, null, options); in.close(); in = null; // save width and height inWidth = options.outWidth; inHeight = options.outHeight; // decode full image pre-resized in = new FileInputStream(pathOfInputImage); options = new BitmapFactory.Options(); // calc rought re-size (this is no exact resize) options.inSampleSize = Math.max(inWidth/dstWidth, inHeight/dstHeight); // decode full image Bitmap roughBitmap = BitmapFactory.decodeStream(in, null, options); // calc exact destination size Matrix m = new Matrix(); RectF inRect = new RectF(0, 0, roughBitmap.getWidth(), roughBitmap.getHeight()); RectF outRect = new RectF(0, 0, dstWidth, dstHeight); m.setRectToRect(inRect, outRect, Matrix.ScaleToFit.CENTER); float[] values = new float[9]; m.getValues(values); // resize bitmap Bitmap resizedBitmap = Bitmap.createScaledBitmap(roughBitmap, (int) (roughBitmap.getWidth() * values[0]), (int) (roughBitmap.getHeight() * values[4]), true); // save image try { FileOutputStream out = new FileOutputStream(pathOfOutputImage); resizedBitmap.compress(Bitmap.CompressFormat.JPEG, 80, out); } catch (Exception e) { Log.e("Image", e.getMessage(), e); } } catch (IOException e) { Log.e("Image", e.getMessage(), e); }
为什么不使用API?
int h = 48; // height in pixels int w = 48; // width in pixels Bitmap scaled = Bitmap.createScaledBitmap(largeBitmap, w, h, true);
到目前为止还没有其他的优秀答案,我所见过的最好的代码是在照片拍摄工具的文档中。
请参阅“解码缩放图像”一节。
http://developer.android.com/training/camera/photobasics.html
它提出的解决scheme是像这里的其他解决scheme一样resize的解决scheme,但它非常整齐。
为了方便起见,我已经将以下代码复制为随时可用的function。
private void setPic(String imagePath, ImageView destination) { int targetW = destination.getWidth(); int targetH = destination.getHeight(); // Get the dimensions of the bitmap BitmapFactory.Options bmOptions = new BitmapFactory.Options(); bmOptions.inJustDecodeBounds = true; BitmapFactory.decodeFile(imagePath, bmOptions); int photoW = bmOptions.outWidth; int photoH = bmOptions.outHeight; // Determine how much to scale down the image int scaleFactor = Math.min(photoW/targetW, photoH/targetH); // Decode the image file into a Bitmap sized to fill the View bmOptions.inJustDecodeBounds = false; bmOptions.inSampleSize = scaleFactor; bmOptions.inPurgeable = true; Bitmap bitmap = BitmapFactory.decodeFile(imagePath, bmOptions); destination.setImageBitmap(bitmap); }
阅读这些答案和Android文档后 ,下面的代码调整位图的大小而不将其加载到内存中:
public Bitmap getResizedBitmap(int targetW, int targetH, String imagePath) { // Get the dimensions of the bitmap BitmapFactory.Options bmOptions = new BitmapFactory.Options(); //inJustDecodeBounds = true <-- will not load the bitmap into memory bmOptions.inJustDecodeBounds = true; BitmapFactory.decodeFile(imagePath, bmOptions); int photoW = bmOptions.outWidth; int photoH = bmOptions.outHeight; // Determine how much to scale down the image int scaleFactor = Math.min(photoW/targetW, photoH/targetH); // Decode the image file into a Bitmap sized to fill the View bmOptions.inJustDecodeBounds = false; bmOptions.inSampleSize = scaleFactor; bmOptions.inPurgeable = true; Bitmap bitmap = BitmapFactory.decodeFile(imagePath, bmOptions); return(bitmap); }
当我有大的位图,我想解码他们resized我使用以下
BitmapFactory.Options options = new BitmapFactory.Options(); InputStream is = null; is = new FileInputStream(path_to_file); BitmapFactory.decodeStream(is,null,options); is.close(); is = new FileInputStream(path_to_file); // here w and h are the desired width and height options.inSampleSize = Math.max(options.outWidth/w, options.outHeight/h); // bitmap is the resized bitmap Bitmap bitmap = BitmapFactory.decodeStream(is,null,options);
这对于查看这个问题的其他人可能是有用的。 我重写了Justin的代码,以允许方法接收所需的目标大小对象。 这在使用Canvas时效果很好。 所有的功劳都应该归功于JUSTIN他的伟大的初始代码。
private Bitmap getBitmap(int path, Canvas canvas) { Resources resource = null; try { final int IMAGE_MAX_SIZE = 1200000; // 1.2MP resource = getResources(); // Decode image size BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(resource, path, options); int scale = 1; while ((options.outWidth * options.outHeight) * (1 / Math.pow(scale, 2)) > IMAGE_MAX_SIZE) { scale++; } Log.d("TAG", "scale = " + scale + ", orig-width: " + options.outWidth + ", orig-height: " + options.outHeight); Bitmap pic = null; if (scale > 1) { scale--; // scale to max possible inSampleSize that still yields an image // larger than target options = new BitmapFactory.Options(); options.inSampleSize = scale; pic = BitmapFactory.decodeResource(resource, path, options); // resize to desired dimensions int height = canvas.getHeight(); int width = canvas.getWidth(); Log.d("TAG", "1th scale operation dimenions - width: " + width + ", height: " + height); double y = Math.sqrt(IMAGE_MAX_SIZE / (((double) width) / height)); double x = (y / height) * width; Bitmap scaledBitmap = Bitmap.createScaledBitmap(pic, (int) x, (int) y, true); pic.recycle(); pic = scaledBitmap; System.gc(); } else { pic = BitmapFactory.decodeResource(resource, path); } Log.d("TAG", "bitmap size - width: " +pic.getWidth() + ", height: " + pic.getHeight()); return pic; } catch (Exception e) { Log.e("TAG", e.getMessage(),e); return null; } }
Justin的代码非常有效地减less了使用大型位图的开销。
我不知道我的解决scheme是否是最佳做法,但我通过使用inDensity
和inTargetDensity
选项实现了我所需的缩放比例加载位图。 inDensity
在初始时是不加载可绘制资源的,所以这种方法是加载非资源图像。
variablesimageUri
, maxImageSideLength
和context
是我的方法的参数。 为了清楚起见,我只发布了没有包装AsyncTask的方法实现。
ContentResolver resolver = context.getContentResolver(); InputStream is; try { is = resolver.openInputStream(imageUri); } catch (FileNotFoundException e) { Log.e(TAG, "Image not found.", e); return null; } Options opts = new Options(); opts.inJustDecodeBounds = true; BitmapFactory.decodeStream(is, null, opts); // scale the image float maxSideLength = maxImageSideLength; float scaleFactor = Math.min(maxSideLength / opts.outWidth, maxSideLength / opts.outHeight); // do not upscale! if (scaleFactor < 1) { opts.inDensity = 10000; opts.inTargetDensity = (int) ((float) opts.inDensity * scaleFactor); } opts.inJustDecodeBounds = false; try { is.close(); } catch (IOException e) { // ignore } try { is = resolver.openInputStream(imageUri); } catch (FileNotFoundException e) { Log.e(TAG, "Image not found.", e); return null; } Bitmap bitmap = BitmapFactory.decodeStream(is, null, opts); try { is.close(); } catch (IOException e) { // ignore } return bitmap;
考虑到你想调整到确切的大小,并希望保持尽可能多的质量,我认为你应该尝试这个。
- 通过调用BitmapFactory.decodeFile找出resize的图像的大小,并提供checkSizeOptions.inJustDecodeBounds
- 计算您可以在设备上使用的最大可能inSampleSize不超过内存。 bitmapSizeInBytes = 2 * width * height; 一般来说,你的图片inSampleSize = 2会很好,因为你将只需要2 * 1944×1296)=4.8Mbб这应该在内存
- 使用带有inSampleSize的BitmapFactory.decodeFile来加载位图
- 将位图缩放到确切的大小。
动机:多步缩放可以为您提供更高质量的图片,但是不能保证它比使用inSampleSize更好。 其实我觉得你也可以使用inSampleSize(如5)(不是2的pow)在一个操作中有直接缩放。 或者只是使用4,然后你可以在UI中使用该图像。 如果你把它发送到服务器 – 比你可以缩放到服务器端的确切大小,让您使用高级缩放技术。
注意:如果在步骤3中加载的位图至less大4倍(所以4 * targetWidth <宽度),则可能可以使用多个resize来实现更好的质量。 至less在genericsjava中工作,在android中你没有select指定用于缩放的插值http://today.java.net/pub/a/today/2007/04/03/perils-of-图像getscaledinstance.html
我使用这样的代码:
String filePath=Environment.getExternalStorageDirectory()+"/test_image.jpg"; BitmapFactory.Options options=new BitmapFactory.Options(); InputStream is=new FileInputStream(filePath); BitmapFactory.decodeStream(is, null, options); is.close(); is=new FileInputStream(filePath); // here w and h are the desired width and height options.inSampleSize=Math.max(options.outWidth/460, options.outHeight/288); //Max 460 x 288 is my desired... // bmp is the resized bitmap Bitmap bmp=BitmapFactory.decodeStream(is, null, options); is.close(); Log.d(Constants.TAG, "Scaled bitmap bytes, "+bmp.getRowBytes()+", width:"+bmp.getWidth()+", height:"+bmp.getHeight());
我试过原始图像是1230 x 1230,得到的位图是330 x 330。
如果尝试2590 x 3849,我会得到OutOfMemoryError。
我追溯它,它仍然抛出OutOfMemoryError在线“BitmapFactory.decodeStream(是,null,选项);”,如果原始位图太大…
上面的代码做了一点清洁。 InputStreams最后closures包装,以确保它们也closures:
*注意
input:InputStream是,int w,int h
输出:位图
try { final int inWidth; final int inHeight; final File tempFile = new File(temp, System.currentTimeMillis() + is.toString() + ".temp"); { final FileOutputStream tempOut = new FileOutputStream(tempFile); StreamUtil.copyTo(is, tempOut); tempOut.close(); } { final InputStream in = new FileInputStream(tempFile); final BitmapFactory.Options options = new BitmapFactory.Options(); try { // decode image size (decode metadata only, not the whole image) options.inJustDecodeBounds = true; BitmapFactory.decodeStream(in, null, options); } finally { in.close(); } // save width and height inWidth = options.outWidth; inHeight = options.outHeight; } final Bitmap roughBitmap; { // decode full image pre-resized final InputStream in = new FileInputStream(tempFile); try { final BitmapFactory.Options options = new BitmapFactory.Options(); // calc rought re-size (this is no exact resize) options.inSampleSize = Math.max(inWidth/w, inHeight/h); // decode full image roughBitmap = BitmapFactory.decodeStream(in, null, options); } finally { in.close(); } tempFile.delete(); } float[] values = new float[9]; { // calc exact destination size Matrix m = new Matrix(); RectF inRect = new RectF(0, 0, roughBitmap.getWidth(), roughBitmap.getHeight()); RectF outRect = new RectF(0, 0, w, h); m.setRectToRect(inRect, outRect, Matrix.ScaleToFit.CENTER); m.getValues(values); } // resize bitmap final Bitmap resizedBitmap = Bitmap.createScaledBitmap(roughBitmap, (int) (roughBitmap.getWidth() * values[0]), (int) (roughBitmap.getHeight() * values[4]), true); return resizedBitmap; } catch (IOException e) { logger.error("Error:" , e); throw new ResourceException("could not create bitmap"); }
为了以“正确”的方式缩放图像,不必跳过任何像素,就必须挂钩到图像解码器,逐行执行下采样。 Android(以及底层的Skia库)不提供这样的钩子,所以你必须自己推出。 假设你正在说jpeg图像,最好的办法是直接在C中使用libjpeg。
鉴于所涉及的复杂性,使用两步子采样然后重新调整可能是最适合图像预览types的应用程序。
这里是一个文章,采取不同的方法来resize。 它将尝试根据进程中的可用内存将最大可能的位图加载到内存中,然后执行转换。
http://bricolsoftconsulting.com/2012/12/07/handling-large-images-on-android/
如果你绝对要做一步resize,你可能会加载整个位图,如果android:largeHeap = true,但正如你可以看到这是不可取的。
从docs:android:largeHeap无论你的应用程序的进程应该用一个大的Dalvik堆创build。 这适用于为应用程序创build的所有进程。 它仅适用于加载到进程中的第一个应用程序; 如果您使用共享用户标识来允许多个应用程序使用某个进程,则他们都必须始终使用此选项,否则将会产生不可预知的结果。 大多数应用程序不应该需要这个,而应该专注于减less整体内存使用量,以提高性能。 启用此function也不能保证可用内存的固定增加,因为某些设备受到其可用内存总量的限制。
在Android开发者网站上有一篇关于这个确切问题的文章: 高效地加载大的位图
这对我有效。 该函数获取SD卡上文件的path,并返回最大可显示大小的位图。 该代码是从Ofir与一些像sd图像文件的变化,而不是一个资源,witdth和heigth从显示对象得到。
private Bitmap makeBitmap(String path) { try { final int IMAGE_MAX_SIZE = 1200000; // 1.2MP //resource = getResources(); // Decode image size BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(path, options); int scale = 1; while ((options.outWidth * options.outHeight) * (1 / Math.pow(scale, 2)) > IMAGE_MAX_SIZE) { scale++; } Log.d("TAG", "scale = " + scale + ", orig-width: " + options.outWidth + ", orig-height: " + options.outHeight); Bitmap pic = null; if (scale > 1) { scale--; // scale to max possible inSampleSize that still yields an image // larger than target options = new BitmapFactory.Options(); options.inSampleSize = scale; pic = BitmapFactory.decodeFile(path, options); // resize to desired dimensions Display display = getWindowManager().getDefaultDisplay(); Point size = new Point(); display.getSize(size); int width = size.y; int height = size.x; //int height = imageView.getHeight(); //int width = imageView.getWidth(); Log.d("TAG", "1th scale operation dimenions - width: " + width + ", height: " + height); double y = Math.sqrt(IMAGE_MAX_SIZE / (((double) width) / height)); double x = (y / height) * width; Bitmap scaledBitmap = Bitmap.createScaledBitmap(pic, (int) x, (int) y, true); pic.recycle(); pic = scaledBitmap; System.gc(); } else { pic = BitmapFactory.decodeFile(path); } Log.d("TAG", "bitmap size - width: " +pic.getWidth() + ", height: " + pic.getHeight()); return pic; } catch (Exception e) { Log.e("TAG", e.getMessage(),e); return null; } }
Bitmap yourBitmap; Bitmap resized = Bitmap.createScaledBitmap(yourBitmap, newWidth, newHeight, true);
要么:
resized = Bitmap.createScaledBitmap(yourBitmap,(int)(yourBitmap.getWidth()*0.8), (int)(yourBitmap.getHeight()*0.8), true);
这里是我使用的代码,没有任何问题在Android上的内存解码大图像。 只要我的input参数在1024×1024附近,我就可以解码大于20MB的图像。 您可以将返回的位图保存到另一个文件。 下面这个方法是另一种方法,我也用来缩放图像到一个新的位图。 随意使用这个代码,如你所愿。
/***************************************************************************** * public decode - decode the image into a Bitmap * * @param xyDimension * - The max XY Dimension before the image is scaled down - XY = * 1080x1080 and Image = 2000x2000 image will be scaled down to a * value equal or less then set value. * @param bitmapConfig * - Bitmap.Config Valid values = ( Bitmap.Config.ARGB_4444, * Bitmap.Config.RGB_565, Bitmap.Config.ARGB_8888 ) * * @return Bitmap - Image - a value of "null" if there is an issue decoding * image dimension * * @throws FileNotFoundException * - If the image has been removed while this operation is * taking place */ public Bitmap decode( int xyDimension, Bitmap.Config bitmapConfig ) throws FileNotFoundException { // The Bitmap to return given a Uri to a file Bitmap bitmap = null; File file = null; FileInputStream fis = null; InputStream in = null; // Try to decode the Uri try { // Initialize scale to no real scaling factor double scale = 1; // Get FileInputStream to get a FileDescriptor file = new File( this.imageUri.getPath() ); fis = new FileInputStream( file ); FileDescriptor fd = fis.getFD(); // Get a BitmapFactory Options object BitmapFactory.Options o = new BitmapFactory.Options(); // Decode only the image size o.inJustDecodeBounds = true; o.inPreferredConfig = bitmapConfig; // Decode to get Width & Height of image only BitmapFactory.decodeFileDescriptor( fd, null, o ); BitmapFactory.decodeStream( null ); if( o.outHeight > xyDimension || o.outWidth > xyDimension ) { // Change the scale if the image is larger then desired image // max size scale = Math.pow( 2, (int) Math.round( Math.log( xyDimension / (double) Math.max( o.outHeight, o.outWidth ) ) / Math.log( 0.5 ) ) ); } // Decode with inSampleSize scale will either be 1 or calculated value o.inJustDecodeBounds = false; o.inSampleSize = (int) scale; // Decode the Uri for real with the inSampleSize in = new BufferedInputStream( fis ); bitmap = BitmapFactory.decodeStream( in, null, o ); } catch( OutOfMemoryError e ) { Log.e( DEBUG_TAG, "decode : OutOfMemoryError" ); e.printStackTrace(); } catch( NullPointerException e ) { Log.e( DEBUG_TAG, "decode : NullPointerException" ); e.printStackTrace(); } catch( RuntimeException e ) { Log.e( DEBUG_TAG, "decode : RuntimeException" ); e.printStackTrace(); } catch( FileNotFoundException e ) { Log.e( DEBUG_TAG, "decode : FileNotFoundException" ); e.printStackTrace(); } catch( IOException e ) { Log.e( DEBUG_TAG, "decode : IOException" ); e.printStackTrace(); } // Save memory file = null; fis = null; in = null; return bitmap; } // decode
注意:除了上面的createScaledBitmap调用解码方法之外,方法与对方无关。 注意宽度和高度可以从原始图像改变。
/***************************************************************************** * public createScaledBitmap - Creates a new bitmap, scaled from an existing * bitmap. * * @param dstWidth * - Scale the width to this dimension * @param dstHeight * - Scale the height to this dimension * @param xyDimension * - The max XY Dimension before the original image is scaled * down - XY = 1080x1080 and Image = 2000x2000 image will be * scaled down to a value equal or less then set value. * @param bitmapConfig * - Bitmap.Config Valid values = ( Bitmap.Config.ARGB_4444, * Bitmap.Config.RGB_565, Bitmap.Config.ARGB_8888 ) * * @return Bitmap - Image scaled - a value of "null" if there is an issue * */ public Bitmap createScaledBitmap( int dstWidth, int dstHeight, int xyDimension, Bitmap.Config bitmapConfig ) { Bitmap scaledBitmap = null; try { Bitmap bitmap = this.decode( xyDimension, bitmapConfig ); // Create an empty Bitmap which will contain the new scaled bitmap // This scaled bitmap should be the size we want to scale the // original bitmap too scaledBitmap = Bitmap.createBitmap( dstWidth, dstHeight, bitmapConfig ); float ratioX = dstWidth / (float) bitmap.getWidth(); float ratioY = dstHeight / (float) bitmap.getHeight(); float middleX = dstWidth / 2.0f; float middleY = dstHeight / 2.0f; // Used to for scaling the image Matrix scaleMatrix = new Matrix(); scaleMatrix.setScale( ratioX, ratioY, middleX, middleY ); // Used to do the work of scaling Canvas canvas = new Canvas( scaledBitmap ); canvas.setMatrix( scaleMatrix ); canvas.drawBitmap( bitmap, middleX - bitmap.getWidth() / 2, middleY - bitmap.getHeight() / 2, new Paint( Paint.FILTER_BITMAP_FLAG ) ); } catch( IllegalArgumentException e ) { Log.e( DEBUG_TAG, "createScaledBitmap : IllegalArgumentException" ); e.printStackTrace(); } catch( NullPointerException e ) { Log.e( DEBUG_TAG, "createScaledBitmap : NullPointerException" ); e.printStackTrace(); } catch( FileNotFoundException e ) { Log.e( DEBUG_TAG, "createScaledBitmap : FileNotFoundException" ); e.printStackTrace(); } return scaledBitmap; } // End createScaledBitmap
使用以下代码调整位图大小
public static Bitmap decodeFile(File file, int reqWidth, int reqHeight){ // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(file.getPath(), options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory.decodeFile(file.getPath(), options); } private static int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw height and width of image final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { // Calculate ratios of height and width to requested height and width final int heightRatio = Math.round((float) height / (float) reqHeight); final int widthRatio = Math.round((float) width / (float) reqWidth); // Choose the smallest ratio as inSampleSize value, this will guarantee // a final image with both dimensions larger than or equal to the // requested height and width. inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; } return inSampleSize; }
以下提示/技巧也解释了这一点
http://www.codeproject.com/Tips/625810/Android-Image-Operations-Using-BitmapFactory