Android Matrix(变形矩阵)

本文详细介绍了Android中通过Matrix实现的图像变换,包括平移、旋转、缩放和错切等核心变换方式,并提供了实例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概要:

Matrix(矩阵),这是一个数学的概念。通常的nxm(n行,m列)行列数是不受限制的。但这里主要讲的是

3x3矩阵(3行3列)如图:


Matrix对图形的变形通常有如下四种形式:

Translate       平移变换 

Rotate            旋转变换   

Scale              缩放变换

Skew              错切变换

从字面上的意思,MACALE负责缩放,MSKEW负责错切,MARANS负责平移。MPERSP 用于处理透视的

变化(不常用),当然我们不能完全按照上面的意思理解。


针对上面的四种变换,Android进行了封装,提供三种方法setXX(设值),preXX(前乘),postXX(后乘)。


分析:

一,Translate      平移变换

将一个点 ,平移到点的位置,如下图 :


由图可知:


用矩阵来表示的话:

 


二,Rotate    旋转变换

2.1将一个点 ,围绕坐标原点(0,0)顺时针旋转,如图:


其中点到原点的距离为r。到原点连线与X轴正方向的夹角为α。

有图可知:


用矩阵表示为:



2.2 将一个点 ,围绕坐标某个点,顺时针旋转

有上面的推理可知:


矩阵变换:


分析矩阵表达式:

1.   

  是将坐标原点移动到点后, 的新坐标。

2.     


是将上一步变换后的,围绕新的坐标原点顺时针旋转 。

3.     

经过上一步旋转变换后,再将坐标原点移回到原来的坐标原点。

所以,围绕某一点进行旋转变换,可以分成3个步骤,即首先将坐标原点移至该点,然后围绕新

的坐标原点进行旋转变换,再然后将坐标原点移回到原先的坐标原点。


三,Scale    缩放变换

理论上而言,一个点是不存在什么缩放变换的,这里需要说明一下,因为所有图像都是由点组成,所以图像的

变化是由所有的点的变化来完成的(这也是前面分析的都是点的变化的原因)。例如在这里如果图像在x轴和

y轴方向分别放k1k2倍的话,那么图像中的所有点的x坐标y坐标均会分别放大k1k2倍,即


用矩阵表示就是:


缩放变换比较好理解,就不多说了。

四,错切变化:

错切变换就是让所有点的x坐标(或者y坐标)保持不变,而对应的y坐标(或者x坐标)则按比例发生平移,

如图所示:

1,各点的y坐标保持不变,但其x坐标则按比例发生了平移。这种情况将水平错切。


2,各点的x坐标保持不变,但其y坐标则按比例发生了平移。这种情况叫垂直错切。


假定一个点经过错切变换后得到,对于水平错切而言,应该有如下关系:

用矩阵表示为:

 

同理,对于垂直错切,可以有:

在数学上严格的错切变换就是上面这样的。在Android中除了有上面说到的情况外,还可以同时进行水平、

垂直错切,那么形式上就是:

应用:

我们设置九个EditText,自己设置矩阵。然后根据矩阵取变换图像。

public class MatrixActivity extends Activity {

    private EditText[] edits;
    private Button btn_change, btn_reset;
    private ImageView imageView;
    private static final String tag = "tag_matrix";

    Matrix matrix = new Matrix();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_matrix);

        initViews();
        btn_change.setOnClickListener(onClickListener);
        btn_reset.setOnClickListener(onClickListener);
    }

    private void initViews() {
        imageView = (ImageView) findViewById(R.id.iv_show);
        edits = new EditText[9];
        for (int i = 0; i < 9; i++) {
            edits[i] = (EditText) findViewById(R.id.et_0 + i);
        }
        btn_change = (Button) findViewById(R.id.btn_change);
        btn_reset = (Button) findViewById(R.id.btn_reset);
    }

    View.OnClickListener onClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.btn_change:
                    changeImage();
                    break;
                case R.id.btn_reset:
                    resetImage();
                    break;
            }
        }
    };

    private void changeImage() {
        LogUtil.i(tag, "changeImage");
        float[] data = new float[9];
        for (int i = 0; i < edits.length; i++) {
            String txt = edits[i].getText().toString();
            try {
                if ("".equals(txt)) {
                    data[i] = 0;
                } else {
                    data[i] = Float.parseFloat(txt);
                }
            } catch (Exception e) {
                data[i] = 0;
            }
            LogUtil.i(tag, "" + data[i]);
        }
        matrix.setValues(data);

        BitmapDrawable bitmapDrawable = (BitmapDrawable) getResources().getDrawable(R.drawable.aa2);
        Bitmap tempBitmap = bitmapDrawable.getBitmap();
        Bitmap bitmap = Bitmap.createBitmap(tempBitmap, 0, 0, tempBitmap.getWidth(), tempBitmap.getHeight(), matrix, true);
        imageView.setImageBitmap(bitmap);
    }

    private void resetImage() {
        LogUtil.i(tag, "resetImage");
        matrix.reset();
        imageView.setImageResource(R.drawable.aa2);
    }

}
布局文件:res/layout/activity_matrix

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:id="@+id/ll_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:text="原图"/>

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:gravity="center"
            android:text="变换图"/>
    </LinearLayout>

    <ImageView
        android:id="@+id/iv_init"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:src="@drawable/aa2"
        android:layout_margin="10dp"
        android:layout_below="@+id/ll_title"/>

    <RelativeLayout
        android:layout_below="@+id/ll_title"
        android:layout_toRightOf="@+id/iv_init"
        android:layout_above="@+id/rl_input"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ImageView
            android:id="@+id/iv_show"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:scaleType="center"
            android:src="@drawable/aa2"/>
    </RelativeLayout>


    <RelativeLayout
        android:id="@+id/rl_input"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@+id/ll_button">

        <EditText
            android:id="@+id/et_0"
            android:layout_width="50dp"
            android:layout_marginLeft="20dp"
            android:layout_height="wrap_content" />

        <EditText
            android:id="@+id/et_1"
            android:layout_width="50dp"
            android:layout_centerHorizontal="true"
            android:layout_height="wrap_content" />

        <EditText
            android:id="@+id/et_2"
            android:layout_width="50dp"
            android:layout_height="wrap_content"
            android:layout_marginRight="20dp"
            android:layout_alignParentRight="true"/>

        <EditText
            android:id="@+id/et_3"
            android:layout_width="50dp"
            android:layout_margin="20dp"
            android:layout_below="@+id/et_0"
            android:layout_height="wrap_content" />

        <EditText
            android:id="@+id/et_4"
            android:layout_width="50dp"
            android:layout_margin="20dp"
            android:layout_below="@+id/et_0"
            android:layout_centerHorizontal="true"
            android:layout_height="wrap_content" />
        <EditText
            android:id="@+id/et_5"
            android:layout_width="50dp"
            android:layout_margin="20dp"
            android:layout_below="@+id/et_0"
            android:layout_alignParentRight="true"
            android:layout_height="wrap_content" />

        <EditText
            android:id="@+id/et_6"
            android:layout_width="50dp"
            android:layout_marginLeft="20dp"
            android:layout_marginBottom="20dp"
            android:layout_below="@+id/et_3"
            android:layout_height="wrap_content" />

        <EditText
            android:id="@+id/et_7"
            android:layout_width="50dp"
            android:layout_below="@+id/et_3"
            android:layout_centerHorizontal="true"
            android:layout_height="wrap_content" />

        <EditText
            android:id="@+id/et_8"
            android:layout_width="50dp"
            android:layout_marginRight="20dp"
            android:layout_marginBottom="20dp"
            android:layout_below="@+id/et_3"
            android:layout_alignParentRight="true"
            android:layout_height="wrap_content" />

    </RelativeLayout>

    <LinearLayout
        android:id="@+id/ll_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:orientation="horizontal">

        <Button
            android:id="@+id/btn_change"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="change"/>

        <Button
            android:id="@+id/btn_reset"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="reset"/>
    </LinearLayout>
    
</RelativeLayout>

3.1缩放变换(宽高同时放大到2倍):


3.2错切变换(垂直错切)


3.3 绕原点旋转变换(旋转30度)。


4,平移变换,使用这种方式(

Bitmap.createBitmap(tempBitmap, 0, 0, tempBitmap.getWidth(), tempBitmap.getHeight(), matrix, true)
)不起作用(平移并不会改变图像本身)。

可改用Canvas。drawBitmap(Bitmap, Matrix, Paint);

我们修改一下代码:

        BitmapDrawable bitmapDrawable = (BitmapDrawable) getResources().getDrawable(R.drawable.aa2);
        Bitmap tempBitmap = bitmapDrawable.getBitmap();

        Bitmap bitmap = Bitmap.createBitmap(tempBitmap.getWidth(), tempBitmap.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        MatrixDrawable matrixDrawable = new MatrixDrawable(tempBitmap, matrix);
        matrixDrawable.draw(canvas);

其中MatrixDrawable的代码:


public class MatrixDrawable extends Drawable {

    private final static String tag = "MatrixDrawable";

    //画笔
    Paint mPaint;
    //绘图矩阵
    Matrix mMatrix;
    //原始图
    Bitmap mBitmap;
    //矩阵元素存储的数组
    float[] params;

    Bitmap temp;

    MatrixDrawable(Bitmap bitmap, Matrix matrix) {
        this.mBitmap = bitmap;
        this.mMatrix = matrix;

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
    }
    
    @Override
    public void draw(Canvas canvas) {
        canvas.drawBitmap(mBitmap, mMatrix, mPaint);
    }

    @Override
    public int getIntrinsicWidth() {
        if (mBitmap != null) {
            return mBitmap.getWidth();
        }
        return super.getIntrinsicWidth();
    }

    @Override
    public int getIntrinsicHeight() {
        if (mBitmap != null) {
            return mBitmap.getHeight();
        }
        return super.getIntrinsicHeight();
    }

    @Override
    public void setAlpha(int alpha) {
        mPaint.setAlpha(alpha);
    }

    @Override
    public void setColorFilter(ColorFilter colorFilter) {
        mPaint.setColorFilter(colorFilter);
    }

    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT;
    }

    public Matrix getmMatrix(){
        return mMatrix;
    }
}
效果如图:







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值