zoukankan      html  css  js  c++  java
  • Android图像处理之图形特效处理

    一、Android变形矩阵——Matricx:

    跟Android图像的色彩处理基本一样,只是将ColorMatrix换成了Matrix,ColorMatrix是4*5的矩阵,Matrix是3*3的。每个像素点表达了其坐标的X、Y信息:


    当使用变换矩阵去处理每一个像素点的时候,与颜色矩阵的矩阵乘法一样,计算公式如下所示:




    通常情况下,会让g=h=0i=1,这样就使1=gX+hY+i    恒成立。因此,只需着重关注上面几个参数即可。

    与色彩变换矩阵的初始矩阵一样,图形变换矩阵也有一个初始矩阵。就是对角线元素a、e、i为1,其他元素为0的矩阵,如下图所示:


    图像的变形处理通常包含以下四类基本变换:

    • Translate——平移变换
    • Rotate——旋转变换
    • Scale——缩放变换
    • Skew——错切变换

    1、平移变换

    平移变换的坐标值变换过程就是将每个像素点都进行平移变换,当从P(x0,y0)P(x1,y1)时,所需的平移矩阵如下所示:

    2、旋转变换

    旋转变换即指一个点围绕一个中心旋转到一个新的点。当从P(x0,y0)点,以坐标原点O为旋转中心旋转到P(x1,y1)时,可以将点的坐标都表达成OP与X轴正方向夹角的函数表达式(其中r为线段OP的长度,αOP(x0,y0)XθOP(x0,y0)OP(x1,y1)),如下所示:

    x0=rcosα
    y0=rsinα
    x1=rcos(α+θ)=rcosαcosθrsinαsinθ=x0cosθy0sinθ
    y1=rsin(α+θ)=rsinαcosθ+rcosαsinθ=y0cosθ+x0sinθ

    矩阵形式如下图所示:


    前面是以坐标原点为旋转中心的旋转变换,如果以任意点O为旋转中心来进行旋转变换,通常需要以下三个步骤:

    • 将坐标原点平移到O点
    • 使用前面讲的以坐标原点为中心的旋转方法进行旋转变换
    • 将坐标原点还原

    3、缩放变换

    一个像素点是不存在缩放的概念的,但是由于图像是由很多个像素点组成的,如果将每个点的坐标都进行相同比例的缩放,最终就会形成让整个图像缩放的效果,缩放效果的公式如下

    x1=K1x0
    y1=K2y0

    矩阵形式如下图所示:

    4、错切变换

    错切变换(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);
     

    示例代码:

    activity:

    [java] view plain copy print?
    1. package com.example.androidmatrix;  
    2.   
    3. import android.app.Activity;  
    4. import android.graphics.Bitmap;  
    5. import android.graphics.Bitmap.Config;  
    6. import android.graphics.BitmapFactory;  
    7. import android.graphics.Canvas;  
    8. import android.graphics.Matrix;  
    9. import android.os.Bundle;  
    10. import android.view.View;  
    11. import android.widget.EditText;  
    12. import android.widget.GridLayout;  
    13. import android.widget.ImageView;  
    14.   
    15. public class TestMatrixActivity extends Activity {  
    16.     //定义组件  
    17.     private ImageView imageView;  
    18.     private GridLayout group;  
    19.       
    20.     private Bitmap bitmap;  
    21.     private EditText[] edits = new EditText[9];  
    22.     private float[] edittexts = new float[9];  
    23.     protected void onCreate(Bundle savedInstanceState) {  
    24.         super.onCreate(savedInstanceState);  
    25.         setContentView(R.layout.testmatrix);  
    26.           
    27.         imageView = (ImageView) findViewById(R.id.imageView);  
    28.         group = (GridLayout) findViewById(R.id.group);  
    29.           
    30.         bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);  
    31.         imageView.setImageBitmap(bitmap);  
    32.           
    33.         group.post(new Runnable() {  
    34.             public void run() {  
    35.                 setNineEdits();  
    36.                 fillNineEdits();  
    37.             }  
    38.         });  
    39.     }  
    40.       
    41.     //创建9个编辑框  
    42.     private void setNineEdits(){  
    43.         int width = group.getWidth();  
    44.         int height = group.getHeight();  
    45.         for (int i = 0; i < edits.length; i++) {  
    46.             edits[i] = new EditText(TestMatrixActivity.this);  
    47.             edits[i].setWidth(width / 3);  
    48.             edits[i].setHeight(height / 3);  
    49.             group.addView(edits[i]);  
    50.         }  
    51.     }  
    52.     //给九个编辑框赋值  
    53.     private void fillNineEdits(){  
    54.         for (int i = 0; i < edits.length; i++) {  
    55.             if(i % 4 == 0){  
    56.                 edits[i].setText(String.valueOf(1));  
    57.             }else{  
    58.                 edits[i].setText(String.valueOf(0));  
    59.             }  
    60.         }  
    61.     }  
    62.       
    63.     //重新获取九个编辑框的值  
    64.     private void getNineEdits(){  
    65.         for (int i = 0; i < edits.length; i++) {  
    66.             edittexts[i] = Float.valueOf(edits[i].getText().toString().trim());  
    67.         }  
    68.     }  
    69.       
    70.     private void change(){  
    71.         Matrix matrix = new Matrix();  
    72.         Bitmap bmp = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888);  
    73.         matrix.setValues(edittexts);  
    74.         Canvas canvas = new Canvas(bmp);  
    75.         canvas.drawBitmap(bitmap, matrix, null);  
    76.         imageView.setImageBitmap(bmp);  
    77.     }  
    78.       
    79.     public void onChange(View view){  
    80.         getNineEdits();  
    81.         change();  
    82.     }  
    83.       
    84.     public void onReset(View view){  
    85.         fillNineEdits();  
    86.         change();  
    87.     }  
    88. }  
    package com.example.androidmatrix;
    
    import android.app.Activity;
    import android.graphics.Bitmap;
    import android.graphics.Bitmap.Config;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Matrix;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.EditText;
    import android.widget.GridLayout;
    import android.widget.ImageView;
    
    public class TestMatrixActivity extends Activity {
    	//定义组件
    	private ImageView imageView;
    	private GridLayout group;
    	
    	private Bitmap bitmap;
    	private EditText[] edits = new EditText[9];
    	private float[] edittexts = new float[9];
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.testmatrix);
    		
    		imageView = (ImageView) findViewById(R.id.imageView);
    		group = (GridLayout) findViewById(R.id.group);
    		
    		bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
    		imageView.setImageBitmap(bitmap);
    		
    		group.post(new Runnable() {
    			public void run() {
    				setNineEdits();
    				fillNineEdits();
    			}
    		});
    	}
    	
    	//创建9个编辑框
    	private void setNineEdits(){
    		int width = group.getWidth();
    		int height = group.getHeight();
    		for (int i = 0; i < edits.length; i++) {
    			edits[i] = new EditText(TestMatrixActivity.this);
    			edits[i].setWidth(width / 3);
    			edits[i].setHeight(height / 3);
    			group.addView(edits[i]);
    		}
    	}
    	//给九个编辑框赋值
    	private void fillNineEdits(){
    		for (int i = 0; i < edits.length; i++) {
    			if(i % 4 == 0){
    				edits[i].setText(String.valueOf(1));
    			}else{
    				edits[i].setText(String.valueOf(0));
    			}
    		}
    	}
    	
    	//重新获取九个编辑框的值
    	private void getNineEdits(){
    		for (int i = 0; i < edits.length; i++) {
    			edittexts[i] = Float.valueOf(edits[i].getText().toString().trim());
    		}
    	}
    	
    	private void change(){
    		Matrix matrix = new Matrix();
    		Bitmap bmp = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888);
    		matrix.setValues(edittexts);
    		Canvas canvas = new Canvas(bmp);
    		canvas.drawBitmap(bitmap, matrix, null);
    		imageView.setImageBitmap(bmp);
    	}
    	
    	public void onChange(View view){
    		getNineEdits();
    		change();
    	}
    	
    	public void onReset(View view){
    		fillNineEdits();
    		change();
    	}
    }
    


    界面:

    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    3.     android:layout_width="match_parent"  
    4.     android:layout_height="match_parent"  
    5.     android:orientation="vertical" >  
    6.   
    7.     <ImageView  
    8.         android:id="@+id/imageView"  
    9.         android:layout_width="fill_parent"  
    10.         android:layout_height="0dp"  
    11.         android:layout_weight="2" />  
    12.   
    13.     <GridLayout  
    14.         android:id="@+id/group"  
    15.         android:layout_width="match_parent"  
    16.         android:layout_height="200dp"  
    17.         android:layout_weight="3"  
    18.         android:columnCount="3"  
    19.         android:rowCount="3" >  
    20.     </GridLayout>  
    21.   
    22.     <LinearLayout  
    23.         android:layout_width="match_parent"  
    24.         android:layout_height="wrap_content"  
    25.         android:orientation="horizontal" >  
    26.   
    27.         <Button  
    28.             android:layout_width="wrap_content"  
    29.             android:layout_height="wrap_content"  
    30.             android:layout_weight="1"  
    31.             android:onClick="onChange"  
    32.             android:text="生效" />  
    33.   
    34.         <Button  
    35.             android:layout_width="wrap_content"  
    36.             android:layout_height="wrap_content"  
    37.             android:layout_weight="1"  
    38.             android:onClick="onReset"  
    39.             android:text="重置" />  
    40.     </LinearLayout>  
    41.   
    42. </LinearLayout>  
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >
    
        <ImageView
            android:id="@+id/imageView"
            android:layout_width="fill_parent"
            android:layout_height="0dp"
            android:layout_weight="2" />
    
        <GridLayout
            android:id="@+id/group"
            android:layout_width="match_parent"
            android:layout_height="200dp"
            android:layout_weight="3"
            android:columnCount="3"
            android:rowCount="3" >
        </GridLayout>
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal" >
    
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:onClick="onChange"
                android:text="生效" />
    
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:onClick="onReset"
                android:text="重置" />
        </LinearLayout>
    
    </LinearLayout>

    效果图:

    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);

    示例代码:

    界面代码(就一个ImageView)省略...

    1. private ImageView imageView;  
    2.     private Bitmap bitmap;  
    3.     protected void onCreate(Bundle savedInstanceState) {  
    4.         super.onCreate(savedInstanceState);  
    5.         setContentView(R.layout.testmatrixmethod);  
    6.           
    7.         imageView = (ImageView) findViewById(R.id.imageView);  
    8.           
    9.         bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);  
    10.         imageView.setImageBitmap(changeImage());  
    11.     }  
    12.       
    13.     private Bitmap changeImage(){  
    14.   
    15.         Bitmap bmp = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888);  
    16.         Matrix matrix = new Matrix();  
    17.         //旋转变换,参数是顺时针旋转角度  
    18.         matrix.setRotate(45);  
    19.         //平移变化,参数是要平移到的坐标  
    20.         //matrix.setTranslate(50, 50);  
    21.         //缩放变化  
    22.         //matrix.setScale(10, 10, 10, 10);  
    23.         //错切变换  
    24.         //matrix.setSkew(10, 10, 10, 10);  
    25.         Canvas canvas = new Canvas(bmp);  
    26.         canvas.drawBitmap(bitmap, matrix, null);  
    27.         return bmp;  
    28.     }  
    private ImageView imageView;
    	private Bitmap bitmap;
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.testmatrixmethod);
    		
    		imageView = (ImageView) findViewById(R.id.imageView);
    		
    		bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
    		imageView.setImageBitmap(changeImage());
    	}
    	
    	private Bitmap changeImage(){
    
    		Bitmap bmp = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888);
    		Matrix matrix = new Matrix();
    		//旋转变换,参数是顺时针旋转角度
    		matrix.setRotate(45);
    		//平移变化,参数是要平移到的坐标
    		//matrix.setTranslate(50, 50);
    		//缩放变化
    		//matrix.setScale(10, 10, 10, 10);
    		//错切变换
    		//matrix.setSkew(10, 10, 10, 10);
    		Canvas canvas = new Canvas(bmp);
    		canvas.drawBitmap(bitmap, matrix, null);
    		return bmp;
    	}


    效果图:

    二、像素块分析

    图像的特效处理有两种方式,即使用矩阵来进行图像变换和使用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个点,而每个点的坐标则以x1,y1,x2,y2,...,xn,yn的形式保存在verts数组中。也就是说verts数组的每两位用来保存一个交织点,第一个是横坐标,第二个是纵坐标。而整个drawBitmapMesh()方法改变图像的方式,就是靠这些坐标值的改变来重新定义每一个图像块,从而达到图像效果处理的功能。

    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()方法中改变交叉点的纵坐标的值,为了实现旗帜飘扬的效果,使用一个正弦函数sinx来改变交叉点纵坐标的值,而横坐标不变,并将变化后的值保存到verts数组中,代码如下所示:

        @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;
                }
            }
        }

    这样,每次在重绘时,通过改变相位来改变偏移量,从而造成一个动态的效果,就好象旗帜在风中飘扬一样,效果图如下(这里应该是动态的,似乎一个飘扬的旗帜)。

    主要代码:

    1. package com.mfc.myview;  
    2.   
    3. import android.content.Context;  
    4. import android.graphics.Bitmap;  
    5. import android.graphics.BitmapFactory;  
    6. import android.graphics.Canvas;  
    7. import android.util.AttributeSet;  
    8. import android.view.View;  
    9.   
    10. import com.example.androidmatrix.R;  
    11.   
    12. public class FlagBitmapMeshView extends View {  
    13.   
    14.     private final int WIDTH = 200;  
    15.     private final int HEIGHT = 200;  
    16.     private int COUNT = (WIDTH + 1) * (HEIGHT + 1);  
    17.     private float[] verts = new float[COUNT * 2];  
    18.     private float[] orig = new float[COUNT * 2];  
    19.     private Bitmap bitmap;  
    20.     private float A;  
    21.     private float k = 1;  
    22.   
    23.     public FlagBitmapMeshView(Context context) {  
    24.         super(context);  
    25.         initView(context);  
    26.     }  
    27.   
    28.     public FlagBitmapMeshView(Context context, AttributeSet attrs) {  
    29.         super(context, attrs);  
    30.         initView(context);  
    31.     }  
    32.   
    33.     public FlagBitmapMeshView(Context context, AttributeSet attrs,  
    34.                               int defStyleAttr) {  
    35.         super(context, attrs, defStyleAttr);  
    36.         initView(context);  
    37.     }  
    38.   
    39.     private void initView(Context context) {  
    40.         setFocusable(true);  
    41.         bitmap = BitmapFactory.decodeResource(context.getResources(),  
    42.                 R.drawable.we);  
    43.         float bitmapWidth = bitmap.getWidth();  
    44.         float bitmapHeight = bitmap.getHeight();  
    45.         int index = 0;  
    46.         for (int y = 0; y <= HEIGHT; y++) {  
    47.             float fy = bitmapHeight * y / HEIGHT;  
    48.             for (int x = 0; x <= WIDTH; x++) {  
    49.                 float fx = bitmapWidth * x / WIDTH;  
    50.                 orig[index * 2 + 0] = verts[index * 2 + 0] = fx;  
    51.                 orig[index * 2 + 1] = verts[index * 2 + 1] = fy + 100;  
    52.                 index += 1;  
    53.             }  
    54.         }  
    55.         A = 50;  
    56.     }  
    57.   
    58.     @Override  
    59.     protected void onDraw(Canvas canvas) {  
    60.         flagWave();  
    61.         k += 0.1F;  
    62.         canvas.drawBitmapMesh(bitmap, WIDTH, HEIGHT,  
    63.                 verts, 0, null, 0, null);  
    64.         invalidate();  
    65.     }  
    66.   
    67.     private void flagWave() {  
    68.         for (int j = 0; j <= HEIGHT; j++) {  
    69.             for (int i = 0; i <= WIDTH; i++) {  
    70.                 verts[(j * (WIDTH + 1) + i) * 2 + 0] += 0;  
    71.                 float offsetY =  
    72.                         (float) Math.sin((float) i / WIDTH * 2 * Math.PI +  
    73.                                 Math.PI * k);  
    74.                 verts[(j * (WIDTH + 1) + i) * 2 + 1] =  
    75.                         orig[(j * WIDTH + i) * 2 + 1] + offsetY * A;  
    76.             }  
    77.         }  
    78.     }  
    79. }  
    package com.mfc.myview;
    
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.util.AttributeSet;
    import android.view.View;
    
    import com.example.androidmatrix.R;
    
    public class FlagBitmapMeshView extends View {
    
        private final int WIDTH = 200;
        private final int HEIGHT = 200;
        private int COUNT = (WIDTH + 1) * (HEIGHT + 1);
        private float[] verts = new float[COUNT * 2];
        private float[] orig = new float[COUNT * 2];
        private Bitmap bitmap;
        private float A;
        private float k = 1;
    
        public FlagBitmapMeshView(Context context) {
            super(context);
            initView(context);
        }
    
        public FlagBitmapMeshView(Context context, AttributeSet attrs) {
            super(context, attrs);
            initView(context);
        }
    
        public FlagBitmapMeshView(Context context, AttributeSet attrs,
                                  int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initView(context);
        }
    
        private void initView(Context context) {
            setFocusable(true);
            bitmap = BitmapFactory.decodeResource(context.getResources(),
                    R.drawable.we);
            float bitmapWidth = bitmap.getWidth();
            float bitmapHeight = bitmap.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 + 0] = verts[index * 2 + 0] = fx;
                    orig[index * 2 + 1] = verts[index * 2 + 1] = fy + 100;
                    index += 1;
                }
            }
            A = 50;
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            flagWave();
            k += 0.1F;
            canvas.drawBitmapMesh(bitmap, WIDTH, HEIGHT,
                    verts, 0, null, 0, null);
            invalidate();
        }
    
        private void flagWave() {
            for (int j = 0; j <= HEIGHT; j++) {
                for (int i = 0; i <= WIDTH; i++) {
                    verts[(j * (WIDTH + 1) + i) * 2 + 0] += 0;
                    float offsetY =
                            (float) Math.sin((float) i / WIDTH * 2 * Math.PI +
                                    Math.PI * k);
                    verts[(j * (WIDTH + 1) + i) * 2 + 1] =
                            orig[(j * WIDTH + i) * 2 + 1] + offsetY * A;
                }
            }
        }
    }

    使用drawBitmapMesh()方法可以创建很多复杂的图像效果,但是对它的使用也相对复杂,需要我们对图像处理有很深厚的功底。同时,对算法的要求也比较高,需要计算各种特效下不同的坐标点变化规律,从而设计出不同的特效。

  • 相关阅读:
    龟兔赛跑(动态规划)
    Dividing (多重背包 搜索)
    第k大值01背包问题
    FATE(完全背包)
    01背包 和 完全背包 详解
    放苹果(动态规划)
    max Sum(简单动态规划)
    连连看 优先对列 应用2
    尺取法
    Square(强大的剪枝)
  • 原文地址:https://www.cnblogs.com/xxr1/p/7296340.html
Copyright © 2011-2022 走看看