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_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!