android matrix

1 概述

这里咱们会详细讲解matrix的各个方法,以及它的用法。matrix叫作矩阵,在前面讲解 ColorFilter 的文章中,咱们讲解了ColorMatrix,他是一个4*5的矩阵。而这里,咱们讲解的Matrix不是用于处理颜色的,而是处理图形的。他是一个3*3的矩阵。html

2 原理

先看看matrix的矩阵是什么样子的:canvas

这里写图片描述

这里能够查看Matrix的代码获得。那么这个矩阵分别表明了什么呢,这里经过他们的名字能够看出,scale是缩放,skew是错切(canvas变换中有讲过),trans是平移,persp表明透视(官方文档中,也没有详细讲解,透视在这里只作简单介绍)。这里须要把矩阵根据他们的做用划分为4块:api

这里写图片描述

如上图所示,这四块区域各有做用。后面会详细讲解各个做用,先来看看这个矩阵是如何影响图像的。先看看屏幕的坐标系:数组

这里写图片描述

看上图,这里表示了屏幕的坐标系,其中的x,y轴是你们所熟知的,可是其实,一个物体他是存在于一个三维空间的,因此必然会有z轴。咱们的屏幕,就像是一个窗口,透过它,咱们看到了屏幕后面的世界,那里面有各类物体,咱们看到的是映射在x,y平面上的一个投射图像。屏幕就像是一个镜头同样,将里面的物体映射到x,y平面上,成为一个二维的图像。那么若是,咱们把屏幕这个镜头沿着z轴,拉远或者拉进,那么图像会有什么变化呢,确定会变小或者变大。就比如坐在飞机上透过窗口看地面的汽车,和在地面上看到的大小是不一样的。app

结论就是,在屏幕上显示的像素,不只仅有x,y坐标,其实还有z轴的影响。因此这里对应的像素描述由一个3行一列的矩阵来表示:ide

这里写图片描述

x,y分别表明x,y轴上的坐标,而1表明屏幕在z轴上的坐标为默认的。若是将1变大,那么屏幕会拉远, 图形会变小。函数

如今咱们来看看matrix怎么做用于每一个像素的值。这里须要用到矩阵的乘法,首先须要明确的是,矩阵的前乘和后乘是不相同的,也就是说不知足乘法交换律。post

这里咱们经过一个旋转变换来看看原理,其实一张图片围绕一个点旋转,也就是全部的点都围绕一个点旋转,因此只须要关注一个点的状况便可:spa

假定有一个点 ,相对坐标原点顺时针旋转后的情形,同时假定P点离坐标原点的距离为r,以下图:.net

这里写图片描述

那么就有:

这里写图片描述

换作矩阵运算就以下图:

这里写图片描述

从这里就能够看出,矩阵中的值,是如何做用于像素点的x,y坐标以及z轴远近。

同时,能够看到,上面的矩阵四块区域的切分也是由于矩阵乘法的操做决定的,因为这里的乘法运算中,左上角的四个值,能够和x,y值作乘法运算,因此能够影响到旋转等操做,而右上角的模块,只能作加法,因此只能影响到平移。右下角的模块主要管z轴,天然就能够进行等比的缩放了,左下角的模块通常不去动他,不然会把x,y值加入到z轴中来,会不可控。

3 基本方法解析

讲解完了matrix做用于像素点的原理以后,咱们逐个讲解它的方法。

(1) 构造函数

public Matrix()
public Matrix(Matrix src)

构造函数有两个,第一个是直接建立一个单位矩阵,第二个是根据提供的矩阵建立一个新的矩阵(采用deep copy)

单位矩阵以下:

这里写图片描述

(2) isIdentity与isAffine

public boolean isIdentity()//判断是不是单位矩阵
public boolean isAffine()//判断是不是仿射矩阵

是不是单位矩阵很简单,就不作讲解了,这里是不是仿射矩阵可能你们很差理解。

首先来看看什么是仿射变换。仿射变换其实就是二维坐标到二维坐标的线性变换,保持二维图形的“平直性”(即变换后直线仍是直线不会打弯,圆弧仍是圆弧)和“平行性”(指保持二维图形间的相对位置关系不变,平行线仍是平行线,而直线上点的位置顺序不变),能够经过一系列的原子变换的复合来实现,原子变换就包括:平移、缩放、翻转、旋转和错切。这里除了透视能够改变z轴之外,其余的变换基本都是上述的原子变换,因此,只要最后一行是0,0,1则是仿射矩阵。

(3) rectStaysRect

public boolean rectStaysRect()

判断该矩阵是否能够将一个矩形依然变换为一个矩形。当矩阵是单位矩阵,或者只进行平移,缩放,以及旋转90度的倍数的时候,返回true。

(4) reset

public void reset()

重置矩阵为单位矩阵。

(5) setTranslate

public void setTranslate(float dx, float dy)

设置平移效果,参数分别是x,y上的平移量。 
效果图以下:

这里写图片描述

代码以下:

Matrix matrix = new Matrix();
canvas.drawBitmap(bitmap, matrix, paint);

matrix.setTranslate(100, 1000);
canvas.drawBitmap(bitmap, matrix, paint);

(6) setScale

public void setScale(float sx, float sy, float px, float py)
public void setScale(float sx, float sy)

两个方法都是设置缩放到matrix中,sx,sy表明了缩放的倍数,px,py表明缩放的中心。这里跟上面比较相似不作讲解了。

(7) setRotate

 public void setRotate(float degrees, float px, float py)
 public void setRotate(float degrees)

和上面相似,再也不讲解。

(8) setSinCos

public void setSinCos(float sinValue, float cosValue, float px, float py)
public void setSinCos(float sinValue, float cosValue)

这个方法乍一看可能有点蒙,其实在前面的原理中,咱们讲解了一个旋转的例子,他最终的矩阵效果是这样的:

这里写图片描述

其实旋转,就是使用了这样的matrix,显而易见,这里的参数就清晰了。 
sinValue:对应图中的sin值 
cosValue:对应cos值 
px:中心的x坐标 
py:中心的y坐标

看一个示例,咱们把图像旋转90度,那么90度对应的sin和cos分别是1和0。

这里写图片描述

看代码以下:

Matrixmatrix = new Matrix();
matrix.setSinCos(1, 0, bitmap.getWidth() / 2, bitmap.getHeight() / 2);
canvas.drawBitmap(bitmap, matrix, paint);

(9) setSkew

public void setSkew(float kx, float ky, float px, float py)
public void setSkew(float kx, float ky)

错切,这里kx,ky分别表明了x,y上的错切因子,px,py表明了错切的中心。不了解错切了在前面canvas变换中去查看,这里再也不讲解。

(10) setConcat

public boolean setConcat(Matrix a,Matrix b)

将当前matrix的值变为a和b的乘积,它的意义在下面的 进阶方法中来探讨。

4 进阶方法解析

上面的基本方法中,有关于变换的set方法均可以带来不一样的效果,可是每一个set都会把上个效果清除掉,例如依次调用了setSkew,setTranslate,那么最终只有setTranslate会起做用,那么如何才和将两种效果复合呢。Matrix给咱们提供了不少方法。可是主要都是2类:

preXXXX:以pre开头,例如preTranslate 
postXXXX:以post开头,例如postScale

他们分别表明了前乘,和后乘。看一段代码:

Matrix matrix = new Matrix();
matrix.setTranslate(100, 1000);
matrix.preScale(0.5f, 0.5f);

这里matrix前乘了一个scale矩阵,换算成数学式以下:

这里写图片描述

从上面能够看出,最终得出的matrix既包含了缩放信息也有平移信息。 
后乘天然就是matrix在后面,而缩放矩阵在前面,因为矩阵先后乘并不等价,也就致使了他们的效果不一样。咱们来看看后乘的结果:

这里写图片描述

能够看到,结果跟上面不一样,而且这也不是咱们想要的结果,这里缩放没有更改,可是平移被减半了,换句话说,平移的距离也被缩放了。因此须要注意先后乘法的关系。

来看看他们对应的效果图:

前乘:

这里写图片描述

后乘:

这里写图片描述

能够明显看到,后乘的平移距离受了影响。

了解清除了先后乘的意义,在使用的过程当中,多个效果的叠加时,同样要注意,不然效果达不到预期。

5 其余方法解析

matrix除了上面的方法外,还有一些其余的方法,这里依次解析

(1) setRectToRect

public boolean setRectToRect(RectF src, RectF dst, ScaleToFit stf)

将rect变换成rect,上面的rectStaysRect已经说过,要保持rect只能作缩放平移和选择90度的倍数,那么这里其实也是同样,只是这几种变化,这里经过stf参数来控制。

ScaleToFit 有以下四个值:

FILL: 可能会变换矩形的长宽比,保证变换和目标矩阵长宽一致。 
START:保持坐标变换前矩形的长宽比,并最大限度的填充变换后的矩形。至少有一边和目标矩形重叠。左上对齐。 
CENTER: 保持坐标变换前矩形的长宽比,并最大限度的填充变换后的矩形。至少有一边和目标矩形重叠。 
END:保持坐标变换前矩形的长宽比,并最大限度的填充变换后的矩形。至少有一边和目标矩形重叠。右下对齐。

这里使用谷歌的api demo的图片做为例子:

这里写图片描述

(2) setPolyToPoly

public boolean setPolyToPoly(float[] src, int srcIndex,float[] dst, int dstIndex,int pointCount)

经过指定的0-4个点,原始坐标以及变化后的坐标,来获得一个变换矩阵。若是指定0个点则没有效果。

下面经过例子分别说明1到4个点的能够达到的效果:

这里写代码片##### 1个点,平移 
只指定一个点,能够达到平移效果:

这里写图片描述

代码以下:

float[] src = {0, 0};
int DX = 300;
float[] dst = {0 + DX, 0 + DX};
matrix.setPolyToPoly(src, 0, dst, 0, 1);
canvas.drawBitmap(bitmap, matrix, paint);
2个点,旋转或者缩放

两个点,能够达到旋转效果或者缩放效果,缩放比较简单,这里咱们来看旋转效果,一个点指定中心,一点指出旋转的效果

这里写图片描述

代码以下

int bw = bitmap.getWidth();
int bh = bitmap.getHeight();
float[] src = {bw / 2, bh / 2, bw, 0};
float[] dst = {bw / 2, bh / 2, bw / 2 + bh / 2, bh / 2 + bw / 2};
matrix.setPolyToPoly(src, 0, dst, 0, 2);
canvas.drawBitmap(bitmap, matrix, paint);

图片的中心点做为旋转的中心,先后不变,右上角变化到了下方,因此致使图片旋转了90度。

3个点,错切

使用3个点,能够产生错切效果,指定3个顶点,一个固定,另外两个移动。

看图:

这里写图片描述

代码以下:

Matrix matrix = new Matrix();
int bw = bitmap.getWidth();
int bh = bitmap.getHeight();
float[] src = {0,0, 0, bh,bw,bh};
float[] dst = {0, 0, 200, bh, bw + 200, bh};
matrix.setPolyToPoly(src, 0, dst, 0, 3);
canvas.drawBitmap(bitmap, matrix, paint);
4个点,透视

透视就是观察的角度变化了。致使投射到平面上的二维图像变化了。

咱们看下面的例子,更容易理解:

这里写图片描述

图片看起来好像倾斜了,实现特别简单:

Matrix matrix = new Matrix();
int bw = bitmap.getWidth();
int bh = bitmap.getHeight();
float[] src = {0, 0, 0, bh, bw, bh, bw, 0};
int DX = 100;
float[] dst = {0 + DX, 0, 0, bh, bw, bh, bw - DX, 0};
matrix.setPolyToPoly(src, 0, dst, 0, 4);
canvas.drawBitmap(bitmap, matrix, paint);

能够看到,只是把左右两个顶点往里面收拢了,这样就得出了一个有3d效果的透视图。

(3) invert

public boolean invert(Matrix inverse)

反转当前矩阵,若是能反转就返回true并将反转后的值写入inverse,不然返回false。当前矩阵*inverse=单位矩阵。

反转先后有什么效果,咱们来看看示例:

这里写图片描述

能够看到,反转以后,实际上是对效果的一种反转。

(4) mapPoints

public void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex,int pointCount)
public void mapPoints(float[] dst, float[] src)
public void mapPoints(float[] pts)

映射点的值到指定的数组中,这个方法能够在矩阵变换之后,给出指定点的值。 
dst:指定写入的数组 
dstIndex:写入的起始索引,x,y两个坐标算做一对,索引的单位是对,也就是通过两个值才加1 
src:指定要计算的点 
srcIndex:要计算的点的索引 
pointCount:须要计算的点的个数,每一个点有两个值,x和y。

(5) mapVectors

public void mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex,int vectorCount)
public void mapVectors(float[] dst, float[] src)
public void mapVectors(float[] vecs)

与上面的mapPoionts基本相似,这里是将一个矩阵做用于一个向量,因为向量的平移先后是相等的,因此这个方法不会对translate相关的方法产生反应,若是只是调用了translate相关的方法,那么获得的值和本来的一致。

(6) mapRect

public boolean mapRect(RectF dst, RectF src)
public boolean mapRect(RectF rect)

返回值便是调用的rectStaysRect(),这个方法前面有讲过,这里把src中指定的矩形的左上角和右下角的两个点的坐标,写入dst中。

(7) mapRadius

public float mapRadius(float radius)

返回一个圆圈半径的平均值,将matrix做用于一个指定radius半径的圆,随后返回的平均半径。

以上基本解析完毕了全部matrix的方法,以及一些高阶用法,本篇文章就到这里

相关文章
相关标签/搜索