Lesson 157. Drawing. Bitmap. BitmapFactory. Reading, Canvassing, Basic Information

Lesson 157. Drawing. Bitmap. BitmapFactory. Reading, Canvassing, Basic Information


In this lesson:

– we read Bitmap
– bring him out to the outskirts
– we get information about it

We start with the Bitmap theme. Without it, there is nowhere to draw, because Bitmap is an object that stores images. The same canvas we usually work with is a wrapper that takes commands from us and draws them on the bitmap we see as a result.

We will cover all major operations with Bitmap and will certainly find interesting material from the official site on this topic.

In this lesson, let’s start with the basics. Let’s see what methods are available to create a Bitmap from a file, how to bring it to an outline and what kind of information Bitmap can tell about itself.

The BitmapFactory factory is used to get the image from the file. It has several decode * methods that accept byte array input, file path, stream, file descriptor, or resource identifier. And on the way out we get Bitmap.

You may notice that all of these methods also have versions using the BitmapFactory.Options object. This is a very useful thing and we will discuss it separately in one of the following lessons.

The most common reading methods are, of course, the following:

decodeFile (String pathName) – Get Bitmap from file by specifying its path. That is, this method can be considered an image from an SD card. (GetExternalStorageDirectory)

decodeResource (Resources res, int id) – Get Bitmap from drawable resource by specifying its ID. This method will return us a picture from the res / drawable folders of our application.

Let’s create a project:

Project name: P1571_BitmapRead
Build Target: Android 4.4
Application name: BitmapRead
Package name: ru.startandroid.develop.p1571bitmapread
Create Activity: MainActivity

MainActivity.java:

package ru.startandroid.develop.p1571bitmapread;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
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;
    Rect rectSrc;
    Rect rectDst;
    Matrix matrix;

    public DrawView(Context context) {
      super(context);
      paint = new Paint(Paint.ANTI_ALIAS_FLAG);

      bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
      
      String info = 
          String.format("Info: size = %s x %s, bytes = %s (%s), config = %s",
              bitmap.getWidth(), 
              bitmap.getHeight(),
              bitmap.getByteCount(), 
              bitmap.getRowBytes(),
              bitmap.getConfig());
      Log.d("log", info);

      matrix = new Matrix();
      matrix.postRotate(45);
      matrix.postScale(2, 3);
      matrix.postTranslate(200, 50);
      
      rectSrc = new Rect(0, 0, bitmap.getWidth() / 2, bitmap.getHeight() / 2);
      rectDst = new Rect(300, 100, 500, 200);      
    }

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

  }
}

In the constructor DrawView we get Bitmap from the drawable resource ic_launcher. We passed the resource object and the ID of the required resource to the decodeResource method input.

Next in the variable info we will create a line with the image info:
getWidth – the width of the image in px
getHeight – image height in px
getByteCount – the number of bytes occupied by the image (only available with the Level 12 API)
getRowBytes – number of bytes in one line of the picture
getConfig – Info about how to store pixel data

And display this info in the log.

We adjust the matrix which will rotate the image 45 degrees, will expand the image twice wide and three times high and move it 200 right and 50 down.

we create two Rect object. rectSrc with sides equal to half the sides of the picture. That is, this rectangle covers the top left quarter of the picture. We will take this part for display later in the example. And we will output it in a rectangle rectDst, It’s just an arbitrary area on the screen.

In the method onDraw we draw a picture on the canvas with three different versions of the drawBitmap method. In the first case, we simply output the picture as it is at the point (50,50). In the second, we apply a matrix in which we have already configured rotation, transformation, and displacement. And the third variant will take from the picture the part that is in the rectSrc area (we put the left upper quarter there) and draw it on the canvas in the rectDst area, applying the necessary transformations and displacements.

Run the program.

From left to right, we see all three output options. In the first case unchanged and at the specified point. In the second case, the transformations were described in the matrix. In the third case, we cut off the part of the picture and painted it in the specified area, the canvas itself thus stretched the image to the size of the area.

we look at the log

Info: size = 48 x 48, bytes = 9216 (192), config = ARGB_8888

Image size = 48 (width) by 48 (height). You may have other digits here because the decodeResource method takes into account the density of the device and extracts the image from the desired folder. In my case he took it with drawable-mdpi.

Further we have deduced weight pictures in bytes – 9216, and the number bytes in one line – 192. Here it is clear that the weight of the picture = the number of bytes in the line * height = 192 * 48 = 9126.

And if we divide the number of bytes of a line by width, we get how many bytes one pixel occupies: 192/48 = 4 bytes.

The same is confirmed by config = ARGB_8888. This means that 8 bits (= 1 byte) are allocated for each of the 4 ARGB pixel components (alpha, red, green, blue). Therefore, the pixel will weigh 4 bytes.

In addition to the ARGB_8888 there are several configurations:

ALPHA_8 – pixel contains transparency only, there is no information about color here. Each pixel requires 8 bits (1 byte).

ARGB_4444 is an analogue of ARGB_8888, only 4 bits are assigned to each ARGB component. Accordingly, the pixel weighs 16 bits (2 bytes). With the Level 13 API, this configuration is declared obsolete.

RGB_565 – there is no transparency information here, and three RGB components are allocated, 5.6 and 5 bits respectively. Each pixel will weigh 16 bits or 2 bytes.

All of the above are important enough things to understand and take into account in designing. For example, if your application works with pictures and you know for sure that they will be transparent, it is better to use RGB_565. All your pictures in memory will occupy twice less space than when using the default ARGB_8888. With many pictures, this is a significant optimization!

You should also pay attention to the size (and therefore the weight) of the image. Make sure your in-memory pictures are no larger than you want. Here is an example from practice. One time I had to optimize an application that had a screen with a avatar list of users. The author originally loaded these from the site and cached the SD. The list used a memory cache, but it overflowed instantly and constantly reads the info from the SD.

The autopsy showed that the images were loaded from the site at a resolution of 200 x 200 and stored exactly on SD. In the memory cache they were contained in the same resolution, occupying, respectively, 200 * 200 * 4 = 160 000 bytes each! That is 6 pictures in the cache and already a meter of memory is occupied. And the list is there for hundreds of positions. Of course, no cache is enough when scrolling.

Looked at the layout rows of the list. Each ImageView that displayed the avatar was only 32×32 dp in size. That is, in the case of mdpi we needed a 32×32 pixel image. That is 32 * 32 * 4 = 4096 bytes. It turns out that instead of one avatar 200×200 in the cache could easily accommodate almost 40 avatars 32×32.

As a result, when reading pictures from the site and saving them to SD, they immediately resized to the desired size, and it became much better. Alternatively, the site immediately ask for the desired image size.

In general, try to use the minimum format and size you need. We’ll talk about how to choose a format, resize, and use caches in the following lessons.

In the next lesson:

– create and modify Bitmap
– deal with density and mutable




Discuss in the forum [2 replies]

Leave a Comment