笛卡尔坐标系就是直角坐标系和斜角坐标系的统称。 相交于原点的两条数轴,构成了平面放射坐标系。如两条数轴上的度量单位相等,则称
此放射坐标系为笛卡尔坐标系。两条数轴互相垂直的笛卡尔坐标系,称为笛卡尔直角坐标系,否则称为笛卡尔斜角坐标系。需要指出的是,请> 将数学中的笛卡尔坐标系与电影《异次元杀阵》中的笛卡尔坐标相区分,电影中的定义与数学中定义有出入,请勿混淆。二维的直角坐标系是由两条相互垂直、0 点重合的数轴构成的。在平面内,任何一点的坐标是根据数轴上对应的点的坐标设定的。在平面内,> 任何一点与坐标的对应关系,类似于数轴上点与坐标的对应关系。采用直角坐标,几何形状可以用代数公式明确的表达出来。几何形状的每一> 个点的直角坐标必须遵守这代数公式。
在笛卡尔坐标系中,线性变换的矩阵的表现形式可以这样:
但是,在笛卡尔坐标系中,平移变换却不能用两个矩阵的乘法表示。平移变换T的表现形式为
尽管,平移变换的矩阵表现形式和线性变换不一样,但是, 2 X 2 的矩阵是足以表示它们,为什么 Matrix 是个 3 X 3 的矩阵?
在图形学中,所涉及的变换包括平移、旋转、缩放。当以矩阵表达式来计算时,平移是矩阵相加,旋转和缩放则是矩阵相乘。如果将平移、旋转和缩放综合起来,可以这么表示:
其中:
- m1:旋转或缩放矩阵
- m2:平移矩阵
- p :原向量
- p’:变换后的向量
在图形学中,将两种简单变换的叠加:一个是线性变换,一个是平移变换,统称为“仿射变换” 。为了解决平移变换不能使用乘法的问题,引入了齐次坐标。
“齐次坐标表示是计算机图形学的重要手段之一,它既能够用来明确区分向量和点,同时也更易用于进行仿射(线性)几何变换。”——F.S. Hill, JR。
根据规则,定义坐标(1,2)可以使用(1,2,1),也可以使用(2,4,2),还可以使用(4,8,4),(8,16,8)…,即 (k,2k,k),k∈R(k,2k,k),k∈R 都是“合法”的齐次坐标表示,这些点都映射到欧式空间中的一点,即这些点具有 尺度不变性(Scale Invariant),是“齐性的”(同族的),所以称之为齐次坐标。
在齐次坐标系中,线性变换是如何表示的呢?比如,旋转和缩放:
这时,平移变换也可以使用矩阵乘法表示,也就是这样:
对于一个仿射变换T,可以表示成一个线性变换A后平移t:T(p)=Ap+t,其中p是待变换的点齐次坐标表示。T可以表示成如下的形式:
其中:
Matrix中9个值与仿射变换T的对应关系是这样的:
每个值的作用为:
- MTRANS_X、MTRANS_Y:作用于平移变换(Translate)
- MSCALE_X、MSCALE_Y:作用于缩放变换(Scale)
- MSCALE_X、MSKEW_X、MSCALE_Y、MSKEW_Y:作用于旋转变换(Rotate)
- MSKEW_X、MSKEW_Y:作用于错切变换(Skew)
平移变换
假定有一个点的坐标是,将其移动到,再假定在x轴和y轴方向移动的大小分别为 dx和dy。此时
那么:
用矩阵表示就是:
旋转变换
围绕原点旋转变换
假定有一个点的坐标是,其与x轴的夹角为α,假设p点与原点的距离为r,当p绕着原点顺时针旋转θ,达到。
此时:
如果用矩阵表示:
围绕某个点旋转
假定有一个点,绕着点,沿着顺时针方向旋转θ,达到。
前面已经了解到平移变换和围绕原点旋转变换,那么,我们可以这么做了,将坐标原点移到,将它作为原点。此时:
那么:
对于的计算是这样的:
将坐标原点移到:
将绕着新坐标原点,沿着顺时针方向旋转θ:
将坐标原点移回到原先的坐标原点:
缩放
简单缩放
简单缩放可以直接通过将缩放系数sx,sy与对应x,y坐标相乘:
用矩阵表示就是:
基于某个点缩放
当然,我们需要在一个固定点进行缩放,那么就需要我们选择一个在缩放变换后不改变位置的点,来控制缩放后对象的位置。
得到的公式则是:
用矩形表示就是:
错切变换
错切变换(skew)在数学上又称为Shear mapping(可译为“剪切变换”)或者Transvection(缩并),它是一种比较特殊的线性变换。错切变换的效果就是让所有点的x坐标(或者y坐标)保持不变,而对应的y坐标(或者x坐标)则按比例发生平移,且平移的大小和该点到x轴(或y轴)的垂直距离成正比。错切变换,属于等面积变换,即一个形状在错切变换的前后,其面积是相等的。
在平面上,水平错切(或平行于X轴的错切)是一个将任一点映射到点的操作,m 是固定参数,称为错切因子。
水平错切的效果是将每一点水平移动,移动的长度和该点的纵坐标成比例。
- 则x轴上方的所有点都向右移动,而x坐标轴下的点位置不变
- 则x轴上方的所有点都向左移动, 轴下方点移动的方向对应相反,而x坐标轴上的点位置不变
- 平行于x轴的直线保持不变,其他所有线绕与x轴交点转动不同的角度
- 原来竖直的线则变成斜率1/m的斜线,如此参数, 即竖直线倾斜后的倾角,称为错切角。
假定一个点经过错切变换后得到,对于水平错切而言,应该有如下关系:
矩阵表示就是:
竖直错切的操作类似,就是将x和y互换位置。
其矩阵表示形式为:
刚才提到的水平错切还是垂直错切都是单一方向的错切(x轴或者y轴)。在Android中了,除了使用单一错切外,可能会有混合错切,即水平错切和垂直错切的混合效果。用矩形表示就是:
为什么会有前置后置之分?
对于乘法交换率,应该是十分的熟悉: AB=BA。如果A和B是矩阵,是否依然满足呢?例如:
这也就是,对于矩阵而言:
两个矩阵相乘并不能满足乘法交换律,哪个在前面,哪个在后面,还是值得重视的。Matrix给我们提供了很多方法,尤其注意到了这一点:
- preXX:以pre开头,表示矩阵相乘时其前置,例如preTranslate
- postXX:以post开头,表示矩阵相乘时其后置,例如postScale
相关API
构造函数
- Matrix()
- Matrix(Matrix src)
Matrix类提供了两个构造函数,第一个构造函数用来创建单位矩阵,第二个构造函数用来创建新的矩阵,并将指定的矩阵的内容赋值给新建的矩阵。
单位矩阵:
getValues
- getValues(float[] values)
getValues方法就是获取矩阵中的9个值,并将它们保存到指定的数组values中。
例如,创建了一个单位矩阵,获取它的9个值:
override fun initData() {
super.initData()
val matrix = Matrix()
val values = FloatArray(9)
matrix.getValues(values)
val sb = StringBuffer()
values.joinTo(sb)
// Log: 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0
Log.i("tea", "values: $sb")
}
invert
- invert(Matrix inverse)
invert方法用来判断矩阵是否可逆。如果可以则返回true。当矩阵可逆,而且inverse不为null时,则将矩阵的逆矩阵保存在inverse中。
所谓的逆矩阵就是:矩阵A为n阶方阵,若存在n阶矩阵B,使得矩阵A、B的乘积为单位阵,则称A为可逆阵,B为A的逆矩阵。若方阵的逆阵存
在,则称为可逆矩阵或非奇异矩阵,且其逆矩阵唯一。
例如:
val matrix = Matrix()
val matrixInverse = Matrix()
matrix.setTranslate(3f, 3f)
// 矩阵为[1.0, 0.0, 3.0, 0.0, 1.0, 3.0, 0.0, 0.0, 1.0]
// 判断矩阵是否可逆
val isInverse = matrix.invert(matrixInverse)
Log.e("tea", "isInverse: $isInverse")
// 打印逆矩阵的值
val sb = StringBuffer()
val values = FloatArray(9)
matrixInverse.getValues(values)
values.joinTo(sb)
// Log: 1.0, 0.0, -3.0, 0.0, 1.0, -3.0, 0.0, 0.0, 1.0
Log.i("tea", "values: $sb")
## isAffine & isIdentity - isAffine():判断是否是仿射矩阵 - isIdentity():判断是否是单位矩阵 isIdentity方法用来判断矩阵是否为单位矩阵,不必多说。 isAffine用来判断矩阵是否为仿射变换矩阵。
仿射变换,可以保持原来的线共点、点共线的关系不变,保持原来相互平行的线仍然平行,保持原来的中点仍然是中点,保持原来在一直线上几段线段之间的比例关系不变。但是仿射变换不能保持原线段的长度不变,也不能保持原来的夹角角度不变。
前面已经提到,仿射变换是线性变换和平移变换的叠加,用矩阵表示就是:
就是仿射变换矩阵。
mapXXX
mapPoints
mapPoint方法用于矩阵应用于2D点数组,并将转换后的点保存到相应的2D点数组中。
它有3个变形:
mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex, int pointCount):此矩阵将作用于点数组src,变换从 位置为srcIndex的点开始,共变换pointCount个点并将变换后的点保存到点数组dst中
- dst:目标点数组
- dstIndex:用于保存变换后点的起始索引
- src:原点数组
- srcIndex:从第几个点开始变换
- pointCount:变换的点的个数
- mapPoints(float[] dst, float[] src):它是变形1的特列,此矩阵将作用于点数组src,并将变换后的点保存到点数组dst中
- mapPoints(float[] pts): 此矩阵将作用于点数组pts,并将变换后的点保存到dst中
例如:
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
// 创建两个矩阵
val matrix = Matrix()
// 设置平移效果,沿着x轴向右平移100f,沿着y轴向下平移120f
matrix.setTranslate(100f, 120f)
// 定义点数组
val points = floatArrayOf(100f, 100f, 400f, 300f, 400f, 300f, 600f, 50f)
val pointMap = FloatArray(points.size)
// 将矩阵作用于点数组points
// 其效果是所有的的点平移,沿着x轴向右平移100f,沿着y轴向下平移120f
matrix.mapPoints(pointMap, points)
canvas?.drawLines(points, mPaint)
canvas?.drawLines(pointMap, mPaintMap)
}
mapRadius
- mapRadius(float radius)
mapRadius方法用于矩阵作用于圆的的半径,并变换后的半径值返回。例如,以(300f, 300f)为圆心,以100f为半径绘制圆:
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
val radius = 100f
canvas?.drawCircle(300f, 300f, radius, mPaint)
val matrix = Matrix()
// 缩放倍数为 sx = 0.5, sy = 0.5
matrix.setScale(0.5f, 0.5f)
val radiusScale = matrix.mapRadius(radius)
canvas?.drawCircle(300f, 300f, radiusScale, mPaintMap)
}
缩放矩阵的缩放系数为(0.5f, 0.5f),此时再以以(300f, 300f)为圆心,以缩放后的半径绘制一个圆。
当缩放系数为(0.3f, 0.8f)时:
当缩放系数为(0.8f, 0.3f)时:
mapRect
mapRect方法是将矩阵作用于指定的矩形,并将变换后的矩形保存。
- mapRect(RectF rect)
- mapRect(RectF dst, RectF src)
该方法有2个重载方法,前者是将矩阵作用于指定的矩形rect,并将变换后的矩形保存到rect中。后者是将矩阵作用于源矩形src,并将变换后的矩形保存到目的矩形 dst,此时源矩形是不变的。
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
val rectSrc = RectF(100f, 100f, 400f, 300f)
val rectDst = RectF()
// Log: before - rectSrc: RectF(100.0, 100.0, 400.0, 300.0)
Log.i("teaphy", "before - rectSrc: $rectSrc")
// Log: before - rectDst: RectF(0.0, 0.0, 0.0, 0.0)
Log.i("teaphy", "before - rectDst: $rectDst")
val matrix = Matrix()
// 设置缩放,x轴的缩放系数为0.5f,y轴的错切系数为0.5f
matrix.setScale(0.5f, 0.5f)
matrix.mapRect(rectDst, rectSrc)
// Log: after - rectSrc: RectF(100.0, 100.0, 400.0, 300.0)
Log.i("teaphy", "after - rectSrc: $rectSrc")
// Log: after - rectDst: RectF(50.0, 50.0, 200.0, 150.0)
Log.i("teaphy", "after - rectDst: $rectDst")
canvas?.drawRect(rectSrc, mPaint)
canvas?.drawRect(rectDst, mPaintMap)
}
如果将缩放矩阵作用于矩形时,矩形的端点坐标将做相应的缩放。如下图:
这里尤其要注意的是:不管是线性变换,还是平移变换,甚至是仿射变换,作用于矩形之后,所得到仍然是矩形。
mapVectors
- mapVectors(float[] vecs):将矩阵作用于矢量坐标数组vecs,并将所得的矢量坐标数组取代vecs中的数据。
- mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex, int vectorCount):将矩阵作用于源矢量坐标数组src,其中,从索引 srcIndex开始,转换 vectorCount个点后,将所得的矢量坐标数组保存到dst中,其开始索引为 dstIndex
- mapVectors(float[] dst, float[] src):将矩阵作用于将矩阵作用于源矢量坐标数组src,所得的矢量坐标数组保存到dst中
mapVectors方法是将矩阵作用于矢量。它与mapPoints方法类似,不同的是,它不受平移的影响。
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
val vector = floatArrayOf(2f, 3f)
val point = floatArrayOf(2f, 3f)
val matrix = Matrix()
// 平移变换
matrix.setTranslate(2f, 3f)
matrix.mapVectors(vector)
matrix.mapPoints(point)
// Log: after - vector: 2.0, 3.0
Log.e("teaphy:" , " after - vector: ${vector.joinToString()}")
// Log: after - point: 4.0, 6.0
Log.e("teaphy:" , " after - point: ${point.joinToString()}")
// 缩放变换
matrix.setScale(0.5f, 0.5f)
matrix.mapVectors(vector)
matrix.mapPoints(point)
// after - vector: 1.0, 1.5
Log.e("teaphy:" , " after - vector: ${vector.joinToString()}")
// after - point: 2.0, 3.0
Log.e("teaphy:" , " after - point: ${point.joinToString()}")
}
从示例代码中,可以看出:
- mapPoints不受缩放变换的影响
- mapVectors不受平移变换的影响
postXX & preXX
前面已经提到了,两个矩阵相乘并不能满足乘法交换律,故而,前置和后置的概念被提出来,这里不多赘述。其中:
postXX方法相当于当前矩阵(A)右乘参数矩阵(B),即 BA.表述形式为
M’ = B * A
例如:
用代码表示:
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
val matrix = Matrix()
matrix.setTranslate(100f, 100f)
matrix.postRotate(90f)
// Log: Matrix{[0.0, -1.0, -100.0][1.0, 0.0, 100.0][0.0, 0.0, 1.0]}
Log.e("teaphy", "matrix: $matrix")
}
preXX当于当前矩阵(A)左乘参数矩阵(B),即AB。表述形式为:
M’ = A * B
例如:
用代码表示:
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
val matrix = Matrix()
matrix.setTranslate(100f, 100f)
matrix.preRotate(90f)
// Log: Matrix{[0.0, -1.0, 100.0][1.0, 0.0, 100.0][0.0, 0.0, 1.0]}
Log.e("teaphy", "matrix: $matrix")
}
它们有一个共同点,就是不会重置当前矩阵(A)。
相关API
- postConcat(Matrix other):用当前矩阵右乘指定矩阵
- postRotate(float degrees, float px, float py):用当前矩阵右乘旋转变换矩阵,其中,旋转角度为degrees,旋转中心坐标为(px, py)
- postRotate(float degrees):用当前矩阵右乘旋转变换矩阵,其中,旋转角度为degrees
- postScale(float sx, float sy, float px, float py):用当前矩阵右乘缩放变换矩阵,其中,缩放比例为(Sx, Sy),缩放中心为(px,py)
- postScale(float sx, float sy):用当前矩阵右乘缩放变换矩阵,其中,缩放比例为(Sx, Sy)
- postSkew(float kx, float ky):用当前矩阵右乘错切变换矩阵,其中,错切比例为(kx, ky)
- postSkew(float kx, float ky, float px, float py):用当前矩阵右乘错切变换矩阵,其中,错切比例为(kx, ky),轴心点坐标为(px, py).轴心点是应保持不变的坐标
- postTranslate(float dx, float dy):用当前矩阵右乘平移变换矩阵,其中,x轴方向的位移量为 dx, y轴方向的位移量为 dy
- preConcat(Matrix other):用当前矩阵左乘指定矩阵
- preRotate(float degrees, float px, float py):用当前矩阵左乘旋转变换矩阵,其中,旋转角度为degrees,旋转中心坐标为(px, py)
- preRotate(float degrees):用当前矩阵左乘旋转变换矩阵,其中,旋转角度为degrees
- preScale(float sx, float sy):用当前矩阵左乘缩放变换矩阵,其中,缩放比例为(Sx, Sy)
- preScale(float sx, float sy, float px, float py):用当前矩阵左乘错切变换矩阵,其中错切因子为(kx, ky),轴心点坐标为(px, py).轴心点是应保持不变的坐标
- preSkew(float kx, float ky):用当前矩阵左乘错切变换矩阵,其中错切变换矩阵的错切比例为(kx, ky)
- preSkew(float kx, float ky, float px, float py):用当前矩阵左乘错切变换矩阵,其中,错切比例为(kx, ky),轴心点坐标为(px, py).轴心点是应保持不变的坐标
- preTranslate(float dx, float dy):用当前矩阵左乘平移变换矩阵,其中,x轴方向的位移量为 dx, y轴方向的位移量为 dy
setXX
setXX方法与postXX&preXX方法不同,其首先将当前矩阵重置为单位矩阵,即调用reset方法(),然后再根据相应的变换设置Matrix中的值。
相关API
- setRotate(float degrees, float px, float py):设置旋转变换矩阵,其中,旋转角度为degrees,旋转中心坐标为(px, py)
- setRotate(float degrees):设置旋转变换矩阵,其中,旋转角度为degree
- setScale(float sx, float sy):设置缩放矩阵, 其中,缩放比例为(Sx, Sy)
- setScale(float sx, float sy, float px, float py):设置缩放矩阵,其中,缩放比例为(Sx, Sy)
- setSkew(float kx, float ky):设置错切变换矩阵,其中,错切因子为(kx, ky)
- setSkew(float kx, float ky, float px, float py):设置错切变换矩阵,其中,错切因子为(kx, ky),轴心点坐标为(px, py).轴心点是应保持不变的坐标、
- setTranslate(float dx, float dy):设置平移变换矩阵,其中,x轴方向的位移量为 dx, y轴方向的位移量为 dy
特例API
set(Matrix src)
set(Matrix src)方法将src中Matrix值替换当前Matrix中的值。如果src为null,那么当前矩阵将被重置为单位矩阵。
例如:
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
val matrixsrc = Matrix()
val matrixA = Matrix()
val matrixB = Matrix()
matrixsrc.setValues(floatArrayOf(1f, 2f, 3f,
4f, 5f, 6f,
7f, 8f, 9f))
matrixA.set(matrixsrc)
matrixB.set(null)
// Log: matrixA: Matrix{1.0, 2.0, 3.0
// 4.0, 5.0, 6.0
// 7.0, 8.0, 9.0}
Log.e("teaphy", "matrixA: $matrixA")
// Log: matrixB: Matrix{1.0, 0.0, 0.0
// 0.0, 1.0, 0.0
// 0.0, 0.0, 1.0]}
Log.e("teaphy", "matrixB: $matrixB")
}
setConcat(m1, m2)
setConcat(Matrix m1, Matrix m2):将当前矩阵设置为两个指定矩阵的乘积,计算规则为: m1 * m2。
例如:
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
val matrix = Matrix()
val matrixA = Matrix()
val matrixB = Matrix()
matrixA.setValues(floatArrayOf(1f, 2f, 3f,
4f, 5f, 6f,
7f, 8f, 9f))
matrixB.setValues(floatArrayOf(2f, 3f, 4f,
5f, 6f, 7f,
8f, 9f, 1f))
matrix.setConcat(matrixA, matrixB)
// Log: matrixA: Matrix{18.0, 21.0, 10.5,
// 40.5, 48.0, 28.5,
// 63.0, 75.0, 46.5}
Log.e("teaphy", "matrix: $matrix")
}
这里需要注意的一点是,两个指定矩阵的乘积所得的矩阵的公约数会被提取出来,然后才会得到最终的矩阵。
setSinCos
- setSinCos(float sinValue, float cosValue, float px, float py):设置旋转变换矩阵,其中,旋转角度为degrees,旋转中心坐标为(px, py)
- setSinCos(float sinValue, float cosValue):设置旋转变换矩阵,其中,旋转角度为degrees,旋转中心坐标为(px, py)
setSinCos方法用来设置旋转矩阵。与setRotate方法不同的是,它不是指定旋转的角度,而是,通过指定旋转角度的正弦和余弦值来设置旋转。此时旋转转换矩阵为:
其计算方式是这样:
例如:
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
val matrix = Matrix()
val rectF = RectF(300f, 300f, 600f, 500f)
// 设置旋转变换,余弦值为0.5f,正弦值为0.5f
matrix.setSinCos(0.5f, 0.5f)
// 设置偏移变换,x轴方向偏移200f,y轴方向偏移200f
matrix.postTranslate(200f, 200f)
canvas?.drawBitmap(mBitmap, matrix, mPaint)
}
示例代码中,在设置旋转变换时,将旋转角的正弦和余弦值分别设置为0.5f和0.5f,此时:
效果图:
reset
reset()方法没有啥特殊解释的,就是将矩阵重置为单位矩阵。
setValues
setValues(float[] values)方法就是数组的中的前9值一一赋值给矩阵。数组values的长度必须≥9,如果其长度小于9时,调用此方法,抛出ArrayIndexOutOfBoundsException。如果values数组的长度大于9,将取前9个值进行赋值。
例如:
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
val matrix = Matrix()
val values = floatArrayOf(1f, 2f, 3f,
4f, 5f, 6f,
7f, 8f, 9f, 10f)
matrix.setValues(values)
// Log: matrix: Matrix{[1.0, 2.0, 3.0][4.0, 5.0, 6.0][7.0, 8.0, 9.0]}
Log.e("teaphy", "matrix: $matrix")
}
在示例代码中,创建了一个长度为10的数组,然后调用setValues给matrix赋值。由于浮点数组的长度大于10,而Matrix中只有9个值,setValues只是提取了前9个值对matrix赋值。从Log打印中,也可以清晰的看到这一点。
rectStaysRect
rectStaysRect()方法用来判断矩阵作用于矩形后,是否依然可以获得一个矩形。它的判断标准是:矩阵为单位矩阵,或者只是进行平移、缩放,及旋转的角度为90的倍数,即”仿射变换”中,其旋转的角度必须为90的倍数。
例如:
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
val matrix = Matrix()
// 将矩阵重置为单位矩阵
matrix.reset()
val rsrIdentify = matrix.rectStaysRect()
// Log: rsrIdentify: true
Log.e("teaphy", "rsrIdentify: $rsrIdentify")
// 设置平移变换矩阵
matrix.setTranslate(100f, 100f)
val rsrTranslate = matrix.rectStaysRect()
// Log:rsrTranslate: true
Log.e("teaphy", "rsrTranslate: $rsrTranslate")
// 设置缩放变换矩阵
matrix.setScale(0.2f, 0.3f)
val rsrScale = matrix.rectStaysRect()
// Log:rsrScale: true
Log.e("teaphy", "rsrScale: $rsrScale")
// 设置旋转变换 旋转角度为180
matrix.setRotate(180f)
val rsrRotate = matrix.rectStaysRect()
// Log: rsrRotate: true
Log.e("teaphy", "rsrRotate: $rsrRotate")
// 设置旋转变换 旋转角度为60
matrix.setRotate(60f)
val rsrRotate60 = matrix.rectStaysRect()
// Log: rsrRotate60: false
Log.e("teaphy", "rsrRotate60: $rsrRotate60")
// 设置错切变换
matrix.setSkew(0.5f, 0.5f)
val rsrSkew = matrix.rectStaysRect()
// Log: rsrSkew: false
Log.e("teaphy", "rsrSkew: $rsrSkew")
}
setPolyToPoly
- setPolyToPoly(float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount)
setPolyToPoly方法通过指定的0-4点,源点的坐标及变换后的坐标,来设置变换矩阵。其中:
- 每个点由坐标数组的两个浮点值表示,即[x0, y0, x1, y1, …]
- srcIndex表示源点坐标数组中指定点的起始索引
- dstIndex表示变换后的坐标数组指定点的起始索引
- pointCount用来生成变换矩阵的点数。point不能大于4,如果大于4,将抛出IllegalArgumentException异常。
其计算方式为:
dst = m * src
如果pointCount为0,那么没有任何变换效果。
pointCount为1时,将达到平移效果。
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
val matrix = Matrix()
val width = mBitmap.width.toFloat()
val height = mBitmap.height.toFloat()
val src = floatArrayOf(width/2, height/2)
val dst = floatArrayOf(width, height)
matrix.setPolyToPoly(src, 0, dst,0, 1)
canvas?.drawBitmap(mBitmap, 0f, 0f, mPaint)
canvas?.drawBitmap(mBitmap, matrix, mPaint)
}
pointCount为2时,可以达到缩放、旋转、平移 变换的效果:
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
val matrix = Matrix()
val w = mBitmap.width.toFloat()
val h = mBitmap.height.toFloat()
val src = floatArrayOf(w/2, h/2, w, 0f)
val dst = floatArrayOf(w/2, h/2, w/2 + h/2, w/2 + h/2)
matrix.setPolyToPoly(src, 0, dst,0, 2)
canvas?.drawBitmap(mBitmap, 0f, 0f, mPaint)
canvas?.drawBitmap(mBitmap, matrix, mPaint)
}
pointCount为3时,可以达到 缩放、旋转、平移、错切 效果:
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
val matrix = Matrix()
val w = mBitmap.width.toFloat()
val h = mBitmap.height.toFloat()
val src = floatArrayOf(0f, 0f, 0f, h, w, h)
val dst = floatArrayOf(0f, 0f, 200f, h, w + 200, h)
matrix.setPolyToPoly(src, 0, dst, 0, 3)
matrix.postTranslate(0f, 300f)
canvas?.drawBitmap(mBitmap, 0f, 0f, mPaint)
canvas?.drawBitmap(mBitmap, matrix, mPaint)
}
pointCount为4时,可以达到 缩放、旋转、平移、错切以及任何形变效果。也可以达到透视效果,所谓的透视效果,就是观察角度的改变,导致投射的二维图像发生了变化。
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
val matrix = Matrix()
val w = mBitmap.width.toFloat()
val h = mBitmap.height.toFloat()
val tra = 100f
val src = floatArrayOf(0f, 0f, 0f, h, w, h, w, 0f)
val dst = floatArrayOf(0f + tra, 0f, 0f, h, w, h, w - tra, 0f)
matrix.setPolyToPoly(src, 0, dst, 0, 4)
matrix.postTranslate(0f, 300f)
canvas?.drawBitmap(mBitmap, 0f, 0f, mPaint)
canvas?.drawBitmap(mBitmap, matrix, mPaint)
}
setRectToRect
- setRectToRect(RectF src, RectF dst, Matrix.ScaleToFit stf)
setRectToRect方法是将源矩形的内容填充到目标矩形中。如果源矩形和目标矩形的长宽比例不一样,到底该如何缩放填充呢?ScaleToFit,就是用来指定填充模式
ScaleToFit 有如下四个值:
- FILL: 可能会变换矩形的长宽比,保证变换和目标矩阵长宽一致
- START:保持坐标变换前矩形的长宽比,并最大限度的填充变换后的矩形。至少有一边和目标矩形重叠。左上对齐
- CENTER: 保持坐标变换前矩形的长宽比,并最大限度的填充变换后的矩形。至少有一边和目标矩形重叠
- END:保持坐标变换前矩形的长宽比,并最大限度的填充变换后的矩形。至少有一边和目标矩形重叠。右下对齐
谷歌官方示例图形:
例如:
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
mViewWidth = w.toFloat()
mViewHeight = h.toFloat()
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
val matrix = Matrix()
val w = mBitmap.width.toFloat()
val h = mBitmap.height.toFloat()
val src = RectF(0f, 0f, w, h)
val dst = RectF(0f, 0f, mViewWidth, mViewHeight)
matrix.setRectToRect(src, dst, Matrix.ScaleToFit.END)
canvas?.drawBitmap(mBitmap, 0f, 0f, mPaint)
canvas?.drawBitmap(mBitmap, matrix, mPaint)
}
总结
本篇文章介绍了Matrix的原理及相关的所有API。
参考资料:
如果觉得我的文章对您有用,请随意点赞、评论。您的支持将鼓励我继续创作!不足之处,敬请指出改正!