终极大招之通过像素块调整实现图像变换

在上一篇文章中,我们实现了通过不同的渲染器设置来实现图像的图形变换,没有读过的朋友,可以点击下面的链接:

http://www.cnblogs.com/fuly550871915/p/4886651.html

在这一篇文章,迎来了图像图形变换系列的最终章。所以要讲一个终极大招,利用像素块调整从来实现不同的图像效果。这个将更加细致的实现图像变换。

一、基础知识

配合下面两张图,来解释一下什么叫通过像素快来调整图像变换。如下:

                           

左侧是原图,右侧是被画了小格子的图。我们想象一下,一张图片被分成了许多像素块,正如右侧图片一样,每一个小格子就是一个像素块。而如果获取到这些小格子的交点坐标,并随着改变这些坐标的值,就可以改变对应像素块的图像效果,比如某个像素块被拉伸了,某个被旋切了,那么整张图片就可以呈现更加丰富的图像效果了。

那么android中是怎么将这个想法变为现实的呢?android提供了一个drawBitmapMesh方法,只要我将调整好的这些格子的交点坐标传递进去,就会按照这个调整变换出相应的图形效果。具体方法如下:

/***
         * 第一个参数为绘制的图片
         * 第二个参数为绘制的mesh横向格子数目
         * 第三个参数为绘制的mesh纵向格子数目
         * 第四个参数为绘制的mesh的格子交点坐标,为一个相应的数组
         * 第五个参数为绘制的mesh的格子交点坐标的偏移量,一般设置为0
         * 第六个参数为颜色,一般设置为null
         * 第七个参数为颜色偏移量,设置为0
         * 最后一个为画笔,设置为null即可
         */
        canvas.drawBitmapMesh(bmp, WIDTH, HEIGHT, verts, 0, null, 0    , null);

解释一下,其中bmp就是原来的图像,而我们分隔的纵向格子数目比如说为200,纵向格子数目比如说为300.那么我就在这个图像上就有200*300个小格子。而WIDTH是我们想绘制出的横向格子数目,必须小于等于200,同理HEIGHT就是我们想绘制出的纵向格子数目,必须小于等于300.而verts是一个数值数组,存储的就是这200*300个小格子的交点的坐标值。其他的注释里解释的很清楚了。

或许你还对这个drawBitmapMesh方法很陌生,没关系,看下面的实战代码,一起做,就会加深理解了。

二、实战

废话不多说,自定义view。在这个view里面,我们要实现这个drawBitmapMesh的逻辑。而其中的难点就是怎么遍历取出这个小格子的交点坐标。希望你能好好研究这些代码,必要时可以直接拿来使用。核心的技巧就是两个for循环方法。还有,为了存储坐标,我们将数组的偶数位置存储为x,奇数位置存储为y。注意是怎么存储和再取出来的。注释很详细。如下:

  1 package com.fuly.image;
  2
  3 import android.content.Context;
  4 import android.graphics.Bitmap;
  5 import android.graphics.BitmapFactory;
  6 import android.graphics.BitmapShader;
  7 import android.graphics.Canvas;
  8 import android.graphics.Paint;
  9 import android.graphics.Shader;
 10 import android.util.AttributeSet;
 11 import android.view.View;
 12 /**
 13  * 利用MeshView改变图形变换
 14  * @author fuly1314
 15  *
 16  */
 17     public class MeshView extends View{
 18
 19         private Bitmap bmp;//原始图片
 20         private int WIDTH = 200;//表示图片横向有200个格子
 21         private int HEIGHT = 200;//纵向有200个格子
 22         private int COUNT = (WIDTH+1)*(HEIGHT+1);//表示这样分割图片上共有多少个坐标点
 23      24         /*
 25          * 用来存储图像变换后的坐标点的坐标
 26          * 偶数位置存储x坐标
 27          * 奇数位置存储y坐标
 28          */
 29         private float[] verts = new float[COUNT*2];
 30         /*
 31          * 用来存储图像原始的坐标点的坐标
 32          * 偶数位置存储x坐标
 33          * 奇数数位置存储y坐标
 34          */
 35         private float[] orign = new float[COUNT*2];
 36
 37
 38     public MeshView(Context context) {
 39         super(context);
 40         initView();
 41     }
 42     public MeshView(Context context, AttributeSet attrs) {
 43         super(context, attrs);
 44         initView();
 45     }
 46     public MeshView(Context context, AttributeSet attrs, int defStyleAttr) {
 47         super(context, attrs, defStyleAttr);
 48         initView();
 49     }
 50
 51
 52     public void initView(){
 53         bmp = BitmapFactory.decodeResource(getResources(), R.drawable.test4);
 54         int width = bmp.getWidth();
 55         int height = bmp.getHeight();
 56         int index = 0;//标记位,用来计数
 57         /*
 58          * 这个循环用来存储坐标,必须牢牢掌握,是核心
 59          */
 60         for(int i=0;i<(HEIGHT+1);i++){//遍历横向坐标点
 61             float y = height*i/HEIGHT;//坐标为(i,j)的点的纵坐标
 62
 63             for(int j=0;j<(WIDTH+1);j++){//遍历纵向坐标点
 64                 float x = width*j/WIDTH;//坐标为(i,j)的点的横坐标
 65
 66                 //下面将坐标点存储到相应的数组中
 67                 orign[2*index+0]=verts[2*index+0] = x;
 68                 orign[2*index+1]=verts[2*index+1] = y;
 69
 70                 index++;
 71
 72             }
 73         }
 74     }
 75
 76     protected void onDraw(Canvas canvas) {
 77         super.onDraw(canvas);
 78         //在这个循环层中,我们可以改变verts的值,来实现不同的效果
 79         /*
 80          * 这个循环用来去除坐标,必须牢牢掌握,是核心
 81          */
 82         for (int i = 0; i < HEIGHT + 1; i++) {
 83             for (int j = 0; j < WIDTH + 1; j++) {
 84                 //取出相应的坐标x,不改变值就加0吧干脆
 85                 verts[(i * (WIDTH + 1) + j) * 2 + 0] += 0;
 86                 //K是用来控制周期的,这样子再加上invalidate()方法可以实现动画效果
 87                 float offsetY = (float) Math.sin((float) j / WIDTH * 2 * Math.PI );
 88                 //给Y坐标在原来的位置上加上一个正弦偏移量
 89                 verts[(i * (WIDTH + 1) + j) * 2 + 1] =
 90                         orign[(i * (WIDTH + 1) + j) * 2 + 1] + offsetY * 50;
 91             }
 92         }
 93        94
 95         /***
 96          * 第一个参数为绘制的图片
 97          * 第二个参数为绘制的mesh横向格子数目
 98          * 第三个参数为绘制的mesh纵向格子数目
 99          * 第四个参数为绘制的mesh的格子交点坐标,为一个相应的数组
100          * 第五个参数为绘制的mesh的格子交点坐标的偏移量,一般设置为0
101          * 第六个参数为颜色,一般设置为null
102          * 第七个参数为颜色偏移量,设置为0
103          * 最后一个为画笔,设置为null即可
104          */
105         canvas.drawBitmapMesh(bmp, WIDTH, HEIGHT, verts, 0, null, 0    , null);
106
107
108
109
110     }
111
112 }

好了,最难的部分完成了,下面快速完成。新建mesh.xml将这个view装进去。如下:

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:orientation="vertical"
 6     android:gravity="center">
 7
 8   <com.fuly.image.MeshView
 9       android:layout_marginLeft="20dp"
10       android:layout_marginTop="20dp"
11       android:layout_width="wrap_content"
12       android:layout_height="wrap_content"/>
13
14 </LinearLayout>

然后新建活动显示这个view,注意不要忘记给这个互动注册。如下:

 1 package com.fuly.image;
 2
 3 import android.app.Activity;
 4 import android.os.Bundle;
 5
 6 public class MeshActivity extends Activity{
 7
 8
 9     protected void onCreate(Bundle savedInstanceState) {
10
11         super.onCreate(savedInstanceState);
12         setContentView(R.layout.mesh);
13
14     }
15
16 }

最后就是最后一个按钮的点击事件,修改MainActivity,如下:

 1 package com.fuly.image;
 2
 3 import android.os.Bundle;
 4 import android.view.View;
 5 import android.app.Activity;
 6 import android.content.Intent;
 7
 8
 9 public class MainActivity extends Activity {
10
11
12     protected void onCreate(Bundle savedInstanceState) {
13         super.onCreate(savedInstanceState);
14         setContentView(R.layout.activity_main);
15     }
16
17     //下面是按钮事件
18     public void btnMatrix(View v){
19         Intent intent = new Intent(this,MatrixActivity.class);
20         startActivity(intent);
21     }
22     public void btnXFermode(View v){
23         Intent intent = new Intent(this,XFermodeActivity.class);
24         startActivity(intent);
25     }
26     public void btnShader(View v){
27         Intent intent = new Intent(this,ShaderActivity.class);
28         startActivity(intent);
29     }
30     public void btnLShader(View v){
31         Intent intent = new Intent(this,LinearShaderActivity.class);
32         startActivity(intent);
33     }
34     public void btnMesh(View v){
35         Intent intent = new Intent(this,MeshActivity.class);
36         startActivity(intent);
37     }
38
39
40 }

然后运行程序,实现效果如下:

我们发现实现了一个正弦的效果。

其实更近一步的,我们还可以实现动画效果。不信,哈哈,其实很简单,修改自定义的MeshView,红色部分为修改的。你一看就明白了。如下:

  1 package com.fuly.image;
  2
  3 import android.content.Context;
  4 import android.graphics.Bitmap;
  5 import android.graphics.BitmapFactory;
  6 import android.graphics.BitmapShader;
  7 import android.graphics.Canvas;
  8 import android.graphics.Paint;
  9 import android.graphics.Shader;
 10 import android.util.AttributeSet;
 11 import android.view.View;
 12 /**
 13  * 利用MeshView改变图形变换
 14  * @author fuly1314
 15  *
 16  */
 17     public class MeshView extends View{
 18
 19         private Bitmap bmp;//原始图片
 20         private int WIDTH = 200;//表示图片横向有200个格子
 21         private int HEIGHT = 200;//纵向有200个格子
 22         private int COUNT = (WIDTH+1)*(HEIGHT+1);//表示这样分割图片上共有多少个坐标点
 23         private float K;
 24         /*
 25          * 用来存储图像变换后的坐标点的坐标
 26          * 偶数位置存储x坐标
 27          * 奇数位置存储y坐标
 28          */
 29         private float[] verts = new float[COUNT*2];
 30         /*
 31          * 用来存储图像原始的坐标点的坐标
 32          * 偶数位置存储x坐标
 33          * 奇数数位置存储y坐标
 34          */
 35         private float[] orign = new float[COUNT*2];
 36
 37
 38     public MeshView(Context context) {
 39         super(context);
 40         initView();
 41     }
 42     public MeshView(Context context, AttributeSet attrs) {
 43         super(context, attrs);
 44         initView();
 45     }
 46     public MeshView(Context context, AttributeSet attrs, int defStyleAttr) {
 47         super(context, attrs, defStyleAttr);
 48         initView();
 49     }
 50
 51
 52     public void initView(){
 53         bmp = BitmapFactory.decodeResource(getResources(), R.drawable.test4);
 54         int width = bmp.getWidth();
 55         int height = bmp.getHeight();
 56         int index = 0;//标记位,用来计数
 57         /*
 58          * 这个循环用来存储坐标,必须牢牢掌握,是核心
 59          */
 60         for(int i=0;i<(HEIGHT+1);i++){//遍历横向坐标点
 61             float y = height*i/HEIGHT;//坐标为(i,j)的点的纵坐标
 62
 63             for(int j=0;j<(WIDTH+1);j++){//遍历纵向坐标点
 64                 float x = width*j/WIDTH;//坐标为(i,j)的点的横坐标
 65
 66                 //下面将坐标点存储到相应的数组中
 67                 orign[2*index+0]=verts[2*index+0] = x;
 68                 orign[2*index+1]=verts[2*index+1] = y;
 69
 70                 index++;
 71
 72             }
 73         }
 74     }
 75
 76     protected void onDraw(Canvas canvas) {
 77         super.onDraw(canvas);
 78         //在这个循环层中,我们可以改变verts的值,来实现不同的效果
 79         /*
 80          * 这个循环用来去除坐标,必须牢牢掌握,是核心
 81          */
 82         for (int i = 0; i < HEIGHT + 1; i++) {
 83             for (int j = 0; j < WIDTH + 1; j++) {
 84                 //取出相应的坐标x,不改变值就加0吧干脆
 85                 verts[(i * (WIDTH + 1) + j) * 2 + 0] += 0;
 86                 //K是用来控制周期的,这样子再加上invalidate()方法可以实现动画效果
 87                 float offsetY = (float) Math.sin((float) j / WIDTH * 2 * Math.PI + K * 2 * Math.PI);
 88                 //给Y坐标在原来的位置上加上一个正弦偏移量
 89                 verts[(i * (WIDTH + 1) + j) * 2 + 1] =
 90                         orign[(i * (WIDTH + 1) + j) * 2 + 1] + offsetY * 50;
 91             }
 92         }
 93         K += 0.1F;
 94
 95         /***
 96          * 第一个参数为绘制的图片
 97          * 第二个参数为绘制的mesh横向格子数目
 98          * 第三个参数为绘制的mesh纵向格子数目
 99          * 第四个参数为绘制的mesh的格子交点坐标,为一个相应的数组
100          * 第五个参数为绘制的mesh的格子交点坐标的偏移量,一般设置为0
101          * 第六个参数为颜色,一般设置为null
102          * 第七个参数为颜色偏移量,设置为0
103          * 最后一个为画笔,设置为null即可
104          */
105         canvas.drawBitmapMesh(bmp, WIDTH-100, HEIGHT-100, verts, 0, null, 0    , null);
106
107         invalidate();//刷新,这样就可以造成onDraw方法的循环
108
109
110     }
111
112 }

是不是思路很简单,只要每次画完后让onDraw自己调用刷新方法即可。这样就可以造成无限循环了。这个技巧要记得。运行程序,效果如下:

怎么样,是不是很炫啊!只要你足够大神,drawBitmapMesh方法将是你使用好多花样效果的利器。好了,至此,本篇结束。相信你对像素块调整图片的运用更加熟悉了,同时图形变换的基础系列也到此结束了,相信你对图形变换也有了更加深刻的认知。共同学习,一起进步,希望在android的道路上越走越远。

时间: 2024-12-22 17:57:39

终极大招之通过像素块调整实现图像变换的相关文章

三阶魔方中心块调整配方和记忆方法

你并不需要将普通三阶魔方中心块,字魔方或者拼图魔方等须要调整中心块的角度. 所以拼图三阶或者数字三阶,都能够在普通三阶魔方的基础之上,再加上例如以下公式,便能够转好. 1.顶面中心块旋转180° URL    U2R'L'   URL    U2R'L'     -----可记为----->     (URL U2R'L')2 2.顶面中心块顺时针转90°同一时候前面中心块逆时针转90° FB' LR' UD'     F'   U'D L'R F'B   U  -----可记为----> 

三阶魔方中心块调整公式

普通三阶魔方不需要转中心块,但是数字魔方或者拼图魔方等需要调整中心块的角度. 所以拼图三阶或者数字三阶,都可以在普通三阶魔方的基础之上,再加上如下公式,便可以转好. 1.顶面中心块旋转180° U R L U2 R' L' U R L U2 R' L' 2.顶面中心块顺时针转90°同时前面中心块逆时针转90° F B' L R' U D' F' U' D L' R F' B U -----------------------------------------------------------

终极大招 | 应用性能管理大讲堂2016年终盛典,数位大咖火爆来袭

在即将过去的2016年里,身为程序猿/运维攻城狮的你是否还经常陷在和产品经理剪不断理还乱的复杂关系中,在项目上线遇到性能问题后又沉浸在各部门之间的扯皮中,在这一年里项目研发过程中是否又有意无意的进了不少坑,又不断把它们一个一个填平. "你先别踩,等我填好了坑你再来."变成了程序员之间最赤裸裸的关怀. 这样不对!我们拒绝填那些没有意义的坑! 应用性能管理大讲堂2016年终盛典之心路,即将迎来更全面的技术干货,更强大的讲师阵容,一起听互联网金融.O2O.游戏.OTA.电商.在线视频行业领域

Jenkins自动化多项目编译和Tomcat部署懒人终极大招

项目中频繁的打包发布,搞得烦不胜烦,为了一劳永逸,一次性解决问题,特引入自动化集成部署,大大的节约了时间,点一下按钮,然后出去抽根烟,回来继续工作,或者配置定时打包部署,爽歪歪. 运行环境介绍 1. 服务器版本:Centos7.4 2. svn版本:1.8 2. jenkins版本:2.13 3. tomcat版本:8.55 首先安装Jenkins这里不在啰嗦,自己百度,安装完成之后,在启动的时候请选择安装建议插件,否则自己就要手动安装一些插件好麻烦[坑1]. jenkins安装完成之后开始配置

04 ceph 块设备安装,创建,设备映射,挂载,详情,调整,卸载,映射,删除 快照的创建回滚删除

块设备安装,创建,映射,挂载,详情,调整,卸载,曲线映射,删除 在使用Ceph的块设备工作前,确保您的Ceph的存储集群是在主动 + 清洁状态. vim /etc/hosts 172.16.66.144 ceph-client 在admin节点上执行这个快速启动.1. 在admin节点上,用ceph-deploy在你的ceph-client节点安装Ceph ceph-deploy install ceph-client 2. 在admin节点上,用ceph-deploy复制Ceph配置文件和ce

屏幕尺寸,分辨率,像素,PPI之间到底什么关系?

转载自:http://www.jianshu.com/p/c3387bcc4f6e 感谢博主的无私分享. 今天我给大家来讲讲这几个咱们经常打交道的词到底啥意思,以及他们之间到底有什么关系.这篇文章是我花了一个下午从N多篇文章里提炼出的一个白话版,保证让你看得懂. 咱们从手机开始说起吧.先上一张图,给大家看看关于手机屏幕方面的一些参数.红框内的三个参数,大家一定都不陌生,我也不陌生.不过讲真的,就在不久前,我连手机的屏幕尺寸到底是怎么算出来的都不知道.下面我们开始慢慢讲. 屏幕(主屏)尺寸是什么,

终极秘籍教你怎么找回被盗iPhone 查询ICCID

iPhone不慎丢失后怎么办?普通青年:立刻报警,基本没用.文艺青年:用Find my iPhone查找位置.但那只是个大概位置,iPhone关机后更是没戏,接着是用iCloud锁定手机,发送警告信息乃至清除数据?这虽然保护了个人隐私,但很难找回,小偷会自己或托人重刷系统. 普通青年和文艺青年的方法一般都找不回手机,除非运气极佳,二逼青年可能会直接买个新的吧. 绝处逢生,为丢失iPhone的青年带来希望的终极大招来了,那就是“查询ICCID”,这个方法很直接,运气好的话可以直接帮你找到正在使用你

OpenCV基于字节指针进行高效像素遍历

直接获取Mat对象的像素块的数据指针,基于字节指针操作,实现快速像素遍历方法(1280x720, 彩色,仅需几毫秒完成).Mat对象的数据组织形式与像素块数据的存储方式,Mat对象由两个部分组成,元数据头部加像素数据块部分. 代码实现如下: void img_Byte_ptr(Mat &image) { double t1 = getTickCount(); int w = image.cols; int h = image.rows; for (int row = 0; row < h;

如何识别一个字符串是否Json格式

前言: 距离上一篇文章,又过去一个多月了,近些时间,工作依旧很忙碌,除了管理方面的事,代码方面主要折腾三个事: 1:开发框架(一整套基于配置型的开发体系框架) 2:CYQ.Data 数据层框架(持续的更新,最近也加入了Sybase的支持) 3:工作流流程图设计器. 由于这三个方面都涉及到Json,所以就谈谈这些天在Json上花下的心思. 关于造轮子: 很多人对于造轮子都会有自己的看法,这里提一下个人的观点: 个人认为: 1:首要是要具备造轮子的能力,然后再讨论造不造与浪不浪.轮子与时间的问题.