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

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的道路上越走越远。

posted on 2015-10-16 23:59  fuly  阅读(597)  评论(0编辑  收藏  举报