Lesson 159. Drawing. Bitmap. BitmapFactory.Options, save to file

Lesson 159. Drawing. Bitmap. BitmapFactory.Options, save to file


In this lesson:

– We understand BitmapFactory.Options
– save Bitmap to a file

In the first Bitmap lesson, we discussed that to read a picture from a file (resources, stream, …), Bitmap uses the decode * methods of BitmapFactory. And when reading, we can use the BitmapFactory.Options object, which allows us to set some parameters. Some of these parameters are very specific and rarely used, but there are some that can be useful in everyday work.

Let’s find out why these parameters are needed, and let’s look at some of them in the examples.

inJustDecodeBounds

If this option is enabled, the system will not create Bitmap, but will only return image information in the following fields:
outWidth – width
outHeight – height
outMimeType – mimetype

Let’s create a project:

Project name: P1591_BitmapOptions
Build Target: Android 4.4
Application name: BitmapOptions
Package name: ru.startandroid.develop.p1591bitmapoptions
Create Activity: MainActivity

MainActivity.java:

package com.example.p1591_bitmapoptions;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

public class MainActivity extends Activity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(new DrawView(this));
  }

  class DrawView extends View {

    Paint paint;
    Bitmap bitmap;
    
    public DrawView(Context context) {
      super(context);
      paint = new Paint(Paint.ANTI_ALIAS_FLAG);
      
      BitmapFactory.Options options = new BitmapFactory.Options();
      options.inJustDecodeBounds = true;
      bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher, options);
      
      Log.d("log", String.format("bitmap = %s, width = %s, height = %s, mimetype = %s", 
          bitmap, options.outWidth, options.outHeight, options.outMimeType));
      
    }

    @Override
    protected void onDraw(Canvas canvas) {
      canvas.drawARGB(80, 102, 204, 255);
    }

  }
}

IN DrawView specify inJustDecodeBounds = true, read the standard Android icon and log information about it.

Run, look log:

bitmap = null, width = 48, height = 48, mimetype = image / png

You may have different widths and heights, because when reading pictures from the res / drawable- * dpi folders, the density of the device is taken into account.

Bitmap is null because the system only returned us the info, and Bitmap did not create, and therefore the memory was not occupied.

andSampleSize

Allows you to specify an image size reduction factor when reading. It must be a multiple of 2. If you put another number, it will be changed to the nearest number less than yours and a multiple of 2.

rewrite the class DrawView:

  class DrawView extends View {

    Paint paint;
    Bitmap bitmap;
    
    public DrawView(Context context) {
      super(context);
      paint = new Paint(Paint.ANTI_ALIAS_FLAG);
      
      BitmapFactory.Options options = new BitmapFactory.Options();
      options.inSampleSize = 2;
      bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher, options);
      
      Log.d("log", String.format("width = %s, height = %s", bitmap.getWidth(), bitmap.getHeight()));
    }

    @Override
    protected void onDraw(Canvas canvas) {
      canvas.drawARGB(80, 102, 204, 255);
    }

  }

We use inSampleSize = 2 and log the dimensions obtained by Bitmap:

width = 24, height = 24

As ordered, the picture when reading in Bitmap became twice smaller.

parameters inJustDecodeBounds and andSampleSize can be used to read large images. That is, if you immediately decide to count the big image in Bitmap, you can take up too much memory or get OutOfMemory at all. Therefore, you should first get the size of the picture, and then with a compression ratio to count it in Bitmap approximately the desired size. We will further elaborate on this algorithm in one of the following lessons.

inBitmap

If you pass a Bitmap object to this parameter, it will be used to get the result instead of creating a new Bitmap object.

Here are some features with Android versions.

– In Android 4.4 (API 19) the bitmap transmitted must be at least in size (bytes) than the readable image.

– For earlier versions, the object passed in inBitmap must be the same size (width / height) as the image being read. Also, inSampleSize = 1 must be added to Options.

rewrite DrawView:

  class DrawView extends View {

    Paint paint;
    Bitmap bitmap;
    
    public DrawView(Context context) {
      super(context);
      paint = new Paint(Paint.ANTI_ALIAS_FLAG);
            
      Bitmap tempBitmap = Bitmap.createBitmap(300, 300, Bitmap.Config.ARGB_8888);
      BitmapFactory.Options options = new BitmapFactory.Options();
      options.inBitmap = tempBitmap;
      bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher, options);
      
      Log.d("log", String.format("bitmap = %s (%s,%s), tempBitmap = %s", 
          bitmap, bitmap.getWidth(), bitmap.getHeight(), tempBitmap));
      
    }

    @Override
    protected void onDraw(Canvas canvas) {
      canvas.drawARGB(80, 102, 204, 255);
    }

  }

We create a new tempBitmap Bitmap object and pass it to the inBitmap parameter.

log:

bitmap = android.graphics.Bitmap@5281a428 (48,48), tempBitmap = android.graphics.Bitmap@5281a428

You can see that bitmap and tempBitmap point to one object. That is, the decode method did not create a new Bitmap, but read the image in tempBitmap and returned it as a result. Bitmap size is 48×48. Although initially we created it in size 300×300.

If the system fails, it can return null or generate an IllegalArgumentException.

Once again I will say that this example worked on Android 4.4, but the earlier versions have the nuances that I painted just above.

inPreferredConfig

Specifying the desired Bitmap.Config configuration.

inDensity

Specifies the density value for Bitmap, similar to the setDensity method. Use the DisplayMetrics class DENSITY * constants to set the value.

inTargetDensity, inScaled

If inTargetDensity is different from inDensity, and inScaled = true (default), the image size will be adjusted from inDensity to inTargetDensity.

inScreenDensity

Unfortunately, I couldn’t understand why I needed it. If you have any thoughts, post on the forum.

inPurgeable

Allows the system to temporarily erase the contents of the created Bitmap from memory in the absence of such. When the image is needed again (for example, when displayed), it will be restored from the source. That is, we sacrifice performance for memory.

inInputShareable

If true, Bitmap stores the source references, otherwise the source data. But even if true, it may be at the discretion of the system that data will be stored, not links. This option is only valid when inPurgeable is enabled.

inDither

Attempt to smooth colors if the current color scheme is too small to display the original colors of the image

inMutable

If true, we will get a mutable Bitmap

inPreferQualityOverSpeed

Enabling higher quality decoding at the expense of speed

inPremultiplied

Available from API Level 19. Lets you disable premultiplied mode. When enabled (by default), the RGB components in pixels are immediately calculated based on the alpha component (for better performance). Canva only accepts Bitmap in this mode. Help says that mode exclusion may be required for specific tasks: RenderScript and OpenGL.

inTempStorage

Here we can specify our temporary array to be used in the decoding process

mCancel

From this label, you can determine whether the decoding process has been canceled by the requestCancelDecode method

How to Save Bitmap to a File |

The compress method saves Bitmap in different formats in the output stream. At the entrance accepts:
– format (JPG, PNG, WEBP)
– compression quality, from 0 (worst) to 100 (best)
– flow

Let’s look at an example in which we will create Bitmap, draw something on it and save it on SD.

rewrite DrawView:

class DrawView extends View {

    Paint paint;
    Bitmap bitmap;
    
    public DrawView(Context context) {
      super(context);
      paint = new Paint(Paint.ANTI_ALIAS_FLAG);
      paint.setTextSize(40);
      
      Bitmap bmpIcon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
      bmpIcon = Bitmap.createScaledBitmap(bmpIcon, 500, 500, true);
      
      bitmap = Bitmap.createBitmap(500, 500, Bitmap.Config.RGB_565);
      Canvas canvas = new Canvas(bitmap);
      canvas.drawColor(Color.WHITE);
      canvas.drawBitmap(bmpIcon, 0,0, paint);
      canvas.drawText("Saved bitmap", 100, 50, paint);
      
      File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "savedBitmap.png");
      
      try {
        FileOutputStream fos = null;
        try {
          fos = new FileOutputStream(file);
          bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
        } finally {
          if (fos != null) fos.close();
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
      
    }

    @Override
    protected void onDraw(Canvas canvas) {
      canvas.drawARGB(80, 102, 204, 255);
      canvas.drawBitmap(bitmap, 100, 100, paint);
    }

  }

In bmpIcon we read the standard icon, then resize it to 500×500. We create a new bitmap, fill it with white, draw bmpIcon in it and write the text.

Next, we create a File object that points to the savedBitmap.png file in the Pictures folder. For this file, we create a FileOutputStream stream, which we pass to the compress method. Also in the method we specify JPEG format and quality = 100.

run the program

After starting the program go to the Pictures folder, there should be a savedBitmap.png file.

we open it

In the next lesson:

– read and display large images




Discuss in the forum [7 replies]

Leave a Comment