写在前面:

本篇博文将结合一个简单的Demo,讲解一下如何通过Matrix实现对Bitmap对象的一些简单处理,例如平移,旋转,放缩等。此外还会讲解一下矩阵乘法中的左乘右乘在Android中的代码实现。

一、Matrix类

这里说的Matrix类是位于"android.graphics.Matrix"包下的。它是Android提供的一个矩阵工具类,它本身不能对图像或View进行变换,但它可与其他API结合来控制图形、View的变换,如Canvas。在Matrix类中,提供了一些方法来控制图片变换:

  • setTranslate(float dx,float dy):控制Matrix进行位移。
  • setSkew(float kx,float ky):控制Matrix进行倾斜,kx、ky为X、Y方向上的比例。
  • setSkew(float kx,float ky,float px,float py):控制Matrix以px、py为轴心进行倾斜,kx、ky为X、Y方向上的倾斜比例。
  • setRotate(float degrees):控制Matrix进行depress角度的旋转,轴心为(0,0)。
  • setRotate(float degrees,float px,float py):控制Matrix进行depress角度的旋转,轴心为(px,py)。
  • setScale(float sx,float sy):设置Matrix进行缩放,sx、sy为X、Y方向上的缩放比例。
  • setScale(float sx,float sy,float px,float py):设置Matrix以(px,py)为轴心进行缩放,sx、sy为X、Y方向上的缩放比例。

其中每一个功能又以三个方法为一组,以位移的Translate为例:

  •  setTranslate(float dx,float dy)
  •  preTranslate(float dx,float dy)
  •  postTranslate(float dx,float dy)

其中pre前缀和post前缀分别对应了矩阵乘法中的右乘(前乘) 和左乘(后乘),至于更加详细的部分我会与本文后半部分讲解。

二、通过Matrix实现单矩阵变换

所谓单矩阵变换说的玄乎,其实就是单次变换,比如平移一次或者旋转一次。

2.1.对Bitmap进行放缩

因为单矩阵变换思路都一样,只是采用了不同的set方法去设置矩阵,我就在这里统一说下变换思路:

1.首先要根据变换的情况计算变换后的位图大小,以避免图片变换之后跑出原区域导致显示不全的情况

2.用计算之后的位图创建Canvas实例

3.设置矩阵Matrix

4.开始绘图

5.最后把绘制好的位图设置到ImageView上展示

/** * 图片缩放 * */private void bitmapScale(float x,float y){    newBitmap = Bitmap.createBitmap((int) (mBitmap.getWidth() * x),(int) (mBitmap.getHeight() * y), mBitmap.getConfig());    Canvas canvas = new Canvas(newBitmap);    Matrix matrix = new Matrix();    matrix.setScale(x, y);    //第一个参数为待绘制的bitmap,第二个参数为矩阵,第三个参数为画笔    canvas.drawBitmap(mBitmap, matrix, mPaint);    imageView.setImageBitmap(newBitmap);    mBitmap = newBitmap;}

上述代码中newBitmap是变换之后的图片,mBitmap是变换之前的图片。Bitmap.createBitmap()方法接收三个参数,依次为宽、高、配置项,这里沿用了原图的配置。最后 mBitmap = newBitmap 是为了使后续变换能够在上一次变换的基础上进行。

2.2.对Bitmap进行旋转

/** * 图片旋转 * */private void bitmapRotate(float degrees){    newBitmap = Bitmap.createBitmap(mBitmap.getWidth(),mBitmap.getHeight(),mBitmap.getConfig());    Canvas canvas = new Canvas(newBitmap);    Matrix matrix = new Matrix();    matrix.setRotate(degrees,mBitmap.getWidth()/2,mBitmap.getHeight()/2);    canvas.drawBitmap(mBitmap, matrix, mPaint);    imageView.setImageBitmap(newBitmap);    mBitmap = newBitmap;}

2.3.对Bitmap进行平移

/** * 图片平移 * */private void bitmapTranslate(float dx,float dy){    newBitmap = Bitmap.createBitmap((int)(mBitmap.getWidth()+Math.abs(dx)),(int)(mBitmap.getHeight()+Math.abs(dy)),mBitmap.getConfig());    Canvas canvas = new Canvas(newBitmap);    Matrix matrix = new Matrix();    matrix.setTranslate(dx,dy);    canvas.drawBitmap(mBitmap,matrix,mPaint);    imageView.setImageBitmap(newBitmap);    mBitmap = newBitmap;}

三、通过Matrix实现多矩阵变换

在讲多矩阵变换之前得明确矩阵乘法的基本性质,需要牢记以下两点:

  • 矩阵乘法满足结合律 (AB)C=A(BC),因此可以任何两个先乘, 自由结合
  • 矩阵乘法大多时候不满足交换律,因此不可以颠倒矩阵的顺序进行乘算

弄清楚矩阵乘法之后,我们再来看set前缀pre前缀以及post前缀变换时的区别,先来看看官方API文档:

public boolean postTranslate (float dx, float dy)

Postconcats the matrix with the specified translation. M' = T(dx, dy) * M

public boolean preTranslate (float dx, float dy)

Preconcats the matrix with the specified translation. M' = M * T(dx, dy)

在图形学中,矩阵M右乘A,表示的是 A * M,而矩阵 M 左乘 A,则表示的是 M * A,一比较,我们可以看出,pre其实执行的就是右乘的操作,而post执行的就是左乘的操作。这是因为,在图像处理中,越靠近右边的矩阵越先执行,所以pre(也就是先的意思)所设置的矩阵T(Scale,Rotation也是一样的)就会先于其一开始设置的Scale执行,而post(后的意思)的因为是左乘,所以它会放在最左边,那么就会最后执行。这时你们肯定会问set呢?set前缀的方法首先会将该Matrix设置为对角矩阵,即相当于调用reset()方法,然后再设置该Matrix的变换矩阵。纯文字的讲解估计说的已经云里雾里了,我们来看看具体的矩阵乘法:

以放缩变换Scale(Sx,Sy)为例,其变换矩阵为

因为是多矩阵变换,这里我们再列举一个平移变换Translate(Tx,Ty),其变换矩阵为

如果我们调用了

matrix.setScale(Sx, Sy);

那么对应的矩阵乘法为: 

如果我们调用了

matrix.setTranslate(Tx,Ty);

那么对应的矩阵乘法为 

如果是多矩阵变换的情况,我们调用了

matrix.setScale(Sx,Sy);matrix.preTranslate(Tx,Ty);

那么对应的矩阵乘法为  

因为矩阵乘法满足结合律,因此该变换又可以视为先平移变换后放缩变换。

如果我们调用的是

matrix.setScale(Sx,Sy);matrix.postTranslate(Tx,Ty);

那么对应的矩阵乘法为  

同样的,该变换又可以视为先放缩变换后平移变换,显然结果是不同于先平移后放缩的。

具体实现的代码如下:

1.先平移后缩放

/** * 平移后缩放 * */private void bitmapTranslateAndRScale(float dx,float dy,float sx,float sy){    newBitmap = Bitmap.createBitmap((int)((mBitmap.getWidth()+Math.abs(dx))*sx),(int)((mBitmap.getHeight()+Math.abs(dy))*sy),mBitmap.getConfig());    Canvas canvas = new Canvas(newBitmap);    Matrix matrix = new Matrix();    matrix.setScale(sx,sy);    matrix.preTranslate(dx,dy);    canvas.drawBitmap(mBitmap,matrix,mPaint);    imageView.setImageBitmap(newBitmap);    mBitmap = newBitmap;}

2.先缩放后平移

/** * 缩放后平移 * */private void bitmapScaleAndTranslate(float dx,float dy,float sx,float sy){    newBitmap = Bitmap.createBitmap((int)(mBitmap.getWidth()*sx+Math.abs(dx)),(int)(mBitmap.getHeight()*sy+Math.abs(dy)),mBitmap.getConfig());    Canvas canvas = new Canvas(newBitmap);    Matrix matrix = new Matrix();    matrix.setScale(sx,sy);    matrix.postTranslate(dx,dy);    canvas.drawBitmap(mBitmap,matrix,mPaint);    imageView.setImageBitmap(newBitmap);    mBitmap = newBitmap;}

运行起来后,我们就会发现这两种变换的结果是不同的,但是“先平移后缩放”的变换又和我们先点击平移按钮再点击缩放按钮最终效果一致,这和我们之前的理论分析又对应上了。

最后贴一下源码:

MainActivity.java

import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Matrix;import android.graphics.Paint;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.ImageView;public class MainActivity extends AppCompatActivity implements View.OnClickListener {    private ImageView imageView;    private Button btnReset,btnScale,btnRotate,btnTranslate,btnPostTrans,btnPreTrans;    private Bitmap mBitmap,newBitmap;    private Paint mPaint;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        initViews();        btnReset.setOnClickListener(this);        btnScale.setOnClickListener(this);        btnRotate.setOnClickListener(this);        btnTranslate.setOnClickListener(this);        btnPostTrans.setOnClickListener(this);        btnPreTrans.setOnClickListener(this);        bitmapReset();        mPaint = new Paint();        mPaint.setAntiAlias(true);    }    private void initViews(){        imageView = (ImageView) findViewById(R.id.imageView);        btnReset = (Button) findViewById(R.id.btn_reset);        btnScale = (Button) findViewById(R.id.btn_scale);        btnRotate = (Button) findViewById(R.id.btn_rotate);        btnTranslate = (Button) findViewById(R.id.btn_translate);        btnPostTrans = (Button) findViewById(R.id.btn_post_trans);        btnPreTrans = (Button) findViewById(R.id.btn_pre_trans);    }    @Override    public void onClick(View v) {        switch (v.getId()){            case R.id.btn_reset:                bitmapReset();                break;            case R.id.btn_scale:                bitmapScale(0.5f,0.5f);                break;            case R.id.btn_rotate:                bitmapRotate(45);                break;            case R.id.btn_translate:                bitmapTranslate(100,100);                break;            case R.id.btn_post_trans:                bitmapScaleAndTranslate(100,100,0.5f,0.5f);                break;            case R.id.btn_pre_trans:                bitmapTranslateAndRScale(100,100,0.5f,0.5f);                break;            default:                break;        }    }    private void bitmapReset(){        mBitmap = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(),R.drawable.timg),                400,400,false);        imageView.setImageBitmap(mBitmap);    }    /**     * 图片缩放     * */    private void bitmapScale(float x,float y){        newBitmap = Bitmap.createBitmap((int) (mBitmap.getWidth() * x),(int) (mBitmap.getHeight() * y), mBitmap.getConfig());        Canvas canvas = new Canvas(newBitmap);        Matrix matrix = new Matrix();        matrix.setScale(x, y);        canvas.drawBitmap(mBitmap, matrix, mPaint);        imageView.setImageBitmap(newBitmap);        mBitmap = newBitmap;    }    /**     * 图片旋转     * */    private void bitmapRotate(float degrees){        newBitmap = Bitmap.createBitmap(mBitmap.getWidth(),mBitmap.getHeight(),mBitmap.getConfig());        Canvas canvas = new Canvas(newBitmap);        Matrix matrix = new Matrix();        matrix.setRotate(degrees,mBitmap.getWidth()/2,mBitmap.getHeight()/2);        canvas.drawBitmap(mBitmap, matrix, mPaint);        imageView.setImageBitmap(newBitmap);        mBitmap = newBitmap;    }    /**     * 图片平移     * */    private void bitmapTranslate(float dx,float dy){        newBitmap = Bitmap.createBitmap((int)(mBitmap.getWidth()+Math.abs(dx)),(int)(mBitmap.getHeight()+Math.abs(dy)),mBitmap.getConfig());        Canvas canvas = new Canvas(newBitmap);        Matrix matrix = new Matrix();        matrix.setTranslate(dx,dy);        canvas.drawBitmap(mBitmap,matrix,mPaint);        imageView.setImageBitmap(newBitmap);        mBitmap = newBitmap;    }    /**     * 平移后缩放     * */    private void bitmapTranslateAndRScale(float dx,float dy,float sx,float sy){        newBitmap = Bitmap.createBitmap((int)((mBitmap.getWidth()+Math.abs(dx))*sx),(int)((mBitmap.getHeight()+Math.abs(dy))*sy),mBitmap.getConfig());        Canvas canvas = new Canvas(newBitmap);        Matrix matrix = new Matrix();        matrix.setScale(sx,sy);        matrix.preTranslate(dx,dy);        canvas.drawBitmap(mBitmap,matrix,mPaint);        imageView.setImageBitmap(newBitmap);        mBitmap = newBitmap;    }    /**     * 缩放后平移     * */    private void bitmapScaleAndTranslate(float dx,float dy,float sx,float sy){        newBitmap = Bitmap.createBitmap((int)(mBitmap.getWidth()*sx+Math.abs(dx)),(int)(mBitmap.getHeight()*sy+Math.abs(dy)),mBitmap.getConfig());        Canvas canvas = new Canvas(newBitmap);        Matrix matrix = new Matrix();        matrix.setScale(sx,sy);        matrix.postTranslate(dx,dy);        canvas.drawBitmap(mBitmap,matrix,mPaint);        imageView.setImageBitmap(newBitmap);        mBitmap = newBitmap;    }}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>            

更多相关文章

  1. Android(安卓)RxJava:图文详解 变换操作符
  2. Android中图像变换Matrix的原理、代码验证和应用(一)
  3. android动画Android(安卓)动画实践
  4. 从Android中Activity之间的通信说开来
  5. Android下openGL操作矩阵的函数
  6. Android(安卓)OpenGL之二图像旋转实例
  7. Android(安卓)图片缩放实例详解
  8. OpenGL ES 画正方形
  9. Android下openGL操作矩阵的函数

随机推荐

  1. 搭建Qt 5.3.1 for Windows Phone 8开发环
  2. Android(安卓)OpenGL ES 开发(二)— 绘制三
  3. 安全性帮苹果在IT领域打败Android
  4. android中获取宽高
  5. activity使用theme.dialog且activity中有
  6. “Android第一书”作者郭霖:用心做事,结果
  7. libevent 多线程IO
  8. 在Android(安卓)P中默认使用TLS保护用户
  9. Android(安卓)OpenglES教程(1)
  10. android 把activity和资源文件打包成jar