Android绘图机制与处理技巧(三)——Android图像处理之图形特效处理
来源:互联网 发布:深圳云计算工程师招聘 编辑:程序博客网 时间:2024/05/29 13:37
Android变形矩阵——Matrix
对于图像的图形变换,Android系统是通过矩阵来进行处理的,每个像素点都表达了其坐标的X、Y信息。Android的图形变换矩阵是一个3x3的矩阵,如下图所示:
当使用变换矩阵去处理每一个像素点的时候,与颜色矩阵的矩阵乘法一样,计算公式如下所示:
X1=aX+bY+c
Y1=dX+eY+f
1=gX+hY+i
通常情况下,会让
与色彩变换矩阵的初始矩阵一样,图形变换矩阵也有一个初始矩阵。就是对角线元素a、e、i为1,其他元素为0的矩阵,如下图所示:
图像的变形处理通常包含以下四类基本变换:
- Translate——平移变换
- Rotate——旋转变换
- Scale——缩放变换
- Skew——错切变换
平移变换
平移变换的坐标值变换过程就是将每个像素点都进行平移变换,当从
旋转变换
旋转变换即指一个点围绕一个中心旋转到一个新的点。当从
x0=rcosα
y0=rsinα
x1=rcos(α+θ)=rcosαcosθ−rsinαsinθ=x0cosθ−y0sinθ
y1=rsin(α+θ)=rsinαcosθ+rcosαsinθ=y0cosθ+x0sinθ
矩阵形式如下图所示:
前面是以坐标原点为旋转中心的旋转变换,如果以任意点O为旋转中心来进行旋转变换,通常需要以下三个步骤:
- 将坐标原点平移到O点
- 使用前面讲的以坐标原点为中心的旋转方法进行旋转变换
- 将坐标原点还原
缩放变换
一个像素点是不存在缩放的概念的,但是由于图像是由很多个像素点组成的,如果将每个点的坐标都进行相同比例的缩放,最终就会形成让整个图像缩放的效果,缩放效果的公式如下
x1=K1x0
y1=K2y0
矩阵形式如下图所示:
错切变换
错切变换(skew)在数学上又称为Shear mapping(可译为“剪切变换“)或者Transvection(缩并),它是一种比较特殊的线性变换。错切变换的效果就是让所有点的X坐标(或者Y坐标)保持不变,而对应的Y坐标(或者X坐标)则按比例发生平移,且平移的大小和该点到Y轴(或者X轴)的距离成正比。错切变换通常包含两种——水平错切与垂直错切。
错切变换的计算公式如下:
- 水平错切
x1=x0+K1y0
y1=y0
- 垂直错切
x1=x0
y1=K2x0+y0
矩阵形式如下图
由上面的分析可以发现,这个图形变换3x3的矩阵与色彩变换矩阵一样,每个位置的元素所表示的功能是有规律的,总结如下:
可以发现,a、b、c、d、e、f这六个矩阵元素分别对应以下变换:
- a和e控制Scale——缩放变换
- b和d控制Skew——错切变换
- a和e控制Trans——平移变换
- a、b、d、e共同控制Rotate——旋转变换
通过类似色彩矩阵中模拟矩阵的例子来模拟变形矩阵。在图形变换矩阵中,同样是通过一个一维数组来模拟矩阵,并通过setValues()方法将一个一维数组转换为图形变换矩阵,代码如下所示:
private float[] mImageMatrix = new float[9]; Matrix matrix = new Matrix(); matrix.setValues(mImageMatrix);
当获得了变换矩阵后,就可以通过以下代码将一个图像以这个变换矩阵的形式绘制出来。
canvas.drawBitmap(mBitmap, mMatrix, null);
运行程序后,初始界面如下所示:
Android系统同样提供了一些API来简化矩阵的运算,我们不必每次都去设置矩阵的每一个元素值。Android中使用Matrix类来封装矩阵,并提供了以下几个操作方法来实现上面的四中变换方式:
- matrix.setRotate()——旋转变换
- matrix.setTranslate()——平移变换
- matrix.setScale()——缩放变换
- matrix.setSkew()——错切变换
- matrix.preX和matrix.postY——提供矩阵的前乘和后乘运算
Matrix类的set方法会重置矩阵中的值,而post和pre方法不会,这两个方法常用来实现矩阵的混合作用。不过要注意的是,矩阵运算不满足乘法的交换律,所以矩阵乘法的前乘和后乘是两种不同的运算方式。举例说明,比如需要实现以下效果:
- 先旋转45度
- 再平移到(200, 200)
如果使用后乘运算,表示当前矩阵乘上参数代表的矩阵,代码如下所示:
matrix.setRotate(45); matrix.postTranslate(200, 200);
如果使用前乘运算,表示参数代表的矩阵乘上当前矩阵,代码如下所示:
matrix.setTranslate(200, 200); matrix.preRotate(45);
像素块分析
图像的特效处理有两种方式,即使用矩阵来进行图像变换和使用drawBitmapMesh()方法来进行处理。drawBitmapMesh()与操纵像素点来改变色彩的原理类似,只不过是把图像分成了一个个的小块,然后通过改变每一个图像块来修改整个图像。
drawBitmapMesh()方法代码如下:
public void drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset, Paint paint)
关键的参数如下:
- bitmap:将要扭曲的图像
- meshWidth:需要的横向网格数目
- meshHeight :需要的纵向网格数目
- verts:网格交叉点坐标数组
- vertOffset:verts数组中开始跳过的(x, y)坐标对的数目
要使用drawBitmapMesh()方法就需先将图片分割为若干个图像块。所以,在图像上横纵各画N条线,而这横纵各N条线就交织成了NxN个点,而每个点的坐标则以
drawBitmapMesh()方法的功能非常强大,基本上可以实现所有的图像特效,但使用起来也非常复杂,其关键就是在于计算、确定新的交叉点的坐标。下面举例说明如何使用drawBitmapMesh()方法来实现一个旗帜飞扬的效果。
要想达到旗帜飞扬的效果,只需要让图片中每个交叉点的横坐标较之前不发生变化,而纵坐标较之前坐标呈现一个三角函数的周期性变化即可。
首先获取交叉点的坐标,并将坐标保存到orig数组中,其获取交叉点坐标的原理就是通过循环遍历所有的交叉线,并按比例获取其坐标,代码如下所示:
mBitmap = BitmapFactory.decodeResource(context.getResources(), R.mipmap.test); float bitmapWidth = mBitmap.getWidth(); float bitmapHeight = mBitmap.getHeight(); int index = 0; for (int y = 0; y <= HEIGHT ; y++) { float fy = bitmapHeight * y / HEIGHT; for (int x = 0; x <= WIDTH; x++) { float fx = bitmapWidth * x / WIDTH; orig[index * 2] = verts[ index * 2] = fx; //这里人为将坐标+100是为了让图像下移,避免扭曲后被屏幕遮挡 orig[index * 2 + 1] = verts[ index * 2 + 1] = fy + 100; index++; } }
接下来,在onDraw()方法中改变交叉点的纵坐标的值,为了实现旗帜飘扬的效果,使用一个正弦函数
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); flagWave(); K += 0.1f;//将K的值增加 canvas.drawBitmapMesh(mBitmap, WIDTH, HEIGHT, verts, 0, null, 0, null); invalidate(); } /** * 按当前点所在的横坐标的位置来确定纵坐标的偏移量,其中A代表正弦函数中的振幅大小 */ private void flagWave() { for (int j = 0; j <= HEIGHT; j++) { for (int i = 0; i <= WIDTH; i++) { //在获取纵坐标的偏移量时,利用正弦函数的周期性给函数增加一个周期K * Math.PI,就是为了让图像能够动起来 float offsetY = (float) Math.sin(2 * Math.PI * i / WIDTH + K * Math.PI); verts[(j * (WIDTH + 1) + i) * 2 + 1] = orig[(j * (WIDTH + 1) + i) * 2 + 1] + offsetY * A; } } }
这样,每次在重绘时,通过改变相位来改变偏移量,从而造成一个动态的效果,就好象旗帜在风中飘扬一样,效果图如下。
使用drawBitmapMesh()方法可以创建很多复杂的图像效果,但是对它的使用也相对复杂,需要我们对图像处理有很深厚的功底。同时,对算法的要求也比较高,需要计算各种特效下不同的坐标点变化规律,从而设计出不同的特效。
代码地址
- Android绘图机制与处理技巧(三)——Android图像处理之图形特效处理
- Android绘图机制与处理技巧(四)Android图像处理之图形特效处理
- Android绘图机制与处理技巧(三)Android图像处理之色彩特效处理
- Android绘图机制与处理技巧(二)——Android图像处理之色彩特效处理
- Android绘图机制与处理技巧(四)——Android图像处理之画笔特效处理
- Android绘图机制与处理技巧(五)Android图像处理之画笔特效处理
- Android绘图机制与处理技巧(六)Android图像处理之SurfaceView
- Android图像处理之图形特效处理
- Android群英传--绘图机制与处理技巧(三)
- Android绘图机制与处理技巧(一)——Android绘图技巧
- Android的图形与图像处理之二 图形特效处理
- Android群英传之Android绘图机制与处理技巧
- Android群英传之Android绘图机制与处理技巧
- Android绘图机制与处理技巧
- Android绘图机制与处理技巧
- Android绘图机制与处理技巧
- Android 绘图机制与处理技巧-1
- Android绘图机制与处理技巧
- python resources collection
- IAR 数据类型/扩展关键字/位操作/SRAM操作/中断/头文件/汇编嵌入方式
- Android 架构例子
- Instant Run工作原理及用法
- No resource found that matches the given name 'android:TextAppearance.Material.Widget.Button.Inverse
- Android绘图机制与处理技巧(三)——Android图像处理之图形特效处理
- 图片预览
- 爽爆天ReactNative基础篇 - 2.3 搭建Android工程
- 关于数据处理的杂谈
- SpringMVC入门学习(一) 环境搭建+实例演示
- Clion 使用自己编写的 Makefile编译
- 文本换行问题
- MATLAB帮助文档无法复制怎么办?
- android 进程间通信(Messager)