Android 图形处理 —— Matirx 方法详解及应用场景
上一篇文章 《Matrix 原理剖析》 介绍了 Matrix 的基础原理,本文介绍 Matrix 一些常用方法以及具体的使用场景
Matrix 方法详解
文中部分内容及图片参考自: blog.csdn.net/gb702250823…
方法类别 |
相关 API |
摘要 |
---|---|---|
数值操作 |
set、reset、setValues、getValues |
设置、重置、设置数值、获取数值 |
数值计算 |
mapPoints、mapRadius、mapRect、mapVectors |
计算变换后的数值 |
设置 |
setConcat、setRotates、setScale、setSkew、setTranslate |
设置变换 |
前乘 |
preConcat、preRotate、preScale、preSkew、preTranslate |
前乘变换 |
后乘 |
postConcat、postRotate、postScale、postSkew、postTranslate |
后乘变换 |
特殊方法 |
setPolyToPoly、setRectToRect |
一些特殊操作 |
数值操作
void set(Matrix src)
深拷贝一份
src
中的数据到当前 Matrix 中, 如果
src
为
null
, 则相当于
reset
void reset()
将当前 Matrix 重置成一个单位矩阵
void setValues(float[] values)
将一组浮点数值的前 9 位数据拷贝到 Matrix 中,如果数组长度小于 9,调用该方法会抛出异常
void getValues(float[] values)
从 Matrix 中拷贝数据到
values
浮点数组中
数值计算
mapXXX
有很多签名版本,具体区别可以参考源码注释,这里据其中一个常用的签名方法作为说明,下同
void mapPoints(float[] dst, float[] src)
把当前 Matrix 应用到
src
所指示的所有坐标上,然后将变换后的坐标复制到
dst
数组上
数组中每两个相邻的点表示一个坐标
(x,y)
,因此数组长度一般都是偶数,否则最后一个数值不参与计算
float mapRadius(float radius)
把当前 Matrix 应用到半径为
radius
所指示的圆上,然后返回变换之后的圆的半径,由于圆可能会因为画布变换变成椭圆,所以此处测量的是平均半径
boolean mapRect(RectF dst, RectF src)
和
mapPoints
类似,把当前 Matrix 应用到
src
所指示的四个顶点上,然后将变换后的四个顶点值写入
dst
中,返回值是判断矩形经过变换后是否仍为矩形
void mapVectors(float[] dst, float[] src)
和
mapPoinst
、
mapRect
类似,这里测试计算的是向量,不再赘述
设置
setRotates、setScale、setSkew、setTranslate
这几个方法比较好理解,就是将变换设置给当前 Matrix,得到一个变换后的 Matrix
boolean setConcat(Matrix a, Matrix b)
相当于计算两个矩阵相乘
a × b
,并将结果赋值给当前矩阵,即
c.setConcat(a, b)
表示
c = a × b
前乘后乘
这两类计算也比较好理解,就是对应了数学中的矩阵前乘和后乘
特殊方法
boolean setPolyToPoly
boolean setPolyToPoly (
float[] src, // 原始顶点数组 src [x, y]
int srcIndex, // 原始顶点数组开始位置
float[] dst, // 目标顶点数组 dst [x, y]
int dstIndex, // 目标顶点数组开始位置
int pointCount) // 测控顶点的数量 取值范围是: 0 到 4
复制代码
Poly
全称是
Polygon
,多边形的意思。
调用这个方法后,会计算从原始顶点和到目标顶点的变换(意味着
src
和
dst
要一一对应),把这种变换信息存储到当前 Matrix 中;将得到 的 Matrix 应用到任意图形上,可以实现把这个图形进行 Matrix 所表示的形状变换,效果如下图所示
其中
pointCount
不同数值也代表了不同能力的变换,一般我们最常用的
pointCount
都是 4,0~3 我们都可以用更加简单的方法来代替实现
pointCount |
摘要 |
---|---|
0 |
相当于 reset |
1 |
相当于 Translate |
2 |
可以进行 缩放、旋转、平移 变换 |
3 |
可以进行 缩放、旋转、平移、错切 变换 |
4 |
可以进行 缩放、旋转、平移、错切以及任何形变 |
测控点的选取
测控点可以选择任何你认为方便的位置,只要
src
与
dst
一一对应即可。不过为了方便,通常会选择一些特殊的点: 图形的四个角,边线的中心点以及图形的中心点等。不过有一点需要注意,测控点选取都应当是不重复的 (
src
与
dst
均是如此),如果选取了重复的点会直接导致测量失效,这也意味着,你不允许将一个方形 (四个点) 映射为三角形(四个点,但其中两个位置重叠),但可以接近于三角形。
作用范围
作用范围是设置了 Matrix 的全部区域,如果你将这个 Matrix 赋值给了 Canvas,它的作用范围就是整个画布,如果你赋值给了 Bitmap,它的作用范围就是整张图片
boolean setRectToRect(RectF src, RectF dst, ScaleToFit stf)
了解了
setPolyToPoly
之后,这个方法就比较好理解,简单来说就是将源矩形的内容填充到目标矩形中,然而在大多数的情况下,源矩形和目标矩形的长宽比是不一致的,到底该如何填充呢,这个填充的模式就由第三个参数
stf
来确定
ScaleToFit
是一个枚举类型,共包含了四种模式:
模式 |
效果 |
---|---|
CENTER |
居中,对 src 等比例缩放,并最大限度的填充变换后的矩形,将其居中放置在 dst 中 |
START |
顶部,对 src 等比例缩放,并最大限度的填充变换后的矩形,将其放置在 dst 的左上角,左上对齐 |
END |
底部,对 src 等比例缩放,并最大限度的填充变换后的矩形,将其放置在 dst 的右下角,右下对齐 |
FILL |
充满,拉伸 src 的宽和高,使其完全填充满 dst |
一图胜千言:
Matrix 在 Android 中的使用场景
其实我们日常开发中或多或少已经接触了 Matrix,只是大部分我们都还不知道,比如我们使用的
ImageView
的
ScaleType
,实际上内部就是通过 Matrix 实现的
除此之外,Matrix 的应用十分广泛,这里没法一一列举,大家在学习了基本原理和常用 api 之后可以自行想象和实践,网上也有很对 Matrix 应用的有趣的例子。
这里笔者分享一下自己在实际开发中用到 Matrix 的例子 —— 相机扫描识别二维码
当我在开发这个功能的时候,遇到一个棘手的问题:当相机实时预览识别到二维码之后,需要将当前帧截取下来当成静态背景图,然后在识别到二维码的位置上显示一个小黄点,类似微信扫码的效果:
这里首先需要介绍下,相机识别二维码的大致流程
相机取景框实时取景 -> 图像帧预处理(包括裁剪、灰度化等)-> 扫码 SDK 分析预处理后的帧图像 -> 识别到二维码,返回二维码信息(包括在图中的位置等) -> 将当前图像原始帧设置为背景图 -> 在图上二维码位置出绘制小黄点
由于 SDK 分析的是裁剪灰度化过后的图像,因此返回的二维码位置信息也是基于裁剪过后的坐标系,如果我们直接把这个坐标绘制在屏幕上,必然会发现二维码位置不对
因此这里就涉及到 坐标映射 : 我们需要把裁剪后的坐标映射回手机屏幕坐标
先看看我们当前有哪些数据:
- 裁剪后的图像
- 二维码位置信息,是一组顶点(上下左右四个位置的点 x,y )
- 取景框尺寸
我们可以分析出,这里发生了变化的是两个矩形:取景框和裁剪后的图像
根据之前学到的内容,我们可以使用
setPolyToPoly
或者
setRectToRect
来描述这一变换,这里我们以
setPolyToPoly
举例,伪代码如下:
// 实例化一个原始矩阵(单位矩阵)
val matrix = Matrix()
// cropRect 是裁剪后的图像的矩形
val source = floatArrayOf(
cropRect.left.toFloat(),
cropRect.top.toFloat(),
cropRect.right.toFloat(),
cropRect.top.toFloat(),
cropRect.right.toFloat(),
cropRect.bottom.toFloat(),
cropRect.left.toFloat(),
cropRect.bottom.toFloat()
// previewView 是取景框视图
val destination = floatArrayOf(
previewView.width.toFloat(),
previewView.width.toFloat(),
previewView.height.toFloat(),
previewView.height.toFloat()
// 使用 setPolyToPoly 描述这种变换,得到一个矩阵 Matrix