MasterImageCompress
Github link: github.com/xiaojigugu/…
Thread pool + queue + observer mode + builder mode to achieve multithreaded picture compression
use
- Add it in your root build.gradle at the end of repositories:
allprojects {
repositories{... maven { url'https://jitpack.io'}}}Copy the code
- Add the dependency:
dependencies {
implementation 'com. Making. Xiaojigugu: MasterImageCompress: 1.0.1'
}
Copy the code
- start
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
Copy the code
// Configure compression conditions
CompressConfig compressConfig = CompressConfig
.builder()
.keepSource(true) // Whether to keep the source file
// The compression mode is divided into TYPE_QUALITY, TYPE_PIXEL, TYPE_PIXEL_AND_QUALITY, and be careful to use separate TYPE_QUALITY (easy to OOM)!
.comPressType(CompressConfig.TYPE_PIXEL)
// Target long edge pixel, valid for TYPE_PIXEL (eg: original image resolution :7952 X 5304, compressed 7952 will end up less than 1280)
.maxPixel(1280)
// Target size up to 200KB, valid for TYPE_QUALITY
.targetSize(200 * 1024)
.format(Bitmap.CompressFormat.WEBP, Bitmap.Config.ARGB_8888) // Compress the configuration
.outputDir("storage/emulated/0/DCIM/image_compressed/") // Output directory
.build();
/ / or sentence CompressConfig CompressConfig = CompressConfig. GetDefault ();
// Add the image path to compress
List<String> images = new ArrayList<>();
for (File file1 : files) {
String path = file1.getAbsolutePath();
SystemOut.println("ImageCompressor ===> image,path=" + path);
images.add(path);
}
ImageCompressManager.builder()
.paths(images)
.config(compressConfig)
.listener(new ImageCompressListener() {
@Override
public void onStart(a) {
SystemOut.println("ImageCompressor ===> Start compression");
}
@Override
public void onSuccess(List<ImageInstance> images) {
SystemOut.println("ImageCompressor ===> Compressed successfully");
}
@Override
public void onFail(boolean allOrSingle, List<ImageInstance> images, CompressException e) {
SystemOut.println("ImageCompressor ===> Compression failed, isAll=" + allOrSingle);
}
})
.compress();
Copy the code
The efficiency of contrast
The size of the original image on the left and the compressed size on the right
Time (based on MUMU simulator environment) :
Thread Pool Description
I only have 3 core threads open, Max 5 threads (PS: how many are open depends on project requirements)
Observer model
When data in ImageCompressManager is updated, the compression utility class of Compressor will receive a notification, which enables multithreading to perform compression tasks
ImageCompressManager class:
Compressor in the class:
Picture compression principle analysis
In fact, there are a lot of code about image compression on the Internet, basically the same, including the very popular Luban framework. The original intention of writing this framework was simply that luban had too few configurable items to meet my own needs, so I wrote a new one. Android picture compression generally has three means, 1. Sampling compression 2. Start with libjpeg. If you want to use libjpeg to compress images, you’ll need to import the so package, which will increase the size of the package. For most projects, our image compression requirements are not that strict. So obviously, this approach is not worth the cost. In the end, MasterImage Press uses the first two methods and gives the developer the option to use either alone or in combination.
- Sample compression (I like to call it pixel compression) Sample compression is pixel size (resolution) sample compression core code:
BitmapFactory.Options options = new BitmapFactory.Options();
// Only the width and height are required to calculate the sampling rate
options.inJustDecodeBounds = true;
// The width and height are already available in option
BitmapFactory.decodeFile(imageInstance.getInputPath(), options);
// Calculate and set the sampling rate
options.inSampleSize = calculateSampleSize(options, compressConfig);
// Reset to decode the entire image, ready to compress
options.inJustDecodeBounds = false;
// Apply the new configuration
Bitmap bitmap = BitmapFactory.decodeFile(imageInstance.getInputPath(), options);
Copy the code
To put it bluntly, sampling compression is to calculate a reasonable zoom ratio (sampling rate) according to the width and height of the image, and then use this zoom ratio to ignore some pixels to achieve the purpose of compression of the image. Given the poor memory footprint of bitmaps, it is common to use inJustDecodeBounds to control the width and height of an image from taking edges to taking the entire image.
inJustDecodeBounds
outWidth/outHeight
width/height
Scaling ratio (sampling rate)
MasterImageCompress
() — + — ()
It’s not guaranteed
- The quality of compressed
Quality compression changes transparency, bit depth, etc. It does not change the memory footprint of the loaded Bitmap, but it can actually change the disk footprint
The core code is one sentence:
Bitmap.compress(compressConfig.getComPressFormat(), quality, byteArrayOutputStream); We do this by modifying the quality parameter. As usual, look at the source code comment:
True · Artificial Intelligence
To a given output stream to write a compressed version of the bitmap. If the returns true, then you can by pass the corresponding inputstream BitmapFactory. DecodeStream () to reconstruct the bitmap. Note: Not all formats directly support all bitmap configurations, so it is possible that bitmaps returned from the BitmapFactory will have different bit depths and/or may lose the alpha value for each pixel (e.g., JPEG only supports opaque pixels).
@param quality: Prompt compressor, values 0-100.0 for minimum size, 100 for maximum size, some lossless image formats such as PNG will ignore quality Settings. Compress () is the same as compress().
/** ** quality compression */
private void compressQuality(ImageInstance imageInstance, CompressConfig compressConfig) {
SystemOut.println("ImageCompressor ===>compressQuality()");
Bitmap inputBitmap = BitmapFactory.decodeFile(imageInstance.getInputPath());
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
// Press 90% on the way up
int quality = 90;
inputBitmap.compress(compressConfig.getComPressFormat(), quality, byteArrayOutputStream);
// If the image is still >targetSize, the compression continues (the whole process is enabled in the thread pool).
while (byteArrayOutputStream.toByteArray().length > compressConfig.getTargetSize()) {
byteArrayOutputStream.reset();
quality -= 10;
if (quality <= 10) {// In order to shorten the compression times and save time, each time the mass is reduced by 10%
quality = 5;// Limit the minimum compression to 5
}
inputBitmap.compress(compressConfig.getComPressFormat(), quality, byteArrayOutputStream);
if (quality == 5) {
// Compression ends
inputBitmap.recycle();
break; }}}Copy the code
Ok ~ end here