2.3 Matrix类中函数用法详解(二)
在2.2节中,我们讲解了Matrix类的一些常用功能函数的用法,但还有其他的一些Matrix功能函数,在本节中我们将继续讲解。
注意:如果在使用Matrix类的功能函数时,发现函数效果不同,请关闭硬件加速重试。
2.3.1 mapPoints
mapPoint函数的声明如下:
该函数的作用是计算出输入的系列点位置在当前矩阵变换情况下的新位置(因为点必然是由两个数值x、y组成的,所以参数中float数组的长度一般都是偶数,若为奇数,则最后一个元素不参与计算)。
2.3.1.1 void mapPoints(float[] pts)
void mapPoints(float[] pts)函数仅有一个参数pts数组,其作为参数用于传递原始数值,计算结果仍存放在pts数组中:
输出结果如下:
在进行了Matrix映射后,得到Matrix操作后的各点位置并保存在pts数组中。可见,pts既是输入参数,也是输出参数。
2.3.1.2 void mapPoints(float[] dst,float[] src)
void mapPoints(float[] dst,float[] src)函数中参数的意义如下。
●float[] dst:用于保存输出结果的数组。
●float[] src:原始点坐标数组。
在有两个参数时,就有入参和出参的区别了。src作为参数传递原始数据,计算结果存放在dst数组中,src不会发生变化,因为它不会改变原始点坐标数组的内容,所以一般用此函数较多。
示例代码如下:
在这里,分别将操作前和操作后的数组内容全部输出,结果如下:
2.3.1.3 void mapPoints (float[] dst,int dstIndex,float[] src,int srcIndex,int pointCount)
void mapPoints(float[] dst,int dstIndex,float[] src,int srcIndex,int pointCount)函数中参数的意义如下。
●float[] dst:目标数据。
●int dstIndex:目标数据存储位置起始下标。
●float[] src:原始数据。
●int srcIndex:原始数据存储位置起始下标。
●int pointCount:计算的点个数。
很明显,这个函数能够截取目标数组和原始数组中的一部分进行操作和存储,并不是对全部数组进行操作的。
示例:将第2个点、第3个点计算后得到的数据存储进dst数组最开始的位置。
输出结果如下:
2.3.2 其他map相关函数
除了mapPoints函数外,还有其他一系列map相关函数,都基于当前的矩阵,用于计算出当前操作下对应矩阵的值。
2.3.2.1 mapRadius
mapRadius函数的声明如下:
该函数用于测量一个指定半径(radius)的圆在执行完当前矩阵变换后所对应的圆半径。因为我们通过Scale操作能够分别缩放X轴、Y轴的标尺密度,所以圆经过操作后很可能变成椭圆,而此处测量的是平均半径。
示例如下:
输出结果如下:
2.3.2.2 mapRect
mapRect函数的声明如下:
该函数用于获取源矩形src经Matrix操作后对应的矩形dst。
同样地,当只有一个参数时,入参和出参是同一个变量。当有两个参数时,入参与出参不同。
返回值可用于判断矩形经过变换后的形状,即是否仍是矩形。
示例如下:
输出结果如下:
2.3.2.3 mapVectors
mapVectors函数用于测量向量。向量是线性代数中的基本概念,该函数用得不多,所以这里就不扩展了,仅列举其用法:
可以看到,mapVectors函数与mapPoints函数的用法基本相同,可以直接参考上面的mapPoints函数。
2.3.3 setPolyToPoly
setPolyToPoly函数的声明如下:
Poly是Polygon的简写,代表多边形。setPolyToPoly函数的作用是通过多点映射的方式来直接设置如何进行变换。“多点映射”的意思就是把指定的点移动到给定的位置,从而发生形变。
例如,(0,0)→(100,100)表示把(0,0)位置的像素移动到(100,100)位置。这是单点映射,单点映射可以实现平移,而多点映射则可以让绘制内容任意扭曲。
在参数中,float[] src和int srcIndex定义了原始数组及其开始位置,而float[] dst和int dstIndex则定义了目标数组及其开始位置。
需要非常注意的是,这里不像上面的map相关函数,求在当前矩阵下原始数组的目标数组位置,这里的原始数组和目标数组都是有值的。
setPolyToPoly函数主要用于图像的变换,根据图像4个点的映射来求出对应的Matrix数组。
假设src数组的内容是{0,0,400,0,0,400,400,400},它对应一个正方形图像的4个点,left-top是(0,0)、right-bottom是(400,400),而它所要映射的dst数组的内容是{100,200,450,440,120,720,810,940},这4个点就很难想象了,如果画出来,会是下面这样的,如图2-24所示。
图2-24
扫码查看彩色图
从图2-24可以看出,黑框及其标注表示src数组中的4个点所对应的正方形,而红框及其标注对应dst数组表示的四边形。
下面就利用这两个数组来看看setPolyToPoly函数的具体用法。
1.pointCount取1时
当pointCount取1时,有如下示例:
这里总共分为3个步骤,分别介绍如下。
第1步,利用setPolyToPoly函数的映射关系求出Matrix数组:
在setPolyToPoly函数中,pointCount的取值为1,表示仅映射一个点,即将原始数组中的(0,0)映射为(100,200),而在该函数执行完之后,matrix的值就是要完成两个点映射所需的矩阵,这个矩阵就是当前的matrix。从最后的结果可以看出,这个矩阵如下:
很容易知道,在这个矩阵的基础上,(0,0)点确实会被映射为(100,200)点。有关矩阵运算及位置矩阵中各个标志位的含义,在2.1节中已经详细介绍过,如果不了解的话,可以回顾一下。
第2步,根据matrix,重新打印出换算后的点阵列表并进行对比。
为了看看setPolyToPoly函数中pointCount参数的具体作用,我们利用上面学到的mapPoints函数,在计算出来的matrix的基础上重新计算原始数组,然后与dst数组进行对比:
输出结果如下:
扫码查看彩色图
after数组是通过matrix.mapPoints生成的,通过将其与dst数组进行对比,可以看出,第1个点的坐标位置是完全相同的。这就是pointCount参数的意义,它的作用就是指定当前原始数组和目标数组中有几个点参与了映射。而setPolyToPoly函数的作用就是根据这些映射的点生成对应的矩阵。
2.pointCount取2时
同样的代码,如果我们将setPolyToPoly函数中的pointCount值设为2,即:
此时的结果如下:
扫码查看彩色图
同样地,从after与dst的对比可以看出,标红的两个点是完全相同的,而其他值是不对应的,这是因为pointCount为2,在计算matrix时,只使用src与dst数组中的前两个点来映射,从而生成matrix矩阵。
3.pointCount取3时
如果我们将setPolyToPoly函数中的pointCount值设为3,即:
此时的结果如下:
扫码查看彩色图
可见,在after与dst中,前3个点都是相互对应的。
4.pointCount取4时
如果我们将setPolyToPoly函数中的pointCount值设为4,即:
此时的结果如下:
扫码查看彩色图
可见,此时的after与dst是完全相同的。setPolyToPoly函数对控件和图像做变换,只需要知道4个点即可,pointCount的最大取值即为4。
5.pointCount取0时
当pointCount取0时,表示src数组与dst数组中没有点相互映射,这表示原始矩阵只需要保持不变即可,即不会改变原矩阵的内容。我们前面说过,凡set系列函数都会将matrix中的原始矩阵内容清空,所以当pointCount为0时,调用setPolyToPoly函数后,matrix会变为单位矩阵。
我们尝试一下,将setPolyToPoly中的pointCount值设为0,即:
此时的结果如下:
可以看到,matrix所表示的矩阵是原始矩阵。before与after两个数组的内容是完全相同的。这就说明了,当pointCount取0时,会将数组复原,反映到图像上则是将图像还原到初始状态。
2.3.4 setRectToRect
setRectToRect函数的声明如下:
简单来说,就是将源矩形的内容填充到目标矩形中,这个函数的作用是根据源矩形映射到目标矩形区域的时候,生成所需的矩阵。其原理与setPolyToPoly函数的相同,都是根据映射关系生成矩阵的函数。
然而在大多数情况下,源矩形和目标矩形的长宽比是不一致的,那么到底该如何填充呢?填充模式其实是由第3个参数stf来确定的。
ScaleToFit是一个枚举类型,共包含4种模式,如表2-1所示。
表2-1 ScaleToFit包含的模式详情
下面来看看不同宽高比的src与dst在不同模式下是怎样的。
假设灰色部分是dst,橙色部分是src,由于是测试不同的宽高比,因此示例中让dst保持不变,观察两种宽高比的src在不同模式下的填充情况,如表2-2所示。
表2-2 两种宽高比的src在不同模式下的填充情况
下面给出居中示例的相关代码:
这段代码的核心点主要在onDraw函数中。为了观察setRectToRect函数的作用,我们先用Canvas.drawColor(Color.YELLOW)来将整个View涂为黄色,以便清楚地看出View所占空间。接着,src矩形是Bitmap原本的大小,dst矩形的大小跟整个View一样。然后,利用setRectToRect将Bitmap放在View中,模式是Matrix.ScaleToFit.CENTER,以生成对应的矩阵。
最后,根据matrix来绘图,效果如图2-25所示。
图2-25
扫码查看彩色图
其实,很容易理解,这个函数的作用相当于我们把一个Bitmap放在一个ImageView中,Bitmap会根据ImageView的不同模式进行缩放和摆放,而这里就是生成缩放和摆放位置的矩阵。
2.3.5 其他函数
2.3.5.1 rectStaysRect
rectStaysRect函数的声明如下:
该函数用于判断在矩形应用了当前的矩阵之后是否还是矩形。
假如通过矩阵进行了平移、缩放操作,则画布仅仅位置和大小改变,矩形变换后仍然为矩形,但假如通过矩阵进行了非90°倍数的旋转或者错切操作,则矩形变换后就不再是矩形了。前面的mapRect函数的返回值就是根据rectStaysRect来判断的。
2.3.5.2 isIdentity
isIdentity函数的声明如下:
该函数主要用于判断矩阵是否为单位矩阵。当我们利用Matrix matrix=new Matrix()创建一个原始矩阵的时候,创建的矩阵就是单位矩阵:
示例如下:
输出结果如下:
2.3.5.3 getValues
getValues函数的声明如下:
float[] values是入参同时也是出参,该函数会将它的矩阵元素逐个复制到values数组的前9位中。获取的矩阵元素位置与values数组索引的对应关系如下:
示例如下:
这段代码比较简单,主要是组装一个矩阵,然后利用matrix.toShortString将矩阵打印出来,同时也利用getValues函数获取这个矩阵的元素并打印出来,进行对比,可以看到values数组元素的存储顺序与矩阵元素位置的对应关系。
输出结果如下:
2.3.5.4 setValues
setValues函数的声明如下:
很明然,getValues和setValues是一对函数。setValues的参数是浮点型的一维数组,长度需要大于9,复制数组的前9位元素并赋值给当前的矩阵。
2.3.5.5 set
set函数的声明如下:
该函数没有返回值但有一个参数,作用是将参数src的数值复制到当前的矩阵中。如果参数为空,则重置当前矩阵,相当于reset函数起到的作用。
2.3.5.6 toString
toString函数的声明如下:
该函数用于将矩阵转换为字符串:
2.3.5.7 toShortString
toShortString函数的声明如下:
该函数用于将矩阵转换为短字符串:
2.3.5.8 hashCode
hashCode函数的声明如下:
该函数用于获取矩阵的散列值。
2.3.5.9 equals
equals函数的声明如下:
该函数用于比较两个矩阵的元素是否相同。
2.3.6 Matrix与Canvas
前面已经基本介绍了Matrix具有的函数,在位置变换相关的类中,Canvas与Camera都具有位置操作相关功能。
至于Camera,我们已经详细介绍过,它主要用来执行3D坐标系中的操作,但最终是用Matrix来实现在屏幕上显示的。
而Canvas也具有一些操作位置函数,它直接利用Matrix来实现,Canvas的这些函数如下。
移动相关:
缩放相关:
旋转相关:
错切相关:
矩阵相关:
可以看到,Canvas中这些与位置相关的操作,都只是Matrix中操作的“缩影”,所以如果可以直接使用Canvas中的相关函数来实现变位、变换需求的话,可以直接使用;如果不能实现,则需要考虑自己操作Matrix来实现。
到这里,有关Matrix的基本用法就讲解完了。下面通过实例来看看Matrix的具体应用场景。