概要:
Matrix(矩阵),这是一个数学的概念。通常的nxm(n行,m列)行列数是不受限制的。但这里主要讲的是
3x3矩阵(3行3列)如图:
Matrix对图形的变形通常有如下四种形式:
Translate 平移变换
Rotate 旋转变换
Scale 缩放变换
Skew 错切变换
从字面上的意思,MACALE负责缩放,MSKEW负责错切,MARANS负责平移。MPERSP 用于处理透视的
变化(不常用),当然我们不能完全按照上面的意思理解。
针对上面的四种变换,Android进行了封装,提供三种方法setXX(设值),preXX(前乘),postXX(后乘)。
分析:
一,Translate 平移变换
将一个点 ,平移到点
的位置,如下图 :
由图可知:
用矩阵来表示的话:
二,Rotate 旋转变换
2.1将一个点 ,围绕坐标原点(0,0)顺时针旋转
,如图:
其中点到原点的距离为r。
到原点连线与X轴正方向的夹角为α。
有图可知:
用矩阵表示为:
2.2
将一个点 ,围绕坐标某个点
,顺时针旋转
,
有上面的推理可知:
矩阵变换:
分析矩阵表达式:
1.
是将坐标原点移动到点
后,
的新坐标。
2.
是将上一步变换后的,围绕新的坐标原点顺时针旋转
。
3.
所以,围绕某一点进行旋转变换,可以分成3个步骤,即首先将坐标原点移至该点,然后围绕新
的坐标原点进行旋转变换,再然后将坐标原点移回到原先的坐标原点。
三,Scale 缩放变换
理论上而言,一个点是不存在什么缩放变换的,这里需要说明一下,因为所有图像都是由点组成,所以图像的
变化是由所有的点的变化来完成的(这也是前面分析的都是点的变化的原因)。例如在这里如果图像在x轴和
y轴方向分别放大k1和k2倍的话,那么图像中的所有点的x坐标和y坐标均会分别放大k1和k2倍,即
用矩阵表示就是:
缩放变换比较好理解,就不多说了。
四,错切变化:
错切变换就是让所有点的x坐标(或者y坐标)保持不变,而对应的y坐标(或者x坐标)则按比例发生平移,
如图所示:
1,各点的y坐标保持不变,但其x坐标则按比例发生了平移。这种情况将水平错切。
2,各点的x坐标保持不变,但其y坐标则按比例发生了平移。这种情况叫垂直错切。
假定一个点经过错切变换后得到
,对于水平错切而言,应该有如下关系:
用矩阵表示为:
同理,对于垂直错切,可以有:
在数学上严格的错切变换就是上面这样的。在Android中除了有上面说到的情况外,还可以同时进行水平、
垂直错切,那么形式上就是:
应用:
我们设置九个EditText,自己设置矩阵。然后根据矩阵取变换图像。
public class MatrixActivity extends Activity {
private EditText[] edits;
private Button btn_change, btn_reset;
private ImageView imageView;
private static final String tag = "tag_matrix";
Matrix matrix = new Matrix();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_matrix);
initViews();
btn_change.setOnClickListener(onClickListener);
btn_reset.setOnClickListener(onClickListener);
}
private void initViews() {
imageView = (ImageView) findViewById(R.id.iv_show);
edits = new EditText[9];
for (int i = 0; i < 9; i++) {
edits[i] = (EditText) findViewById(R.id.et_0 + i);
}
btn_change = (Button) findViewById(R.id.btn_change);
btn_reset = (Button) findViewById(R.id.btn_reset);
}
View.OnClickListener onClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_change:
changeImage();
break;
case R.id.btn_reset:
resetImage();
break;
}
}
};
private void changeImage() {
LogUtil.i(tag, "changeImage");
float[] data = new float[9];
for (int i = 0; i < edits.length; i++) {
String txt = edits[i].getText().toString();
try {
if ("".equals(txt)) {
data[i] = 0;
} else {
data[i] = Float.parseFloat(txt);
}
} catch (Exception e) {
data[i] = 0;
}
LogUtil.i(tag, "" + data[i]);
}
matrix.setValues(data);
BitmapDrawable bitmapDrawable = (BitmapDrawable) getResources().getDrawable(R.drawable.aa2);
Bitmap tempBitmap = bitmapDrawable.getBitmap();
Bitmap bitmap = Bitmap.createBitmap(tempBitmap, 0, 0, tempBitmap.getWidth(), tempBitmap.getHeight(), matrix, true);
imageView.setImageBitmap(bitmap);
}
private void resetImage() {
LogUtil.i(tag, "resetImage");
matrix.reset();
imageView.setImageResource(R.drawable.aa2);
}
}
布局文件:res/layout/activity_matrix
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/ll_title"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="原图"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="变换图"/>
</LinearLayout>
<ImageView
android:id="@+id/iv_init"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:src="@drawable/aa2"
android:layout_margin="10dp"
android:layout_below="@+id/ll_title"/>
<RelativeLayout
android:layout_below="@+id/ll_title"
android:layout_toRightOf="@+id/iv_init"
android:layout_above="@+id/rl_input"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/iv_show"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:scaleType="center"
android:src="@drawable/aa2"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/rl_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/ll_button">
<EditText
android:id="@+id/et_0"
android:layout_width="50dp"
android:layout_marginLeft="20dp"
android:layout_height="wrap_content" />
<EditText
android:id="@+id/et_1"
android:layout_width="50dp"
android:layout_centerHorizontal="true"
android:layout_height="wrap_content" />
<EditText
android:id="@+id/et_2"
android:layout_width="50dp"
android:layout_height="wrap_content"
android:layout_marginRight="20dp"
android:layout_alignParentRight="true"/>
<EditText
android:id="@+id/et_3"
android:layout_width="50dp"
android:layout_margin="20dp"
android:layout_below="@+id/et_0"
android:layout_height="wrap_content" />
<EditText
android:id="@+id/et_4"
android:layout_width="50dp"
android:layout_margin="20dp"
android:layout_below="@+id/et_0"
android:layout_centerHorizontal="true"
android:layout_height="wrap_content" />
<EditText
android:id="@+id/et_5"
android:layout_width="50dp"
android:layout_margin="20dp"
android:layout_below="@+id/et_0"
android:layout_alignParentRight="true"
android:layout_height="wrap_content" />
<EditText
android:id="@+id/et_6"
android:layout_width="50dp"
android:layout_marginLeft="20dp"
android:layout_marginBottom="20dp"
android:layout_below="@+id/et_3"
android:layout_height="wrap_content" />
<EditText
android:id="@+id/et_7"
android:layout_width="50dp"
android:layout_below="@+id/et_3"
android:layout_centerHorizontal="true"
android:layout_height="wrap_content" />
<EditText
android:id="@+id/et_8"
android:layout_width="50dp"
android:layout_marginRight="20dp"
android:layout_marginBottom="20dp"
android:layout_below="@+id/et_3"
android:layout_alignParentRight="true"
android:layout_height="wrap_content" />
</RelativeLayout>
<LinearLayout
android:id="@+id/ll_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="horizontal">
<Button
android:id="@+id/btn_change"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="change"/>
<Button
android:id="@+id/btn_reset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="reset"/>
</LinearLayout>
</RelativeLayout>
3.1缩放变换(宽高同时放大到2倍):
3.3 绕原点旋转变换(旋转30度)。
4,平移变换,使用这种方式(
Bitmap.createBitmap(tempBitmap, 0, 0, tempBitmap.getWidth(), tempBitmap.getHeight(), matrix, true))不起作用(平移并不会改变图像本身)。
可改用Canvas。drawBitmap(Bitmap, Matrix, Paint);
我们修改一下代码:
BitmapDrawable bitmapDrawable = (BitmapDrawable) getResources().getDrawable(R.drawable.aa2);
Bitmap tempBitmap = bitmapDrawable.getBitmap();
Bitmap bitmap = Bitmap.createBitmap(tempBitmap.getWidth(), tempBitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
MatrixDrawable matrixDrawable = new MatrixDrawable(tempBitmap, matrix);
matrixDrawable.draw(canvas);
其中MatrixDrawable的代码:
public class MatrixDrawable extends Drawable {
private final static String tag = "MatrixDrawable";
//画笔
Paint mPaint;
//绘图矩阵
Matrix mMatrix;
//原始图
Bitmap mBitmap;
//矩阵元素存储的数组
float[] params;
Bitmap temp;
MatrixDrawable(Bitmap bitmap, Matrix matrix) {
this.mBitmap = bitmap;
this.mMatrix = matrix;
mPaint = new Paint();
mPaint.setAntiAlias(true);
}
@Override
public void draw(Canvas canvas) {
canvas.drawBitmap(mBitmap, mMatrix, mPaint);
}
@Override
public int getIntrinsicWidth() {
if (mBitmap != null) {
return mBitmap.getWidth();
}
return super.getIntrinsicWidth();
}
@Override
public int getIntrinsicHeight() {
if (mBitmap != null) {
return mBitmap.getHeight();
}
return super.getIntrinsicHeight();
}
@Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
mPaint.setColorFilter(colorFilter);
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
public Matrix getmMatrix(){
return mMatrix;
}
}
效果如图: