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?

Author: Machado, 2015-08-24

2 answers

This answer is summarized from the Article Loading large bitmaps Efficiently , which explains how to use inSampleSize to load a low-scale bitmap

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:

CreateScaledBitmap API

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.

InSampleSize flag

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.

 4
Author: Machado, 2015-08-24 18:45:03

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);
 -1
Author: Douglas Caldas, 2015-08-24 19:46:09