正文之前
这两天看了深入理解 Android 中的 Matrix和Matrix原理。这两篇文章写的都挺好的,但是我看完了,还有一些地方没明白,所以今天就好好看了一下native层的源代码。我始终觉得源码是解答很多问题的最好途径。
Matrix
首先看一下官方对Matrix的解释:
The Matrix class holds a 3x3 matrix for transforming coordinates.
从解释可知Matrix是一个3x3的矩阵。矩阵的每一个位置的值是干什么的呢?这一个疑问。那么就从他的用途来找答案
preXXX和postXXX源码解读
以preTranslate
为例解答上面的疑问。来看一下上层的preTranslate
源码:
/**
* Preconcats the matrix with the specified translation.
* M' = M * T(dx, dy)
*/
public boolean preTranslate(float dx, float dy) {
native_preTranslate(native_instance, dx, dy);
return true;
}
param dx
,dy
分别是x,y方向的位移。调用了native层的native_preTranslate
:
386 static void native_preTranslate(long native_object, float dx, float dy) {
387 Matrix_Delegate d = sManager.getDelegate(native_object);
388 if (d != null) {
389 d.preTransform(getTranslate(dx, dy));
390 }
391 }
先看getTranslate(dx, dy)
:
952 /*package*/ static float[] setTranslate(float[] dest, float dx, float dy) {
953 dest[0] = 1;
954 dest[1] = 0;
955 dest[2] = dx;
956 dest[3] = 0;
957 dest[4] = 1;
958 dest[5] = dy;
959 dest[6] = 0;
960 dest[7] = 0;
961 dest[8] = 1;
962 return dest;
963 }
这里构造了一个数组或者说是一个3x3的矩阵,然后将dx
,dy
放到特定的位置。然后返回的结果是:
仔细观察其他几个preXX
,他们最终都会调用preTransform
。
preTransform
源码:
831 /**
832 * Adds the given transformation to the current Matrix
833 * <p/>This in effect does this = matrix*this
834 * @param matrix
835 */
836 private void preTransform(float[] matrix) {
837 float[] tmp = new float[9];
838 multiply(tmp, matrix, mValues);
839 mValues = tmp;
840 }
代码思路很简单,先调用multiply(tmp, matrix, mValues);
计算矩阵相乘的结果,然后把结果再赋给mValues
矩阵。
其实矩阵相乘本身是个数学问题,而且很简单,但是我还是想列出来,因为这里我认为作者在写注释的时候并没有注意左乘和右乘在写法上的区别,以至于在上层api的代码中对左乘和右乘的解释中,pre、postpost与native层中的pre、post是相反的。
920 /**
921 * multiply two matrices and store them in a 3rd.
922 * <p/>This in effect does dest = a*b
923 * dest cannot be the same as a or b.
924 */
925 /*package*/ static void multiply(float dest[], float[] a, float[] b) {
926 // first row
927 dest[0] = b[0] * a[0] + b[1] * a[3] + b[2] * a[6];
928 dest[1] = b[0] * a[1] + b[1] * a[4] + b[2] * a[7];
929 dest[2] = b[0] * a[2] + b[1] * a[5] + b[2] * a[8];
930
931 // 2nd row
932 dest[3] = b[3] * a[0] + b[4] * a[3] + b[5] * a[6];
933 dest[4] = b[3] * a[1] + b[4] * a[4] + b[5] * a[7];
934 dest[5] = b[3] * a[2] + b[4] * a[5] + b[5] * a[8];
935
936 // 3rd row
937 dest[6] = b[6] * a[0] + b[7] * a[3] + b[8] * a[6];
938 dest[7] = b[6] * a[1] + b[7] * a[4] + b[8] * a[7];
939 dest[8] = b[6] * a[2] + b[7] * a[5] + b[8] * a[8];
940 }
从代码中很明显的能看出作者想计算的应该是ba而不是ab**。这样就导致了在上层中对于操作的解释不同:
native
层对pre的解释:
/ * Adds the given transformation to the current Matrix
* <p/>This in effect does this = matrix*this
* @param matrix
*/
上层api对pre的解释:
/ * Preconcats the matrix with the specified translation.
* M' = M * T(dx, dy)
*/
其他几个操作需要构造的矩阵如下:
这里读者可以仔细揣摩一下。
我认为引入3x3矩阵的好处是: 把三种操作归并到矩阵相乘
post操作只不过是吧mValues
和构造出的矩阵在作为参数传入multiply
的时候交换了位置。
几种操作对应需要构造的矩阵:
965 /*package*/ static float[] getScale(float sx, float sy) {
966 return new float[] { sx, 0, 0, 0, sy, 0, 0, 0, 1 };
967 }
1015 /*package*/ static float[] setRotate(float[] dest, float sin, float cos) {
1016 dest[0] = cos;
1017 dest[1] = -sin;
1018 dest[2] = 0;
1019 dest[3] = sin;
1020 dest[4] = cos;
1021 dest[5] = 0;
1022 dest[6] = 0;
1023 dest[7] = 0;
1024 dest[8] = 1;
1025 return dest;
1026 }
1049 /*package*/ static float[] getSkew(float kx, float ky) {
1050 return new float[] { 1, kx, 0, ky, 1, 0, 0, 0, 1 };
1051 }
把四种情况综合起来:
较为特殊的Scale和Skew
为什么说Scale和Skew较为特殊呢?因为在api中多提供了一个方法:
/**
* Preconcats the matrix with the specified scale.
* M' = M * S(sx, sy, px, py)
*/
public boolean preScale(float sx, float sy, float px, float py) {
native_preScale(native_instance, sx, sy, px, py);
return true;
}
/**
* Preconcats the matrix with the specified skew.
* M' = M * K(kx, ky, px, py)
*/
public boolean preSkew(float kx, float ky, float px, float py) {
native_preSkew(native_instance, kx, ky, px, py);
return true;
}
对着native代码对这个函数进行解释:
976 static float[] getScale(float sx, float sy, float px, float py) {
977 float[] tmp = new float[9];
978 float[] tmp2 = new float[9];
979
980 // TODO: do it in one pass
981
982 // translate tmp so that the pivot is in 0,0
983 setTranslate(tmp, -px, -py);
984
985 // scale into tmp2
986 multiply(tmp2, tmp, getScale(sx, sy));
987
988 // translate back the pivot back into tmp
989 multiply(tmp, tmp2, getTranslate(px, py));
990
991 return tmp;
992 }
分为三次矩阵相乘,第一步把中心点移动到(0 , 0),然后缩放,最后把中心点移回原处。
IDENTITY_MATRIX(单位矩阵)
单位矩阵I和矩阵M相乘,得到的结果还是M。所以有IXM= MXI=M
IDENTITY_MATRIX
在Matrix
中存在的太明显了,而且用的也很多。Matrix
的构造方法:
/**
* Create an identity matrix
*/
public Matrix() {
native_instance = native_create(0);
}
构造出的矩阵长这样:
这种情况下pre和post操作的结果是一样的。
需要说明的是,虽然看上去pre和post操作可以写成矩阵链相乘的形式,但是实际上还是按照出现的先后顺序计算的。