MasterImageCompress

Github link: github.com/xiaojigugu/…

Thread pool + queue + observer mode + builder mode to achieve multithreaded picture compression

use

  1. Add it in your root build.gradle at the end of repositories:
	allprojects {
		repositories{... maven { url'https://jitpack.io'}}}Copy the code
  1. Add the dependency:
	dependencies {
	        implementation 'com. Making. Xiaojigugu: MasterImageCompress: 1.0.1'
	}
Copy the code
  1. 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.

  1. 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

  1. 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