平移变换
从坐标(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的变换之前发生。