What is the most efficient way to resize bitmaps on Android?
When a device has lower screen resolutions, you need to resize the bitmaps to optimize their display on the display properly.
The problem is that using createScaledBitmap can generate a lot of out of memory errors after resizing a set of small images.
What is the most efficient way to resize bitmaps on Android?
2 answers
This answer is summarized from the Article Loading large bitmaps Efficiently , which explains how to use
inSampleSize
to load a low-scalebitmap
In particular, pre-scaling bitmaps explains the details of various methods, including showing how to combine them, and which ones are more efficient for the device's memory.
There are three dominant ways to resize a bitmap on Android, where each has different memory properties:
This API will take in an existing bitmap , and create a new bitmap with the exact dimensions you selected.
On the plus side, it is possible to get exactly the size of the image you are looking for. The downside is that this API requires a bitmap
existing to work. That is, the image would have to be loaded, decoded and a bitmap created, before be able to create a new , smaller version .
This procedure is ideal for obtaining its exact dimensions, but horrible in terms of additional memory overhead. Most application developers who tend to worry about memory avoid using this method.
BitmapFactory.Options
it has a property referenced as inSampleSize
that will resize your image and decode it at the same time, to avoid the process decoding for a temporary bitmap
.
The value integer used here will load the image reducing to half its size. Basically, the result will always be a factor of 2 times less than your source image.
When we talk about memory, inSampleSize
is an extremely fast operation. Effectively, it will decode every pixel of your image. However, there are two major problems when using inSampleSize
:
You will not have exact resolutions . This function only reduces the size of your bitmap by the factor of 2.
This function does not produce the best resize quality . Many resizing filters produce great-looking images by reading blocks of pixels, and then weighting them to produce the pixel in question.
inSampleSize
avoids all this by just reading each pixel. The result is high performance and low memory, but the quality remains to be desired.
If you are only dealing with reducing your image by a small factor, and filtering is not an issue, then you will not find such a memory efficient method as inSampleSize
.
InScaled, inDensity, inTargetDensity flags
If you need to resize an image to a dimension that is not attainable with the factor of 2, then you will need to use the flags of the BitmapOptions
: inScaled
, inDensity
and inTargetDensity
. When inScaled
is set, the system will fetch the appropriate scale value to apply to your bitmap by dividing the values of inTargetDensity
by inDensity
.
mBitmapOptions.inScaled = true;
mBitmapOptions.inDensity = srcWidth;
mBitmapOptions.inTargetDensity = dstWidth;
// este método carrega e redimensiona a imagem para a dimensão de 1/inSampleSize
mCurrentBitmap = BitmapFactory.decodeResources(getResources(),
mImageIDs, mBitmapOptions);
Using this method will resize your image and also apply a resize filter. The result will apparently be much better due to the additional math involved during the resizing step. But pay attention: this extra step requires a longer processing time, and can quickly resolve problems for large images, resulting in smaller dimensions and extra memory allocation for the filter.
It is generally not a good idea to apply this technique to an image that is significantly larger than the desired size, due to the extra memory cost of the filter.
Combining memory and Performance
In terms of memory and performance, it is possible to combine these options for best results. (Setting the flags inSampleSize
, inScaled
, inDensity
and inTargetDensity
).
inSampleSize
it will be the first to be applied to the image, making it come close to the factor of 2 times greater than the target size. So, inDensity
& inTargetDensity
they will be used to scale the result to the exact dimensions you want by applying an operation filter to clear the image.
Combining these two makes the operation much faster, since inSampleSize
will reduce the number of pixels where the filter will need to be applied.
mBitmapOptions.inScaled = true;
mBitmapOptions.inSampleSize = 4
mBitmapOptions.inDensity = srcWidth;
mBitmapOptions.inTargetDensity = dstWidth * mBitmapOptions.inSampleSize;
// carrega e redimensiona a imagem para ser da dimensão de 1/inSampleSize
mCurrentBitmap = BitmapFactory.decodeFile(fileName, mBitmapOptions);
If you need to fit an image to specific dimensions and add good image filters, then this technique is the best way to get the correct size, yet done quickly and consuming little memory during Operation.
Getting the image dimensions
Getting the size of the image without having to decode it entirely.
To be able to resize your bitmap
, you will need to know the input dimensions. You can use the inJustDecodeBounds
flag to help you pick up the dimensions of the image, no need to decode the data.
mBitmapOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(fileName, mBitmapOptions);
srcWidth = mBitmapOptions.outWidth;
srcHeight = mBitmapOptions.outHeight;
// agora redimensione a imagem para o tamanho que você deseja
You can use this flag to decode the size first, and then calculate the appropriate values for a scale the resolution of your target image.
Once you have your bitmap created, resize with the createScaledBitmap(). Ex:
// Cria o objeto
Bitmap bmp = BitmapFactory.decodeStream(url.openConnection().getInputStream());
// Redimensiona para 60 x 80 px
bmp = Bitmap.createScaledBitmap(bmp, 60, 80, false);