您的位置:首页 > 移动开发 > Android开发

关于Android Matrix pre post 的理解

2017-01-30 00:28 495 查看

前言

最近由于项目需要,接触到矩阵相关的知识较多,在实践后于此做知识记录,方便下次用到的时候可以快速的捡起来。

正文

理论上,矩阵是按照长方形阵列排列的复数或实数集合

实际上,从源码看Matrix是用于坐标变换的3x3的矩阵【本文讨论的是graphics包下的矩阵,非opengl包下的矩阵】

单个Matrix对象在Java层保留一个Native层对象的指针,在Native堆上进行实际内存分配、使用、回收

Java层内存是个【壳】,Native层内存才是【核】。

关键字段



3x3矩阵中各个字段





以(0,0)点为轴心旋转一定的角度,则实际改变的是左上角的四个数据

平移,右上角2个元素

旋转和缩放,左上角4个元素

数据结构

float数组,长度9

单位矩阵

⎡⎣⎢⎢100010001⎤⎦⎥⎥

第0、4、8号地址数值为1,其它地址为0的矩阵。单位矩阵乘法满足交换律。

变换过程

假设原坐标点为(x0, y0),作用于矩阵M,则变换后的坐标点为(x1, y1)。

(x1, y1, 1) = M * (x0, y0, 1)


这里的难点在于【M = M1 * M2 … * Mn】,即M可以为多个矩阵变换之后的结果。

计算顺序

M = M1 * M2 * ... * Mn
后 <------------------------------------ 先


从右边开始向左边进行矩阵的乘法运算,最后得出结果矩阵M

M(n-1) * Mn 结果为矩阵Nn

M(n-2) * Nn结果为矩阵N(n-1)

M(n-3) * N(n-1)结果为N(n-2)



为什么从右向左计算?个人理解是一种栈结构,每次做变换的时候都会在栈顶压入一个变换,最后退栈时依次从栈顶做矩阵乘法,故形成一种从右向左计算

先乘

M’ = M * X


当前矩阵为M

变换矩阵为X

在当前矩阵的【右边】乘以矩阵X,此处X可以为平移矩阵T, 旋转矩阵R, 缩放矩阵S

由于矩阵从右向左运算,故右边称之为【先】乘。

后乘

M’ = X * M


当前矩阵为M

变换矩阵为X

在当前矩阵的【左边】乘以矩阵X

由于矩阵从右边向左边运算,故左边称之为【后】乘。

Canvas中的矩阵变换

Canvas中的矩阵变换都是先乘,也就是在当前矩阵的右边乘以一个矩阵

在ViewRootImpl从上向下遍历的过程中,在draw阶段作用在同一个Canvas对象的不同矩阵将不断放在右边

最后从右向左计算出整个变换矩阵M

然后拿着M矩阵对原坐标(x0, y0)进行矩阵变换

利用这个属性可以对某个View进行缩放截图等。

例如,Canvas中相对某点做缩放的代码

/**
572     * Preconcat the current matrix with the specified scale.
573     *
574     * @param sx The amount to scale in X
575     * @param sy The amount to scale in Y
576     * @param px The x-coord for the pivot point (unchanged by the scale)
577     * @param py The y-coord for the pivot point (unchanged by the scale)
578     */
579    public final void scale(float sx, float sy, float px, float py) {
580        translate(px, py);
581        scale(sx, sy);
582        translate(-px, -py);
583    }


translate

551    /**
552     * Preconcat the current matrix with the specified translation
553     *
554     * @param dx The distance to translate in X
555     * @param dy The distance to translate in Y
556    */
557    public void translate(float dx, float dy) {
558        native_translate(mNativeCanvasWrapper, dx, dy);
559    }


scale

561    /**
562     * Preconcat the current matrix with the specified scale.
563     *
564     * @param sx The amount to scale in X
565     * @param sy The amount to scale in Y
566     */
567    public void scale(float sx, float sy) {
568        native_scale(mNativeCanvasWrapper, sx, sy);
569    }


上面以(px, py)为轴心进行缩放(sx, sy)的scale API内部做的事情:

假如当前矩阵为M1,则

translate(px, py)

M1 * T1 先乘平移矩阵T1

scale(sx, sy)

M1 * T1 * S 先乘缩放矩阵S

translate(-px, -py)

M1 * T1 * S * T2 先乘平移矩阵T2

这三步的结果M = M1 * (T1 * (S * T2))

把M作用在原来的坐标点 (x1, y1, 1) = M * (x0, y0, 1)
从感官上理解经过以下步骤

整个图像先做相对于轴点的逆向平移变换

再做缩放变换

最后把图像平移回轴点,这样轴点的坐标不会受到缩放的影响。

pre和post混合使用

在理解了pre和post实际做的事情之后,就可以分析别人代码在干什么以及合理使用矩阵来达到你想要的效果。

例如我们要做变换M = T1 * R1 * S1 则写法可以是:

【注】

sin 30° = 0.5

cos 30° = 0.8660254

2 * sin 30° = 1.0

2 * cos 30° = 1.7320508

第一种写法

Matrix matrix = new Matrix();
matrix.preTranslate(1,1);
matrix.preRotate(30);
matrix.preScale(2, 2);


结果矩阵为:

Matrix{[1.7320508, -1.0, 1.0][1.0, 1.7320508, 1.0][0.0, 0.0, 1.0]}


第二种写法

Matrix matrix = new Matrix();
matrix.postScale(2, 2);
matrix.postRotate(30);
matrix.postTranslate(1,1);


结果矩阵为:

Matrix{[1.7320508, -1.0, 1.0][1.0, 1.7320508, 1.0][0.0, 0.0, 1.0]}


第三种写法

Matrix matrix = new Matrix();
matrix.preScale(2, 2);
matrix.postRotate(30);
matrix.postTranslate(1,1);


结果矩阵为:

Matrix{[1.7320508, -1.0, 1.0][1.0, 1.7320508, 1.0][0.0, 0.0, 1.0]}


第四种写法

Matrix matrix = new Matrix();
matrix.preRotate(30);
matrix.postTranslate(1,1);
matrix.preScale(2, 2);


结果矩阵为:

Matrix{[1.7320508, -1.0, 1.0][1.0, 1.7320508, 1.0][0.0, 0.0, 1.0]}




总结

上文通过对Matrix的一些基本概念和pre、post变换的原理进行记录,方便根据需求做相应的变换来处理Bitmap和Canvas。因本人水平有限,难免理解不到位,请大家多多指正。

参考

android matrix 最全方法详解与进阶(完整篇)

Android UI学习|对Canvas和Matrix的理解
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android java matrix