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操作可以写成矩阵链相乘的形式,但是实际上还是按照出现的先后顺序计算的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容