• graphics包Matrix类函数理解


    二维图形变换的矩阵如下:

    |ScaleX  SkewX  TransX|

    |SkewY  ScaleY  TransY|

    |Persp0  Persp1  Persp2|

    ScaleX:x方向缩放倍率

    ScaleY:y方向缩放倍率

    TransX:x方向平移值

    TransY:y方向平移值

    SkewX:x方向错切值

    SkewY:y方向错切值

    Persp:齐次坐标的值,一般取值0或1。

    graphics包中的Matrix类的方法调用native计算,jni调用了skia库中SkMatrix.cpp文件下的函数计算,各个函数实际上都是在对二维变换矩阵进行赋值:

    void SkMatrix::setScale(SkScalar sx, SkScalar sy) {
        if (1 == sx && 1 == sy) {
            this->reset();
        } else {
            fMat[kMScaleX] = sx;
            fMat[kMScaleY] = sy;
            fMat[kMPersp2] = 1;
    
            fMat[kMTransX] = fMat[kMTransY] =
            fMat[kMSkewX]  = fMat[kMSkewY] =
            fMat[kMPersp0] = fMat[kMPersp1] = 0;
    
            this->setTypeMask(kScale_Mask | kRectStaysRect_Mask);
        }
    }
    

     这是以原点为中心进行缩放的矩阵赋值。

    void SkMatrix::setScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
        if (1 == sx && 1 == sy) {
            this->reset();
        } else {
            fMat[kMScaleX] = sx;
            fMat[kMScaleY] = sy;
            fMat[kMTransX] = px - sx * px;
            fMat[kMTransY] = py - sy * py;
            fMat[kMPersp2] = 1;
    
            fMat[kMSkewX]  = fMat[kMSkewY] =
            fMat[kMPersp0] = fMat[kMPersp1] = 0;
    
            this->setTypeMask(kScale_Mask | kTranslate_Mask | kRectStaysRect_Mask);
        }
    }
    

     这是以指定坐标(px, py)为中心点进行缩放的矩阵赋值。与上一个函数对比可以发现,不仅进行了缩放还进行了平移,所以才可以表现为在指定点处缩放。

    void SkMatrix::setTranslate(SkScalar dx, SkScalar dy) {
        if (dx || dy) {
            fMat[kMTransX] = dx;
            fMat[kMTransY] = dy;

            fMat[kMScaleX] = fMat[kMScaleY] = fMat[kMPersp2] = 1;
            fMat[kMSkewX]  = fMat[kMSkewY] =
            fMat[kMPersp0] = fMat[kMPersp1] = 0;

            this->setTypeMask(kTranslate_Mask | kRectStaysRect_Mask);
        } else {
            this->reset();
        }
    }


    void SkMatrix::preTranslate(SkScalar dx, SkScalar dy) { if (!dx && !dy) { return; } if (this->hasPerspective()) { SkMatrix m; m.setTranslate(dx, dy); this->preConcat(m); } else { fMat[kMTransX] += sdot(fMat[kMScaleX], dx, fMat[kMSkewX], dy); fMat[kMTransY] += sdot(fMat[kMSkewY], dx, fMat[kMScaleY], dy); this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask); } } void SkMatrix::postTranslate(SkScalar dx, SkScalar dy) { if (!dx && !dy) { return; } if (this->hasPerspective()) { SkMatrix m; m.setTranslate(dx, dy); this->postConcat(m); } else { fMat[kMTransX] += dx; fMat[kMTransY] += dy; this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask); } } static inline SkScalar sdot(SkScalar a, SkScalar b, SkScalar c, SkScalar d) { return a * b + c * d; }

     上面的这四个函数说明了set,pre和post的区别,假如有如下操作:

    (1)

    Matrix.scale(0.5, 0.5);

    Matrix.preTranslate(100, 100);

    (2)

    Matrix.scale(0.5, 0.5);

    Matrix.postTranslate(100, 100);

    第一段代码会平移(100*0.5, 100*0.5),第二段代码会平移(100, 100),post不受之前的矩阵变换的影响。

    而set则会清除所有值钱的矩阵设置。

    其它函数的pre和post的意思都是如此。

    比如preRotate():那么它的旋转中心点会受到之前矩阵变换的影响,而postRotate()不会。

    preTranslate(100, 100);

    preRotate(45):那么它的旋转中心点是(100, 100)

    postRotate(45):旋转中心点是原点

    图片的矩阵变换是对图片在屏幕上的每个坐标点进行变换。

    比如矩形框Rect(100, 100, 600, 600),缩放scale(0.5f, 0.5f)之后变为Rect(50, 50, 300, 300)在屏幕上表现为矩形本身有缩小,但位置也有了偏移,这个时候不能简单的认为是矩形框保持矩形中心位置不变或左上角位置不变作缩放。

    所以一般的绕中心缩放,倍数缩放操作之后还需要平移。因为中心坐标点位置不变,所以有关系式

    先缩放后平移:

    centerX * scaleX + translateX = centerX

    centerY * scaleY + translateY = centerY

    先平移后缩放:

    (centerX + translateX) * scaleX = centerX

    (centerY + translateY) * scaleY = centerY

    再来理解下preScale(scaleX, scaleY, oriX, oriY),这个指定缩放原点的函数的意思。之前说图片的矩阵变化是对图片在屏幕上的每个坐标点进行变换,这里需要说明的是坐标是相对于坐标原点为(0, 0)而言的。而这里指定了坐标原点,那么所有的矩阵变换是相对于指定的这个坐标原点而言的。指定坐标原点对平移没有影响,但是对旋转和缩放是有影响的。比如RectF(0, 600, 0, 600),矩阵变换后:

    preScale(0.5, 0.5) ——> RectF(0, 300, 0, 300)

    preScale(0.5, 0.5, 900, 900) ——> RectF(450, 750, 450, 750):可以想象坐标原点移到了(900,900),原矩形上的所有点相对于这个原点的位置进行缩放得到新的矩形。

    查看连接矩阵变换的skia库源码发现对于矩阵变换顺序如下:

    pre(A)

    post(B)

    那么连接的矩阵变换为A*B

    pre(A)

    pre(B)

    那么连接的矩阵变换为B*A

    即pre的意思是改变矩阵变换的顺序

    注意pre和post是相对于在这个操作之前的操作而言的pre(A)之前没有任何操作,所以此时pre和post和set无差别

    另外:

    scale缩放取值为-1时可以得到镜像矩阵

        protected static Matrix getHorizontalMatrix(float width) {
            Matrix flipHorizontalMatrix = new Matrix();
            flipHorizontalMatrix.setScale(-1, 1);
            flipHorizontalMatrix.postTranslate(width, 0);
            return flipHorizontalMatrix;
        }
    

     上面的矩阵表示对原图进行水平镜像变换。

  • 相关阅读:
    Linux系统运维之MYSQL数据库集群部署(主从复制)
    Linux系统运维之负载均衡Tengine
    Linux系统运维之subversionEdge部署
    Linux系统运维之Hadoop、Hive、Flume数据处理
    CoIDE在STM32系列单片机中的使用实践
    软硬件调试九法:第三条规则 不要想而要看
    《产品经理》读书笔记
    <读书笔记> 代码整洁之道
    关于鼠标手的症状和恢复方法
    <读书笔记>软件调试之道 :从大局看调试-理想的调试环境
  • 原文地址:https://www.cnblogs.com/fordreamxin/p/4721497.html
Copyright © 2020-2023  润新知