Android Canvas的drawBitmapMesh实现扭曲图像

Canvas提供了一个drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset, Paint paint)方法,该方法可以对bitmap进行扭曲.这个方法非常灵活,如果用好这个方法,可以在Android应用上开发出"水波荡漾","风吹旗帜"等各种扭曲效果.

drawBitmapMesh方法关键参数的说明:

> bitmap: 指定需要扭曲的源位图.

> meshWidth: 该参数控制在横向上把该源位图划分成多少格.

> meshHeight: 该参数控制在纵向上把该源位图划分为多少格.

> verts: 该参数是一个长度为(meshWidth + 1)*(meshHeight+1)* 2 的数组,它记录了扭曲后的位图各"顶点"位置.虽然它是个一维数组,实际上它记录的数据是形如(x0, y0),(x1, y1),(x2, y2)......(xN, yN)格式的数据,这些数组元素控制对bitmap位图的扭曲效果.

> vertOffset: 控制verts数组中从第几个数组元素开始才对bitmap进行扭曲(忽略vertOffset之前数据的扭曲效果).

drawBitmapMesh方法对源位图扭曲时最关键的参数是meshWidth, meshHeight, verts,这三个参数对扭曲的控制如下图:


从上图可以看出,当程序希望调用drawBitmapMesh方法对位图进行扭曲是,关键是计算verts数组的值----该数组的值记录了扭曲后的位图上各"顶点"的坐标.

下面的实例是通过drawBitmapMesh方法来控制图片的扭曲.

1.自定义MeshView组件

package com.example.matrixdemo.view;

import com.example.matrixdemo.R;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

/***
 * 通过Canvas的drawBitmapMesh方法控制图片的扭曲效果
 * @author Administrator
 *
 */
public class MyMeshView extends View {
	/** 源图片 */
	private Bitmap bitmap;
	/** 定义常量,常量指定该图片横向上被分为20格 */
	private final int MESH_WIDTH = 20;
	/** 定义常量,常量指定该图片纵向上被分为20格 */
	private final int MESH_HEIGHT = 20;
	/** 记录该图片上包含的441个顶点 */
	private final int COUNT = (MESH_WIDTH + 1) * (MESH_HEIGHT + 1);
	/** 定义一个数组,保存Bitmap上的21*21个点的坐标 */
	private float[] verts = new float[COUNT * 2];
	/** 定义一个数组,保存Bitmap上的21*21个点经过扭曲后的坐标 */
	// 对图片进行扭曲的关键就是修改该数组里元素的值
	private float[] orig = new float[COUNT * 2];

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

	public MyMeshView(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public MyMeshView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		setFocusable(true);
		// 加载Bitmap资源
		bitmap = ((BitmapDrawable) getResources().getDrawable(
				R.drawable.bg02)).getBitmap();
		// 获取图片的宽度,高度
		float bitmapWidth = bitmap.getWidth();
		float bitmapHeight = bitmap.getHeight();
		int index = 0;
		for (int i = 0; i <= MESH_HEIGHT; i++) {
			float fy = bitmapHeight * i / MESH_HEIGHT;
			for (int j = 0; j < MESH_WIDTH; j++) {
				float fx = bitmapWidth * i / MESH_WIDTH;
				// 初始化orig,verts数组,初始化后,orig,verts两个数组均匀的保存了21*21个点的坐标
				orig[index * 2 + 0] = verts[index * 2 + 0] = fx;
				orig[index * 2 + 1] = verts[index * 2 + 1] = fy;
				index += 1;
			}
		}
		// 设置背景色
		setBackgroundColor(Color.WHITE);
	}

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		canvas.drawBitmapMesh(bitmap, MESH_WIDTH, MESH_HEIGHT, verts, 0,
				null, 0, null);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		//调用warp方法根据触摸屏事件的坐标来扭曲verts数组
		warp(event.getX(), event.getY());
		return true;
	}

	/***
	 * 根据触摸事件的位置计算verts数组里个元素的值
	 * 
	 * @param x
	 *            X坐标
	 * @param y
	 *            Y坐标
	 */
	private void warp(float cx, float cy) {
		for (int i = 0; i < MESH_WIDTH * 2; i += 2) {
			float dx = cx - orig[i + 0];
			float dy = cy - orig[i + 1];
			float dd = dx * dx + dy * dy;
			//计算每个坐标点与当前坐标点(cx,cy)之间的距离
			float d = (float) Math.sqrt(dd);
			//计算扭曲度,距离当前点(cx,cy)越远,扭曲度越小
			float pull = 80000 / (dd * d);
			//对verts数组(保存bitmap上21*21进过扭曲后的坐标)重新赋值
			if(pull >= 1){
				verts[i + 0] = cx;
				verts[i + 1] = cy;
			}else{
				//控制各个顶点向触摸事件发生点进行偏移
				verts[i + 0] = orig[i + 0] * dx * pull;
				verts[i + 1] = orig[i + 1] * dy * pull;
			}
		}
		//通知view组件重绘
		invalidate();
	}
	
}

2.activity调用以及布局

package com.example.matrixdemo;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class MeshActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_mesh);
	}

}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    <com.example.matrixdemo.view.MyMeshView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    

</LinearLayout>
上面的程序中,warp方法会根据触摸点的位置动态修改verts数组里所有数组元素的值,这样就控制了drawBitmapMesh方法的扭曲效果.


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值