Android: simple understanding and use of Android learning notes Bitmap

Posted by BigMike on Fri, 14 Jan 2022 14:05:38 +0100

Android Bitmap

I Definition of Bitmap

Bitmap is one of the most important classes of image processing in Android system. It can be used to obtain the image file information, cut, rotate and zoom the image, and save the image file in a specified format.

There are two main uses of Bitmap:

  • 1. Set the background for ImageView
  • 2. Use as canvas

Corresponding to the following two methods

 imageView.setImageBitmap(Bitmap bm);
 Canvas canvas = new Canvas(Bitmap bm)

II Format of Bitmap

We know that Bitmap is a Bitmap, which is composed of pixels, which involves two problems,

  • First: how to store each pixel?
  • Second: how to compress pixels?

There are two internal enumeration classes in Bitmap: config and CompressFormat. Config is used to set color configuration information and CompressFormat is used to set compression mode

2.1 storage format


2.2 compressed format

We might as well calculate that if a Bitmap image with the same size as the mobile phone screen uses ARGB_ How much memory is required for 8888 format storage!

According to the screen size of 1024 * 768, each pixel needs 32 bits, that is, 4 bytes,

result = 1024*768*32B=25165824B=24MB

A Bitmap picture the size of a mobile phone screen should be 24M? It's not surprising that my app has been flashing back, but dozens of them have been created with the for loop and used in the sliding list.

Therefore, we must compress the image. The compression format uses the enumeration class bitmap CompressFormat

Bitmap.CompressFormat.JPEG: JPEG compression algorithm is a lossy compression format, which will change the original image quality in the compression process. The worse the image quality, the greater the damage to the original image quality, but the resulting file is relatively small, and JPEG does not support transparency. When transparency pixels are encountered, it will be filled with black background.

Bitmap.CompressFormat.PNG: using PNG algorithm, it is a lossless compression format that supports transparency.

Bitmap.CompressFormat.WEBP:WEBP is a picture file format that provides both lossy compression and lossless compression. When 14 < = API < = 17, WEBP is a lossy compression format and does not support transparency. After api18, WEBP is a lossless compression format and supports transparency. When lossy compression, under the same quality, the picture volume of WEBP format is 40% smaller than JPEG, But the encoding time is 8 times longer than JPEG. In lossless compression, lossless WEBP images are 26% smaller than PNG compression, but the compression time of WEBP is 5 times that of PNG format.

III Bitmap creation method

How do we create a Bitamap object? Google offers us two ways:

  • 1. Static method of Bitmap createBitmap(XX)
  • 2. decodeXX series static method of BitmapFactory

3.1 BitmapFactory

BitmapFactory provides a variety of static methods for creating bitmap s


decodeFile, decodeResource, decodeStream and decodebyte array are used to load a Bitmap object from the file system, resource, input stream and byte array respectively. The decodeFile and decodeResource indirectly call the decodeStream method. These four types of methods are finally implemented at the bottom of Android and correspond to several native methods of BitmapFactory class.

3.1.1, Bitmap.Options class

How to load bitmap efficiently?

Via bitmapfactory Options loads the reduced image according to a certain sampling rate and displays the reduced image in ImageView, which will reduce the memory occupation, avoid OOM to a certain extent and improve the performance of Bitmap loading.

Interpretation of sampling rate:
BitmapFactory supports all four methods of loading pictures provided by BitmapFactory Options parameter
Via bitmapfactory Options to zoom the picture, mainly using the inSampleSize parameter, that is, the sampling rate.

  • When inSampleSize is 1, the size of the sampled picture is the original size of the picture;
  • When inSampleSize is greater than 1, such as 2, the width / height of the sampled image is 1 / 2 of the size of the original image, the number of pixels is 1 / 4 of the original image, and its memory size is also 1 / 4 of the original image.

Take one × For 1024 pixel pictures, if they are stored in ARGB8888 format, the memory occupied by them is 1024 × one thousand and twenty-four × 4, that is, 4MB. If inSampleSize is 2, the memory occupation of the sampled picture is only 512 × five hundred and twelve × 4, i.e. 1MB.

The sampling rate acts on the width / height at the same time, which will cause the scaled picture size to decrease in the form of the second power of the sampling rate, that is, the scaling scale is 1 / (the second power of insamplesize)

  • For example, if inSampleSize is 4, the scale is 1 / 16.
  • There is a special case, that is, when inSampleSize is less than 1, its effect is equivalent to 1, that is, there is no scaling effect.

Parameter Description:

Examples

try {
    FileInputStream fis = new FileInputStream(filePath);
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    // Setting inJustDecodeBounds to true and then using methods such as decodeFile() will not really allocate space,
    //That is, the decoded Bitmap is null, but the width and height of the original picture can be calculated, that is, options Outwidth and options outHeight
    BitmapFactory.decodeFileDescriptor(fis.getFD(), null, options);
    float srcWidth = options.outWidth;
    float srcHeight = options.outHeight;
    int inSampleSize = 1;

    if (srcHeight > height || srcWidth > width) {
        if (srcWidth > srcHeight) {
            inSampleSize = Math.round(srcHeight / height);
        } else {
            inSampleSize = Math.round(srcWidth / width);
        }
    }

    options.inJustDecodeBounds = false;
    options.inSampleSize = inSampleSize;

    return BitmapFactory.decodeFileDescriptor(fis.getFD(), null, options);
} catch (Exception e) {
    e.printStackTrace();
}

Process Description:

  • (1) BitmapFactory Set the inJustDecodeBounds parameter of options to true and load the picture. When this parameter is true, BitmapFactory will only parse the original width and height information of the picture and will not actually load the picture. At the same time, the obtained information and the location of the picture are related to the device where the program runs
  • (2) From bitmapfactory Take out the original width and height information of the picture in options, which correspond to the outWidth and outHeight parameters.
  • (3) Calculate the sampling rate inSampleSize according to the rules of the sampling rate and the required size of the target View
  • (4) Bitmapfactory Set the inJustDecodeBounds parameter of options to false, and then reload the picture.

3.2 Bitmap static method

//Width and height are length and width units Px, and config is the storage format
static Bitmap createBitmap(int width , int height Bitmap.Config config)
// Create as like as two peas of an image.
static Bitmap createBitmap(Bitmap bm)
//Intercept a bitmap. The starting point is (x, y). Width and height correspond to width and height respectively
static Bitmap createBitmap(Bitmap bm,int x,int y,int width,int height)
//There are two more parameters than the above clipping function. Matrix: Add matrix to the clipped image. boolean filter: whether to add filtering effect to the image
static Bitmap createBitmap(Bitmap bm,int x,int y,int width,int height,Matrix m,boolean filter);
//Used to scale bitmap. Dstwidth and dstHeight are the target width and height respectively
createScaledBitmap(Bitmap bm,int dstWidth,int dstHeight,boolean filter)

These methods can be roughly divided into three categories:

  • 1. Create a new Bitmap based on an existing Bitmap
/**
* Returns an immutable subset of the original Bitmap in the form of a matrix. The new Bitmap may return the original Bitmap, or it may be copied.
* The new Bitmap has the same density and color space as the original Bitmap;
*
* @param source   Original Bitmap
* @param x        The starting coordinate in the x direction of the original Bitmap (you may only need a part in the x direction of the original Bitmap)
* @param y        The starting coordinate of the y direction in the original Bitmap (you may only need a part of the y direction of the original Bitmap)
* @param width    The width of the Bitmap (px) needs to be returned (an error will be reported if it exceeds the original Bitmap width)
* @param height   The height (px) of the Bitmap needs to be returned (an error will be reported if it exceeds the original Bitmap height)
* @param m        Matrix Type, indicating the transformation operation to be done
* @param filter   Whether filtering is required. Only matrix transformation, not only translation, is effective
*/
public static Bitmap createBitmap(@NonNull Bitmap source, int x, int y, int width, int height,
            @Nullable Matrix m, boolean filter) 

  • 2. Create an empty Bitmap from an array of pixels
/**
     * 
     * Returns an immutable bitmap with a specified width and height, each pixel value set to the corresponding value in the colors array.
     * Its initial density is determined by the given DisplayMetrics. The newly created bitmap is in the sRGB color space.
     * @param display  Displays the metrics that will display this bitmap
     * @param colors   sRGB array used to initialize pixels
     * @param offset   The number of values to skip before the first color in the color array
     * @param stride   Number of colors in the array between rows (must be > = width or < = - width)
     * @param width    Width of bitmap
     * @param height   The height of the bitmap
     * @param config   The bitmap configuration to create. If the configuration does not support alpha per pixel (for example, RGB_565),
     * Then the alpha byte in colors [] will be ignored (assuming FF)
     */
    public static Bitmap createBitmap(@NonNull DisplayMetrics display,
            @NonNull @ColorInt int[] colors, int offset, int stride,
            int width, int height, @NonNull Config config) 

  • 3. Create a scaled Bitmap
/**
* Scale the Bitmap to a new Bitmap with wide dstWidth and high dstHeight
*/
public static Bitmap createScaledBitmap(@NonNull Bitmap src, int dstWidth, int dstHeight,boolean filter)

3.3 summary of creating Bitmap

  • 1. BitmapFactory and bitmap can be used to load images Create series methods
  • 2. You can zoom the picture, obtain the picture information, configure the zoom ratio and other functions through Options
  • 3. If you need to crop or zoom the picture, you can only use the create series functions
  • 4. Pay attention to loading and creating bitmap events, and catch OOM exceptions through try catch

IV Common functions

4.1 function and its parameters

copy(Config config,boolean isMutable)
//Create a copy from the original image, but you can specify the pixel storage format of the copy
//Parameter meaning.
//  config: the storage format of pixels in memory, but you can specify the pixel storage format of the copy
//  boolean isMutable: can the pixel value of the new bitmap be modified

extractAlpha()
//The main function is to obtain the Alpha value from the bitmap and generate an image with only Alpha value. The storage format is Alpha_ eight

getByteCount()//Gets the number of bytes of the bitmap

recycle()://Unused bitmap s must be recycled in time to avoid causing oom

isRecycled()//Judge whether the bitmap is recycled. If it is recycled and cannot be used, it will cause crash

Comprehensive case demonstration

        String items[] = {"copy","extractAlpha 1","extractAlpha 2","bitmap size","recycle","isRecycled()"};
        ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item,items);
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spinner.setAdapter(adapter);
        spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                switch (position){
                    case 0:
                        //copy
                        Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.photo);
                        Bitmap copy = bm.copy(Bitmap.Config.ARGB_8888, true);
                        imageView.setImageBitmap(copy);
                        bm.recycle();
                        break;
                    case 1:
                        //extractAlpha without parameters
                        Bitmap bp = BitmapFactory.decodeResource(getResources(), R.drawable.photo);
                        Bitmap alpha = bp.extractAlpha();
                        imageView.setImageBitmap(alpha);
                        bp.recycle();
                        break;
                    case 2:
                        //extractAlpha with parameters
                        Bitmap bp1 = BitmapFactory.decodeResource(getResources(), R.drawable.photo);
                        Paint paint = new Paint();
                        BlurMaskFilter blurMaskFilter = new BlurMaskFilter(6, BlurMaskFilter.Blur.NORMAL);
                        paint.setMaskFilter(blurMaskFilter);
                        int[] offsetXY = new int[2];
                        Bitmap alpha1 = bp1.extractAlpha(paint, offsetXY);
                        imageView.setImageBitmap(alpha1);
                        break;
                    case 3:
                        //Get bitmap size
                        Bitmap b = BitmapFactory.decodeResource(getResources(), R.drawable.photo);
                        Toast.makeText(getApplicationContext(), "Picture size:"+b.getByteCount()+"byte", Toast.LENGTH_SHORT).show();
                        break;
                    case 4:
                        //Recycling bitmap
                        Bitmap b1 = BitmapFactory.decodeResource(getResources(), R.drawable.photo);
                        b1.recycle();
                        if(b1.isRecycled()){
                            Toast.makeText(getApplicationContext(), "Has been recycled", Toast.LENGTH_SHORT).show();
                        }
                        //isRecycled() determines whether it is recycled
                        break;
                }

            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {

            }
        });

4.2 common operations

1. Crop, scale, rotate, move

Matrix matrix = new Matrix();  
// zoom 
matrix.postScale(0.8f, 0.9f);  
// Left rotation, the parameter is regular right rotation
matrix.postRotate(-45);  
// Pan, modify the set again based on the last modification, and each operation is the latest, which will overwrite the last operation
matrix.postTranslate(100, 80);
// Crop and do the above
Bitmap bitmap = Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix, true);

2. Save and release

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test);
File file = new File(getFilesDir(),"test.jpg");
if(file.exists()){
    file.delete();
}
try {
    FileOutputStream outputStream=new FileOutputStream(file);
    bitmap.compress(Bitmap.CompressFormat.JPEG,90,outputStream);
    outputStream.flush();
    outputStream.close();
} catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}
//Releasing the resources of bitmap is an irreversible operation
bitmap.recycle();

3. Picture compression

public static Bitmap compressImage(Bitmap image) {
    if (image == null) {
        return null;
    }
    ByteArrayOutputStream baos = null;
    try {
        baos = new ByteArrayOutputStream();
        image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
        byte[] bytes = baos.toByteArray();
        ByteArrayInputStream isBm = new ByteArrayInputStream(bytes);
        Bitmap bitmap = BitmapFactory.decodeStream(isBm);
        return bitmap;
    } catch (OutOfMemoryError e) {
        e.printStackTrace();
    } finally {
        try {
            if (baos != null) {
                baos.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return null;
}

V common problem

5.1 relationship between bitmap and Canvas, View and Drawable

5.2 how does Bitmap cause memory overflow?

Personally, I think that Bitmap is easy to cause memory overflow because the Bitmap is large and the ARGB of a screen size_ There are 24M pictures in 8888 storage format. If there are several pictures of this magnitude in memory and they are not recycled in time, it will easily cause OOM

5.3 how to solve or avoid Bitmap memory overflow?

  • 1. We can compress bitmaps by PNG, JPEG and webp
  • 2. The unused Bitmap must be recycled in time.
  • 3. When creating Bitmap, use the try catch step to make the program more robust. Even if OOM occurs, it will not flash back, resulting in a bad use experience

5.4 conversion between bitmap and Drawable

5.4.1 converting drawable to Bitmap

public static Bitmap drawableToBitmap(Drawable drawable) {  
        // Take the length and width of drawable  
        int w = drawable.getIntrinsicWidth();  
        int h = drawable.getIntrinsicHeight();  
  
        // Take the color format of drawable  
        Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888  
                : Bitmap.Config.RGB_565;  
        // Establish corresponding bitmap  
        Bitmap bitmap = Bitmap.createBitmap(w, h, config);  
        // Create a canvas corresponding to bitmap  
        Canvas canvas = new Canvas(bitmap);  
        drawable.setBounds(0, 0, w, h);  
        // Draw drawable content onto canvas  
        drawable.draw(canvas);  
        return bitmap;  
    }  

5.4.2 converting bitmap to Drawable

Bitmap bm=Bitmap.createBitmap(xxx); 
BitmapDrawable bd= new BitmapDrawable(getResource(), bm);

reference resources

1,Little things about Bitmap image resource optimization
2,The most detailed complete solution in the history of Bitmap
3,Detailed explanation of Android Bitmap
4,How is the memory size of Android Bitmap calculated?
5,Android Bitmap explanation: everything you need to know about Bitamp

Topics: Android Android Studio bitmap