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
>
- setTranslate (float dx, float dy): Controls the Matrix for translation.
- setRotate (float degrees, float px, float py): Rotates, with parameters being: rotation angle, pivot point (x, y).
- setScale (float sx, float sy, float px, float py): Scales, with parameters being: scale ratios on the X and Y axes; scaling pivot point.
- setSkew (float kx, float ky): Skews (distorts), with parameters being: skew ratios on the X and Y axes.
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:
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
- 1.2.1 Developing Android Apps with Eclipse + ADT + SDK
- 1.2.2 Developing Android Apps with Android Studio
- 1.3 Solving SDK Update Issues
- 1.4 Genymotion Emulator Installation
- 1.5.1 Git Tutorial: Basic Operations on Local Repositories
- 1.5.2 Git: Setting Up a Remote Repository on GitHub
- 1.6 How to Use 9-Patch Images
- 1.7 Interface Prototype Design
- 1.8 Project Analysis (Various Files, Resource Access)
- 1.9 Android App Signing and Packaging
- 1.11 Decompiling APK to Retrieve Code & Resources
- 2.1 Concepts of View and ViewGroup
- 2.2.1 LinearLayout (Linear Layout)
- 2.2.2 RelativeLayout (Relative Layout)
- 2.2.3 TableLayout (Table Layout)
- 2.2.4 FrameLayout (Frame Layout)
- 2.2.5 GridLayout (Grid Layout)
- 2.2.6 AbsoluteLayout (Absolute Layout)
- 2.3.1 Detailed Guide to TextView (Text Field)
- 2.3.2 Detailed Guide to EditText (Input Field)
- 2.3.3 Button and ImageButton
- 2.3.4 ImageView (Image View)
- 2.3.5 RadioButton (Radio Button) & Checkbox (Checkbox)
- 2.3.6 ToggleButton and Switch
- 2.3.7 ProgressBar (Progress Bar)
- 2.3.8 SeekBar (Drag Bar)
- 2.3.9 RatingBar (Star Rating Bar)
- 2.4.1 ScrollView (Scroll Bar)
- 2.4.2 Date & Time Components (Part 1)
- 2.4.3 Date & Time Components (Part 2)
- 2.4.4 Adapter Basics
- 2.4.5 Simple Usage of ListView
- 2.4.6 Optimization of BaseAdapter
- 2.4.7 Focus Issues with ListView
- 2.4.8 Solving Checkbox Misalignment in ListView
- 2.4.9 Data Update Issues in ListView
- 2.5.0 Building a Reusable Custom BaseAdapter
- 2.5.1 Implementing Multiple Item Layouts in ListView
- 2.5.2 Basic Usage of GridView (Grid View)
- 2.5.3 Basic Usage of Spinner (Dropdown List)
- 2.5.4 Basic Usage of AutoCompleteTextView (Auto-complete Text Field)
- 2.5.5 Basic Usage of ExpandableListView (Expandable List)
- 2.5.6 Basic Usage of ViewFlipper (Flip View)
- 2.5.7 Basic Usage of Toast
- 2.5.8 Detailed Guide to Notification (Status Bar Notification)
- 2.5.9 Detailed Guide to AlertDialog (Dialog Box)
- 2.6.0 Basic Usage of Other Common Dialogs
- 2.6.1 Basic Usage of PopupWindow (Floating Box)
- 2.6.2 Menu (Menu)
- 2.6.3 Simple Usage of ViewPager
- 2.6.4 Simple Usage of DrawerLayout (Official Side Menu)
- 3.1.1 Event Handling Mechanism Based on Listeners
- 3.2 Event Handling Mechanism Based on Callbacks
- 3.3 Analysis of Handler Message Passing Mechanism
- 3.4 TouchListener vs OnTouchEvent + Multi-touch
- 3.5 Listening for Content Changes in EditText
- 3.6 Responding to System Setting Changes (Configuration Class)
- 3.7 AsyncTask (Asynchronous Task)
- 3.8 Gestures (Gestures)
- 4.1.1 Getting Started with Activity
- 4.1.2 Intermediate Activity
- 4.1.3 Advanced Activity
- 4.2.1 Introduction to Service
- 4.2.2 Intermediate Service
- 4.2.3 Advanced Service
- 4.3.1 Basic BroadcastReceiver
- 4.3.2 Advanced BroadcastReceiver
- 4.4.1 Introduction to ContentProvider
- 4.4.2 Advanced ContentProvider – Document Provider
- 4.5.1 Basic Usage of Intent
- 4.5.2 Passing Complex Data with Intent
- 5.1 Basic Overview of Fragment
- 5.2.1 Fragment Example: Bottom Navigation Bar Implementation (Method 1)
- 5.2.2 Fragment Example: Bottom Navigation Bar Implementation (Method 2)
- 5.2.3 Fragment Example: Bottom Navigation Bar Implementation (Method 3)
- 5.2.4 Fragment Example: Bottom Navigation Bar + ViewPager for Page Swiping
- 5.2.5 Fragment Example: Simple Implementation of News/Shopping App List Fragment
- 6.1 Data Storage and Access – File Storage and Reading
- 6.2 Data Storage and Access - SharedPreferences for Saving User Preferences
- 6.3.1 Data Storage and Access - Introduction to SQLite Database
- 6.3.2 Data Storage and Access - Further Exploration of SQLite Database
- 7.1.1 Android Network Programming Essentials and Learning HTTP Protocol
- 7.1.2 Learning Android HTTP Request Headers and Response Headers
- 7.1.3 Android HTTP Request Methods: HttpURLConnection
- 7.1.4 Android HTTP Request Methods: HttpClient
- 7.2.1 Android XML Data Parsing
- 7.2.2 Android JSON Data Parsing
- 7.3.1 Android File Upload
- 7.3.2 Android File Download (1)
- 7.3.3 Android File Download (2)
- 7.4 Android Calling WebService
- 7.5.1 Basic Usage of WebView (Web View)
- 7.5.2 WebView and JavaScript Interaction Basics
- 7.5.3 Important Considerations for WebView in Android 4.4 and Later
- 7.5.4 WebView File Download
- 7.5.5 WebView Cache Issues
- 7.5.6 WebView Handling Webpage Error Code Information
- 7.6.1 Socket Learning Network Basics Preparation
- 7.6.2 Socket Communication Based on TCP Protocol (1)
- 7.6.3 Socket Communication Based on TCP Protocol (2)
- 7.6.4 Socket Communication Based on UDP Protocol
- 8.1.1 Summary of 13 Drawable Types in Android Part 1
- 8.1.2 Summary of 13 Drawable Types in Android Part 2
- 8.1.3 Summary of 13 Drawable Types in Android Part 3
- 8.2.1 Comprehensive Analysis of Bitmap (Bitmap) Part 1
- 8.2.2 OOM Issues Caused by Bitmap
- 8.3.1 Detailed Explanation of Three Drawing Tool Classes
- 8.3.2 Practical Examples of Drawing Classes
- 8.3.3 Paint API - MaskFilter (Mask)
- 8.3.4 Paint API - Xfermode and PorterDuff Detailed Explanation (Part 1)
- 8.3.5 Paint API - Xfermode and PorterDuff Detailed Explanation (Part 2)
- 8.3.6 Paint API - Xfermode and PorterDuff Detailed Explanation (Part 3)
- 8.3.7 Paint API - Xfermode and PorterDuff Detailed Explanation (Part 4)
- 8.3.8 Paint API - Xfermode and PorterDuff Detailed Explanation (Part 5)
- 8.3.9 Paint API - ColorFilter (Color Filter) (1/3)
- 8.3.10 Paint API - ColorFilter (Color Filter) (2/3)
- 8.3.11 Paint API - ColorFilter (Color Filter) (3/3)
- 8.3.12 Paint API - PathEffect (Path Effect)
- 8.3.13 Paint API - Shader (Image Rendering)
- 8.3.14 Paint Enumerations/Constants and ShadowLayer Shadow Effects
- 8.3.15 Paint API - Typeface (Font Style)
- 8.3.16 Detailed Explanation of Canvas API (Part 1)
- 8.3.17 Detailed Explanation of Canvas API (Part 2) Clipping Methods Collection
- 8.3.18 Detailed Explanation of Canvas API (Part 3) Matrix and drawBitmapMesh
- 8.4.1 Android Animation Collection - Frame Animation
- 8.4.2 Android Animation Collection - Tween Animation
- 8.4.3 Android Animation Collection - Property Animation - Introduction
- 8.4.4 Android Animation Collection - Property Animation - Further Exploration
- 9.1 Using SoundPool to Play Sound Effects (Duang~)
- 9.2 MediaPlayer for Playing Audio and Video
- 9.3 Using Camera to Take Photos
- 9.4 Using MediaRecord for Recording
- 10.1 TelephonyManager (Telephony Manager)
- 10.2 SmsManager (SMS Manager)
- 10.3 AudioManager (Audio Manager)
- 10.4 Vibrator (Vibrator)
- 10.5 AlarmManager (Alarm Service)
- 10.6 PowerManager (Power Service)
- 10.7 WindowManager (Window Management Service)
- 10.8 LayoutInflater (Layout Service)
- 10.9 WallpaperManager (Wallpaper Manager)
- 10.10 Sensor Topics (1) - Introduction
- 10.11 Sensor Topics (2) - Orientation Sensor
- 10.12 Sensor Topics (3) - Accelerometer/Gyroscope Sensor
- 10.12 Sensor Topics (4) - Understanding Other Sensors
- 10.14 Introduction to Android GPS
- 11.0 Completion of the 2015 Latest Android Basic Beginner's Tutorial
- 12.1 Android Practical: DrySister App (Version 1) - Project Setup and Simple Implementation
- 12.2 DrySister App (Version 1) - Parsing Backend Data
- 12.3 DrySister App (Version 1) - Image Loading Optimization (Building a Simple Image Cache Framework)
- 12.4 DrySister App (Version 1) - Adding Data Caching (Introducing SQLite)
- 12.5 DrySister App (Version 1) - Code Review, Adjustment, and Logging Class Writing 12.6 DrySister View Girls App (Version 1) - 6. Icon Creation, Obfuscation, Signing and Packaging, APK Slimming, App Release