Compress Bitmap Based on ImageView size to avoid OOM and imageviewoom

Source: Internet
Author: User

Compress Bitmap based on the size of the ImageView to avoid OOM and imageviewoom

This article is reproduced in: http://www.cnblogs.com/tianzhijiexian/p/4254110.html

Bitmap is one of the culprit of OOM. When we download images from the network, we cannot know the exact size of the images. Therefore, to save memory, a thumbnail is usually cached on the server, improves the download speed. In addition, we can compress the image before displaying the image locally so that it fully complies with the imageview size, so that the memory will not be wasted.

I. Ideas

Idea: Calculate the size of the imageview to display bitmap, compress the bitmap according to the size of the imageview, and finally make the bitmap as big as the imageview.

 

2. Obtain the width and height of the ImageView

Int android. view. view. getWidth () // return The width of your view, in pixels. int android. view. view. getWidth () // return The height of your view, in pixels.

Through these two methods, I can get the actual size of the imageview, in the unit of pix. This method must be called after the imageview is loaded; otherwise, null is returned. If you do not know when the load will be completed, you can put it into the view. post method.

View. post (new Runnable () {@ Override public void run () {// method stub automatically generated by TODO }})

 

3. Get the compressed bitmap through BitmapFactory

3.1 BitmapFactory. Options

The BitmapFactory class provides multiple parsing methods (decodeByteArray, decodeFile, decodeResource, etc.) for creating Bitmap objects. We should select an appropriate method based on the image source.

  • The decodeFile method can be used for SD card images: Bitmap android. graphics. BitmapFactory. decodeFile (String pathName, Options opts)
  • The decodeStream method can be used for images on the Network: Bitmap android. graphics. BitmapFactory. decodeResource (Resources res, int id, Options opts)
  • The decodeResource method can be used for images in the resource file: Bitmapandroid. graphics. BitmapFactory. decodeResource (Resources res, int id, Options opts)

These methods will allocate memory for a bitmap. If your image is too large, it will easily cause bitmap. Therefore, a BitmapFactory. Options object can be passed in for configuration.

BitmapFactory.Options options = new BitmapFactory.Options();

BitmapFactory. Options has an inJustDecodeBounds attribute. If this attribute is set to true, null is returned when the preceding three methods are called. This is because it prohibits these methods from allocating memory to bitmap. So what is its use? After inJustDecodeBounds is set to true, the outWidth, outHeight, and outMimeType attributes of BitmapFactory. Options are assigned values. This technique allows us to obtain the length and width of the image and the MIME type before loading the image, so as to compress the image as needed. It is equal to not reading images, but obtaining various image parameters greatly saves memory.

BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = true;BitmapFactory.decodeResource(getResources(), R.id.myimage, options);int imageHeight = options.outHeight;int imageWidth = options.outWidth;String imageType = options.outMimeType;

 

3.2 initial computation of compression ratio

BitmapFactory. Options has an inSampleSize attribute, which can be understood as the compression ratio. After the compression ratio is set, call decodexxxx () above to get a thumbnail. For example, if inSampleSize is 4, the loaded thumbnail is 1/4 of the source image size.

To avoid OOM exceptions, we recommend that you check the size of each image when parsing it. The following factors need to be considered:

  • Estimate the memory used to load the entire image.

  • How much memory do you want to provide to load this image?

  • The actual size of the widget used to display this image

  • Screen Size and resolution of the Current Device

For example, your ImageView only has a size of 128*96 pixels, just to display a thumbnail. It is obviously not worthwhile to load a 1024*768 pixel image into the memory. For example, if we have a 2048*1536 pixel image and set the value of inSampleSize to 4, we can compress the image to 512*384 pixels. Originally, loading this image requires 13 MB of memory. After compression, only 0.75M is needed. (assume that the image is of the ARGB_8888 type, that is, each pixel occupies 4 bytes ).

Similarly, if the source image is X, we can leave x space for the thumbnail. Then inSampleSize = min (1500/100, 700/100) = 7. The thumbnail we can obtain is 1/7 of the source image.

The following code calculates the value of inSampleSize:

Public static int calculateInSampleSize (BitmapFactory. options options, int reqWidth, int reqHeight) {// the height and width of the source image final int height = options. outHeight; final int width = options. outWidth; int inSampleSize = 1; if (height> reqHeight | width> reqWidth) {// calculate the final int heightRatio = Math. round (float) height/(float) reqHeight); final int widthRatio = Math. round (float) width/(fl Oat) reqWidth); // The minimum ratio of width to height is used as the value of inSampleSize. This ensures that the width and height of the final image must be greater than or equal to the width and height of the target. InSampleSize = heightRatio <widthRatio? HeightRatio: widthRatio;} return inSampleSize ;}

However, the fact is not as good as our phenomenon. Note one of the notes for inSampleSize.

If set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory. The sample size is the number of pixels in either dimension that correspond to a single pixel in the decoded bitmap. For example, inSampleSize == 4 returns an image that is 1/4 the width/height of the original, and 1/16 the number of pixels. Any value <= 1 is treated the same as 1.

Note: the decoder uses a final value based on powers of 2, any other value will be rounded down to the nearest power of 2. 

Even if inSampleSize = 7 is set, the thumbnail is 1/4 of the source image because inSampleSize can only be an integer power of 2. If not, obtain the maximum integer power of 2 down, and search for the integer power of 2 down to 7, that is, 4. The reason for this design is probably to compress the bitmap gradient. After all, it is more efficient and convenient to compress the bitmap to the power of 2.

 

3.3 re-calculate the compression ratio

Through the above analysis, we know that bitmap can be compressed. We can compress bitmap as needed, but the bitmap obtained by compress may be larger than I need. Therefore, you have to improve the method! Then we found this method in Bitmap:

Bitmap android.graphics.Bitmap.createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter)

CreateScaledBitmap () can give us a bitmap after stretching/shrinking as required. We can use this method to narrow down the larger thumbnail we have obtained, make it fully consistent with the actual display size. Now let's write the code:

/*** @ Description calculate the compression ratio of the image ** @ param options parameter * @ param reqWidth target width * @ param reqHeight target height * @ return */private static int calculateInSampleSize (BitmapFactory. options options, int reqWidth, int reqHeight) {// the height and width of the source image final int height = options. outHeight; final int width = options. outWidth; int inSampleSize = 1; if (height> reqHeight | width> reqWidth) {// calculate the ratio of the actual width to the target width. final int halfHeight = height/2; final int halfWidth = width/2; while (halfHeight/inSampleSize)> reqHeight & (halfWidth/inSampleSize)> reqWidth) {inSampleSize * = 2 ;}} return inSampleSize ;}
/*** @ Description: compress data by passing in bitmap, obtain the standard bitmap ** @ param src * @ param dstWidth * @ param dstHeight * @ return */private static Bitmap createScaleBitmap (Bitmap src, int dstWidth, int dstHeight, int inSampleSize) {
// If the image is enlarged, the filter determines whether the image is smooth. If the image is reduced, the filter does not affect the image. Therefore, the filter parameter is set to false Bitmap dst = Bitmap. createScaledBitmap (src, dstWidth, dstHeight, false); if (src! = Dst) {// if there is no scaling, do not recycle src. recycle (); // release the Bitmap native pixel array} return dst ;}

Now we have completed the following four processes.

1. Use inJustDecodeBounds to read only the length and width of bitmap.
2. Calculate the size of inSampleSize Based on the length and width of the bitmap long section and the target thumbnail.
3. Use inSampleSize to load A thumbnail A larger than imageview.
4. Use createScaseBitmap to compress A again and generate the required thumbnail B for thumbnail.
5. Reclaim thumbnail A (if the ratio of A to B is the same, do not recycle ).

Some friends asked, why do we first generate A thumbnail A from inSampleSize, instead of directly scaling the original bitmap to the target image through createScaseBitmap?

If you want to scale the image directly from the original bitmap, you need to put the original image into the memory, which is very dangerous !!! Now A thumbnail A can be calculated, which is much smaller than the source image and can be directly loaded into the memory. This makes it safer to stretch it.

 

4. Generate tools

Now all the technical difficulties have been overcome. You can write a tool class to generate thumbnails. The tool class should contain important methods for generating thumbnails:

/*** @ Description: load images from Resources ** @ param res * @ param resId * @ param reqWidth * @ param reqHeight * @ return */public static Bitmap decodeSampledBitmapFromResource (Resources res, int resId, int reqWidth, int reqHeight) {final BitmapFactory. options options = new BitmapFactory. options (); options. inJustDecodeBounds = true; // if this parameter is set to true, the bitmap width and height BitmapFactory are obtained without memory occupation. decodeResource (res, resId, options); // The first decoding. The purpose is to read the image length and width options. inSampleSize = calculateInSampleSize (options, reqWidth, reqHeight); // call the method defined above to calculate the inSampleSize value. // use the obtained inSampleSize value to parse the image options again. inJustDecodeBounds = false; Bitmap src = BitmapFactory. decodeResource (res, resId, options); // generate a slightly larger thumbnail return createScaleBitmap (src, reqWidth, reqHeight, options. inSampleSize ); // use the bitmap to generate a thumbnail of the target size}/*** @ description to load the image from the SD card ** @ param pathName * @ param reqWidth * @ param reqHeight * @ return */public static Bitmap decodeSampledBitmapFromFile (String pathName, int reqWidth, int reqHeight) {final BitmapFactory. options options = new BitmapFactory. options (); options. inJustDecodeBounds = true; BitmapFactory. decodeFile (pathName, options); options. inSampleSize = calculateInSampleSize (options, reqWidth, reqHeight); options. inJustDecodeBounds = false; Bitmap src = BitmapFactory. decodeFile (pathName, options); return createScaleBitmap (src, reqWidth, reqHeight, options. inSampleSize );}

The process of the two methods is completely consistent. An option object is generated, the inJustDecodeBounds parameter is set, and bitmap is parsed for the first time to obtain the width and height data of bitmap, and then the ratio of the thumbnail is calculated through the width and height data. After obtaining the thumbnail ratio, we set inJustDecodeBounds to false and start to formally parse bitmap. what we get is a bitmap that may be slightly larger than the desired thumbnail. The last part is to check whether bitmap is the bitmap we want. If it is returned, it starts to stretch. In short, a bitmap that fully complies with the imageview size will be returned without wasting a little memory.

 

The complete tool class code is as follows:

Package com. kale. bitmaptest; import android. content. res. resources; import android. graphics. bitmap; import android. graphics. bitmapFactory;/*** @ author: Jack Tony * @ description: * @ web: * http://developer.android.com/training/displaying-bitmaps/load-bitmap.html * http://www.cnblogs.com/kobe8/p/3877125.html ** @ date: january 27, 2015 */public class BitmapUtils {/*** @ description calculate the image compression ratio ** @ param options parameter * @ param reqWidth target width * @ param reqHeight target height *@ return */private static int calculateInSampleSize (BitmapFactory. options options, int reqWidth, int reqHeight) {// the height and width of the source image final int height = options. outHeight; final int width = options. outWidth; int inSampleSize = 1; if (height> reqHeight | width> reqWidth) {final int halfHeight = height/2; final int halfWidth = width/2; // Calculate the largest inSampleSize value that is a power of 2 and keeps both // height and width larger than the requested height and width. while (halfHeight/inSampleSize)> reqHeight & (halfWidth/inSampleSize)> reqWidth) {inSampleSize * = 2 ;}} return inSampleSize ;} /*** @ description: compress data by passing in bitmap, obtain the standard bitmap ** @ param src * @ param dstWidth * @ param dstHeight * @ return */private static Bitmap createScaleBitmap (Bitmap src, int dstWidth, int dstHeight, int inSampleSize) {
// If the image is enlarged, the filter determines whether the image is smooth. If the image is reduced, the filter does not affect the image. Therefore, the filter parameter is set to false Bitmap dst = Bitmap. createScaledBitmap (src, dstWidth, dstHeight, false); if (src! = Dst) {// if there is no scaling, src is not recycled. recycle (); // release the native pixel array of Bitmap} return dst ;} /*** @ description: load images from Resources ** @ param res * @ param resId * @ param reqWidth * @ param reqHeight * @ return */public static Bitmap decodeSampledBitmapFromResource (Resources res, int resId, int reqWidth, int reqHeight) {final BitmapFactory. options options = new BitmapFactory. options (); options. inJustDecodeBounds = true; // if this parameter is set to true, the bitmap width and height BitmapFactory are obtained without memory occupation. decodeResource (res, resId, options); // read the image length and width to get the options of the image width and height. inSampleSize = calculateInSampleSize (options, reqWidth, reqHeight); // call the method defined above to calculate the inSampleSize value. // use the obtained inSampleSize value to parse the image options again. inJustDecodeBounds = false; Bitmap src = BitmapFactory. decodeResource (res, resId, options); // load a slightly larger thumbnail return createScaleBitmap (src, reqWidth, reqHeight, options. inSampleSize); // obtain the bitmap, further obtain the target size thumbnail}/*** @ description load the image from the SD card ** @ param pathName * @ param reqWidth * @ param reqHeight * @ return */public static Bitmap decodeSampledBitmapFromFile (String pathName, int reqWidth, int reqHeight) {final BitmapFactory. options options = new BitmapFactory. options (); options. inJustDecodeBounds = true; BitmapFactory. decodeFile (pathName, options); options. inSampleSize = calculateInSampleSize (options, reqWidth, reqHeight); options. inJustDecodeBounds = false; Bitmap src = BitmapFactory. decodeFile (pathName, options); return createScaleBitmap (src, reqWidth, reqHeight, options. inSampleSize );}}

 

V. Test

Create an imageview of 100x100dp in the layout, and then put an image in res.

5.1 layout File

Put two buttons. One button starts to load bitmap Using the conventional method without compression, and the other starts the compression algorithm.

View Code

 

5.2 java code

Public void butonListener (View v) {switch (v. getId () {case R. id. original_button: loadBitmap (false); // load the source image break; case R. id. clip_button: loadBitmap (true); // load the thumbnail break;} public void loadBitmap (boolean exactable) {int bmSize = 0; Bitmap bm = null; if (exactable) {// use the tool class to generate a thumbnail that conforms to the ImageView. Because the ImageView size is 50x50, the thumbnail size here should be the same as the size of bm = BitmapUtils. decodeSampledBitmapFromResource (getResources (), R. drawable. saber, iv. getWidth (), iv. getHeight ();} else {// directly load the source image bm = BitmapFactory. decodeResource (getResources (), R. drawable. saber);} iv. setImageBitmap (bm); bmSize + = bm. getByteCount (); // obtain the bitmap size int kb = bmSize/1024; int mb = kb/1024; Toast. makeText (this, "bitmap size =" + mb + "MB" + kb + "KB", Toast. LENGTH_LONG ). show ();}

Different methods are triggered by clicking different buttons. The obtained bitmap is put into the imageview and the current bitmap size is displayed. After running, we can find that bitmap obtained by the compression algorithm is much smaller, which saves more memory.

Result: The Source image is 1 MB +, and the thumbnail is 156kb.

Note:This compression algorithm is very effective when your imageview is much smaller than the size of the bitmap source image. However, if the bitmap and imageview are of the same size, you will find that the function of this algorithm is not so obvious, and do not think that OOM will never occur when compression is used.

PS: in actual use, our bitmap is often larger than imageview, so we recommend this method.

 

Source code download: http://download.csdn.net/detail/shark0017/8402227

 

Reference:

Http://www.cnblogs.com/kobe8/p/3877125.html

Https://developer.android.com/training/displaying-bitmaps/load-bitmap.html

Http://stormzhang.com/android/2013/11/20/android-display-bitmaps-efficiently/

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.