zoukankan      html  css  js  c++  java
  • 终极大招之通过像素块调整实现图像变换

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

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

  • 相关阅读:
    错误提示窗口-“操作系统当前的配置不能运行此应用程序”
    打印机无法打印的10种解决方法
    开发进度三
    人月神话阅读笔记二
    开发进度二
    开发进度1
    人月神话阅读笔记一
    库存物资管理系统
    四则运算
    动手动脑5
  • 原文地址:https://www.cnblogs.com/fuly550871915/p/4886726.html
Copyright © 2011-2022 走看看