Canvas 如何确定它的剪辑范围?

How does Canvas determine its clip bounds?

我一直在做一些关于 Android 的 Canvas 的工作,特别是试图确定它的 getClipBounds 结果是如何确定的。我知道 Canvas 内部保留了一个转换 Matrix,它在我调用 translatescale 等时更新,但试图复制 Matrix 的结果有把我弄糊涂了。

@Override
public void onDraw(Canvas canvas) {
    Rect clipBounds;
    RectF viewport;

    canvas.save();
    canvas.concat(translationMatrix);
    //viewportMatrix.preConcat(canvas.getMatrix());
    viewportMatrix.set(canvas.getMatrix());

    clipBounds = canvas.getClipBounds();
    viewport = GetViewport();

    Log.d("clipBounds", clipBounds.toString() + " (" + clipBounds.width() + ", " + clipBounds.height() + ")");
    Log.d("viewport", viewport.toString() + " (" + viewport.width() + ", " + viewport.height() + ")");

    //drawing is done here

    canvas.restore();
}

//viewport code modeled after 
private RectF GetViewport() {
    RectF viewport = new RectF();

    viewportMatrix.mapRect(viewport, originalViewport);

    return viewport;
}

private void Translate(float x, float y) {
    translationMatrix.postTranslate(x, y);

    invalidate();
}

private void Scale(float scaleFactor, PointF focusPoint) {
    if (focusPoint == null) {
        translationMatrix.postScale(scaleFactor, scaleFactor);
    }
    //keep the focus point in focus if possible
    else {
        translationMatrix.postScale(scaleFactor, scaleFactor, focusPoint.x, focusPoint.y);
    }

    invalidate();
}

private final Matrix translationMatrix = new Matrix();
private final RectF originalViewport = new RectF();
private final Matrix viewportMatrix = new Matrix();

originalViewport 设置为 0, 0, canvas.getWidth(), canvas.getHeight()TranslateScale 是从正确工作的手势事件处理程序调用的。

让我困惑的部分是viewportMatrix。做不做好像都无所谓

viewportMatrix.set(canvas.getMatrix());

viewportMatrix.preConcat(canvas.getMatrix());

甚至一次性调用

viewportMatrix.set(canvas.getMatrix());

开始,然后并排 Translate/Scale 调用两个矩阵。我什至尝试完全忽略 Canvas 的内置 Matrix 并将 GetViewport 重写为

//viewport code modeled after 
private RectF GetViewport() {
    RectF viewport = new RectF();

    translationMatrix.mapRect(viewport, originalViewport);

    return viewport;
}

我似乎永远无法匹配getClipBounds(),而且差异相当严重:

viewportMatrix.set(canvas.getMatrix):

clipBounds: Rect(-97, -97 - 602, 452) (699, 549)
viewport: RectF(97.04178, 97.06036, 797.04175, 647.06036) (700.0, 550.0)

viewportMatrix.preConcat(canvas.getMatrix):

clipBounds: Rect(-97, -96 - 602, 453) (699, 549)
viewport: RectF(2708.9663, 2722.2754, 3408.9663, 3272.2754) (700.0, 550.0)

translationMatrix.mapRect:

clipBounds: Rect(-96, -96 - 603, 453) (699, 549)
viewport: RectF(96.73213, 96.85794, 796.7321, 646.8579) (700.0, 550.0)

一次性调用 viewportMatrix.preConcat(canvas.getMatrix()),然后并排 Translate/Scale 调用:

clipBounds: Rect(-96, -97 - 603, 452) (699, 549)
viewport: RectF(96.57738, 97.78168, 796.5774, 647.7817) (700.0, 550.0)

一次性调用 viewportMatrix.set(canvas.getMatrix()),然后并排 Translate/Scale 调用:

clipBounds: Rect(-96, -96 - 603, 453) (699, 549)
viewport: RectF(96.40051, 96.88153, 796.4005, 646.88153) (700.0, 550.0)

我什至无法检查 the Canvas source,因为所有 Matrix 代码都消失在未显示代码的私有本机调用中。

为什么我的 GetViewport 通话如此严重,getClipBounds 的幕后情况如何?

我通读了 this answer on GameDev SE,它使用矩阵求逆在屏幕和世界坐标系之间交换:

To go from screen to world space simply use Vector2.Transform. This is commonly used to get the location of the mouse in the world for object picking.

Vector2.Transform(mouseLocation, Matrix.Invert(Camera.TransformMatrix));

To go from world to screen space simply do the opposite.

Vector2.Transform(mouseLocation, Camera.TransformMatrix);

受这种方法的启发,我尝试像这样反转变换矩阵:

private RectF GetViewport() {
    RectF viewport = new RectF();
    Matrix viewportMatrix = new Matrix();

    translationMatrix.invert(viewportMatrix);

    viewportMatrix.mapRect(viewport, originalViewport);

    return viewport;
}

此视口与 getClipBounds 返回的结果正确匹配。

This matrix tutorial 解释了我从 transform 调用的结果中注意到的,但没有应用于我的矩阵的内容:

In a computer program the camera doesn’t move at all and in actuality, the world is just moving in the opposite direction and orientation of how you would want the camera to move in reality.

In order to understand this correctly, we must think in terms of two different things:

  1. The Camera Transformation Matrix: The transformation that places the camera in the correct position and orientation in world space (this is the transformation that you would apply to a 3D model of the camera if you wanted to represent it in the scene).
  2. The View Matrix: This matrix will transform vertices from world-space to view-space. This matrix is the inverse of the camera’s transformation matrix described above.