Easy Tutorial
❮ Verilog2 Clock Switch Cpp Multithread Demo ❯

8.3.18 Canvas API Detailed Explanation (Part 3): Matrix and drawBitmapMesh

Category Android Basic Tutorial

Introduction to This Section:

>

In the Canvas API documentation, we come across a method: drawBitmap (Bitmap bitmap, Matrix matrix, Paint paint)

This Matrix is a significant topic. Previously, while studying the ColorFilter in Paint's API, we discussed the ColorMatrix, a 4 * 5 matrix, which allows us to modify hue, saturation, and more by adjusting the matrix values. Today, the Matrix we discuss can be combined with other APIs to control transformations of graphics and components. For instance, Canvas provides the aforementioned drawBitmap method to achieve matrix transformation effects! Let's delve into this topic gradually.

Official API Documentation: Matrix


1. Common Transformation Methods in Matrix

>

These are largely consistent with the transformation methods of Canvas. After setting the above transformations for Matrix, call the Canvas's drawBitmap() method with the matrix.


2. Matrix Usage Example:

Result Image:

btn_zoomin = (Button) findViewById(R.id.btn_zoomin);
btn_zoomout = (Button) findViewById(R.id.btn_zoomout);
myView = (MyView) findViewById(R.id.myView);

btn_reset.setOnClickListener(this);
btn_left.setOnClickListener(this);
btn_right.setOnClickListener(this);
btn_zoomin.setOnClickListener(this);
btn_zoomout.setOnClickListener(this);

}

@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_reset:
myView.setMethod(0);
break;
case R.id.btn_left:
myView.setMethod(1);
break;
case R.id.btn_right:
myView.setMethod(2);
break;
case R.id.btn_zoomin:
myView.setMethod(3);
break;
case R.id.btn_zoomout:
myView.setMethod(4);
break;
}
}
}

The usage is very simple, so I won't explain it~


3. Distorting Images with drawBitmapMesh

In the API documentation, there is a method called drawBitmapMesh:

drawBitmapMesh (Bitmap bitmap, int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset, Paint paint)

The parameters are as follows:

bitmap: The original bitmap to be distorted.

meshWidth / meshHeight: The number of divisions in the horizontal/vertical direction on the original bitmap.

verts: An array of length (meshWidth+1) * (meshHeight+2), which records the positions of the vertices (intersection points of the grid lines) of the distorted bitmap. Although it is a one-dimensional array, it actually records data in the format of (x0,y0), (x1,y1)..(xN,Yn). These array elements control the distortion effect on the bitmap.

vertOffset: Controls which element in the verts array to start distorting the bitmap from (ignoring the distortion effect of the data before verOffset).

Code Example:

Running Effect:

Code Implementation:

/**
 * Created by Jay on 2015/11/11 0011.
 */
public class MyView extends View {

    // Divide the horizontal and vertical directions into 20 grids
    private final int WIDTH = 20;
    private final int HEIGHT = 20;
    private final int COUNT = (WIDTH + 1) * (HEIGHT + 1);  // Record the number of points in the image, which is 21*21
    private final float[] verts = new float[COUNT * 2];    // Coordinates of the 21*21 points before distortion
    private final float[] orig = new float[COUNT * 2];    // Coordinates of the 21*21 points after distortion
    private Bitmap mBitmap;
    private float bH, bW;

    public MyView(Context context) {
        this(context, null);
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private void init() {
        mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.img_wuliao);
        bH = mBitmap.getWidth();
        bW = mBitmap.getHeight();
        int index = 0;
        // Initialize the orig and verts arrays.
        for (int y = 0; y <= HEIGHT; y++) {
            float fy = bH * y / HEIGHT;
            for (int x = 0; x <= WIDTH; x++) {
                float fx = bW * x / WIDTH;
                orig[index * 2 + 0] = verts[index * 2 + 0] = fx;
                orig[index * 2 + 1] = verts[index * 2 + 1] = fy;
                index += 1;
            }
        }
        // Set the background color
        setBackgroundColor(Color.WHITE);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmapMesh(mBitmap, WIDTH, HEIGHT, verts, 0, null, 0, null);
    }

    // Utility method to calculate the values of the verts array based on the touch event position
    private void warp(float cx, float cy) {
        for (int i = 0; i < COUNT * 2; i += 2) {
            float dx = cx - orig[i + 0];
            float dy = cy - orig[i + 1];
            float dd = dx * dx + dy * dy;
            // Calculate the distance between each coordinate point and the current point (cx, cy)
            float d = (float) Math.sqrt(dd);
            // Calculate the distortion degree, the farther from the current point (cx, cy), the smaller the distortion degree
            float pull = 80000 / ((float) (dd * d));
            // Reassign the verts array (stores the coordinates of the 21 * 21 points on the bitmap after distortion)
            if (pull >= 1) {
                verts[i + 0] = cx;
                verts[i + 1] = cy;
            } else {
                // Control each vertex to shift towards the touch event point
                verts[i + 0] = orig[i + 0] + dx * pull;
                verts[i + 1] = orig[i + 1] + dy * pull;
            }
        }
        // Notify the View component to redraw
        invalidate();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // Call the warp method to distort the verts array based on the coordinates of the touch event
        warp(event.getX(), event.getY());
        return true;
    }
}

Implementation Flow Analysis:

First, you need to understand what the verts array stores. For example, verts[0] and verts[1] represent the x and y coordinates of our first point. Knowing this, you understand why there are 21 * 21 points and why the array length is equal to this value * 2. The initialization part should then be clear.

Next, let's look at the implementation of calculating the values of the verts array elements based on touch events: Get the x, y coordinates of the touch point, subtract these values from the corresponding point's x, y values to calculate the distance between the touch point and each coordinate point. Then calculate the distortion degree: 80000 / ((float) (dd * d)). For distortion degrees >= 1, directly make the coordinate point point to the touch point. For < 1, make each vertex shift towards the touch point, and then call invalidate() to redraw. That's it. Take your time to think about it. If you still don't understand, don't worry. Just know that this exists.


4. Example Downloads:

CanvasDemo3.zip

CanvasDemo4.zip


Summary:

Most of the content in this section is taken from Li Gang's "Android Crazy Lecture," which might be easier to understand. Matrix should be understandable for most people, while distorting images with drawBitmapMesh might take some time to digest. If you don't understand, it's okay. That's it for this section. Thank you.

-1.0 Android Basic Introduction Tutorial

-1.0.1 2015 Latest Android Basic Introduction Tutorial Table of Contents

-1.1 Background and System Architecture Analysis

-1.2 Development Environment Setup

Follow on WeChat

❮ Verilog2 Clock Switch Cpp Multithread Demo ❯