美文网首页
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