preface

In the last interview, I was asked about the method of picture compression, but I didn’t give any answer. I only vaguely remembered the quality compression and size compression, but couldn’t explain why. So today I researched the image compression method of Android, after baidu and looking at Google Docs, I temporarily know the total quality compression, size compression (or cropping), sample rate compression, image formatting, call libjpeg compression through JNI

1. Quality compression

Change the bit depth and transparency of the image while maintaining the pixels (i.e., erase (assimilate) close pixels in the image through algorithms) to reduce the quality of the compressed file. Application scenario: Compress an image and upload it to the server or save it locally

* The first parameter is the bitmap image object to compress, the second parameter is the position to save the compressed image. * The options attribute is 0-100. To compress (because PNG is lossless compression, * * @param BMP * @param file */ public static void qualityCompress(Bitmap BMP, File File) {// 0-100 100 = 0 int quality = 20; ByteArrayOutputStream baos = new ByteArrayOutputStream(); / / the compressed data stored in baos bmp.com press (Bitmap.Com pressFormat. JPEG, quality, baos); try { FileOutputStream fos = new FileOutputStream(file); fos.write(baos.toByteArray()); fos.flush(); fos.close(); } catch (Exception e) { e.printStackTrace(); }}Copy the code

2. Size compression

By reducing the pixel value per unit size, you really reduce the pixel size (by scaling the image pixels to reduce the image memory).

@param BMP @param file */ public static void sizeCompress(Bitmap BMP, File File) {// Size compression ratio, the larger the value, the smaller the size int ratio = 8; Bitmap result = bitmap.createBitmap (bmp.getwidth ()/ratio, bmp.getheight ()/ratio, Config.ARGB_8888); Canvas canvas = new Canvas(result); Rect rect = new Rect(0, 0, bmp.getWidth() / ratio, bmp.getHeight() / ratio); canvas.drawBitmap(bmp, null, rect, null); ByteArrayOutputStream baos = new ByteArrayOutputStream(); / / the compressed data stored in baos result.com press (Bitmap.Com pressFormat. JPEG, 100, baos); try { FileOutputStream fos = new FileOutputStream(file); fos.write(baos.toByteArray()); fos.flush(); fos.close(); } catch (Exception e) { e.printStackTrace(); }}Copy the code

3. Sampling rate compression

Set the image sampling rate, reduce the image pixel benefits: it is not read into the memory first, greatly reduce the memory use, and do not have to consider the release of the large image after reading into the memory. Problem: Because the sampling rate is an integer, the quality of the image cannot be guaranteed. If we need a sampling rate between 2 and 3, the image will be a little larger if we use 2, but the image quality will be significantly decreased if we use 3, which cannot fully meet my needs. GitHub’s famous image compression algorithm uses sample rate compression, and its core algorithm is to calculate this sample value

private int computeSize() { srcWidth = srcWidth % 2 == 1 ? srcWidth + 1 : srcWidth; srcHeight = srcHeight % 2 == 1 ? srcHeight + 1 : srcHeight; int longSide = Math.max(srcWidth, srcHeight); int shortSide = Math.min(srcWidth, srcHeight); float scale = ((float) shortSide / longSide); If (scale <= 1 && scale > 0.5625) {if (longSide < 1664) {return 1; } else if (longSide < 4990) { return 2; } else if (longSide > 4990 && longSide < 10240) { return 4; } else { return longSide / 1280 == 0 ? 1 : longSide / 1280; }} else if (scale <= 0.5625&&scale > 0.5) {return longSide / 1280 == 0? 1 : longSide / 1280; } else {return (int) math.ceil (longSide/(1280.0 / scale)); }}Copy the code

The flow of the Luban framework looks like this: 1. Pass in the address of the image, or the URI of the image, or the image stream 2 through the builder mode. Turn the image into a stream and add it to a list 3. Judge the image format, whether it needs to be rotated, etc. 4. ** options.insamplesize = computeSize(); * * 5. Compression

 File compress() throws IOException {
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inSampleSize = computeSize();

    Bitmap tagBitmap = BitmapFactory.decodeStream(srcImg.open(), null, options);
    ByteArrayOutputStream stream = new ByteArrayOutputStream();

    if (Checker.SINGLE.isJPG(srcImg.open())) {
      tagBitmap = rotatingImage(tagBitmap, Checker.SINGLE.getOrientation(srcImg.open()));
    }
    tagBitmap.compress(focusAlpha ? Bitmap.CompressFormat.PNG : Bitmap.CompressFormat.JPEG, 60, stream);
    tagBitmap.recycle();

    FileOutputStream fos = new FileOutputStream(tagImg);
    fos.write(stream.toByteArray());
    fos.flush();
    fos.close();
    stream.close();

    return tagImg;
  }
Copy the code

4. Use JNI to call the libjpeg library for compression

A bug in the Android image processing engine usually makes IOS photos at around 1M clearer than Android photos at 5M, all in the same environment, saving JPEG

  • (1) Process

1995 JPEG processing engine, the original image processing engine for PCS. In 2005 skia open source engine, developed a set of JPEG processing engine based on the second development. Easy to use by browser. In 2007, Google took Skia, the skia engine for Android (a neutered version), and removed a coding algorithm called Huffman. Fixed-length coding algorithm is adopted. But decoding still preserves Huffman’s algorithm, resulting in larger files after image processing.

  • (2) Reasons

At that time, because the CPU and memory on the phone are very tight performance, because Huffman algorithm is very CPU hungry, forced to use other algorithms.

  • (3) Optimization scheme

Bypass the Android Bitmap API layer to code your own implementation — fix using Huffman algorithm.

JNI development steps

  • The preparatory work
(1) http://www.ijg.org/ download JPEG engine library --libjpeg library, based on the engine to do certain development ---- to achieve their own coding, JNI development. Mk file: Android. Mk, Applicatoin.mk file: C++: xx.cpp, C: xx.cCopy the code
  • The development process
(1) Decode android bitmap and convert it to RGB data a picture information -- pixel point (ARGB), alpha remove (2) JPEG object space allocation and initialization (3) Specify compression data source (4) obtain file information (5) set parameters for compression, Examples include image size, type, color space Boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ (6) start compression -- jpeg_start_compress() (7) end compression -- jpeg_finish_compress() (8) free resourcesCopy the code
  • code
** @param image Bitmap object * @param filePath to save the specified directory * @description: */ public static void jniUltimateCompress(Bitmap image, String filePath) { Int quality = 20; // JNI calls this key nativeUtil. saveBitmap(image, quality, filePath, true); } /** * 1. @param bit Bitmap * @param fileName specifies the directory name * @param optimize whether to use haverman table data calculation quality difference 5-10 times * @Description: Public static void jniBasicCompress(Bitmap bit, String fileName, Boolean optimize) {saveBitmap(bit, DEFAULT_QUALITY, fileName, optimize); } /** * call native methods ** @param bit * @param quality * @param fileName * @param optimize * @description: function Description */ private static void saveBitmap(Bitmap bit, int quality, String fileName, boolean optimize) { compressBitmap(bit, bit.getWidth(), bit.getHeight(), quality, fileName.getBytes(), optimize); } /** * Call the method in the underlying bitherlibjni.c ** @param bit * @param w * @param h * @param quality * @param fileNameBytes * @param Optimize * @return * @description: function Description */ private static native String compressBitmap(Bitmap bit, int w, int h, int quality, byte[] fileNameBytes, boolean optimize); Static {system.loadLibrary ("jpegbither"); static {system.loadLibrary ("jpegbither"); System.loadLibrary("bitherjni"); }Copy the code

5. Picture format

Android currently uses PNG, JPEG, and WebP image formats,

PNG: lossless compressed image format, which supports Alpha channel and is used for cutting image materials on Android

Jpeg: lossy compressed image format, does not support transparent background, suitable for large image compression with rich colors, such as photos, not suitable for logo

Webp: Lossless WebP is a picture format that provides lossless compression and lossless compression at the same time, derived from video encoding format VP8, from Google’s official website, lossless WebP is 26% smaller than PNG on average, lossless WebP is 25%~34% smaller than JPEG on average, lossless WebP supports Alpha channel, lossless WebP is also supported under certain conditions, Lossy webp is supported after Android4.0 (API 14), lossless and transparent is supported after Android4.3 (API18)

Webp can effectively reduce the disk space occupied by the picture while maintaining the clarity of the picture

conclusion

Thank you guys for open source and blogging, with links attached

  • https://blog.csdn.net/chenliguan/article/details/54409442
  • Github.com/Curzibn/Lub…