美文网首页
Android 变形矩阵——Matrix

Android 变形矩阵——Matrix

作者: 这是一个随便起的昵称 | 来源:发表于2018-04-09 21:55 被阅读120次

Matrix 常用来实现图片平移,图片旋转,图片错切,图片缩放。在查看MPAndroidChart源码时,发现其中的图表平移等功能用到了Metrix。刷新我的认知,原来还可以这样来写自定义View。果然还是一直活在梦里。。。

下图是一张Matrix内容图

1890917-4168fa9b2791e659.png

0.getValues(float[] values)/setValues(float[] values)

public static final int MSCALE_X = 0;   //!< use with getValues/setValues
public static final int MSKEW_X  = 1;   //!< use with getValues/setValues
public static final int MTRANS_X = 2;   //!< use with getValues/setValues
public static final int MSKEW_Y  = 3;   //!< use with getValues/setValues
public static final int MSCALE_Y = 4;   //!< use with getValues/setValues
public static final int MTRANS_Y = 5;   //!< use with getValues/setValues
public static final int MPERSP_0 = 6;   //!< use with getValues/setValues
public static final int MPERSP_1 = 7;   //!< use with getValues/setValues
public static final int MPERSP_2 = 8;   //!< use with getValues/setValues

可以获取相对应的坐标点信息。

1. public boolean isIdentity()

判断是否为单位矩阵

Matrix matrix = new Matrix();
System.out.println("matrix = "+matrix.toShortString() + "\n" + "matrix is isIdentity " + matrix.isIdentity());
matrix.postTranslate(20, 20);
System.out.println("matrix = "+matrix.toShortString() + "\n" + "matrix is isIdentity " + matrix.isIdentity());

output

System.out:matrix = [1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0]
System.out:matrix is isIdentity true
System.out:matrix = [1.0, 0.0, 20.0][0.0, 1.0, 20.0][0.0, 0.0, 1.0]
System.out:matrix is isIdentity false

2.public boolean isAffine()

判断是否为仿射矩阵(https://www.zhihu.com/question/20666664)一般来说我们变换过的基本上是仿射矩阵。感觉这里比较深奥,不继续探讨。

3. public boolean rectStaysRect()

Returns true if will map a rectangle to another rectangle. This can be true if the matrix is identity, scale-only, or rotates a multiple of 90 degrees.

长方形 变换之后 任然为长方形的返回 true,也就是说一般的平移,缩放都会返回 true(旋转需要90 的整数倍)。这些操作不会改变物体的形状。错切会改变形状。

 Matrix matrix = new Matrix();
       
 matrix.postTranslate(20, 20);
 System.out.println("rectStaysRect " + matrix.rectStaysRect());//true

 matrix.postRotate(90);
 System.out.println("rectStaysRect " + matrix.rectStaysRect());//true

 matrix.postRotate(18);
 System.out.println("rectStaysRect " + matrix.rectStaysRect());//false

 matrix.postSkew(4, 4);
 System.out.println("rectStaysRect " + matrix.rectStaysRect());//false

4.public void set(Matrix src)

重新设置矩阵中的数值

5.public boolean equals(Object obj)

判断两个矩阵是否相等

6.public void reset()

将矩阵重新设置为单位矩阵


下面是一些重要的 API

7.setXXX/preXXX/postXXX

方法名 用途
setScale/Rotate/SinCos/Skew/Concat/Translate 重新设置
preScale/Rotate/SinCos/Skew/Concat/Translate 前乘,相当于矩阵右乘
postScale/Rotate/SinCos/Skew/Concat/Translate 后乘,相当于矩阵左乘

这里需要注意 pre与post 的使用。举个例子:

private void toTest() {
        int width = mImageView.getMeasuredWidth();
        int height = mImageView.getMeasuredHeight();
        Bitmap bitmap = Bitmap.createBitmap(width + 300, height + 300, baseBitmap.getConfig());
        Canvas canvas = new Canvas(bitmap);
        Matrix matrix = new Matrix();
        matrix.postTranslate(300, 300);
        //matrix = [1.0, 0.0, 300.0][0.0, 1.0, 300.0][0.0, 0.0, 1.0]
        matrix.preScale(0.5f ,0.5f);
        //matrix =[0.5, 0.0, 300.0][0.0, 0.5, 300.0][0.0, 0.0, 1.0]
        canvas.drawBitmap(baseBitmap, matrix, mPaint);
        mImageViewAfter.setImageBitmap(bitmap);
}
private void toTest() {
        ....
        matrix.postTranslate(300, 300);
        //matrix = [1.0, 0.0, 300.0][0.0, 1.0, 300.0][0.0, 0.0, 1.0]
        matrix.postScale(0.5f ,0.5f);
        //matrix = [0.5, 0.0, 150.0][0.0, 0.5, 150.0][0.0, 0.0, 1.0]
        ....
    }

上面两段代码执行之后的效果是不一样的


WechatIMG15.jpeg WechatIMG16.jpeg

上面两个矩阵的计算过程如下:


WechatIMG18.jpeg

所以执行之后的效果是不一样的。有的地方讲的是 pre是先执行,post是后执行。这种说法是完全错的。看上面代码的日志打印。我们需要用矩阵乘法来理解。

8.public boolean postConcat(Matrix other)

Postconcats the matrix with the specified matrix. M' = other * M

 Matrix matrix = new Matrix();
 matrix.postTranslate(20, 20);
 System.out.println("matrix = " + matrix.toShortString());
//matrix = [1.0, 0.0, 20.0][0.0, 1.0, 20.0][0.0, 0.0, 1.0]
 Matrix matrix2 = new Matrix();
 matrix2.postConcat(matrix);
 System.out.println("matrix2 = " + matrix2.toShortString());
//matrix2 = [1.0, 0.0, 20.0][0.0, 1.0, 20.0][0.0, 0.0, 1.0]

原始矩阵 与 目标矩阵相乘

9.public boolean setConcat(Matrix a, Matrix b)

将原始矩阵设为 a * b

Matrix matrix = new Matrix();
matrix.postTranslate(20, 20);
System.out.println("matrix = " + matrix.toShortString());
//matrix = [1.0, 0.0, 20.0][0.0, 1.0, 20.0][0.0, 0.0, 1.0]
Matrix matrix2 = new Matrix();
Matrix matrix3 = new Matrix();
matrix3.setConcat(matrix, matrix2);
System.out.println("matrix3 = " + matrix3.toShortString());
//matrix3 = [1.0, 0.0, 20.0][0.0, 1.0, 20.0][0.0, 0.0, 1.0]

10.public boolean invert(Matrix inverse)

矩阵反转,可以把转换后的数据还原成初始数据(前提是目标矩阵没有变)

Matrix matrix = new Matrix();
matrix.postTranslate(20, 0);
float[] pts = {0, 20, 20, 30};
matrix.mapPoints(pts);
System.out.println(Arrays.toString(pts));
//System.out: [20.0, 20.0, 40.0, 30.0]
Matrix temp = new Matrix();
matrix.invert(temp);
temp.mapPoints(pts);

System.out.println(Arrays.toString(pts));
//System.out: [0.0, 20.0, 20.0, 30.0]

可以看到矩阵反转把初始数据还原了

11.public void mapPoints

这个个方法是可以把对应的点坐标集,进行矩阵转换。例如点平移,“缩放”等。这个方法有几个重载方法,下面来看一个例子。

Matrix matrix = new Matrix();
Matrix tmp = new Matrix();
matrix.postTranslate(20.f, 0f);

float[] points = {0, 0, 10, 10, 20, 20, 30, 30};
matrix.mapPoints(points);
System.out.println(Arrays.toString(points));
//System.out: [20.0, 0.0, 30.0, 10.0, 40.0, 20.0, 50.0, 30.0]

tmp.reset();
matrix.invert(tmp);
tmp.mapPoints(points);//利用反转恢复初始数据

float[] dest = new float[points.length];

matrix.mapPoints(dest, points);
System.out.println(Arrays.toString(points));
//System.out: [0.0, 0.0, 10.0, 10.0, 20.0, 20.0, 30.0, 30.0]
System.out.println(Arrays.toString(dest));
//System.out: [20.0, 0.0, 30.0, 10.0, 40.0, 20.0, 50.0, 30.0]

Arrays.fill(dest, 0f);//重新填充数组
matrix.mapPoints(dest, 2, points, 2, 2);
System.out.println(Arrays.toString(points));
//System.out: [0.0, 0.0, 10.0, 10.0, 20.0, 20.0, 30.0, 30.0]

System.out.println(Arrays.toString(dest));
//System.out: [0.0, 0.0, 30.0, 10.0, 40.0, 20.0, 0.0, 0.0]

需要注意的是 dest 数组长度要等于 src 数组长度。

public void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex,int pointCount) 
//destIndex 是 dest 数组中开始转变的数组元素下标。可以是奇数,可以是偶数!
//这里需要注意的是 pointCount 这里是指点的个数。两两一对。pointCount=2,则是四个数组元素

12.public void mapVectors

这个方法可以过滤掉 “位移”的影响。

Matrix matrix = new Matrix();
matrix.postTranslate(20.f, 0f);

float[] points = {0, 0, 10, 10, 20, 20, 30, 30};
matrix.mapVectors(points);
System.out.println(Arrays.toString(points));
//System.out: [0.0, 0.0, 10.0, 10.0, 20.0, 20.0, 30.0, 30.0]

matrix.postScale(1, 2);
matrix.mapVectors(points);
System.out.println(Arrays.toString(points));
//System.out: [0.0, 0.0, 10.0, 20.0, 20.0, 40.0, 30.0, 60.0]

13.public boolean mapRect

Matrix matrix = new Matrix();
matrix.postTranslate(20f, 0f);
RectF rect = new RectF(0,0,10,10);
matrix.mapRect(rect);//mapRect(RectF dst, RectF src)即不改变原有数据源,用新数据源存储数据
System.out.println(rect);
//System.out: RectF(20.0, 0.0, 30.0, 10.0)

14.public float mapRadius(float radius)

Matrix matrix = new Matrix();
matrix.postScale(0.5f,1f);
int radius = 10;
float result = matrix.mapRadius(radius);
System.out.println(result);
//System.out: 7.071068

这里圆变成了椭圆,这里得到的是平均半径。

15.public boolean setPolyToPoly(float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount)

 /**
     * Set the matrix such that the specified src points would map to the specified dst points. The
     * "points" are represented as an array of floats, order [x0, y0, x1, y1, ...], where each
     * "point" is 2 float values.
     *
     * @param src The array of src [x,y] pairs (points)
     * @param srcIndex Index of the first pair of src values
     * @param dst The array of dst [x,y] pairs (points)
     * @param dstIndex Index of the first pair of dst values
     * @param pointCount The number of pairs/points to be used. Must be [0..4]
     * @return true if the matrix was set to the specified transformation
     */

贴一下官方解释文档。设置矩阵以便于源坐标集合 映射成 目标集合。即把指定的点移到指定的位置。

int width = mImageView.getMeasuredWidth();
int height = mImageView.getMeasuredHeight();

float[] src = {0 ,0 , width, 0, width, height, 0, height };
float[] dest = {0, 0, width, height / 5, width/2, height, 0, height};
Bitmap bitmap = Bitmap.createBitmap(width + 300, height + 300, baseBitmap.getConfig());
Canvas canvas = new Canvas(bitmap);
Matrix matrix = new Matrix();
matrix.setPolyToPoly(src, 0, dest, 0, 4);
canvas.drawBitmap(baseBitmap, matrix, mPaint);
mImageViewAfter.setImageBitmap(bitmap);
WechatIMG19.jpeg

上面这个示例把 右上角 和 右下角的点分别移动,所以出现上图所示的变换。自己可以在纸上画一下。

pointCount 功能
0 什么事也做不了
1 平移
2 平移,旋转
3 平移,旋转,错切
4 任意形状的变换

16.public boolean setRectToRect(RectF src, RectF dst, ScaleToFit stf)

把原矩阵内容填充到目标矩阵中。如何填充看第三个参数。

ScaleToFit 结果
FILL 填充满目标矩阵
START 填充在目标矩阵开始位置
CENTER 填充在目标矩阵中心位置
END 填充在目标矩阵结束位置

相关文章

网友评论

      本文标题:Android 变形矩阵——Matrix

      本文链接:https://www.haomeiwen.com/subject/drvmhftx.html