Graphics2D 的 transform 方法有什么作用?

What does Graphics2D's transform method do?

在 class 来自 Java 的 Graphics2D 中,它有一个变换方法。

public abstract void transform(AffineTransform Tx)

这是根据文档的描述:

Composes an AffineTransform object with the Transform in this Graphics2D according to the rule last-specified-first-applied. If the current Transform is Cx, the result of composition with Tx is a new Transform Cx'. Cx' becomes the current Transform for this Graphics2D. Transforming a point p by the updated Transform Cx' is equivalent to first transforming p by Tx and then transforming the result by the original Transform Cx. In other words, Cx'(p) = Cx(Tx(p)). A copy of the Tx is made, if necessary, so further modifications to Tx do not affect rendering.

我已经读了好几遍了,但还是不明白它的作用。我也试过寻找其他来源,但g2d的转换方法并不多。

所以我的问题是:转换到底做了什么?我们什么时候使用它?

如果有人能用简单的方式解释一下就太好了。

您可以使用此方法修改 Graphics2D 对象在呈现到设备 space(例如屏幕或一张纸)之前的显示方式。例如,您可以使用它来执行缩放、旋转和剪切图像等操作,以响应用户在您的应用程序中按下 Scale/Rotate/Shear 按钮。

Graphics2D 对象天生带有一个用于跟踪变换的字段,它表示对象将被渲染到的 'device space'(例如,1920 x 1080)的近似值。此转换在运行时计算并应用到对象,以便它在可用的屏幕空间(或打印机页面或投影仪显示器等)中正确呈现

通过应用 transform(AffineTransform Tx) 方法,您告诉系统您想要更改 Graphics2D 对象在最终呈现时的外观;例如,缩放或旋转对象。文档所说的是,您的新变换将应用于 Graphics2D 对象 ,然后 应用其原始变换以使其适合连接到 JVM 所在设备的显示器 运行;所有这些都发生在渲染之前。

可能的答案范围很广,从 "It does what the JavaDoc says" 到完全引用 the graphics programming bible 的几个章节。我会尝试给出一个介于两者之间的答案。它更接近第一个选项,但某些链接指向的信息可能比掌握基本概念所需的信息更详细。

Graphics2D is an AffineTransform that describes how objects should be transformed before they are drawn. Such an AffineTransform is a Matrix that can be imagined as something that modifies the positions of all points of the drawn objects. For example, it can be a rotation matrix 的变换,以便对象在绘制之前旋转。

一个例子:当你在某个JPanelpaintComponent方法中,并且得到了Graphics2D,那么你可以进行绘图操作:

protected void paintComponent(Graphics gr) {
    super.paintComponent(gr);
    Graphics2D g = (Graphics2D)gr;
    ...

    // Draw a rectangle
    g.drawRect(0,0,50,100);
}

在那里绘制的矩形将为 50 像素宽和 100 像素高。

现在,您可以在绘制之前对 Graphics2D 对象应用新的转换:

protected void paintComponent(Graphics gr) {
    super.paintComponent(gr);
    Graphics2D g = (Graphics2D)gr;
    ...

    // Obtain an AffineTransform that describes a scaling
    AffineTransform scalingTransform =      
        AffineTransform.getScaleInstance(2,2);

    // Apply the transform to the graphics
    g.transform(scalingTransform );

    // Draw a rectangle. 
    g.drawRect(0,0,50,100);
}

现在绘制的矩形将是 100 像素宽和 200 像素高,因为变换会将所有内容缩放 2 倍。您也可以使用执行旋转的变换,例如

g.transform(AffineTransform.getRotateInstance(Math.toDegrees(45)));

这样矩形就会旋转 45 度。

这基本上就是 transform 方法所做的,关于 API 及其效果。

旁注:Graphics2Drotate(...)translate(...)shear(...)scale(...) 方法确实只是方便的方法。而不是

g.transform(AffineTransform.getRotateInstance(Math.toDegrees(45)));

你可以简单地调用

g.rotate(Math.toDegrees(45));

达到同样的效果。 transform 方法非常通用,因为它可以接收可能在其他地方组成的任意变换矩阵 - 例如在其他一些方法中,Graphics2D 对象根本不可用。


如果您想知道该方法的作用内部:这实际上很简单。它只是执行 Matrix Multiplication。它将 Graphics2D 中包含的 AffineTransform 与传递给 transform 方法的 AffineTransform 相乘。

这种变换矩阵的乘法可以想象为以相反的顺序应用相应的变换:

AffineTransform scalingByFactor2 = 
    AffineTransform.getScaleInstance(2,2);

AffineTransform rotationBy45degrees = 
    AffineTransform.getRotateInstance(Math.toDegrees(45));

g.transform(rotationBy45degrees);
g.transform(scalingByFactor2);
g.drawRect(0,0,50,100);

结果会画出一个矩形,即

  1. 首先 在 x 和 y 方向缩放 2 倍
  2. 然后旋转45度

所以结果将是一个更大的旋转矩形。

如果您应用了这样的操作

g.transform(scalingByFactor2);
g.transform(rotationBy45degrees);

然后矩形看起来像一个大的菱形物体。

Edit in response to the comments:

对象看起来不同的原因是转换是独立应用的。也就是说,它们应用于可能已经被其他变换变换过的对象。我认为矩形(或正方形)的示例在这里可能相当直观:

这与通常的绘画或绘图程序基本相同,您对一个对象应用一种变换,然后再应用另一种,变换的顺序会影响结果。


关于transformsetTransform方法的区别,一直有一些猜测。正如 MadProgrammer 最后指出的那样:

transform 方法将图形对象的当前变换与新变换连接起来(即:它乘以矩阵)。您可以将其用于 "compose" 多种转换,如上面的旋转和缩放示例所示。

与此相反,Graphics2D#setTransform 方法有完全不同的用途,文档包含相应的警告:

WARNING: This method should never be used to apply a new coordinate transform on top of an existing transform

它应该只用于恢复图形的原始变换,在对 transform 调用的序列之后,如 JavaDoc 的示例所示。