在android上调整位图大小的最有效内存方法? [英] Most memory efficient way to resize bitmaps on android?

查看:17
本文介绍了在android上调整位图大小的最有效内存方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在构建一个图像密集型社交应用,其中图像从服务器发送到设备.当设备的屏幕分辨率较小时,我需要在设备上调整位图的大小,以匹配其预期的显示尺寸.

I’m building an image-intensive social app where images are sent from the server to the device. When the device has smaller screen resolutions, I need to resize the bitmaps, on device, to match their intended display sizes.

问题在于使用 createScaledBitmap 导致我在调整一大群缩略图后遇到很多内存不足的错误.

The problem is that using createScaledBitmap causes me to run into a lot of out-of-memory errors after resizing a horde of thumbnail images.

在 Android 上调整位图大小的最节省内存的方法是什么?

What’s the most memory efficient way to resize bitmaps on Android?

推荐答案

这个答案总结自有效加载大位图它解释了如何使用 inSampleSize 加载缩小的位图版本.

This answer is summarised from Loading large bitmaps Efficiently which explains how to use inSampleSize to load a down-scaled bitmap version.

特别是 预缩放位图 解释了各种细节方法,如何组合它们,以及哪种方法最节省内存.

In particular Pre-scaling bitmaps explains the details of various methods, how to combine them, and which are the most memory efficient.

在 Android 上调整位图大小的主要方法有 3 种,它们具有不同的内存属性:

There are three dominant ways to resize a bitmap on Android which have different memory properties:

createScaledBitmap API

此 API 将采用现有位图,并使用您选择的确切尺寸创建新位图.

This API will take in an existing bitmap, and create a NEW bitmap with the exact dimensions you’ve selected.

从好的方面来说,您可以准确获得所需的图像尺寸(无论其外观如何).但缺点是,此 API 需要现有位图才能工作.这意味着必须先加载、解码图像并创建位图,然后才能创建新的较小版本.这在获得确切尺寸方面是理想的,但在额外的内存开销方面很糟糕.因此,对于大多数具有内存意识的应用开发者来说,这是一种破坏

On the plus side, you can get exactly the image size you’re looking for (regardless of how it looks). But the downside, is that this API requires an existing bitmap in order to work. Meaning the image would have to be loaded, decoded, and a bitmap created, before being able to create a new, smaller version. This is ideal in terms of getting your exact dimensions, but horrible in terms of additional memory overhead. As such, this is kind-of a deal breaker for most app developers who tend to be memory conscious

inSampleSize 标志

BitmapFactory.Options 有一个标记为 inSampleSize 的属性,它会在解码时调整图像的大小,以避免解码为临时位图.此处使用的此整数值将以 1/x 缩小的尺寸加载图像.例如,将 inSampleSize 设置为 2 会返回一半大小的图像,将其设置为 4 会返回大小为 1/4 的图像.基本上,图像尺寸总是比源尺寸小一些.

BitmapFactory.Options has a property noted as inSampleSize that will resize your image while decoding it, to avoid the need to decode to a temporary bitmap. This integer value used here will load an image at a 1/x reduced size. For example, setting inSampleSize to 2 returns an image that’s half the size, and Setting it to 4 returns an image that’s 1/ 4th the size. Basically image sizes will always be some power-of-two smaller than your source size.

从内存的角度来看,使用 inSampleSize 是一个非常快速的操作.实际上,它只会将图像的每个 Xth 像素解码为生成的位图.inSampleSize 有两个主要问题:

From a memory perspective, using inSampleSize is a really fast operation. Effectively, it will only decode every Xth pixel of your image into your resulting bitmap. There’s two main issues with inSampleSize though:

  • 它不会为您提供准确的分辨率.它只会将位图的大小减小 2 的幂.

  • It doesn’t give you exact resolutions. It only decreases the size of your bitmap by some power of 2.

它不会产生最佳质量的调整大小.大多数调整大小的过滤器通过读取像素块,然后对它们进行加权以产生有问题的调整大小的像素来生成好看的图像.inSampleSize 通过每隔几个像素读取一次就避免了这一切.结果是性能非常好,内存也很低,但质量会受到影响.

It doesn’t produce the best quality resize. Most resizing filters produce good looking images by reading blocks of pixels, and then weighting them to produce the resized pixel in question. inSampleSize avoids all this by just reading every few pixels. The result is quite performant, and low memory, but quality suffers.

如果您只是将图像缩小一些 pow2 大小,并且过滤不是问题,那么您找不到比 inSampleSize.

If you're only dealing with shrinking your image by some pow2 size, and filtering isn't an issue, then you can't find a more memory efficient (or performance efficient) method than inSampleSize.

inScaled、inDensity、inTargetDensity 标志

如果您需要将图像缩放到不等于 2 的幂的维度,则需要 inScaledinDensityinTargetDensityBitmapOptions 标志.设置 inScaled 标志后,系统将通过将 inTargetDensity 除以 inDensity 值来推导出应用于您的位图的缩放值.

If you need to scale an image to a dimension that’s not equal to a power of two, then you’ll need the inScaled, inDensity and inTargetDensity flags of BitmapOptions. When inScaled flag has been set, the system will derive the scaling value to apply to your bitmap by dividing the inTargetDensity by the inDensity values.

mBitmapOptions.inScaled = true;
mBitmapOptions.inDensity = srcWidth;
mBitmapOptions.inTargetDensity =  dstWidth;

// will load & resize the image to be 1/inSampleSize dimensions
mCurrentBitmap = BitmapFactory.decodeResources(getResources(), 
      mImageIDs, mBitmapOptions);

使用此方法将重新调整图像大小,并对其应用调整大小过滤器",也就是说,最终结果看起来会更好,因为在调整大小步骤中考虑了一些额外的数学运算.但请注意:额外的过滤步骤需要额外的处理时间,并且可以快速增加大图像,导致调整速度缓慢,并为过滤器本身分配额外的内存.

Using this method will re-size your image, and also apply a ‘resizing filter’ to it, that is, the end result will look better because some additional math has been taken into account during the resizing step. But be warned: that extra filter step, takes extra processing time, and can quickly add up for big images, resulting in slow resizes, and extra memory allocations for the filter itself.

由于额外的过滤开销,将此技术应用于明显大于所需尺寸的图像通常不是一个好主意.

It’s generally not a good idea to apply this technique to an image that’s significantly larger than your desired size, due to the extra filtering overhead.

魔法组合

从内存和性能的角度来看,您可以组合这些选项以获得最佳结果.(设置inSampleSizeinScaledinDensityinTargetDensity 标志)

From a memory and performance perspective, you can combine these options for the best results. (setting the inSampleSize, inScaled, inDensity and inTargetDensity flags)

inSampleSize 将首先应用于图像,使其成为比目标尺寸大的下一个 2 的幂.然后,inDensity &inTargetDensity 用于将结果缩放到您想要的精确尺寸,应用过滤操作来清理图像.

inSampleSize will first be applied to the image, getting it to the next power-of-two LARGER than your target size. Then, inDensity & inTargetDensity are used to scale the result to exact dimensions that you want, applying a filter operation to clean up the image.

将这两者结合起来是一个更快的操作,因为 inSampleSize 步骤将减少生成的基于密度的步骤应用其调整大小过滤器所需的像素数.

Combining these two is a much faster operation, since the inSampleSize step will reduce the number of pixels that the resulting Density-based step will need to apply it’s resizing filter on.

mBitmapOptions.inScaled = true;
mBitmapOptions.inSampleSize = 4;
mBitmapOptions.inDensity = srcWidth;
mBitmapOptions.inTargetDensity =  dstWidth * mBitmapOptions.inSampleSize;

// will load & resize the image to be 1/inSampleSize dimensions
mCurrentBitmap = BitmapFactory.decodeFile(fileName, mBitmapOptions);

如果您需要使图像适合特定尺寸,一些更好的过滤,那么此技术是获得正确尺寸的最佳桥梁,但需要快速、低内存足迹操作.

If you're needing to fit an image to specific dimensions, and some nicer filtering, then this technique is the best bridge to getting the right size, but done in a fast, low-memory footprint operation.

获取图像尺寸

在不解码整个图像的情况下获取图像大小为了调整位图的大小,您需要知道传入的尺寸.您可以使用 inJustDecodeBounds 标志来帮助您获取图像的尺寸,而无需实际解码像素数据.

Getting the image size without decoding the whole image In order to resize your bitmap, you’ll need to know the incoming dimensions. You can use the inJustDecodeBounds flag to help you get the dimensions of the image, w/o needing to actually decode the pixel data.

// Decode just the boundaries
mBitmapOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(fileName, mBitmapOptions);
srcWidth = mBitmapOptions.outWidth;
srcHeight = mBitmapOptions.outHeight;


//now go resize the image to the size you want

您可以先使用此标志解码大小,然后计算适当的值以缩放到您的目标分辨率.

You can use this flag to decode the size first, and then calculate the proper values for scaling to your target resolution.

这篇关于在android上调整位图大小的最有效内存方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆