从最基本的开始,将点P(x,y) x轴移动a个单位,y轴移动b个单位到(x',y')处,会移动吧,无非就是x'=x+a;y'=y+b;用矩阵表示如下
x' | 1 0 a | | x |
y' = | 0 1 b | * | y |
1 | 0 0 1 | | 1 |
很显然通过矩阵乘法可以得到如下结论:x'=x+a;y'=y+b;(在计算机中,一个矩阵实际上就是一个二维数组。一个m行n列的矩阵与一个n行p列的矩阵可以相乘,得到的结果是一个m行p列的矩阵,其中的第i行第j列位置上的数为第一个矩阵第i行上的n个数与第二个矩阵第j列上的n个数对应相乘后所得的n个乘积之和。比如,下面的算式表示一个2行2列的矩阵乘以2行3列的矩阵,其结果是一个2行3列的矩阵。)。中间含有a、b的那个矩阵就是参数矩阵,也就对应着android提供的Matrix类。
有个这个最基本的矩阵等式后,就可以推出N多等式来,比如放大操作(x'=x*a;y'=y*a),旋转操作(x’ = x cosθ+ y sinθ;y'=-xsinθ+ycosθ)等,只要摆好刚才那个矩阵乘法等式然后填充参数矩阵的相关参数即可。
比如android中将原图像水平翻转,可以这样写:
float[] floats=new float[] { -1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f };
Matrix matrix = new Matrix();
matrix.setValues(floats);
return Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix, true);
其中floats数组就对应了刚才介绍那个参数矩阵。
当然android封装了很多方法来简化我们的代码,比如matrix.setTranslate(a,b);就相当于将(x,y)x轴移动a,y轴移动b。完全等价于matrix.setValues(new float[1,0,a,0,1,b,0,0,1]);
OK,单次更新图像就到这了。很简单吧。。下面就是稍微复杂点的复合更新。
将点P(x,y)绕着点(a,b)旋转θ角度。为了达到这个目的,一般分为三步来执行。先将坐标系原点移动到(a,b)点,然后图像绕原点旋转θ角度,然后再将坐标原点移回原来的位置。其中,移动坐标原点到(a,b)点,相当于将图像x轴移动-a,y轴移动-b。将上述三步表示成矩阵为:
1 0 -a cosθ -sinθ 0 1 0 a
0 -1 -b sinθ cosθ 0 0 -1 b
0 0 1 0 0 1 0 0 1
下面引入一个定理,很重要:设对给定的图像依次进行了基本变化F1、F2、F3…..、Fn,它们的变化矩阵分别为T1、T2、T3…..、Tn,图像复合
变化的矩阵T 可以表示为:T = TnTn-1…T1。
意思就是矩阵乘法是要按照步骤的顺序倒序来乘的。那么上面的三步表示为矩阵乘法就是:
1 0 a cosθ -sinθ 0 1 0 -a
0 -1 b * sinθ cosθ 0 * 0 -1 -b
0 0 1 0 0 1 0 0 1
OK,有兴趣的童鞋可以用几何知识验证一下。
有了上面的知识,就可以来理解android Matrix里的pre和post的用法和区别了。由于矩阵乘法不满足交换律,即AB!=BA.pre和post就是针对这个问题而产生的。
pre就是先做,当然在矩阵乘法中对应着右乘法。post是后做,当然在矩阵乘法中对应着左乘。于是上面提到的问题用android提供的方法可以写为:
matrix.setTranslate(a,b);
matrix.preRotate(θ);
matrix.preTranslate(-a,-b);
即先将图像x轴移动-a,y轴移动-b(具体操作是将原点移动到(a,b));然后绕原点旋转θ角;然后将坐标系移动到原点(原点移动(-a,-b),图像移动(a,b))。
有一个问题要特别注意,就是调用setXXXXXXX时,会内部调用reset()将矩阵重置。所以setXXXXX一般是放到最前面写(只是写到前面,不代表步骤是在前面),并且在一个复合变化中只调用一次。
camera类,这个类位于package android.graphics 包下,用于图像3D变换,通过旋转、偏移等方式,最终通过getMatrix(Matrix)方法拿到矩阵,作用于canvas,使得画面呈现出立体效果。
接着我们来看看Camera变换的几个轴坐标,图有点难看哈。