【问题标题】:How does Matrix.postScale( sx, sy, px, py) work?Matrix.postScale(sx, sy, px, py) 是如何工作的?
【发布时间】:2015-08-05 13:12:37
【问题描述】:

初读Taig's question

泰格说:

当调用 Matrix.postScale(sx, sy, px, py);矩阵得到 缩放和平移(取决于给定的点 x,y)。那 预定此方法用于放大图像,因为我 可以轻松聚焦某一特定点。 android 文档描述了这样的方法:

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

乍一看这似乎很荒谬,因为 M 应该是 3x3-矩阵。挖掘我发现android使用了一个 4x4-Matrix 用于其计算(同时仅在其 API 上提供 3x3)。 由于这段代码是用 C 编写的,我很难尝试 了解实际发生的情况。

我在Wolfram看到了视觉转换

我的问题和泰格一样

我真正想知道的:如何应用这种缩放 (带有焦点)到我可以在我的内部访问的 3x3 矩阵 Java 代码?

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

【问题讨论】:

  • 我在eoeandroid.com/thread-304325-1-1.html找到了解释,但我不懂中文
  • 不明白你的问题。查找“仿射变换”,看看它是否为您提供了您正在寻找的公式。
  • M' = S(sx, sy, px, py) * M M 是一个 3x3 矩阵。我不会计算

标签: android matrix 2d


【解决方案1】:

更仔细地查看 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 的代码 sn-p:

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。

SkewShear 类似于缩放但具有相反的轴:

X' =​​ Y * skewX Y' = X * 偏斜Y

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

旋转是通过添加一个小三角来完成的:

theta = 旋转角度 scaleX = cos(θ) skewX = -sin(θ) skewY = sin(theta) scaleY = cos(θ)

然后是android.graphics.Camera(相对于android.hardware.Camera)可以采用2D 平面并在3D 空间中旋转/平移它。这就是MPERSP_0MPERSP_1MPERSP_2 发挥作用的地方。我不是在做那些方程式;我是程序员,不是数学家。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-18
    • 2018-01-17
    • 1970-01-01
    相关资源
    最近更新 更多