Android 如何在不首先读取整个图像的情况下就地缩放流位图?
我有一个Android应用程序,它非常注重图像。我目前正在使用缩放图像到所需的大小。然而,这个方法要求我已经在内存中有了原始位图,它可以相当大Android 如何在不首先读取整个图像的情况下就地缩放流位图?,android,bitmap,streaming,scale,Android,Bitmap,Streaming,Scale,我有一个Android应用程序,它非常注重图像。我目前正在使用缩放图像到所需的大小。然而,这个方法要求我已经在内存中有了原始位图,它可以相当大 如何缩放正在下载的位图而不首先将整个内容写入本地内存或文件系统?此方法将从图像读取标题信息以确定其大小,然后读取图像并将其缩放到所需大小,而无需为原始大小的完整图像分配内存 它还使用,这似乎是一个文档稀少但理想的选项,用于在使用大量位图时防止OoM异常。更新:不再使用Inpurgable,请参阅Romain 它的工作原理是,在通过InputStream读
如何缩放正在下载的位图而不首先将整个内容写入本地内存或文件系统?此方法将从图像读取标题信息以确定其大小,然后读取图像并将其缩放到所需大小,而无需为原始大小的完整图像分配内存 它还使用,这似乎是一个文档稀少但理想的选项,用于在使用大量位图时防止OoM异常。更新:不再使用Inpurgable,请参阅Romain 它的工作原理是,在通过InputStream读取整个图像之前,使用BufferedInputStream读取图像的标题信息
/**
* Read the image from the stream and create a bitmap scaled to the desired
* size. Resulting bitmap will be at least as large as the
* desired minimum specified dimensions and will keep the image proportions
* correct during scaling.
*/
protected Bitmap createScaledBitmapFromStream( InputStream s, int minimumDesiredBitmapWith, int minimumDesiredBitmapHeight ) {
final BufferedInputStream is = new BufferedInputStream(s, 32*1024);
try {
final Options decodeBitmapOptions = new Options();
// For further memory savings, you may want to consider using this option
// decodeBitmapOptions.inPreferredConfig = Config.RGB_565; // Uses 2-bytes instead of default 4 per pixel
if( minimumDesiredBitmapWidth >0 && minimumDesiredBitmapHeight >0 ) {
final Options decodeBoundsOptions = new Options();
decodeBoundsOptions.inJustDecodeBounds = true;
is.mark(32*1024); // 32k is probably overkill, but 8k is insufficient for some jpgs
BitmapFactory.decodeStream(is,null,decodeBoundsOptions);
is.reset();
final int originalWidth = decodeBoundsOptions.outWidth;
final int originalHeight = decodeBoundsOptions.outHeight;
// inSampleSize prefers multiples of 2, but we prefer to prioritize memory savings
decodeBitmapOptions.inSampleSize= Math.max(1,Math.min(originalWidth / minimumDesiredBitmapWidth, originalHeight / minimumDesiredBitmapHeight));
}
return BitmapFactory.decodeStream(is,null,decodeBitmapOptions);
} catch( IOException e ) {
throw new RuntimeException(e); // this shouldn't happen
} finally {
try {
is.close();
} catch( IOException ignored ) {}
}
}
这是我的版本,基于@emmby solution(谢谢你!) 我已经包括了第二个阶段,在这个阶段中,你可以将缩小后的位图再次缩放,以完全匹配你想要的尺寸。 我的版本采用文件路径而不是流
protected Bitmap createScaledBitmap(String filePath, int desiredBitmapWith, int desiredBitmapHeight) throws IOException, FileNotFoundException {
BufferedInputStream imageFileStream = new BufferedInputStream(new FileInputStream(filePath));
try {
// Phase 1: Get a reduced size image. In this part we will do a rough scale down
int sampleSize = 1;
if (desiredBitmapWith > 0 && desiredBitmapHeight > 0) {
final BitmapFactory.Options decodeBoundsOptions = new BitmapFactory.Options();
decodeBoundsOptions.inJustDecodeBounds = true;
imageFileStream.mark(64 * 1024);
BitmapFactory.decodeStream(imageFileStream, null, decodeBoundsOptions);
imageFileStream.reset();
final int originalWidth = decodeBoundsOptions.outWidth;
final int originalHeight = decodeBoundsOptions.outHeight;
// inSampleSize prefers multiples of 2, but we prefer to prioritize memory savings
sampleSize = Math.max(1, Math.max(originalWidth / desiredBitmapWith, originalHeight / desiredBitmapHeight));
}
BitmapFactory.Options decodeBitmapOptions = new BitmapFactory.Options();
decodeBitmapOptions.inSampleSize = sampleSize;
decodeBitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565; // Uses 2-bytes instead of default 4 per pixel
// Get the roughly scaled-down image
Bitmap bmp = BitmapFactory.decodeStream(imageFileStream, null, decodeBitmapOptions);
// Phase 2: Get an exact-size image - no dimension will exceed the desired value
float ratio = Math.min((float)desiredBitmapWith/ (float)bmp.getWidth(), (float)desiredBitmapHeight/ (float)bmp.getHeight());
int w =(int) ((float)bmp.getWidth() * ratio);
int h =(int) ((float)bmp.getHeight() * ratio);
return Bitmap.createScaledBitmap(bmp, w,h, true);
} catch (IOException e) {
throw e;
} finally {
try {
imageFileStream.close();
} catch (IOException ignored) {
}
}
}
您可能还需要在调用BitmapFactory.decodeStream之前进行显式GC调用,正如下面的回答:在FileInputStream上使用new/close而不是mark/reset可能是有意义的。但是这个想法很好。为什么我在
is.reset()上得到java.io.IOException:Mark已经无效了
代码>我还收到了“标记已失效”错误,必须将标记更改为64k才能处理某些图像:标记(64*1024)是否可能重复