The article directories
- preface
- RGB is introduced
- Image memory calculation
- Read bitmap size and type
- If a ‘500*500’ PNG ‘hd image is loaded in memory. How much memory should it take up?
- If the image is a local source image, is it still 0.95MB?
- Bitmap compression
- The compression principle
- Space occupied by compressed Bitmap disks
- Compress the memory occupied by Bitmap
- Use inSampleSize for compression
- Use createScaledBitmap or Matrix
- BitmapFactory. Options three-piece suit
- Bitmap local decoding
preface
Android official website processing bitmaps and efficient loading of large bitmaps in the two articles have done very clearly pointed out how to efficiently load large maps. This article is just a summary and extension of the content (such as image memory calculation, image compression, etc.).
To prevent the OOM from crashing when loading Bitmap, we first need to know:
- An image is loaded to
Bitmap
When the occupation of how memory calculation; - How to compress images to reduce memory usage when occupying too much memory;
RGB is introduced
RGB color model: The most common color model, device dependent. R, G, and B represent the red, green, and blue color channels respectively, and their values are [0,255].
RGB 8-bit color: indicates the use of 8-bit (bit) color, a total of 2^8 = 128 colors. Analogously RGB 16 bit color, RGB 24 bit color, RGB 32 bit color, the more bits you use, the more colors you can represent, the number of 24 bits of color has been a lot, called “true color”.
32 bits and 24 bits represent the same amount of color, one more transparency.
Android Bitmap uses three color formats:
- ALPHA_8 – Each pixel is 1 byte and stores transparency information, no color information.
- RGB_565 — Each pixel contains 2 bytes of color information, R 5 bits, G 6 bits, and B 5 bits, representing 2^16 colors.
- ARGB_8888-4 bytes per pixel for color information, 1 byte each for 2^24 colors, and 1 byte for transparency information.
Image memory calculation
The size of Bitmap memory is calculated as follows: Image length x image width x number of bytes occupied by a pixel.
Read bitmap size and type
The BitmapFactory class provides several decoding methods for creating bitmaps from various sources (decodeByteArray(), decodeFile(), decodeResource(), and so on). Choose the most appropriate decoding method based on your image data source. These methods attempt to allocate memory for the constructed bitmap and can easily cause OutOfMemory exceptions. Each type of decoding method has an additional signature that allows you to specify decoding Options through the BitmapFactory.options class. Setting the inJustDecodeBounds property to True at decode time avoids memory allocation, returns null for bitmap objects, but sets outWidth, outHeight, and outMimeType. This method lets you read the size and type of the image data before constructing a bitmap and allocating memory for it.
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;
Copy the code
To avoid java.lang.OutOfMemory exceptions, check the size of the bitmap before decoding it, unless you absolutely trust the source to provide you with predictable image data that can easily fit into the available memory.
If I load one in memory500 * 500
的 png
Hd pictures. How much memory should they take up?
PNG images should have an alpha channel, so bitmap. Config is ARGB_8888. Four 8-bits each occupy 32 bits. Final answer: 500 * 500 * 4 = 1000000Bytes = 0.95MB
If the image is a local source image, is it still 0.95MB?
To start with, let’s take a look at some of the basics:
To provide good graphics quality on devices with different pixel densities, you should provide multiple versions of each bitmap in your application (one version for each density level) at the appropriate resolution. Otherwise, the Android system must scale the bitmap so that it takes up the same amount of visible space on each screen, leading to scaling distortions such as blurring.
For example, if you have a graphable bitmap resource that has a size of 48×48 pixels on a medium-density screen, it should have a size of 48×48 pixels on a screen of various other densities:
- 36×36 (0.75x) – Low density (LDPI)
- 48×48 (1.0x baseline) – Medium density (MDPI)
- 72×72 (1.5x) – High density (HDPI)
- 96×96 (2.0x) – Ultra High Density (XHDPI)
- 144×144 (3.0x) – Ultra High Density (XXHDPI)
- 192×192 (4.0x) – Ultra Ultra High Density (XXXHDPI)
Then, place the generated image file in the appropriate subdirectory under RES /, and the system will automatically select the correct file according to the pixel density of the device running the application. Later, each time you reference @drawable/ XXX, the system selects the appropriate bitmap based on the DPI of the screen. If you do not provide a density-specific resource for a density, the system selects the next best match and scales it to fit the screen.
Measurement: 1520 x 2688 size 334.28KB picture, screen density of 480 mobile phone;
- On the
drawable-xxdpi
The loaded into theBitmap
The memory occupied by is16343040 (1520 * 2688 * 4)
Since the image does not need to be scaled, it only needs to be computedARGB_8888
The number of bytes occupied is fine; - On the
drawable-mdpi
The loaded into theBitmap
The memory occupied by is147087360 (1520 * 3 * 2688 * 3 * 4)
Because themdip
到xxdpi
The width and height of the image will be enlarged by 4 times;
Resources in the nodpi directory are treated as density-independent and will not be scaled.
Bitmap compression
The compression principle
Image compression is a very common development scenario in Android. There are two main compression methods: one is down-sampling compression and the other is quality compression.
- The former is to reduce the image size, change the image storage volume;
- The latter achieves the same goal by losing color accuracy without changing the image size.
Space occupied by compressed Bitmap disks
// Returns true if compressed data was successfully written to the output stream. Public Boolean compress(bitmap.pressformat format, // image compression format; Int quality,// Image compression ratio, 0-100. Zero compresses 100%, 100 means no compression; OutputStream stream) ; // Write the output stream of compressed data;Copy the code
Bitmap.CompressFormat.PNG
No matter how the second value changes, the image size will not changePNG images
The compression. becausePNG
The format is lossless, it can no longer be mass compressed,quality
This parameter has no effect and is ignored, so the final image saved to the file size does not change;CompressFormat.WEBP
The format isgoogle
Roll out the picture format and it will be better thanJPEG
More space saving. Officials say they can save money25% - 34%.
The space;
Compress the memory occupied by Bitmap
Image size is actually modified by modifying the number of pixels, the process of amplification is called up sampling, the process of reduction is called down sampling.
To know how to compress the Bitmap to reduce the memory footprint, you need to know how to calculate the memory footprint of the Bitmap. Calculating the memory footprint of an image is explained in detail in this article.
Use inSampleSize for compression
Now that the image size is known, it can be used to determine whether the full image should be loaded into memory, or whether the lower sampled version should be loaded instead. Here are some factors to consider:
- Estimated memory usage for loading the full image in memory.
- Depending on any other memory requirements of your application, the amount of memory you would like to allocate to load this image.
- The size of the target ImageView or interface component to which the image is loaded.
- Screen size and density of the current device.
For example, if a 1024×768 pixel image ends up showing up as a 128×96 pixel thumbnail in ImageView, it’s not worth loading it into memory.
To have the decoder downsample the image to load smaller versions into memory, set inSampleSize to true in the bitmapFactory.options object.
For example, an image with a resolution of 2048×1536 decoded with 4 as inSampleSize will produce a bitmap of approximately 512×384. Loading this image into memory takes 0.75MB instead of the 12MB required for the full image (assuming the bitmap is configured to ARGB_8888).
The following method is used to calculate the sample size value, which is a power of 2 based on the target width and height:
public static int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw height and width of 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; }Copy the code
Note: According to the inSampleSize documentation, the power of 2 is computed because the final value used by the decoder will be rounded down to the nearest power of 2.
To use this method, first set inJustDecodeBounds to true to decode, pass the options, then use the new inSampleSize value and set it to false to decode again:
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
Copy the code
Android inSampleSize uses the sampling algorithm of Nearest Neighbour Resampling to calculate the sampling rate, and x (x is a multiple of 2) pixels correspond to one pixel at last. For example, the sampling rate is set to 1/2, so two pixels make one pixel. The method of adjacent sampling is rough. One pixel is directly selected as the generated pixel and the other pixel is directly discarded.
Use createScaledBitmap or Matrix
Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/test.png"); Bitmap compress = Bitmap.createScaledBitmap(bitmap, bitmap.getWidth()/2, bitmap.getHeight()/2, true); // Or use matrix directly to scale, View Bitmap. CreateScaledBitmap source code is the use of matrix zooming Bitmap Bitmap. = BitmapFactory decodeFile ("/sdcard/test. The PNG "); Matrix matrix = new Matrix(); Matrix. SetScale (0.5 0.5 f, f); bm = Bitmap.createBitmap(bitmap, 0, 0, bit.getWidth(), bit.getHeight(), matrix, true);Copy the code
Bilinear Resampling is adopted in this way. Unlike neighboring sampling algorithm, which directly and rudely selects a pixel, this algorithm refers to the value of 2×2 points around the corresponding position of the source pixel and takes corresponding weight according to the relative position. After calculation, the target image is obtained.
Different sampling algorithms will produce different effects. In addition to the two commonly used sampling algorithms in Android, there are also more common ones, such as Bicubic Resampling and Lanczos Resampling. If you are not satisfied with the results of the two sampling algorithms used by Android, you can introduce other algorithms if necessary.
BitmapFactory. Options three-piece suit
inScaled
+inDensity
+inTargetDensity
When inScaled is set to true (when this flag is set), if inDensity and inTargetDensity are not 0, the Bitmap will be directly scaled to match inTargetDensity when it is loaded, rather than when it is drawn. (Loading into heap memory is already scaled, this flag is ignored in the.9 figure)
InDensity: The original width of the image to load. If this density does not match inTargetDensity, it will be scaled to the target density before returning the Bitmap. InTargetDensity: The display width of the target image, used with inScaled and inDensity to determine how to scale it before returning the Bitmap.
The second example of calculating the Bitmap size mentioned above is that the same image is loaded into different drawable-dpi file directories and the Bitmap size is different in memory because of inDensity and inTargetDensity inconsistency.
Bitmap local decoding
BitmapRegionDecoder can be used to decode rectangular regions in an image. BitmapRegionDecoder is especially useful when the original image is large and only part of the image is needed. To create a BitmapRegionDecoder, call newInstance(). Given a BitmapRegionDecoder, the user can call encodeRegio() repeatedly to get the decoded Bitmap of the specified region.
try {
inputStream = getResources().getAssets().open("qq.jpg");
BitmapRegionDecoder mRegionDecoder = BitmapRegionDecoder.newInstance(inputStream, false);
BitmapFactory.Options sOptions = new BitmapFactory.Options();
sOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
sOptions.inSampleSize = 2;
Rect mRect = new Rect();
mRect.top = 0;
mRect.left = 0;
mRect.right = 100;
mRect.bottom = 100;
Bitmap bitmap = mRegionDecoder.decodeRegion(mRect, sOptions);
//bitmap.getByteCount()=40000
} catch (IOException e) {
e.printStackTrace();
}
Copy the code
Note that the width and height of the mRect cannot be too large. Otherwise, an OOM error will occur when loading the Bitmap.