Android Bitmap related knowledge points - type, creation, compression, conversion, etc

Keywords: Android kotlin bitmap

1, Bitmap storage format and memory calculation

When we need to optimize performance or prevent OOM, we usually use RGB_565 this type.
Because alpha_ Type 8 bitmap has only transparency and is not very useful. ARGB_4444 the display picture is not clear. ARGB_8888 takes up the most memory space.

BItmap typeMemory occupied by one pixel
ALPHA_81 byte [8 bits (A: 8)]
RGB_5652 bytes [16 bits (R: 5; G: 6; B: 5)]
ARGB_44442 bytes [16 bits (A: 4; R: 4; G: 4; B: 4)]
ARGB_88884 bytes [32 bits (A: 8; R: 8; G: 8; B: 8)]

Memory size occupied by Bitmap = width pixels × Height pixel × The amount of memory occupied by one pixel.

There are two methods to obtain the memory footprint in Bitmap:

  • getByteCount(): added by API12, representing the minimum memory required to store the pixels of the Bitmap.
  • getAllocationByteCount(): added by API19, representing the memory size allocated for BItmap in memory, instead of the getByteCount() method.

2, Loading of Bitmap

How to load a picture? BitmapFactory class provides four methods: decodeFile(), decodeResource(), decodeStream(), and decodebyte array(), which 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 methods correspond to the corresponding native methods in the underlying implementation.

decodeFile():

BitmapFactory.decodeFile(pathName: String!, opts: BitmapFactory.Options!)

decodeResource():

BitmapFactory.decodeResource(res: Resources!, resId: Int, opts: BitmapFactory.Options!)

decodeStream():

BitmapFactory.decodeStream(is: InputStream?, outPadding: Rect?, opts: BitmapFactory.Options?)

decodeByteArray():

BitmapFactory.decodeByteArray(data: ByteArray!, offset: Int,  length: Int, opts: BitmapFactory.Options!)

BitmapFactory.Options class

The above four methods have the same parameter BitmapFactory.Options, which plays a great role.
Through this parameter, you can configure the bitmap. For example, set the sampling rate to reduce the pixels of the bitmap and prevent OOM.

Some important member variables and methods in BitmapFactory.Options class:
inSampleSize: set the sampling rate and scale the picture. For example, when inSampleSize = 2, the width and height of the loaded picture are one-half of the original, the memory size is one-quarter of the original, and the value of inSampleSize needs to be an exponent of 2. When the set value is not an index of 2, the system will automatically select the nearest index of 2 to replace it.

outWidth: gets the width of the picture.

outHeight: get the height of the picture.

inJustDecodeBounds: Boolean type. When set to true, the image is not obtained and memory is not allocated, but the height (options.outHeight), width (options.outWidth) and MIME type (options.outMineType) of the image can be obtained.

Inscreen density: gets the pixel density of the current screen.

3, Compression method of Bitmap

1. Compression format

When compressing pictures, there are three compression formats:

CompressFormatdescribe
Bitmap.CompressFormat.JPEGIndicates that the image is compressed by jpeg compression algorithm. The compressed format can be. jpg or. jpeg, which is a lossy compression
Bitmap.CompressFormat.PNGIndicates that PNG compression algorithm is used for compression. It is a lossless compression format that supports transparency
Bitmap.CompressFormat.WEBPIt is a kind of image file format that supports lossy compression and lossless compression. The volume of lossless webp image is smaller than that of lossless png image

2. Sample rate compression

How to compress a picture to make its memory smaller? First, the simplest way is to set the sampling rate to reduce the size of the picture and reduce the memory occupation of the picture.
For example, the size of ImageView is 100 × 100 pixels, while the original size of the picture is 200 × 200, you only need to set the sampling rate inSampleSize to 2.
How to determine the value of the sampling rate can be calculated by the following two methods to obtain a suitable sampling rate.

fun decodeSampledBitmapFromResource(res: Resources, resId: Int, reqWidth: Int, reqHeight: Int): Bitmap{
        val options = BitmapFactory.Options()
        options.inJustDecodeBounds = true
        BitmapFactory.decodeResource(res, resId, options)
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight)
        options.inJustDecodeBounds = false
        return BitmapFactory.decodeResource(res, resId, options)
    }

    private fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
        val width = options.outWidth
        val height = options.outHeight
        println("width: $width ,height: $height")
        var inSampleSize = 1
        if (width > reqWidth || height > reqHeight) {
            val halfHeight = height / 2
            val halfWidth = width / 2
            while ((halfWidth / inSampleSize) >= reqWidth && (halfHeight / inSampleSize) >= reqHeight){
                inSampleSize *= 2
            }
        }
        println("inSampleSize: $inSampleSize")
        return inSampleSize
    }

Here, the coderesource () method is used to load images. The other three decode methods also support sample rate loading, and the processing method is similar.
Therefore, to efficiently load pictures through the sampling rate, the main steps are as follows:

  1. Set the injustdecodeboundaries parameter of BitampFactory.Options to true;
  2. Get the original width and height information of the picture from BitampFactory.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. Set the inJustDecodeBounds parameter of BitampFactory.Options to false, and then reload the picture.

3. Mass compression

Sample rate compression will change the original size of the image. If you want to compress the image size while maintaining the original image size, you can use the following method.
Through the compress(format:Bitmap.CompressFormat, quality: Int, stream: OutputStream) method of Bitmap object, you can not only compress the picture quality, but also set the format of the compressed picture. The first parameter of the method is the compressed format, the second parameter is the compression quality (0 ~ 100), 100 is the uncompressed picture quality, and the third parameter is the output stream to be written.
Note that if the format is Bitmap.CompressFormat.PNG, setting the compression quality has no effect, because PNG is lossless compression.

Please refer to the code for specific usage:

/**
     * Compress Bitmap
     * quality Is the compression mass (0 ~ 100)
     */
    fun compressBitmap(bitmap: Bitmap, quality: Int): Bitmap?{
        if (bitmap == null) {
            return null
        }
        var baos: ByteArrayOutputStream? = null
        try {
            baos = ByteArrayOutputStream()
            bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos)
            val bytes = baos.toByteArray()
            val isBm = ByteArrayInputStream(bytes)
            return BitmapFactory.decodeStream(isBm)
        } catch (e: OutOfMemoryError){
            e.printStackTrace()
        } finally {
            try {
                baos?.close()
            } catch (e: IOException){
                e.printStackTrace()
            }
        }
        return null
    }

4, Conversion between Bitmap and Drawable

 /**
     * Drawable Transfer to Bitmap
     */
    fun drawableToBitmap(drawable: Drawable): Bitmap{
        // Gets the width and height of the drawable
        val width = drawable.intrinsicWidth
        val height = drawable.intrinsicHeight
        // Gets the color type of drawable
        val config = if(drawable.opacity != PixelFormat.OPAQUE) Bitmap.Config.ARGB_8888 else Bitmap.Config.RGB_565
        // Creating bitmap objects
        val bitmap = createBitmap(width, height, config)
        // Create corresponding canvas
        val canvas = Canvas(bitmap)
        drawable.setBounds(0, 0, width, height)
        // Draw drawable content onto canvas
        drawable.draw(canvas)
        return bitmap
    }

    /**
     * bitmap Turn to drawable
     */
    fun bitmapToDrawable(resources: Resources, bitmap: Bitmap) = BitmapDrawable(resources, bitmap)

5, Working with images using Matrix

Through the Matrix, you can zoom, rotate, move, crop and other operations on the picture.
Some common methods provided by Matrix

Method nameeffect
setScale(sx: Float,sy: Float)Picture scaling, sx and sy are the scaling scale
setRotate(degrees: Float,px: Float,py: FLoat)Rotate the picture. degrees is the angle, px and py are the coordinates of the rotation center
setTranslate(dx: Float,dy: Float)Image translation, dx and dy are the moving distance
setSkew(kx: Float,ky: Float)The picture is tilted, kx and ky are the tilt scale

Usage:

/**
     * Zoom picture
     */
    fun scaleBitmap(bitmap: Bitmap): Bitmap{
        val matrix = Matrix()
        matrix.setScale(0.6f, 0.6f)
        return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
    }

Posted by p3rk5 on Wed, 20 Oct 2021 13:07:10 -0700