注册

Android 图形处理 —— Matrix 原理剖析

Matrix 简介


Android 图形库中的 android.graphics.Matrix 是一个 3×3 的 float 矩阵,其主要作用是坐标变换


它的结构大概是这样的


matrix


其中每个位置的数值作用和其名称所代表的的含义是一一对应的



  • MSCALE_X、MSCALE_Y:控制缩放
  • MTRANS_X、MTRANS_Y:控制平移
  • MSKEW_X、MSKEW_X:控制错切
  • MSCALE_X、MSCALE_Y、MSKEW_X、MSKEW_X:控制旋转
  • MPERSP_0、MPERSP_1、MPERSP_2:控制透视

matrix_1


在 Android 中,我们直接实例化一个 Matrix,内部的矩阵长这样:


matrix_3


是一个左上到右下为 1,其余为 0 的矩阵,也叫单位矩阵,一般数学上表示为 I


Matrix 坐标变换原理


前面说到 Matirx 主要的作用就是处理坐标的变换,而坐标的基本变换有:平移、缩放、旋转和错切



这里所说的基本变换,也称仿射变换 ,透视不属于仿射变化,关于透视相关的内容不在本文的范围内



当矩阵的最后一行是 0,0,1 代表该矩阵是仿射矩阵,下文中所有的矩阵默认都是仿射矩阵


线性代数中的矩阵乘法


在正式介绍 Matrix 是如何控制坐标变换的原理之前,我们先简单复习一下线性代数中的矩阵乘法,详细的讲解可参见维基百科或者翻翻大学的《线性代数》,这里只做最简单的介绍




  • 两个矩阵相乘,前提是第一个矩阵的列数等于第二个矩阵的行数




  • 若 A 为 m × n 的矩阵,B 为 n × p 的矩阵,则他们的乘积 AB 会是一个 m × p 的矩阵,表达可以写为





  • 由定义计算,AB 中任意一点(a,b)的值为 A 中第 a 行的数和 B 中第 b 列的数的乘积的和







了解矩阵乘法的基本方法之后,我们还需要记住几个性质,对后续的分析有用



  • 满足结合律,即 A(BC)=(AB)C
  • 满足分配律,即 A(B + C) = AB + AC (A + B)C = AC + BC
  • 不满足交换律,即 AB != BA
  • 单位矩阵 I 与任意矩阵相乘,等于矩阵本身,即 IA = ABI = B

缩放(Scale)


我们先想想,让我们实现把一个点 (x0, y0) 的 x 轴和 y 轴分别缩放 k1 和 k2 倍,我们会怎么做,很简单


val x = k1 * x0
val y = k2 * y0

那如果用矩阵怎么实现呢,前面我们讲到 Matrix 中 MSCALE_XMSCALE_Y 是用来控制缩放的,我们在这里填分别设置为 k1 和 k2,看起来是这样的


image-20211109103257621

而点 (x0, y0) 用矩阵表示是这样的


image-20211109103824496

有些人会疑问,最后一行这里不是还有一个 1 吗,这是使用了齐次坐标系的缘故,在数学中我们的点和向量都是这样表示的 (x, y),两者看起来一样,计算机无法区分,为了让计算机也可以区分它们,增加了一个标志位,即


(x, y, 1) -> 点
(x, y, 0) -> 向量

现在 Matrix 和点都可以用矩阵表示了,接下来我们看看怎么通过这两个矩阵得到一个缩放之后的点 (x, y). 前面我们已经介绍过矩阵的乘法,让我们看看把上面两个矩阵相乘会得到什么结果


image-20211109104922576

可以看到,矩阵相乘得到了一个(k1x0, k2y0,1)的矩阵,上面说过,计算机中,这个矩阵就代表点 (k1x0, k2y0), 而这个点刚好就是我们要的缩放之后的点


以上所有过程用代码来实现,看起来就是像下面这样


val xy = FloatArray(x0, y0)
Matrix().apply {
setScale(k1, k2)
mapPoints(xy)
}

平移(Translate)


平移和缩放也是类似的,实现平移,我们一般可写为


val x = x0 + deltaX
val y = y0 + deltaY

而用矩阵来实现则是


val xy = FloatArray(x0, y0)
Matrix().apply {
setTranslate(k1, k2)
mapPoints(xy)
}

换成数学表示


translate


根据矩阵乘法


x = 1 × x0 + 0 × y0 + deltaX × 1 = x0 + deltaX
y = 0 × x0 + 1 × y0 + deltaY × 1 = y0 + deltaY

可得和一开始的实现也是效果一致的


错切(Skew)


错切相对于平移和缩放,可能大部分人对这个名词比较陌生,直接看三张图大家可能会比较直观


水平错切


x = x0 + ky0
y = y0

矩阵表示



水平错切


垂直错切


x = x0
y = kx0 + y0

矩阵表示




复合错切


x = x0 + k1y0
y = k2x0 + y0

矩阵表示




旋转(Rotate)


旋转相对以上三种变化又有一点复杂,这里涉及一些三角函数的计算,忘记的可以去维基百科 先复习下



image-20211108215739508

同样我们先自己实现一下旋转,假设一个点 A(x0, y0), 距离原点的距离为 r,与水平夹角为 α,现绕原点顺时针旋转 θ 度,旋转之后的点为 B(x, y)



用矩阵表示




Matrix 复合操作原理


前面介绍了四种基本变换,如果我们需要同时应用上多种变化,比如先绕原点顺时针旋转 90° 再 x 轴平移 100,y 轴平移 100, 最后 x、y 轴缩放0.5 倍,那么就需要用到复合操作


还是先用自己的实现来实现一下


x = ((x0 · cosθ - y0 · sinθ) + 100) · 0.5
y = ((y0 · cosθ + x0 · sinθ) + 100) · 0.5

矩阵表示


image-20211206155715836


按照前面的方式逐个推导,最终也能得到和上述一样的结果


到此,我们可以对 Matrix 做出一个基本的认识:Matrix 基于矩阵计算的原理,解决了计算机中坐标映射和变化的问题


作者:GeeJoe
链接:https://juejin.cn/post/7038557734517604382
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册