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

How does Matrix.postScale( sx, sy, px, py) work?

第一次阅读Taig's question

泰格说:

When calling Matrix.postScale( sx, sy, px, py ); the matrix gets scaled and also translated (depending on the given point x, y). That predestines this method to be used for zooming into images because I can easily focus one specific point. The android doc describes the method like this:

Postconcats the matrix with the specified scale. M' = S(sx, sy, px, py) * M

At a first glance this seems ridiculous because M is supposed to be a 3x3-Matrix. Digging around I've found out that android uses a 4x4-Matrix for its computations (while only providing 3x3 on its API). Since this code is written in C I'm having a hard time trying to understand what is actually happening.

我在 Wolfram

看到了视觉转换

我的问题和泰格一样

What I actually want to know: How can I apply this kind of scaling (with a focused point) to the 3x3 Matrix that I can access within my Java-code?

谁能给我一个 10 岁小孩都能理解的示例和具有 4 个参数(sx、sy、px、py)的二维缩放公式?

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

由于我们只在二维中工作,矩阵实际上是一个 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,就好像您正在平移,缩放,然后平移回来。这使得缩放看起来好像 expansion/shrinking 以点 px, py.

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

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

SkewShear 类似于缩放,但轴相反:

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

由于您在缩放和平移时没有扭曲,因此 skewX 和 skewY 设置为零。所以它们仍然用于矩阵乘法,只是不影响最终结果。

旋转 是通过添加一个小触发器完成的:

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

然后是 android.graphics.Camera(与 android.hardware.Camera 相对)可以拍摄 2D 平面和 rotate/translate 3D space。这就是 MPERSP_0MPERSP_1MPERSP_2 发挥作用的地方。我不是在做那些方程式;我是程序员,不是数学家。

但我不需要成为数学家。我什至不需要知道 Matrix 是如何计算的。我一直在研究支持 pinch/zoom 的 ImageView 子类。所以我使用 ScaleGestureDetector 来告诉我用户何时缩放。它有方法 getScaleFactor()getFocusX()getFocusY()。我将这些值插入 matrix.postScale(),并且我的 ImageView 的比例类型设置为 MATRIX,我用我的比例矩阵调用 ImageView.setImageMatrix()。瞧,图像会根据用户的手势完全按照用户期望的方式缩放。

所以我不明白所有关于摸索 Matrix 如何在引擎盖下工作的焦虑。不过,我希望我在这里写的东西能给你正在寻找的答案。