The image compression method explored in this paper is mainly based on Bitmap API implementation. In this paper, there are four kinds of compression methods: quality compression, sampling rate compression, Matrix scaling compression, image coding format compression

Introduction to Knowledge

Android images are mostly in the form of bitmaps, so compressing images is about reducing the size of the Bitmap. The size of a Bitmap can be calculated using the following formula: size = width * height * number of bytes per pixel. So compressing the image can be done by changing three variables in the formula.

The amount of space taken up by a single pixel varies in Android, as shown below

format What occupy a space instructions
Bitmap.Config.ALPHA_8 1B This format means that the image has only transparency and no color, and one pixel takes up eight bits
Bitmap.Config.ARGB_4444 2B This format means that transparent channel A and color R, G, B occupy 4 bits each, A total of 16 bits
Bitmap.Config.ARGB_8888 4B This format means that transparent channel A and color R, G, B occupy 8 bits each, A total of 32 bits
Bitmap.Config.RGB_565 2B This format means that the picture has no transparent channel, and the colors R, G and B occupy 5, 6 and 6 bits respectively, with a total of 16 bits

Android uses ARGB_8888 by default, so loading a 3000 * 4000 image takes up about 45MB of space by default, which is still a lot of 😂

The test code

fun showBitmapInfo(bitmap: Bitmap){
        Log.d("Tag"."Compressed image size:${bitmap.byteCount/1024/1024}MB, width:${bitmap.width}, height:${bitmap.height}")}Copy the code

The results of

The body of the

There are four types of compression

1. Quality compression

Quality compression is mainly achieved through bitmap.press (), method introduction

/ * * * *@paramFormat Format of compressed images *@paramQuality Prompt compressor, 0-100. The value is interpreted differently depending on the bitmap.pressformat. *@paramStream - An output stream that writes compressed data. *@returnTrue */ if successfully compressed to the specified stream
public boolean compress(CompressFormat format, int quality, OutputStream stream) {}Copy the code

CompressFormat stands for image compression format, and the Android source code contains five formats

The format of explain
CompressFormat.JPEG Compress to JPEG format. Quality 0 Indicates that the compression is the minimum size. 100 represents compression for maximum visual quality.
CompressFormat.PNG Compressed to PNG format. PNG is lossless, so quality is ignored.
CompressFormat.WEBP Compressed to WEBP format. Quality 0 Indicates that the compression is the minimum size. 100 represents compression for maximum visual quality. From build.version_codes.q, the value of 100 causes the file to be in lossless WEBP format. Otherwise, the file will be in lossy WEBP format
CompressFormat.WEBP_LOSSY Compressed to WEBP lossy format. Quality 0 Indicates that the compression is the minimum size. 100 represents compression for maximum visual quality.
CompressFormat.WEBP_LOSSLESS Compressed to WEBP lossless format. Quality refers to how much effort is put into compression. The value 0 indicates rapid compression, resulting in a relatively large file size. 100 means it takes more time to compress, making the file smaller.

Through the above description, we know that the commonly used compression format is JPEG, because PNG is lossless and WEBP is not long to use, the following practice to see the effect

The test code

/** ** compressed image quality */
fun getCompressBitmap(bitmap: Bitmap,quality:Int): Bitmap {
    val baos = ByteArrayOutputStream()
    bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos)
    val byte = baos.toByteArray()
    val ins = ByteArrayInputStream(byte)
    val bm = BitmapFactory.decodeStream(ins)
    ins.close()
    baos.close()
    return bm
}
Copy the code

The effect








According to the above log, you will see that quality compression does not change the size of the image in memory, because quality compression does not change the image resolution nor the individual pixel size of the image.

Then you might wonder: what’s the point of all this transformation and distortion when you can’t change the size?

A: The compress method writes a compressed version of the bitmap to the specified output stream. So it should affect the number of bytes in the output stream

validation

val baos = ByteArrayOutputStream()
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos)
val byte = baos.toByteArray()
Log.d("Tag"."quality=$quality,byte-size=${byte.size}")
Copy the code

The result is really an impact on the number of bytes in the output stream

2. Sampling rate compression

Bitmapfactory. Options has a property called inSampleSize, which is used to compress the sampling rate in the system

/** * If set to a value greater than 1, request the decoder to re-sample the original image and return a smaller image to save memory. * The sample size is the number of pixels in any dimension that correspond to a single pixel in the decoded bitmap. For example, inSampleSize == 4 * returns an image that is 1/4 of the original width/height and 1/16 of the number of pixels. Any value less than or equal to 1 is the same as 1. * Note: The decoder uses final values based on powers of 2, and any other values will be rounded to the nearest power of 2. * * /
 public int inSampleSize;
Copy the code

Go straight to code

    /** * Calculate the scaling ratio according to the specified width and height */
    fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
        val height = options.outHeight
        val width = options.outWidth
        var inSampleSize = 1
        if (height > reqHeight || width > reqWidth) {
            val heightRatio = round(height.toFloat() / reqHeight.toFloat()).toInt()
            val widthRatio = round(width.toFloat() / reqWidth.toFloat()).toInt()
            inSampleSize = if (heightRatio < widthRatio) heightRatio else widthRatio
        }
        return inSampleSize
    }

    /** * get the zoomed image */
    fun getSmallBitmap(filePath: String,reqWidth: Int,reqHeight: Int): Bitmap {
        val options = BitmapFactory.Options()
        options.inJustDecodeBounds = true // Don't load bitmap into memory, just get its basic information
        BitmapFactory.decodeFile(filePath, options)
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight)
        options.inJustDecodeBounds = false
        return BitmapFactory.decodeFile(filePath, options)
    }
Copy the code

The results of

We use a lot of sampling rate compression, because the size of the image we get may be quite large, but the size we display on the phone may not be that large, so we scale the image to the size we need.

3, zoom compression

This method mainly relies on Matrix Matrix transformation to process images. Matrix has a lot of apis for image transformation and I’m just going to use the zoom function, but you can figure it out

code

    /** * scaling by matrix */
    fun matrixBitmap(bitmap: Bitmap,scale:Float):Bitmap{
        val matrix = Matrix()
        matrix.setScale(scale,scale)
        var bm = Bitmap.createBitmap(bitmap,0.0,bitmap.width,bitmap.height,matrix,true)
        return bm
    }
Copy the code

When the zoom ratio is set to 0.5, the overall image is scaled to 1/4 of its original size

4, RGB_565 by changing the image format to achieve compression

The default format is ARGB_8888, so we can just change the options value

fun rgb565Bitmap(filePath: String):Bitmap{
        val options = BitmapFactory.Options()
        options.inPreferredConfig = Bitmap.Config.RGB_565
        var bitmap = BitmapFactory.decodeFile(filePath,options)
        return bitmap
}

Copy the code

The resulting image is half the original

conclusion

For image compression, first you can change the image format to RGB_565, reduce half of such images and then for image display can use sampling rate compression or zoom compression to we display the way to the resolution of the image size, if is to change the image upload server so you can use the quality compression way, However, this method does not support PNG images.

If you think this article is good, give it a thumbs up and a pat on the back (o^^o).

Support original, if you need to reprint please indicate the address of this article, thank you!