Performance optimization series

APP startup optimization

UI rendering optimization

Memory optimization

Image compression

Long figure optimization

Power optimization

Dex encryption

Dynamic replacement Application

Exploring the principle of hot fix for APP stability

APP continuous running process alive implementation

ProGuard compresses code and resources

APK limit compression

use

  1. Add the following code to project/build.gradle

    allprojects {
    		repositories {
    			...
    			maven { url 'https://jitpack.io'}}}Copy the code
  2. Add dependencies in app/build.gradle

    dependencies {
            implementation 'com. Making. Yangkun19921001: LIBJPEG_SAMPLE: v1.0.1'
    }
    Copy the code
  3. Compression using

    // Bitmap: indicates the bitmap to be compressed
    //q: Compression quality recommendation 30-50
    //outputFilePath: the address of the image stored after compression
    JpegUtils.native_Compress(Bitmap bitmap,int q,String outputFilePath);
    Copy the code

What is JEPG?

I believe that some of the iPhone with wechat to send pictures, clearly the size of the picture is only 1M, but the definition than Android phone 5 M image size is even clear, so why? .

When Google was developing Android, it took into account that most phones didn’t have that much configuration, so it used Skia for image processing. Of course, the bottom of the library is still using JPEG image compression processing. However, in order to be able to adapt to low-end mobile phones (the low-end here refers to the previous mobile phones with low hardware configuration, the CPU and memory on the mobile phone are very tight, poor performance), due to the Huffman algorithm is relatively CPU eating and decoding slow, forced to use other algorithms. So Skia was doing the image processing and in the lower version it didn’t turn on the Huffman algorithm.

So, what exactly is JEPG? JEPG (Joint Photographic Experts Group) is a common image format. Why do I mention JEPG here? Because open source C/C++ library is based on Huffman algorithm for image compression (libJPEG), we will focus on the next libjpeg library

Introduction of libjpeg

Libjpeg-turbo is a JPEG image codec that uses SIMD instructions (MMX, SSE2, AVX2, NEON, AltiVec) to accelerate baseline JPEG compression and decompression on x86, x86-64, ARM, and PowerPC systems. And progressive JPEG compression x86 and x86-64 systems. On such systems, libjpeg-Turbo is typically 2-6 times faster than libjpeg, all else being equal. With its highly optimized Huffman coding routines, libjpeg-Turbo can still vastly outperform libjpeg on other types of systems. In many cases, libjpeg-Turbo’s performance is comparable to that of proprietary high-speed JPEG codecs. Libjpeg-turbo implements the traditional libjpeg API as well as the less powerful but more direct TurboJPEG API. Libjpeg-turbo also has a color space extension that allows it to decompress from/to 32-bit and big-endeb pixel buffers (RGBX, XBGR, etc.), as well as a full-featured Java interface. Libjpeg-turbo was originally based on libjpeg/SIMD, an MMX-accelerated derivative of libjpeg V6B developed by Miyasaka Masaru. The TigerVNC and VirtualGL projects made significant enhancements to the codecs in 2009, and in early 2010, libjpeg-Turbo was split into a separate project with the goal of providing high-speed JPEG compression/decompression technology to a wider range of users. Developers.

Now that we have an overview of libjpeg as an image codec library, we need to prepare the environment to compile libjpeg.

Compilation preparation

System: Ubuntu 18.04 can also use I downloaded good extract code: BiYT

Libjpeg: libjepg 2.0.2

Cmake: cmake – 3.14.4 – Linux – x86_64. Tar. Gz

ndk: android-ndk-r17c

Start and prepare to compile

  1. Download libjpeg in Ubuntu

    1. wget Github.com/libjpeg-tur…

    2. Decompress the tar XVF 2.0.2.tar.gz file

    3. Compile the reference

  2. Install cmake in Ubuntu

    1. Delete the original apt-get autoremove cmake

    2. wget Github.com/Kitware/CMa…

    3. Decompress tar ZXVF cmake-3.14.3.tar.gz

    4. Creating a Soft Link

      1. The mv cmake – 3.14.3 – Linux – x86_64 / opt/cmake – 3.14.3
      2. Ln – sf/opt/cmake 3.14.3 / bin / * / usr/bin /

      Run the cmake — version command. If something like this is displayed, the installation is successful

  3. Go to the libjpeg directory and generate the shell script

    1. Vim build.sh Creates a new file

      MY_SOURCE_DIR=/home/yangkun/libjpeg-turbo-2.0.2 MY_BUILD_DIR=yangkun # android-cmake CMAKE_PATH=/opt/cmake-3.14.4/bin export PATH=${CMAKE_PATH}/bin:$PATH NDK_PATH = / home/yangkun / / android - the NDK - r17c BUILD_PLATFORM = Linux - x86_64 TOOLCHAIN_VERSION = 4.9 ANDROID_VERSION = 24 ANDROID_ARMV5_CFLAGS="-march=armv5te" ANDROID_ARMV7_CFLAGS="-march=armv7-a -mfloat-abi=softfp -mfpu=neon" # -mfpu=vfpv3-d16 -fexceptions -frtti ANDROID_ARMV8_CFLAGS="-march=armv8-a" # -mfloat-abi=softfp -mfpu=neon -fexceptions -frtti ANDROID_X86_CFLAGS="-march=i386 -mtune=intel -mssse3 -mfpmath=sse -m32" ANDROID_X86_64_CFLAGS="-march=x86-64 -msse4.2-mpopcnt-m64-mtune = Intel "# params($1:arch,$2:arch_abi,$3:host,$4:compiler,$5:cflags,$6:processor) build_bin() { echo "-------------------star build $2-------------------------" ARCH=$1 # arm arm64 x86 x86_64 ANDROID_ARCH_ABI=$2 # armeabi armeabi-v7a x86 MIPS # Final compiled installation directory PREFIX=$(PWD)/dist/${MY_LIBS_NAME}/${ANDROID_ARCH_ABI}/ HOST=$3 COMPILER=$4 PROCESSOR=$6 SYSROOT=${NDK_PATH}/platforms/android-${ANDROID_VERSION}/arch-${ARCH} CFALGS="$5" TOOLCHAIN=${NDK_PATH}/ Toolchains /${HOST}-${TOOLCHAIN_VERSION}/prebuilt/${BUILD_PLATFORM} # Build middleware BUILD_DIR=./${MY_BUILD_DIR}/${ANDROID_ARCH_ABI} export CFLAGS="$5 -Os -D__ANDROID_API__=${ANDROID_VERSION} --sysroot=${SYSROOT} \ -isystem ${NDK_PATH}/sysroot/usr/include \ -isystem ${NDK_PATH}/sysroot/usr/include/${HOST} " export LDFLAGS=-pie echo "path==>$PATH" echo "build_dir==>$BUILD_DIR" echo "ARCH==>$ARCH" echo "ANDROID_ARCH_ABI==>$ANDROID_ARCH_ABI" echo "HOST==>$HOST" echo "CFALGS==>$CFALGS" echo "COMPILER==>$COMPILER-gcc" echo Mkdir -p ${BUILD_DIR} ${BUILD_DIR} ${BUILD_DIR} ${BUILD_DIR Toolchain.cmake cat >toolchain.cmake << EOF set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR $6) set(CMAKE_C_COMPILER ${TOOLCHAIN}/bin/${COMPILER}-gcc) set(CMAKE_FIND_ROOT_PATH ${TOOLCHAIN}/${COMPILER}) EOF cmake -G"Unix Makefiles" \ -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake \ -DCMAKE_POSITION_INDEPENDENT_CODE=1 \ -DCMAKE_INSTALL_PREFIX=${PREFIX} \ -DWITH_JPEG8=1 \ ${MY_SOURCE_DIR} make clean ${BUILD_DIR} = ${BUILD_DIR} = ${BUILD_DIR} /.. / echo "-------------------$2 build end-------------------------" } # build armeabi build_bin arm armeabi arm-linux-androideabi arm-linux-androideabi "$ANDROID_ARMV5_CFLAGS" arm #build armeabi-v7a build_bin arm armeabi-v7a arm-linux-androideabi arm-linux-androideabi "$ANDROID_ARMV7_CFLAGS" arm #build arm64-v8a build_bin arm64 arm64-v8a aarch64-linux-android aarch64-linux-android "$ANDROID_ARMV8_CFLAGS" aarch64 #build x86 build_bin x86 x86 x86 i686-linux-android "$ANDROID_X86_CFLAGS" i386 #build x86_64 build_bin x86_64 x86_64 x86_64 x86_64-linux-android "$ANDROID_X86_64_CFLAGS" x86_64Copy the code
    2. If compilation encountered permission issues

      Give it executable permission chmod +x build.sh

    3. Continue to perform

    4. Compile the complete

      Here we find that we already have the static library. A and the dynamic library. So that we need

    5. Create a simple project in AndroidStudio to test whether the compression was successful

      1. Directory structure

      The red flags are important files. Include headers and libs/armeabi-v7a are the files we just compiled

      So let’s just run it and see what it looks like

      1. Compress the main code

        Jni code

        #include <jni.h>
        #include <string>
        #include ".. /include/jpeglib.h"
        #include <malloc.h>
        #include <android/bitmap.h>
        
        
        void write_JPEG_file(uint8_t *data, int w, int h, jint q, const char *path) {
        // 3.1, create jpeg compression object
            jpeg_compress_struct jcs;
            // Error callback
            jpeg_error_mgr error;
            jcs.err = jpeg_std_error(&error);
            // Create the compressed object
            jpeg_create_compress(&jcs);
        // 3.2. Specify a write binary file
            FILE *f = fopen(path, "wb");
            jpeg_stdio_dest(&jcs, f);
        // 3.3. Set compression parameters
            jcs.image_width = w;
            jcs.image_height = h;
            //bgr
            jcs.input_components = 3;
            jcs.in_color_space = JCS_RGB;
            jpeg_set_defaults(&jcs);
            // Enable Huffman
            jcs.optimize_coding = true;
            jpeg_set_quality(&jcs, q, 1);
        // 3.4. Start compression
            jpeg_start_compress(&jcs, 1);
        // 3.5. Loop to write each row of data
            int row_stride = w * 3;// Number of bytes in a row
            JSAMPROW row[1];
            while (jcs.next_scanline < jcs.image_height) {
                // Fetch a row of data
                uint8_t *pixels = data + jcs.next_scanline * row_stride;
                row[0] = pixels;
                jpeg_write_scanlines(&jcs, row, 1);
            }
        // 3.6. Compression is complete
            jpeg_finish_compress(&jcs);
        // 3.7, release jpeg object
            fclose(f);
            jpeg_destroy_compress(&jcs);
        }
        
        
        extern "C"
        JNIEXPORT void JNICALL
        Java_com_yk_libjpeg_1sample_libjpeg_JpegUtils_native_1Compress__Landroid_graphics_Bitmap_2ILjava_lang_String_2( JNIEnv *env, jclass type, jobject bitmap, jint q, jstring path_) {
            const char *path = env->GetStringUTFChars(path_, 0);
            // Obtain arGB data from bitmap
            AndroidBitmapInfo info;/ / info = new object ();
            // Get the information inside
            AndroidBitmap_getInfo(env, bitmap, &info);// void method(list)
            // Get the pixel information in the image
            uint8_t *pixels;//uint8_t char Java byte *pixels
            AndroidBitmap_lockPixels(env, bitmap, (void **) &pixels);
            // Remove his a ===> RGB from jpeg argb
            int w = info.width;
            int h = info.height;
            int color;
            // Open a block of memory for storing RGB information
            uint8_t *data = (uint8_t *) malloc(w * h * 3);// Data can store all the contents of the image
            uint8_t *temp = data;
            uint8_t r, g, b;//byte
            // Loop through each pixel of the image
            for (int i = 0; i < h; i++) {
                for (int j = 0; j < w; j++) {
                    color = *(int *) pixels;//0 to 3 bytes color4 bytes a dot
                    / / remove RGB
                    r = (color >> 16) & 0xFF;// #00rrggbb 16 0000rr 8 00rrgg
                    g = (color >> 8) & 0xFF;
                    b = color & 0xFF;
                    // Store, previously the mainstream jpeg BGR format
                    *data = b;
                    *(data + 1) = g;
                    *(data + 2) = r;
                    data += 3;
                    // The pointer skips 4 bytes
                    pixels += 4; }}// Store the new image information in a new file
            write_JPEG_file(temp, w, h, q, path);
        
            // Free memory
            free(temp);
            AndroidBitmap_unlockPixels(env, bitmap);
            env->ReleaseStringUTFChars(path_, path);
        }
        Copy the code

        The calling code

        public class JpegUtils {
            static {
                System.loadLibrary("jpeg-yk");
            }
        
            /** * A native method that is implemented by the 'native-lib' native library, * which is packaged with this application. */
            public native  static void native_Compress(Bitmap bitmap, int q, String path);
        
        }
        Copy the code

The effect

  1. To compress

        public void click(View view) {
            File input = new File(Environment.getExternalStorageDirectory(), "/girl.jpg");
            ImageView preImg = findViewById(R.id.pre);
            mNextImg = findViewById(R.id.next);
            inputBitmap = BitmapFactory.decodeFile(input.getAbsolutePath());
            preImg.setImageBitmap(inputBitmap);
           JpegUtils .native_Compress(inputBitmap, 10, Environment.getExternalStorageDirectory() + "/girl4.jpg");
            Toast.makeText(this."Execution completed", Toast.LENGTH_SHORT).show();
            String filePath = Environment.getExternalStorageDirectory() + "/girl4.jpg";
            mNextImg.setImageBitmap(BitmapFactory.decodeFile(filePath));
        }
    Copy the code
  2. Animation effects

    Compression effect: compression quality in 10 with compressed quality is still good, only a little fuzzy around, but it is recommended that the compression quality between 30 -50.

    Compression rate: the compressed image is approximately 6 times smaller than the original image.

data

For more information on this code, visit GitHub

Provides libjpeg compilation scripts

Libjpeg compiled source code and dynamic/static library in dist directory extract code: B0cs

plan

Picture optimization plan out of three articles

  1. Compile and use libjpeg
  2. Long and large graph optimization
  3. Bitmap memory manages level 3 caches