Android Matrix图形变换

平移变换

从坐标(x0,y0)变换到(x, y)

x = x0 + △x

y =y0 + △y

坐标x,y写作矩阵C

[ x

  y

 1 ]

原坐标x0,y0写作矩阵B

[x0

  y0

  1 ]

想通过矩阵B得到矩阵C,根据矩阵乘法公式,

x = a*x0 + b*y0 + c*1, 由于x = x0 + △x, 所以推导出, a = 1, b = 0, c = △x

y = d*x0 + e*y0 + f * 1, 由于x = x0 + △x, 所以推导出, d = 0, e = 1, f = △y

1 = g *x0 + h*y0+ i*1 ,  所以推导出,g = 0, h = 0 ,i= 1

由上述推导可得左乘矩阵B的矩阵A为:

[ 1  0  △x

  0  1  △y

  0   0   1 ]


旋转变换


如图所示,点(x0, y0)以o为圆心,旋转θ角度,到一个新的点(x,y),四个点分别用公式表示:

x0 = rcosα

y0 = rsinα

x = rcos(α+θ) = rcosαcosθ - rsinαsinθ = x0cosθ - y0sinθ

y = rsin(α+θ) = rsinαcosθ + rcosαsinθ = y0cosθ + x0sinθ

坐标x,y写作矩阵C

[ x

  y

 1 ]

原坐标x0,y0写作矩阵B

[x0

  y0

  1 ]

想通过矩阵B得到矩阵C,根据矩阵乘法公式,

x = a*x0 + b*y0 + c*1, 由于x = x0cosθ - y0sinθ, 所以推导出, a = cosθ, b = -sinθ, c = 0

y = d*x0 + e*y0 + f * 1, 由于x = y0cosθ + x0sinθ, 所以推导出, d = sinθ, e = cosθ, f = 0

1 = g *x0 + h*y0+ i*1 ,  所以推导出,g = 0, h = 0 ,i= 1

由上述推导可得左乘矩阵B的矩阵A为:

[ cosθ  -sinθ 0

  sinθ   cosθ 0

  0       0     1 ]


缩放变换

如图,坐标公式

x = k*x0

y = k*y0

坐标x,y写作矩阵C

[ x

  y

 1 ]

原坐标x0,y0写作矩阵B

[x0

  y0

  1 ]

想通过矩阵B得到矩阵C,根据矩阵乘法公式,

x = a*x0 + b*y0 + c*1, 由于x = kx0, 所以推导出, a = k1, b =0, c = 0

y = d*x0 + e*y0 + f * 1, 由于x =ky0, 所以推导出, d = 0, e = k2, f = 0

1 = g *x0 + h*y0+ i*1 ,  所以推导出,g = 0, h = 0 ,i= 1

由上述推导可得左乘矩阵B的矩阵A为:

[ k1  0  0

  0  k2  0

  0  0  1 ]


错切变换

水平错切:

垂直错切:


如图,坐标公式:

x = x0 + k1 * y0

y = k2*x0 + y0

坐标x,y写作矩阵C

[ x

  y

 1 ]

原坐标x0,y0写作矩阵B

[x0

  y0

  1 ]

想通过矩阵B得到矩阵C,根据矩阵乘法公式,

x = a*x0 + b*y0 + c*1, 由于x = x0 + k1*y0 , 所以推导出, a = 1, b =k1, c = 0

y = d*x0 + e*y0 + f * 1, 由于y =k2*x0 + y0, 所以推导出, d = k2, e = 1, f = 0

1 = g *x0 + h*y0+ i*1 ,  所以推导出,g = 0, h = 0 ,i= 1

由上述推导可得左乘矩阵B的矩阵A为:

[ 1   k1  0

 k2  1    0

 0     0   1 ]


由以上几个推导看出,对于矩阵:

[ a   b  c

 d    e  f

 g    h   i ]

a,e 控制缩放,b,d 控制错切,c,f 控制平移


下面用一个例子说明:

创建一个Activity,布局文件:

<?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" >

    <ImageView
        android:id="@+id/imageview"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginTop="10dp"
        android:layout_weight="2" />

    <GridLayout
        android:id="@+id/group"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="3"
        android:columnCount="3"
        android:rowCount="3" >
    </GridLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="btnChange"
            android:text="Change" />

        <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="btnReset"
            android:text="Reset" />
    </LinearLayout>

</LinearLayout>
最上面是显示图片的ImageView,下方GridView中会通过代码增加3*3的EditText,用于控制Matrix的值。

Activity:

public class MainActivity extends ActionBarActivity {

	private ImageView mImageView;
	private GridLayout mGroup;
	private int mEtWidth, mEtHeight;
	private EditText[] mEts = new EditText[9];
	private float[] mMatrix = new float[9];
	private Bitmap originBitmap;

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

		mImageView = (ImageView) findViewById(R.id.imageview);
		mGroup = (GridLayout) findViewById(R.id.group);

		originBitmap = BitmapFactory.decodeResource(getResources(),
				R.drawable.dragon);
		mImageView.setImageBitmap(originBitmap);

		// GridView的大小确定后,
		// 计算其中每个EditText应有的大小(3*3),
		// 并添加到GridView中。
		mGroup.post(new Runnable() {

			@Override
			public void run() {
				mEtWidth = mGroup.getWidth() / 3;
				mEtHeight = mGroup.getHeight() / 3;
				addEts();
			}
		});
	}

	public void btnChange(View view) {
		getMatrix();
		setImageMatrix();
	}

	public void btnReset(View view) {
		initMatrix();
		getMatrix();
		setImageMatrix();
	}

	private void setImageMatrix() {

		Bitmap newBitmap = Bitmap.createBitmap(originBitmap.getWidth(),
				originBitmap.getHeight(), Bitmap.Config.ARGB_8888);
		
		// 设置新的Matrix.
		Matrix matrix = new Matrix();
		matrix.setValues(mMatrix);
		// 使用新的Matrix绘制Bitmap
		Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
		
		Canvas canvas = new Canvas(newBitmap);
		canvas.drawBitmap(originBitmap, matrix, paint);
		mImageView.setImageBitmap(newBitmap);
	}

	private void addEts() {
		for (int i = 0; i < 9; i++) {
			EditText editText = new EditText(MainActivity.this);
			editText.setBackground(getResources().getDrawable(
					R.drawable.common_textfield_background));
			//editText.setInputType(InputType.TYPE_CLASS_NUMBER
			//		| InputType.TYPE_NUMBER_FLAG_DECIMAL);
			mEts[i] = editText;
			mGroup.addView(editText, mEtWidth, mEtHeight);
		}
	}

	//将输入框内的数字组装成为Matrix内数组的值
	private void getMatrix() {
		for (int i = 0; i < 9; i++) {
			String etNum = mEts[i].getText().toString();
			if (TextUtils.isEmpty(etNum)) {
				mEts[i].setText("0");
				etNum = "0";
			}
			mMatrix[i] = Float.valueOf(etNum);
		}
	}

	/**
	 * 初始化矩阵数组为: [ 1 0 0 ,0 1 0 ,0 0 1 ]
	 * */
	private void initMatrix() {
		for (int i = 0; i < 9; i++) {
			if (i % 4 == 0) {
				mEts[i].setText(String.valueOf(1));
			} else {
				mEts[i].setText(String.valueOf(0));
			}
		}
	}
}

以上代码,setImageMatrix方法通过用户输入,改变Matrix内部数组的值,并用这个Matrix绘制新的Bitmap,来改变图片的显示效果。getMatrix方法用于把EditText里面的数字赋值到 mMatrix数组中。

下面运行这个例子,尝试几个变换:

缩放:

   

平移: 

 

错切:


旋转30°:


Matrix类提供了一些方法直接实现以上的一些效果:

setRotate:旋转

setSkew:错切

setTranslate:平移

setScale:缩放

如果需要做组合变换,可以使用postXXX/preXXX方法:

postScale/postSkew/postTranslate/postRotate

preScale/preSkew/preTranslate/preRotate

post和pre的区别是,以postTranslate为例,M' = T(dx, dy) * M,新的矩阵是由这个平移变换的矩阵左乘M矩阵,意味着最后做平移这个变换。

preTranslate:M' = M * T(dx, dy),右乘M矩阵,意味着这个平移变换在M的变换之前发生。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值