Matrix.postScale(sx,sy,px,py)如何工作?

时间:2015-05-23 16:15:31

标签: android matrix 2d

首先阅读Taig's question

泰格说:

  

调用Matrix.postScale时(sx,sy,px,py);矩阵得到   缩放并翻译(取决于给定的点x,y)。那   预定这个方法用于放大图像,因为我   可以轻松集中一个特定点。   android doc描述了这样的方法:

Postconcats the matrix with the specified scale. M' = S(sx, sy, px, py) * M
     乍一看这看起来很荒谬,因为M应该是一个   3×3矩阵。挖掘我发现android使用了一个   用于计算的4x4-Matrix(虽然其API只提供3x3)。   由于此代码是用C语言编写的,因此我很难尝试   了解实际发生的事情。

我在Wolfram

看到了视觉转换

我的问题与Taig相同

  

我真正想知道的是:如何应用这种缩放   (有一个重点)到3x3矩阵,我可以访问我的   Java的代码?

谁可以给我一个示例和2d缩放公式,其中包含一个10岁小孩会理解的4个参数(sx,sy,px,py)?

1 个答案:

答案 0 :(得分:17)

仔细观察Matrix方法。您会看到getValue()setValue()。文档说它们使用带有9个值的float数组。还有一堆常量:MSCALE_XMSCALE_YMTRANS_XMTRANS_Y等等。这些常量是float[9]数组的索引。

由于我们只处理2维,因此矩阵实际上是 2x2 矩阵。但由于此矩阵支持仿射变换,因此矩阵扩展为3x3矩阵。 3x3 = 9,对应float[9]数组。那就是你的3x3矩阵。

Matrix的实际内容是用C ++编写的,可以通过JNI访问,因为这些操作必须快速,快速,快速。它们甚至使用特殊的非标准浮点数格式(“16.16”),该格式针对计算速度进行了优化。

我不知道您在哪里获得有关4x4阵列的信息。这是C ++ JNI的代码片段:

SkScalar         fMat[9];
mutable uint32_t fTypeMask;

void setScaleTranslate(SkScalar sx, SkScalar sy, SkScalar tx, SkScalar ty) {
    fMat[kMScaleX] = sx;
    fMat[kMSkewX]  = 0;
    fMat[kMTransX] = tx;

    fMat[kMSkewY]  = 0;
    fMat[kMScaleY] = sy;
    fMat[kMTransY] = ty;

    fMat[kMPersp0] = 0;
    fMat[kMPersp1] = 0;
    fMat[kMPersp2] = 1;

    unsigned mask = 0;
    if (sx != 1 || sy != 1) {
        mask |= kScale_Mask;
    }
    if (tx || ty) {
        mask |= kTranslate_Mask;
    }
    this->setTypeMask(mask | kRectStaysRect_Mask);
}

它是仿射变换的3x3矩阵。

当您致电matrix.postScale()时,您正在修改scaleX,scaleY,transX和transY。 (pre..()post...()方法会保留矩阵中的任何变换。)Matrix应用新变换,如下所示:

X' = X * scaleX + transX
Y' = Y * scaleY + transY

这是整个矩阵乘法的简化版本。如果我有一个带点(2,2)的数字并且我将其缩放2倍,那么新点将是(4,4)。要沿X轴或Y轴移动,我只需添加一个常量。

因为Matrix.postScale()实际上是一个焦点,所以该方法在内部调整transX& transY,就好像你正在翻译,缩放,然后翻译出来一样。这使得缩放看起来好像扩展/收缩以点px为中心,py。

transX = (1 - scaleX) * px
transY = (1 - scaleY) * py

因此,对于焦点,我通过将px和py直接添加到原始x,y值来将图形移动到(px,py)。然后我做缩放。但是为了撤消翻译,我必须考虑到我的原始焦点现在已经缩放,因此我不得不减去scalex * px和scaleY * py,而不是减去px和py。

歪斜剪切就像缩放但是有相反的轴:

X' = Y * skewX
Y' = X * skewY

由于您在没有变形的情况下进行缩放和平移,因此skewX和skewY设置为零。因此它们仍然用于矩阵乘法,它们只是不影响最终结果。

旋转通过添加一点trig来完成:

theta = angle of rotation
scaleX = cos(theta)
skewX = -sin(theta)
skewY = sin(theta)
scaleY = cos(theta)

然后有android.graphics.Camera(而不是android.hardware.Camera)可以拍摄2D平面并在3D空间中旋转/平移它。这是MPERSP_0MPERSP_1和& MPERSP_2开始发挥作用。我不是在做那些方程式;我是程序员,不是数学家。

但我不需要成为一名数学家。我甚至不需要知道Matrix如何进行计算。我一直在研究支持捏合/缩放的ImageView子类。所以我使用ScaleGestureDetector告诉我用户何时缩放。它有方法getScaleFactor()getFocusX()getFocusY()。我将这些值插入matrix.postScale(),并将我的ImageView缩放类型设置为MATRIX,然后使用我的缩放矩阵调用ImageView.setImageMatrix()。 Voilà,图像完全按照用户期望的方式缩放,基于他们的手势。

所以我不明白所有关于如何Matrix在幕后工作的焦虑。不过,我希望我在这里写的东西能为你提供所需的答案。