Introduction to Bitmap

A Bitmap file with a. BMP or. Dib extension. Bitmaps are Windows standard format graphics files that define images as dots (pixels), each of which can be represented by a variety of colors, including 2, 4, 8, 16, 24, and 32 bit colors. Bitmap files are in uncompressed format and require large storage space.

For example, the memory size of a 32-bit image with a resolution of 1920X1080 is 1920 x 1080 x 32 = 66355200 bit = 8294400 Byte = 8100KB = 7.91m

1M = 1024KB = 1024X1024 Byte = 1024X1024X8 bit

In Android, bitmap images are usually ARGB_8888 (ARGB stands for transparency, red, green, and blue, respectively, and each value is recorded with 8 bits, that is, a pixel will occupy 4 bytes, a total of 32 bits). To store it.

Android has four color formats for images

Color format Memory per pixel (byte) Memory per pixel (in bit)
ALPHA_8 1 8
ARGB_8888 (default) 4 32
ARGB_4444 2 16
RGB_565 2 16

ARGB_8888 placeholder calculation: 8+8+8+8 =32

1 bit 0/1; The smallest unit of

1 byte = 8bit 10101010 (0-255)00000000 — 11111111

Description:

ARGB_8888: ARGB represents transparency, red, green, and blue. Each value is recorded in 8bits, i.e., a pixel takes up 4bytes, a total of 32bits.

ARGB_4444: ARGB is for each value to be recorded in 4bits, each pixel will occupy 2bytes, a total of 16 bits.

RGB_565: R=5bit,G=6bit,B=5bit, there is no transparency, each pixel will occupy 2byte, a total of 16 bits.

ALPHA_8: this pixel only stores transparency and will occupy 1byte, a total of 8 bits.

In practical applications, ARGB_8888 and RGB_565 are recommended. If you don’t need transparency, choose RGB_565 to cut your memory footprint in half.

Bitmap recycling

Before Android3.0, bitmaps are stored in memory, and we need to reclaim the memory of the native layer and Java layer

After Android3.0, bitmaps are stored in the heap and we can simply reclaim the heap memory

It is recommended that we use the recycle () method for recycling after 3.0. This method can not be called actively because the garbage collector will automatically collect unusable Bitmap objects for recycling

Recycle ()

Releases the local object associated with this bitmap and clears references to pixel data. This does not release pixel data synchronously; It just allows it to be garbage collected if there is no other reference. The bitmap is marked “dead”, meaning that if getPixels () or setPixels () is called, it will throw an exception and nothing will be drawn. This operation is not reversible, so it should only be called if you are sure there is no further bitmap use. This is an advanced call and is usually not needed because the normal GC process will free the memory when there are no more references to the bitmap.

Introduce LruCache

LruCache is a generic class with internal LinkedHashMap to implement the cache mechanism. It provides get methods and PUT methods to obtain and add caches. The most important method, trimToSize, is used to remove the least used cache and the oldest used cache, and add the latest cache to the queue

Calculate inSampleSize

The compression ratio is obtained by the desired width and height

    public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        final int width = options.outWidth;
        final int height = options.outHeight;
        int inSampleSize = 1;
        
        if (width > reqWidth || height > reqHeight) {
            if (width > height) {
                inSampleSize = Math.round((float) height / (float) reqHeight);
            } else {
                inSampleSize = Math.round((float) width / (float) reqWidth); }}return inSampleSize;
    }
Copy the code

The thumbnail

Gets a thumbnail of the bitmap with the specified width and height

   public static Bitmap thumbnail(String  path, int maxWidth, int maxHeight) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        Bitmap bitmap = BitmapFactory.decodeFile(path,options);
        int sampleSize = calculateInSampleSize(options,maxWidth,maxHeight);
        options.inJustDecodeBounds = false;
        options.inSampleSize =sampleSize;
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        options.inPurgeable =true;
        options.inInputShareable = true;
        if(bitmap ! =null&&! bitmap.isRecycled()){ bitmap.recycle(); } bitmap = BitmapFactory.decodeFile(path,options);return bitmap;
    }
Copy the code

Save the Bitmap

Save the Bitmap to the specified file and set the image quality (described below)

public static String save(Bitmap  bitmap, Bitmap.CompressFormat format, int quality,File desFile) {
        try{
            FileOutputStream out = new FileOutputStream(desFile);
            if(bitmap.compress(format,quality,out)){
                out.flush();
                out.close();
            }
            if(bitmap! =null&&! bitmap.isRecycled()){ bitmap.recycle(); }return  desFile.getAbsolutePath();
        }catch (FileNotFoundException e){
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
Copy the code

Save to SD card

public static String save(Bitmap  bitmap, Bitmap.CompressFormat format, int quality,Context context) {
        if(! Environment.getExternalStorageState() .equals(Environment.MEDIA_MOUNTED)){return null;
        }
        File dir = new File(Environment.getExternalStorageDirectory()+
        "/"+context.getPackageName());
        if(! dir.exists()){ dir.mkdir(); } File desFile = new File(dir, UUID.randomUUID().toString());return save(bitmap,format,quality,desFile);
    }
Copy the code

The compression

First, quality compression

The method used is Bitmap compress();

Quality compression method: change the depth and transparency of the picture under the premise of maintaining the pixel, so as to achieve the purpose of compression picture:

1. The size of the bitmap image does not change

2. Bytes. length decreases as quality decreases.

This is suitable for passing binary image data, such as sharing images, to pass in binary data, limited to 500KB.

public static Bitmap compressImage(Bitmap image) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayInputStream Bitmap = null; image.compress(Bitmap.CompressFormat.JPEG, 5, baos); byte[] bytes = baos.toByteArray(); / / the baos compressed data storage to bitmap = BitmapFactory in bytes. The decodeByteArray (bytes, 0, bytes. Length);if(bitmap ! = null) { loga(bitmap, baos.toByteArray()); }returnbitmap; } /** * @param image target image * @param maxSize Maximum image size, * @return*/ public static Bitmap compressImage(Bitmap image,long maxSize) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayInputStream Bitmap = null; / / quality compression method, the value of the options is 0-100 to 100 indicates the quality of the original pictures here, without compression, the compressed data stored in baos image.com press (Bitmap.Com pressFormat. JPEG, 100, baos); int options = 90; // Check if the compressed image is larger than maxSizewhile(baos.tobytearray ().length > maxSize) {// Reset baos baos.reset(); / / compression options here %, the compressed data stored in baos image.com press (Bitmap.Com pressFormat. JPEG, options, baos); // Decrease by 10 each time, stop when it is 1, and decrease by 1 when options<10if(options == 1){
            break;
        }else if (options <= 10) {
            options -= 1;
        } else{ options -= 10; } } byte[] bytes = baos.toByteArray(); / / the baos compressed data storage to bitmap = BitmapFactory in bytes. The decodeByteArray (bytes, 0, bytes. Length);if(bitmap ! = null) { loga(bitmap, baos.toByteArray()); }return bitmap;
}
Copy the code

Second, sampling rate compression

If inSampleSize (int) is set to n, the width and height of inSampleSize are 1/n, and the memory size is reduced. The above code used options. InJustDecodeBounds = true; Why is this compression method called sampling rate compression because I am fixed to sample the data?

Because with inJustDecodeBounds, first get the width and height of the image (this process is sampling).

Then set the inSampleSize value dynamically based on the width and height obtained. When inJustDecodeBounds is set to true, BitmapFactory will return a null Bitmap when decoding images through decodeResource or decodeFile to avoid memory allocation. But it can return the Bitmap width, height, and MimeType.

usage

int inSampleSize = getScaling(bitmap); bitmap = samplingRateCompression(path,inSampleSize);

private Bitmap samplingRateCompression(String path, int scaling) {

BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = scaling;

Bitmap bitmap = BitmapFactory.decodeFile(path, options);

int size = (bitmap.getByteCount() / 1024 / 1024);

Log.i("wechat"."Compressed image size" + (bitmap.getByteCount() / 1024 / 1024)
        + "M width is" + bitmap.getWidth() + "Altitude is" + bitmap.getHeight());
returnbitmap; } /** * get the scale * @param bitmap * @return*/ private int getScaling(Bitmap Bitmap) {// set the Targetsize (with the width of the pixel) int Targetsize = 1500; int width = bitmap.getWidth(); int height = bitmap.getHeight(); Int handleValue = width > height? width : height; Int I = 1;while(handleValue / i > Targetsize) { i++; }}Copy the code

Three, zoom compression, the effect and method 2

In Android, Matrix is used for image scaling, rotation, translation, miter and other transformations. Matrix is a 3*3 Matrix, and its values are as follows:

|scaleX, skewX, translateX|

|skewY, scaleY, translateY|

| 0, 0, scale |

Matrix provides some methods to control image transformation:

  • SetTranslate (float dx,float dy) : Controls the Matrix for displacement.
  • SetSkew (float Kx,float KY) : Controls the Matrix to tilt. Kx and ky are the ratios of X and Y directions.
  • SetSkew (float Kx,float KY,float px,float py) : Controls the skew ratio of Matrix with PX and py as axes, and Kx and KY as X and Y directions.
  • SetRotate (float degrees) : controls the Matrix to rotate the depress Angle, and the axis is (0,0).
  • SetRotate (float degrees,float px,float py) : Controls the Matrix to rotate the depress Angle with the axis of (px,py).
  • SetScale (float sx,float SY) : setScale(float sx,float SY) : setScale(float sx,float SY) : setScale(float sx,float SY) : setScale(float sx,float SY) : setScale(float sx,float SY) : set Matrix to scale.
  • SetScale (float sx,float sy,float px,float py) : setScale(float sx,float sy,float py) : setScale(float sx,float sy,float py) : setScale(float sx,float sy,float py) :

Note: the above set methods all have corresponding POST and pre methods. When Matrix calls a series of set,pre, and POST methods, it can be regarded as inserting these methods into a queue. Of course, the calls are executed in the order from beginning to end in the queue. Pre indicates that a method is inserted at the head of the queue and POST indicates that a method is inserted at the end of the queue. Set, on the other hand, empties the current queue and is always in the middle of the queue. After a set is executed: the pre method is always inserted at the front of the set, and the POST method is always inserted at the back of the set

private Bitmap ScalingCompression(Bitmap bitmap) { Matrix matrix = new Matrix(); Matrix. SetScale (0.25 0.25 f, f); Bitmap bm = bitmap.createBitmap (Bitmap, 0, 0, bitmap.getwidth (), bitmap.getheight (), matrix,true);
    Log.i("wechat"."Compressed image size" + (bm.getByteCount() / 1024 / 1024)
        + "M width is" + bm.getWidth() + "Altitude is" + bm.getHeight());
    return bm;
}
Copy the code

Fourth, the Bitmap. The Config

Original image size: 4M—- converted to File– Bitmap size

  • ALPHA_8 — — — — — — — — — — 6.77 M — — — — — — — 45 M
  • ARGB_4444 — — — — — — — 9.37 M — — — — — — – 22 M
  • ARGB_8888 — — — — — — — 6.77 M — — — — — — – 45 M
  • RGB_565 — — — — — — — — — — – 8.13 M — — — — — — — 22 M

The default is usually ARGB8888, which is the most memory intensive, because a pixel is 32 bits, 8 bits =1 byte, so a pixel is 4 bytes of memory. If you have a 480×800 image in ARGB8888 format, it will take up 1500KB of memory.

private Bitmap bitmapConfig(String path) {
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inPreferredConfig = Bitmap.Config.RGB_565;

    Bitmap bm = BitmapFactory.decodeFile(path, options);
    Log.i("wechat"."Compressed image size" + (bm.getByteCount() / 1024f / 1024f)
    + "M width is" + bm.getWidth() + "Altitude is" + bm.getHeight());
    return bm;
} 
Copy the code

createScaledBitmap

methods

Bitmap.createScaledBitmap(src, dstWidth, dstHeight, filter);

Parameter Description:

  • SRC is used to build the source bitmap of the subset
  • DstWidth Expected width of the new bitmap
  • DstHeight The desired height of the new bitmap
  • If filter is true, anti-aliasing is selected

Add anti – aliasing knowledge

In Android, right now, I know of two instances of jagged conditions.

  • 1. When we draw a bitmap with Canvas, if we select the bitmap, the bitmap will appear jagged.
  • 2. When using a View’s RotateAnimation to animate, if the View contains a large number of graphics, it will also appear jagged.

Let’s consider these two cases separately. Drawing a bitmap on a Canvas. When drawing bitmaps on Canvas, we generally use the drawBitmap family of functions. Each of these functions has a Paint parameter that we use to prevent aliasing. As follows:

First in your constructor, you need to create a Paint. Paint mPaint = new Paint (); Then, you need to set two parameters:

  • 1)mPaint.setAntiAlias(Boolean aa);
  • 2) mPaint. SetBitmapFilter (true).

The first function is used to prevent jagged edges (true: the edges are relatively clear and the jagged marks are not obvious, false: the words are not full, beautiful, and not very clear).

The second function is used to filter the bitmap.

Finally, while drawing, the drawBitmap function is called, simply passing in the entire Paint.

Sometimes, when you do RotateAnimation, you’ll see that the sawtooth is there again. At this point, since you can’t control the drawing of the bitmap, you have to use other methods to prevent aliasing. Also, if you draw a lot of bitmaps. You don’t want to pass a Paint for every bitmap drawn. Other times, when it is not possible to control the drawing of each window, you need to do this for the entire Canvas.

  • 1) In your constructor, create a Paint filter. PaintFlagsDrawFilter mSetfil = new PaintFlagsDrawFilter(0, Paint.FILTERBITMAPFLAG); The first argument is the flag bit you want to clear, and the second argument is the flag bit you want to set. Set to filter the bitmap.
  • 2) When you are drawing, if it is a View then onDraw, if it is a ViewGroup then call the following function in dispatchDraw. canvas.setDrawFilter( mSetfil );

In addition, in Drawable and its subclasses, there is a function called setFilterBitmap that can be used to filter bitmaps so that when you select Drawable, it will have an anti-aliasing effect.

private Bitmap createScaledBitmap(Bitmap bitmap) {
    Bitmap bm = Bitmap.createScaledBitmap(bitmap, 200, 200, true);
    Log.i("wechat"."Compressed image size" + (bm.getByteCount() / 1024) + "KB width is"
        + bm.getWidth() + "Altitude is" + bm.getHeight());
    return bm;
}
Copy the code

Vi. Auxiliary methods (of the above methods) :

The method of getting a bitmap by path

  • 1, use BitmapFactory parse file, convert Bitmap Bitmap = bitmapFactory.decodefile (path);
  • 2, write their own decoding, converted to Bitmap process, also need to use BitmapFactory. DecodeByteArray (buf, 0, len); The code is as follows:
private Bitmap getBitmapByPath(String path) {
if(! new File(path).exists()) { System.err.println("GetBitmapByPath: File doesn't exist");
    returnnull; } byte[] buf = new byte[1024 * 1024]; // 1M Bitmap bitmap = null; try { FileInputStream fis = new FileInputStream(path); int len = fis.read(buf, 0, buf.length); bitmap = BitmapFactory.decodeByteArray(buf, 0, len); // If the bitmap is empty, parsing failsif (bitmap == null) {
        System.out.println("File length :" + len);
        System.err.println("path: " + path + "Unresolvable!!");
    }
} catch (Exception e) {
    e.printStackTrace();
}
return bitmap;
}
Copy the code

Save the picture

private void savaPictrue(Bitmap bitmap) {
    File file = new File("storage/emulated/0/DCIM/Camera/test.jpg");
    FileOutputStream stream = null;
    try {
        stream = new FileOutputStream(file);
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
        Log.e("Image size:", file.length() / 1024 / 1024 + "M"); } catch (Exception e) { e.printStackTrace(); }}Copy the code

Clipping the image (this is just clipping the image, but does not affect the size of the image)

Private void crop(Uri Uri) {// intents = new intents ("com.android.camera.action.CROP");
    intent.setDataAndType(uri, "image/*");
    intent.putExtra("crop"."true"); // Intent.putextra (intent.putextra)"aspectX", 1);
    intent.putExtra("aspectY", 1); // Intent.putextra (Intent.putextra)"outputX", 300);
    intent.putExtra("outputY", 300);

    intent.putExtra("outputFormat"."JPEG"); // Intent.putextra (intent.putextra)"noFaceDetection".true); // Disable face recognition intent.putextra ("return-data".true); // Start an Activity with a return value of PHOTO_REQUEST_CUT startActivityForResult(Intent, 200); } private void logp(Bitmap bitmap) { Log.i("wechat"."Image size before compression" + (bitmap.getByteCount() / 1024 / 1024)
        + "M width is" + bitmap.getWidth() + "Altitude is" + bitmap.getHeight());
}

private static void loga(Bitmap bitmap, byte[] bytes) {
    Log.i("wechat"."Compressed image size" + (bitmap.getByteCount() / 1024 / 1024)
        + "M width is" + bitmap.getWidth() + "Altitude is" + bitmap.getHeight()
        + "bytes.length= " + (bytes.length / 1024) + "KB"
    );
}
Copy the code

Conclusion: the article is too rubbish, here is just a note.