Matrix源码解析

正文之前

这两天看了深入理解 Android 中的 MatrixMatrix原理。这两篇文章写的都挺好的,但是我看完了,还有一些地方没明白,所以今天就好好看了一下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放到特定的位置。然后返回的结果是:

Paste_Image.png

仔细观察其他几个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    }
Paste_Image.png
Paste_Image.png

把四种情况综合起来:


较为特殊的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_MATRIXMatrix中存在的太明显了,而且用的也很多。Matrix的构造方法:

   /**
     * Create an identity matrix
     */
    public Matrix() {
        native_instance = native_create(0);
    }

构造出的矩阵长这样:

Paste_Image.png

这种情况下pre和post操作的结果是一样的。

需要说明的是,虽然看上去pre和post操作可以写成矩阵链相乘的形式,但是实际上还是按照出现的先后顺序计算的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容