连接:https://www.jianshu.com/p/11e062284491html
Matrix
主要用于对图像的图形处理。前面学习的ColorMatirx主要是图像色彩的处理android
学习资料canvas
十分感谢 : )api
Matrix
是一个3 * 3
的矩阵,每一个像素点表达了其坐标的X,Y
信息数组
图形矩阵变换ide
处理每一个像素点的计算方法函数
X1 = a * X + b * Y + c Y1 = d * X + e * Y + f L = g * X + h * Y + i
通常,g = h = 0 , i = 1
,这时L = g * X + h * Y + i
恒成立,也就是L = i = 1
布局
Matrix
的初始化矩阵,对角线为1
,其他为0
post
Matrix初始化矩阵学习
Matrix
主要能够对图像作4种基本变换
Matrix
类中的方法,主要也是和这四个变换相关,只是对计算过程作了封装
做用对象是Bitmap
而不是Canvas
平移变换
红点p1
平移到白点p
时,坐标值
x = x1 + x0 y = y1 + y0
矩阵的形式:
平移变换矩阵
为了更好直观表现,先看原始效果
原始效果
private void init() { bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test); //画笔 paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(Color.parseColor("#FF4081")); //矩阵 matrix = new Matrix(); matrix.setTranslate(100f,100f); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawColor(Color.YELLOW); canvas.drawBitmap(bitmap,0,0,null); }
在布局文件中,控件的宽为match_parent
,高为200dp
,onDraw()
方法中,canvas
绘制底色为黄色,又绘制了原始了的bitmap
。bitmap
的大小是没有控件大的,屏幕右侧留下了一块黄色的区域。此时并无用到matrix
Matrix
中提供了一个setTranslate()
方法,很容易就作到平移
简单修改代码
protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawColor(Color.YELLOW); canvas.drawBitmap(bitmap,matrix,null); //在(100,100)处画一个圆 //用来辅助查看matrix做用后的坐标系 canvas.drawCircle(100,100,30,paint); }
平移后
根据小圆能够看出,matrix
的平移对canvas
的坐标系不会形成影响,不像canvas.traslate()
方法。
matrix.setTranslate(100f,100f)
,bitmap
在x,y
轴上移动了100
个px
。
代入到平移的公式中:
平移100个像素
最终
x = x1 + 100 y = y1 + 100
而超出了canvas
的部分,则再也不显示
旋转就是一个点围绕一个中心点旋转到新的位置
以原点的为旋转中心过程学习:
Rotate旋转图示
白点p(x0,yo)
绕原点旋转β°
后,获得红点p(x,y)
斜边为r
,角度为α
,利用三角函数,获得
x0 = r * cosα y0 = r * sinα
同理,能够得出
x = r * cos(α + β) = r * cosα * cosβ - r * sinα * sinβ = x0 * cosβ - y0 * sinβ y = r * sin(α + β) = r * sinα * cosβ + r * cosα * sinβ = y0 * cosβ + x0 * sinβ
过程其实就是三角函数展开,矩阵的形式就是
旋转矩阵变换
根据计算结果y = y0 * cosβ + x0 * sinβ
,须要注意sinβ
和cosβ
在矩阵的位置
上面的状况是以原点为旋转中心,任意点O
为旋转中心进行旋转变换,通常有3个步骤:
O
主要就是考虑任意点与原点的坐标错。然而使用setRotate()
方法时,并不用考虑过多,都进行了封装
旋转方法有两个重载方法:
1. setRotate(float degrees) 2. setRotate(float degrees, float px, float py)
第一个方法简单使用,简单修改2.1中的代码
private void init() { bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test); matrix = new Matrix(); matrix.setRotate(15); }
围绕左上角旋转15度
默认以左上角为旋转中心,bitmap
的宽为r
进行旋转
第2个方法能够指定旋转中心O
,float px
就是O
点的X
轴坐标,float py
就是O
点的Y
轴坐标
matrix.setRotate(15,bitmap.getWidth()/2,bitmap.getHeight()/2);
以Bitmap中心为旋转中心
以bitmap
中心为旋转中心进行旋转15度
对于一个像素点来讲,不存在缩放的概念,但一个图像是由不少个像素点组成,将每一个点的坐标进行相同比例的缩放后,整个图像也就有了缩放的效果。
计算公式:
x = K1 * x0 y = k1 * y0
矩阵形式:
矩阵缩放变换
k1 就是要缩放的比例,负值无效,bitmap
会不显示,0~1f
缩小,k1 > 1
为放大
这个方法也有两个重载方法
1. setScale(float sx, float sy) 2. setScale(float sx, float sy, float px, float py)
根据学习setRotate()
方法,这个方法的两个重载方法比较好理解
第1个方法,简单使用
private void init() { bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test); matrix = new Matrix(); matrix.setScale(0.5f,0.5f); }
缩放二分之一
此时的缩放中心为bitmap
的坐上角
第2个方法,简单使用
matrix.setScale(0.5f,0.5f,bitmap.getWidth()/2,bitmap.getHeight()/2);
以Bitmap中心缩放
此时就是以Bitmap
的中心进行缩放,整个Bitmap
的边缘向中间靠拢
错切变换skew
是一种比较特殊的线性变换,分为水平错切和垂直错切
水平错切效果就是让全部像素点的Y
轴坐标不变,X
轴坐标按照比例进行平移,且平移的大小与该点到Y
轴的距离成成正比
在坐标系中的效果:
水平错切
计算公式:
x = x0 + k1 * y0 y = y0
矩阵形式:
矩阵水平错切变换
X
轴平移的值,是k1 * y0
垂直错切让全部像素点的X
轴坐标不变,Y
轴坐标按照比例进行平移,且平移的大小与该点到X
轴的距离成成正比
在坐标系中的效果:
垂直错切
计算公式:
x = x0 y = y0+ k2 * x0
矩阵形式:
矩阵垂直错切变换
当水平和垂直方向上都作错切变换时
计算公式:
x = x0 + k1 * y0 y = k2 * x0 * y0
矩阵形式:
水平和垂直都错切变换
不管水平还垂直错切,最终的效果其实就是由矩形变做平行四边形
这个方法也有两个重载
1. setSkew(float kx, float ky) 2. setSkew(float kx, float ky, float px, float py)
第2个方法后面两个参数也是为了指定错切的中心
水平错切:
private void init() { bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test); matrix = new Matrix(); matrix.setSkew(0.25f,0f); }
水平错切效果
kx
就是k1
,负值,向左切;正值向右切
垂直错切:
private void init() { bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test); matrix = new Matrix(); matrix.setSkew(0f,0.25f); }
垂直错切效果
ky
就是k2
,负值,向上切;正值,向下切
bitmap
最右边的区域不是错切的效果,是由于bitmap
的宽没有canvas
的宽大,留下的空白区域
两个方向都错切:
matrix.setSkew(0.25f,0.25f);
两个方向错切
此时能够明显看出,错切的中心点为bitmap
的左上角
简单使用:
matrix.setSkew(0.1f,0.1f,bitmap.getWidth()/2,bitmap.getHeight()/2);
指定错切中心点
以bitmap
的中心为错切中心点
这里目前并非很理解指定中心点后错切对坐标系的影响
矩阵
变化过滤
第1行都是影响的X
轴,第2行影响的Y
轴
首先,矩阵的乘法不知足乘法的交换规律
在Matrix
类中,set
方法会重置矩阵中的全部值,而pre
和post
不会
前乘的代码:
private void init() { bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test); matrix = new Matrix(); matrix.setTranslate(100,100); matrix.preRotate(15); }
前乘效果
先进行平移,前乘旋转15°
后乘,简单修改代码:
matrix.postRotate(15);
后乘效果
二者差异,看右下角的区域比较明显
前乘旋转源码
旋转后乘源码
前乘
就是M * R(degrees)
,后乘
就是R(degrees) * M
前乘
就对应线性代数矩阵运算的右乘
后乘
就对应线性代数矩阵运算的左乘
在矩阵运算中:
M
右乘A
,就是A * M
M
左乘A
,就是M * A
简单记法:右乘从右边乘进来,左乘从左边乘进来
7.1
中的矩阵:
矩阵分析
在7.1
中共有3个矩阵,首先平移b
,旋转a
,像素c
在前乘或者后乘以前有一个setTranslate(100,100)
设置的矩阵b
,前乘后后乘也就是相对于b
来讲
在7.1
前乘,计算过程就是:
a
右乘b
,计算就是b * a
,获得一个新的矩阵N
,N * c
后乘的过程:
a
左乘b
,计算就是a * b
,获得一个新的矩阵N
,N * c
总结:
在pre
或者post
方法前进行设置了哪一个矩阵M
,矩阵M
与M
以前全部的矩阵的运算获得的新矩阵N
,N
就看作当前矩阵,前乘或者后乘就是相对于这个当前矩阵N
而言
根据总结,看下下面的两个小练习:
//方式1 matrix = new Matrix(); matrix.preRotate(30); matrix.postTranslate(100f, 100f); //方式2 matrix = new Matrix(); matrix.postTranslate(100f, 100f); matrix.preRotate(30); //方式3 matrix = new Matrix(); matrix.postRotate(30); matrix.preTranslate(100f, 100f);
1
和方式2
结果是否相同? 相同1
和方式3
结果是否相同? 不一样有图,有真相
3种方式的差异
在看问题前,先了解这样一个矩阵的知识点,有助于理解问题:
有3个矩阵A
,B
,C
,相乘,N
= A * B * C
从左向右顺序计算,第1步,X
= A * B
,而后N
= X * C
从右向左倒序计算,第1步,X
= B * C
,而后N
= A * X
这两种计算方式是同样的。
网上有人说,图形处理时,矩阵的运算是从右向左计算的,这也就是为啥有pre
能够理解为先进行计算的一说,但我的感受,从左开始和从右开始计算是同样的。但从右开始计算更容易理解吧
之因此说矩阵不知足乘法的交换规律,是说A * B
≠ B * A
N
= A * B * C
,从左开始计算和从右开始计算结果同样的前提就是,要按照矩阵排列时的顺序来进行计算
N
= A * B
,但N * C
≠ C * N
下面看问题
问题1:
1
的矩阵的形式:方式1矩阵
2
的矩阵的形式:方式2
new Matrix()
或者mMatrix.reset()
获得的就是一个原始矩阵
两个矩阵最终形式其实就是一个矩阵,差异能够经过括号的位置理解,括号内的就是先计算的。方式1
是能够看做从右面开始计算,方式2
能够看作从左面开始计算。但前面说了,计算的顺序并会不影响最终的结果
问题2:
方式3
的矩阵形式与方式1,2
不同:
方式3矩阵
方式3
中与方式1,2
的差异就是旋转和平移两个矩阵交换了位置,而矩阵不知足乘法的交换律,因此方式1
和方式3
,最终结果就不一样
Matrix
中,方法主要4大类,占据了绝大部分,set
,pre
,post
,map
开头的方法
这个方法很是强大,经过改变参数,除了能够实现平移,旋转,缩放,错切
,还能够实现透视
这个方法主要是利用肯定矩形4个顶点,根据4个顶点坐标的变化来对bitmap
进行变换
setPolyToPoly方法
最终的效果主要由src
和dst
两个数组进行控制,两个数组控制4个顶点的坐标,srcIndex,dstIndex
分别是src
和dst
的第一个值的角标,pointCount
是4个顶点中要使用的个数,最大为4,0表示不进行操做变换
透视效果,简单使用:
private void init() { bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test); matrix = new Matrix(); float bWidth = bitmap.getWidth(); float bHeight = bitmap.getHeight(); float[] src = {0, 0, 0, bHeight, bWidth, bHeight,bWidth, 0}; float[] dst = {0 + 150,0, 0, bHeight, bWidth, bHeight, bWidth - 150, 0}; matrix.setPolyToPoly(src, 0, dst, 0, 4); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawColor(Color.YELLOW); canvas.drawBitmap(bitmap,matrix,null); }
透视效果
float[] src
和float[] dst
中的值必定是成对的出现,由于一个点的坐标由(x,y)
来肯定,两两一对控制对应的一个顶点的坐标,最多有4对有效,超过的无效,由于再方法setPolyToPoly()
中,最后一个参数不能超过4
数组值和顶点坐标点的对应关系:
float[] dst = {f0, f1, f3, f3, f4, f5,f6, f7}
坐标和顶点的对应关系
为了方便看,将bitmap
放在了画布比较靠中心的位置
dst
能够看作是底板,最终要显示的效果;
src
能够看作是要截取的bitmap
的要显示的有效区域
控制不一样的点的效果:
setRectToRect(RectF src, RectF dst, ScaleToFit stf)
第一个参数src
,截取资源Bitmap
的显示区域
第二个参数dst
,底板,显示的区域
第三个参数stf
,模式
谷歌api 给的Demo:
ScaleToFit
FILL: 可能会变换矩形的长宽比,保证变换和目标矩阵长宽一致。
START:保持坐标变换前矩形的长宽比,并最大限度的填充变换后的矩形。至少有一边和目标矩形重叠。左上对齐。
CENTER: 保持坐标变换前矩形的长宽比,并最大限度的填充变换后的矩形。至少有一边和目标矩形重叠。
END:保持坐标变换前矩形的长宽比,并最大限度的填充变换后的矩形。至少有一边和目标矩形重叠。右下对齐。
图和文字说明从androidmatrix最全方法详解与进阶(完整篇)摘抄
简单使用:
private void init() { bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher); matrix = new Matrix(); int screenWidth = getResources().getDisplayMetrics().widthPixels; int screenHeight = getResources().getDisplayMetrics().heightPixels; float bWidth = bitmap.getWidth(); float bHeight = bitmap.getHeight(); RectF src = new RectF(0,0,bWidth/2,bHeight/2 ); RectF dst = new RectF(0,0,screenWidth,screenHeight); matrix.setRectToRect(src,dst, Matrix.ScaleToFit.END); } protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawColor(Color.YELLOW); canvas.drawBitmap(bitmap,matrix,null); }
Matrix.ScaleToFit.END
结合上面的图,也比较容易理解
方法名 | 做用 |
---|---|
reset() |
将矩阵恢复为初始化矩阵 |
boolean invert(Matrix inverse) |
反转当前矩阵 |
boolean isIdentity() |
是否为初始化矩阵 |
boolean isAffine() |
是否为仿射矩阵 |
boolean rectStaysRect() |
判断该矩阵是否能够将一个矩形依然变换为一个矩形。当矩阵是单位矩阵,或者只进行平移,缩放,以及旋转90度的倍数的时候,返回true |
仿射变换其实就是二维坐标到二维坐标的线性变换,保持二维图形的“平直性”(即变换后直线仍是直线不会打弯,圆弧仍是圆弧)和“平行性”(指保持二维图形间的相对位置关系不变,平行线仍是平行线,而直线上点的位置顺序不变),能够经过一系列的原子变换的复合来实现,原子变换就包括:平移、缩放、翻转、旋转和错切。这里除了透视能够改变z轴之外,其余的变换基本都是上述的原子变换,因此,只要最后一行是0,0,1则是仿射矩阵。
其余的方法之后用到了再学习补充
本篇主要就学习4种基本变换操做的方法
本人很菜,有错误请指出
感谢学习资料中的大神前辈们
共勉 : )
做者:英勇青铜5 连接:https://www.jianshu.com/p/11e062284491 來源:简书 简书著做权归做者全部,任何形式的转载都请联系做者得到受权并注明出处。