对于Matrix中PreXXX和PostXXX的使用时机的理解

在自定义View的时候我们在绘制bitmap时候
canvas.drawBitmap(bitmap,Matrix,Paint)
用到了Matrix,也就是矩阵,网上有图,说明了这个矩阵影响的方式

1.Matrix矩阵各数值影响

矩阵行列式
第一行影响X轴,分别是scale缩放,skew错切,trans平移
第二行影响Y轴,与第一行相同,作用于图形就是矩阵相乘
具体的矩阵行为控制,图片来自网上
这是影响的旋转和位移,还有一张是说明错切和缩放
缩放和错切
举一个scale,缩放的例子
缩放计算方式
等号右边就是matrix和原数据的某个点坐标,矩阵相乘,行乘以列
x = K1 * X0 + 0 * Y0 + 0 * 1 = K1 * X0
可以看出X坐标被缩放了k1倍
y = 0 * X0 + k2 * Y0 + 0 * 1 = K2 * Y0
可以看出Y坐标倍缩放了K2倍

也就是上图显示的两个位置对于缩放的影响

2.说一下Pre和Post的基本区别

基础说明到这里结束,后面说明我遇到的preXXX方法和postXXX方法的困惑
比如
平移
1.preTranslate(),源码说明

   /**
     * Preconcats the matrix with the specified translation. M' = M * T(dx, dy)
     */
    public boolean preTranslate(float dx, float dy) {
        nPreTranslate(native_instance, dx, dy);
        return true;
    }
    
M' = M * T(dx, dy)
也就是原矩阵在左边,要实现平移转换的的操作在右边,也可以说是原矩阵的左乘

T(dx, dy)就是一个函数表示,表示构造一个平移矩阵
M为原矩阵,当新建一个Matrix,就是一个单位矩阵,下图就是个单位矩阵,
斜边为1,其余为0

单位矩阵

2.如果是postTranslate,源码说明

 /**
     * Postconcats the matrix with the specified translation. M' = T(dx, dy) * M
     */
    public boolean postTranslate(float dx, float dy) {
        nPostTranslate(native_instance, dx, dy);
        return true;

M' = T(dx, dy) * M
也就是原矩阵的右乘

矩阵乘法的特性是:
矩阵乘法不满足交换律,即 AB ≠ BA
矩阵乘法满足结合律,即 (AB)C = A(BC)
矩阵与单位矩阵相乘结果不变,即 A * I = A

对于左乘和右乘有什么影响,特别是多个操作结合,比如preTranslate,preScale
等等。计算起来很复杂

网上有一种说法,说的是

preXXX就是先执行,postXXX就是后执行,但是其实我们知道,代码的执行顺序
是根据你写的代码先后,preXXX和postXXX在我们调用的时候就已经执行,得到
了新的Matrix,比如执行preTranslate,preScale
M’ = M * T * S T表示preTranslate,S表示preScale
调用了M*T就已经产生新的Matrix内部结构,然后再和S操作

所以我们在用的时候我想可以这么理解

用法结论

就是在执行逻辑上,比如,我们想要先位移,再缩放,再旋转,我们想要的是执行这个结果
就可以preRotate,preScale,preTranslate,按这个顺序调用,内部公式就是
M’ = M * R * S * T
我们可以逻辑上认为越靠右边的行为越先执行,但是代码执行顺序不是如此,只是我们为了
达到目标效果,逻辑上这么想,内部经历了复杂计算对应了我们的逻辑执行顺序,
得到我们想要的Matrix,这个Matrix实现了这个逻辑顺序下的计算,但是具体内部是怎么计算
出来的,我也无法得知,也期待有喜欢研究的道友讲解

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)

		//保存画布状态
        canvas!!.save()
        //重置Matrix,避免后续影响
        mMatrix.reset()
        //画布平移,將画布中心移动到屏幕中心
        canvas.translate(width/2f,height/2f)
        //先画出原图做对比
        canvas.drawBitmap(bitmap,0f,0f,null)

		//先画一个只旋转和平移的图,为了和下面的操作对比,看scale和translate谁先执行
		(逻辑上)
        mMatrix.preRotate(90f)
        mMatrix.preTranslate(100f,100f)
        canvas.drawBitmap(bitmap,mMatrix,null)
        mMatrix.reset()
        //实现我们之前说的平移,缩放,旋转的操作,按这个顺序
        mMatrix.preRotate(progress)
        mMatrix.preScale(0.5f,0.5f)
        mMatrix.preTranslate(100f,100f)

        canvas.drawBitmap(bitmap,mMatrix,null)
        canvas.restore()
    }

效果图如下
效果图

最右边的是原图,可以看到在屏幕中心点开始绘制
最左边的是没有缩放的
最小的是3个操作都有的

我平移了(100,100),也就是向右100,向下100(屏幕坐标系是向下为Y轴正向)

假设是先做的缩放再做的平移,那么缩放时候是在原点缩放,也就是屏幕中心,那么他的左上角还应该是(0,0),然后平移了(100,100),左上角旋转后现在最终视觉上成了右上角,
那么右上角坐标现在应该是(-100,100)

但是我们看到只做了旋转和平移的那张图,也就是最左边的,他明显更加远离原点,说明他的位置才是(-100,100)
所以就说明最小的那张图先做了平移操作,坐标变成(100,100),旋转后变成(-100,100),最后坐了缩放,缩放是根据原点缩放,所以那个点缩放了一般变成了(-50,50),所以就是我们说的顺序

再做个实验,如果我们最后一步是postTranslate,能不能达到我们逻辑上的最后做平移呢

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
//        canvas!!.drawBitmap(bitmap,Rect(0,0,(bitmap.width*progress).toInt() ,bitmap.height),
//                RectF(0f,0f,bitmap.width*progress,bitmap.height.toFloat()), Paint())

//        canvas!!.drawBitmap(bitmap,0f,0f,null)
//        canvas.translate(0f,bitmap.height.toFloat()+200f)
        canvas!!.save()
        mMatrix.reset()
        canvas.translate(width/2f,height/2f)
        canvas.drawBitmap(bitmap,0f,0f,null)

        mMatrix.preRotate(progress)
        mMatrix.preTranslate(100f,100f)
        canvas.drawBitmap(bitmap,mMatrix,null)
        mMatrix.reset()
        mMatrix.preRotate(progress)
        mMatrix.preScale(0.5f,0.5f)
        //改成postTranslate,之前是preTranslate
        mMatrix.postTranslate(100f,100f)

        canvas.drawBitmap(bitmap,mMatrix,null)
        canvas.restore()
    }
    这样公式就变成
    M' = T * M * R * S

看下效果图
效果图
可以看到移动后的左上角和原点距离
然后看左边图的右上角到原点距离,两者距离一样,说明图片先经历
了缩放,原点坐标不变,然后再向右下移动了100,100,也证明了上面
的做法是可行的。

重申一遍

代码的执行顺序上并不是按照pre和post而先后执行,只是我们如果想做到
我们逻辑上让他实现一种效果,可以按照上面说的方式去思考,来合理使用
pre和post,每次可以将公式写出来,然后从右向左看公式,看下最后的效果
是不是和猜想的一致。

如果有反例,欢迎大家给我留言,让我有机会改正,谢谢

参考文章:
自定义View进阶-Matrix原理

这个参考文章也是一个系列博客,能学到不少自定义View的东西,大家可以看看

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
得利捷Matrix220是一款高性能的文文字识别设备。使用说明书可以帮助用户快速了解设备的功能和操作步骤。 首先,得利捷Matrix220是一个基于OCR技术的文文字识别设备。它能够通过摄像头捕捉图像,并将其文文字转换为可编辑的文本。 使用该设备时,请先确保设备已接通电源,并正确连接至电脑或其他外部设备。打开软件后,用户需要选择相应的识别模式,例如识别图片模式或实时摄像模式。 对于识别图片模式,用户可以直接从电脑加载图片,然后点击识别按钮进行文字识别。在实时摄像模式,用户可以将设备对准需要识别的文字,然后点击捕捉按钮进行拍摄并开始文字识别。 识别完成后,设备将自动将识别的文字显示在文本编辑区域。用户可以对文本进行编辑、复制、保存等操作。此外,设备还支持对识别的文字进行翻译、搜索等功能。 使用得利捷Matrix220时,需要注意以下几点。首先,确保设备所处环境的照明充足,以便提高文字的清晰度和准确度。其次,避免出现模糊或扭曲的图片,以免影响识别效果。最后,用户需要注意设备的维护和保养,及时清洁镜头和其他部件,以保证设备的正常使用。 总之,得利捷Matrix220是一款功能强大的文文字识别设备,通过使用说明书,用户可以轻松掌握设备的操作。无论是在学习、工作还是生活,该设备都能为用户提供便利和效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值