Summary of common attributes and methods of Bitmap and BitmapFactory

Posted by tlavelle on Mon, 31 Jan 2022 14:39:08 +0100

Bitmap represents a bitmap, and the picture encapsulated in BitmapDrawable is a bitmap object. In order to encapsulate a bitmap object into a BitmapDrawable object, developers can call the constructor of BitmapDrawable:

//Put one Bitmap Object packaging BitmapDrawable object
val drawable = BitmapDrawable(bitmap)

If you need to get the bitmap object wrapped by BitmapDrawable, you can call the getBitmap() method of BitmapDrawable (Kotlin accesses the bitmap attribute through point syntax), as shown in the following code:

//Gets the Bitmap object wrapped by BitmapDrawable 

val bitmap = drawable.bitmap

In addition, Bitmap also provides some static methods to create new Bitmap objects, such as the following common methods.

  • createBitmap(Bitmap source, int x, int y, int width, int height): start from the specified coordinate point (given x, y) of the source Bitmap source, and "dig" a piece of width and height to create a new Bitmap object.
  • createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter): scale the source bitmap src to a new bitmap with wide dstWidth and high dstHeight.
  • createBitmap(int width, int height, Bitmap.Config config): create a new bitmap with width and height h eight.
  • createBitmap(Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter): start from the specified coordinate point (given x, y) of the source Bitmap source, and "dig" a piece of width and height h eight from it to create a new Bitmap object and transform it according to the rules specified by the Matrix.

BitmapFactory is a tool class that provides a large number of methods that can be used to parse and create Bitmap objects from different data sources. BitmapFactory contains the following methods.

  1. Decodebyte array (byte [] data, int offset, int length): parses byte data of length into Bitmap objects starting from the offset position of the specified byte array.
  2. decodeFile(String pathName): parses and creates a Bitmap object from the file specified by pathName.
  3. decodeFileDescriptor(FileDescriptor fd): used to parse and create Bitmap objects from the file corresponding to FileDescriptor.
  4. decodeResource(Resources res, int id): it is used to parse and create a Bitmap object from the specified resource according to the given resource ID, that is, it is used to convert the Drawable resource into a Bitmap object.
  5. decodeStream(InputStream is): used to parse and create Bitmap objects from the specified input stream.

Generally, as long as the picture is placed in the / res/drawable / directory, the Drawable object encapsulating the picture can be obtained through the resource ID corresponding to the picture in the program. However, due to the small memory of the mobile phone system, if the system keeps parsing and creating Bitmap objects, it may cause OutOfMemory error when the program runs because the memory occupied by the previous Bitmap creation has not been recycled.

Android provides Bitmap with two methods to judge whether it has been recycled and force Bitmap to recycle itself.

  1. boolean isRecycled(): Returns whether the Bitimp object has been recycled.
  2. void recycle(): force a Bitmap object to recycle itself immediately.

In addition, if Android applications need to access pictures in other storage paths (such as SD cards), they need to use BitmapFactory to parse and create Bitmap objects.

Mutual conversion between Drawable and Bitmap and between Bitmap and byte array byte []

I Convert Drawable to Bitmap Method 1: use the decodeResource method in BitmapFactory to add r.Drawable ic_ Convert Drawable to Bitmap object

Resources res = getResources();
Bitmap  bmp = BitmapFactory.decodeResource(res, R.drawable.ic_drawable);

Method two: first transform the Drawble object into BitmapDrawable, then call the getBitmap() method to get the Bitmap object.

Resource res      = gerResource();
Drawable drawable = res.getDrawable(R.drawable.ic_drawable);//Get drawable
BitmapDrawable bd = (BitmapDrawable) drawable;
Bitmap bm   = bd.getBitmap();

Method 3: create a new Bitmap object based on the existing Drawable

    public static Bitmap drawableToBitmap(Drawable drawable) {

        int w = drawable.getIntrinsicWidth();
        int h = drawable.getIntrinsicHeight();
        System.out.println("Drawable turn Bitmap");
        Bitmap.Config config =
                drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
                        : Bitmap.Config.RGB_565;
        Bitmap bitmap = Bitmap.createBitmap(w, h, config);
        //Note that the following three lines of code need to be used, otherwise the canvas in View or SurfaceView Drawbitmap will not see the map
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, w, h);
        drawable.draw(canvas);

        return bitmap;
    }

2, Convert Bitmap to Drawable
Use BitmapDrawable to cast Bitmap

Drawable drawable = new BitmapDrawable(bmp);

3, Convert Bitmap to byte []

public static byte[] getBytes(Bitmap bitmap){

        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);

        return baos.toByteArray();   

    }

4, Convert byte [] to Bitmap

public static Bitmap Bytes2Bimap(byte[] b) {

        if (b.length != 0) {
            return BitmapFactory.decodeByteArray(b, 0, b.length);
        } else {
            return null;
        }

    }

BitmapFactory source code can be viewed by opening the editor. Here are some common and important methods and properties:

Here is the first common attribute:

(1)inJustDecodeBounds

        /**
         * If set to true, the decoder will return null (no bitmap), but
         * the <code>out...</code> fields will still be set, allowing the caller to
         * query the bitmap without having to allocate the memory for its pixels.
         */
        public boolean inJustDecodeBounds;

It is an attribute defined in the static class Options inside BitmapFactory. We can make full use of it to avoid the overflow problem of large images.

The API says that if the value is set to true, the actual bitmap object will not be returned and memory space will not be allocated, so as to avoid memory overflow. However, it allows us to query the image information, including the image size information:

options.outHeight: the original height of the picture

option.outWidth: the original width of the picture

In order to better understand the use of this attribute, let's look at another attribute in the Options class: the scaling value of width and height inSampleSize.

(2) inSampleSize

        /**
         * If set to a value > 1, requests the decoder to subsample the original
         * image, returning a smaller image to save memory. The sample size is
         * the number of pixels in either dimension that correspond to a single
         * pixel in the decoded bitmap. For example, inSampleSize == 4 returns
         * an image that is 1/4 the width/height of the original, and 1/16 the
         * number of pixels. Any value <= 1 is treated the same as 1. Note: the
         * decoder uses a final value based on powers of 2, any other value will
         * be rounded down to the nearest power of 2.
         */
        public int inSampleSize;

Through this attribute, we can realize the proportional scaling function for large images.

English annotation translation:

If set to > 1, the decoder is requested to sub sample the original image and return a smaller image to save memory. The sample size is the number of pixels in any dimension corresponding to a single pixel in the decoded bitmap. For example, inSampleSize == 4 returns an image that is 1 / 4 the width / height of the original image and 1 / 16 the number of pixels.

Any value < = 1 will be treated as 1. Note: the decoder uses the final value based on the power of 2, and any other value will be rounded to the nearest power of 2.

By setting inJustDecodeBounds to true, get outheight and outwidth, then calculate an inSampleSize, and then you can take the picture. Note that inSampleSize may be less than 0, so you must make a judgment.

Implementation steps of reading a large picture, preventing memory overflow and scaling:

Step 1: get bitmapfactory Options object, open the parsing boundary information attribute injustdecodeboundaries

Set inJustDecodeBounds to true to obtain the width, height, size and other information of the picture in advance, and will not create a Bitmap object or allocate memory for the picture.

Step 2: bitmapfactory Decodefile (string pathname) or bitmapfactory Decoderesource (resources res, int ID, options, opts) decodes and obtains bitmap or Drawable information:

options. Outheight (original height of picture)

options. Outwidth (the original width of the picture)

Step 3: calculate the scaling ratio, or set a value directly without calculation.

options.inSampleSize = "your zoom factor". If it is 2, the height and width are half of the original.

Step 4: turn off the setting options of parsing boundary information attribute Injustdecodebounds = false, and set other options attribute information;

Step 5: decode the Bitmap object again, and then return the real Bitmap object:

BitmapFactory.decodeFile(String pathName) or bitmapfactory decodeResource(Resources res, int id, Options opts)

give an example:

    /**
     * Kotloin Language coding
     * Draw a square head picture: Avatar png
     */
    private fun getAvatar(width:Int):Bitmap{
        val options = BitmapFactory.Options();
        options.inJustDecodeBounds=true
        //Resolve r.drawable.ID in drawable directory of res resource folder Avatar pictures
        BitmapFactory.decodeResource(resources, R.drawable.avatar,options)
        options.inJustDecodeBounds=false
        options.inDensity=options.outWidth
        options.inTargetDensity =width
        return BitmapFactory.decodeResource(resources,R.drawable.avatar,options);
    }

(3) inDensity attribute

/**
         * The pixel density to use for the bitmap.  This will always result
         * in the returned bitmap having a density set for it (see
         * {@link Bitmap#setDensity(int) Bitmap.setDensity(int)}).  In addition,
         * if {@link #inScaled} is set (which it is by default} and this
         * density does not match {@link #inTargetDensity}, then the bitmap
         * will be scaled to the target density before being returned.
         * 
         * <p>If this is 0,
         * {@link BitmapFactory#decodeResource(Resources, int)}, 
         * {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)},
         * and {@link BitmapFactory#decodeResourceStream}
         * will fill in the density associated with the resource.  The other
         * functions will leave it as-is and no density will be applied.
         *
         * @see #inTargetDensity
         * @see #inScreenDensity
         * @see #inScaled
         * @see Bitmap#setDensity(int)
         * @see android.util.DisplayMetrics#densityDpi
         */
        public int inDensity;

inDensity is the pixel density to be used for bitmaps. This will always cause the returned bitmap to have a density setting (see {@ link bitmap #setdensity (int) bitmap. Setdensity (int)}). In addition, if {@ link #inScaled} (this is the default setting) is set and the density does not match {@ link #inTargetDensity}, the bitmap will be scaled to the target density before returning.  

Note: inDensity will get different values according to the drawable folder where the image resource is located

(4)inTargetDensity

        /**
         * The pixel density of the destination this bitmap will be drawn to.
         * This is used in conjunction with {@link #inDensity} and
         * {@link #inScaled} to determine if and how to scale the bitmap before
         * returning it.
         * 
         * <p>If this is 0,
         * {@link BitmapFactory#decodeResource(Resources, int)}, 
         * {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)},
         * and {@link BitmapFactory#decodeResourceStream}
         * will fill in the density associated the Resources object's
         * DisplayMetrics.  The other
         * functions will leave it as-is and no scaling for density will be
         * performed.
         * 
         * @see #inDensity
         * @see #inScreenDensity
         * @see #inScaled
         * @see android.util.DisplayMetrics#densityDpi
         */
        public int inTargetDensity;

English annotation translation:

The pixel density of the target to which the bitmap will be drawn. This is used with {@ link #inDensity} and {@ link #inScaled} to determine whether and how to scale the bitmap, and then return to it. inTargetDensity is set to the screen pixel density.

Summary:

When BitmapFactory decodes and converts a picture, the size of the picture is not only related to the attribute inSampleSize, but also refers to the attributes inDensity and inTargetDensity.

Because the pictures are placed in the drawable folder, so:

① The inDensity attribute in Options will be assigned according to the resolution of the drawable folder. If you place the drawable folder of the picture followed by the word "- xxxhdpi", the inDensity attribute will be 640 when you decode and convert the picture;

② The inTartgetDensity attribute in Options will be assigned according to the pixel density of the screen;

For the screen, the larger the DPI, the higher the fineness of the screen, and the clearer the screen looks. General screen: ldpi is 120dpi, mdpi is 160dpi, hdpi is 240dpi, xhdpi is 320dpi, xxhdpi is 480dpi, xxhdpi is 640dpi.


(5)inScreenDensity

/**
         * The pixel density of the actual screen that is being used.  This is
         * purely for applications running in density compatibility code, where
         * {@link #inTargetDensity} is actually the density the application
         * sees rather than the real screen density.
         * 
         * <p>By setting this, you
         * allow the loading code to avoid scaling a bitmap that is currently
         * in the screen density up/down to the compatibility density.  Instead,
         * if {@link #inDensity} is the same as {@link #inScreenDensity}, the
         * bitmap will be left as-is.  Anything using the resulting bitmap
         * must also used {@link Bitmap#getScaledWidth(int)
         * Bitmap.getScaledWidth} and {@link Bitmap#getScaledHeight
         * Bitmap.getScaledHeight} to account for any different between the
         * bitmap's density and the target's density.
         * 
         * <p>This is never set automatically for the caller by
         * {@link BitmapFactory} itself.  It must be explicitly set, since the
         * caller must deal with the resulting bitmap in a density-aware way.
         * 
         * @see #inDensity
         * @see #inTargetDensity
         * @see #inScaled
         * @see android.util.DisplayMetrics#densityDpi
         */
        public int inScreenDensity;

English annotation translation:

It refers to the pixel density of the actual screen being used. This is purely for the application to run in density compatibility code, {@ link #inTargetDensity} is actually the density seen by the application, not the real screen density.
<p>By setting this, you allow the loading code to avoid scaling the bitmap's current compatibility density above / below the screen density. Conversely, if {@ link #inDensity} is the same as {@ link # insensitivity}, the bitmap remains as it is. Anything that uses a result bitmap must also use {@ link bitmap #getScaledWidth(int) bitmap.getScaledWidth} and {@ link Bitmap#getScaledHeight bitmap. getScaledHeight} to consider any difference between the density of the bitmap and the density of the target.
<p>{@ link bitmapfactory} itself is not automatically set for callers. It must be set explicitly because the caller must process the resulting bitmap in a density aware manner.

(6) inScaled

        /**
         * When this flag is set, if {@link #inDensity} and
         * {@link #inTargetDensity} are not 0, the
         * bitmap will be scaled to match {@link #inTargetDensity} when loaded,
         * rather than relying on the graphics system scaling it each time it
         * is drawn to a Canvas.
         *
         * <p>BitmapRegionDecoder ignores this flag, and will not scale output
         * based on density. (though {@link #inSampleSize} is supported)</p>
         *
         * <p>This flag is turned on by default and should be turned off if you need
         * a non-scaled version of the bitmap.  Nine-patch bitmaps ignore this
         * flag and are always scaled.
         *
         * <p>If {@link #inPremultiplied} is set to false, and the image has alpha,
         * setting this flag to true may result in incorrect colors.
         */
        public boolean inScaled;

English annotation translation:

When this flag is set, if {@ link #inTargetDensity} and {@ link #inTargetDensity} are not 0, the bitmap will be scaled to match {@ link #inTargetDensity} when loaded.
Instead of relying on the graphics system to scale each time it is drawn on the canvas.
<p>Bitmapregiondecoder ignores this flag and does not scale the output based on density. (although {@ link #insamplesize} is supported)</p>
<p>This flag is turned on by default. If you need a non scaled version of the bitmap, it should be turned off. The point 9 bitmap ignores this flag and always zooms.
<p>If {@ link # inpremixed} is set to false and the image has alpha, setting this flag to true may result in incorrect color.  

(1) Bitmapfactory Relationship between inDensity of options and drawable Directory:

https://blog.csdn.net/Sonic_sTorm/article/details/79222292

(2) Bitmapfactory inDensity and inTargetDensity in options

https://blog.csdn.net/T_yoo_csdn/article/details/80169586?utm_medium=distribute.pc_relevant.none-task-blog-2~default~OPENSEARCH~default-6.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~OPENSEARCH~default-6.control

(3) android image resizing_ Some methods of android image resizing

https://blog.csdn.net/weixin_35857225/article/details/111928986?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-1&spm=1001.2101.3001.4242

 

 

 

Topics: Java Android