In class Graphics2D from Java, it has a transform method.
public abstract void transform(AffineTransform Tx)
This is the description according to the documentation:
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.
I have read it several times but I still don't understand what it does. I have also tried looking for other sources, but there isn't much on g2d's transform method.
So my question is: What does transform exactly do? When do we use it?
It will be great if someone could explain it in a simplified manner.
There is a very broad range of possible answers, ranging from "It does what the JavaDoc says" to fully quoting several chapters of the graphics programming bible. I'll try to give an in-between answer. It's closer to the first option, yet some links lead to information that may be a bit more detailed than necessary to grasp the basic concepts.
The transform of a 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 so that the objects are rotated before they are drawn.
An example: When you are in the paintComponent
method of some JPanel
, and have obtained the Graphics2D
, then you may perform drawing operations:
protected void paintComponent(Graphics gr) {
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
...
// Draw a rectangle
g.drawRect(0,0,50,100);
}
The rectangle that is drawn there will be 50 pixels wide and 100 pixels high.
Now, you can apply a new transformation to the Graphics2D
object before drawing:
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);
}
The rectangle that is drawn there now will be 100 pixels wide and 200 pixels high, due to the transform that scales everything by a factor of 2. You could also have used a transform that performs a rotation, with something like
g.transform(AffineTransform.getRotateInstance(Math.toDegrees(45)));
so that the rectangle would be rotated by 45 degrees.
This is basically what the transform
method does, regarding the API and its effect.
A side note: It is true that the rotate(...)
, translate(...)
, shear(...)
and scale(...)
methods of the Graphics2D
are just convenience methods. Instead of
g.transform(AffineTransform.getRotateInstance(Math.toDegrees(45)));
you can simply call
g.rotate(Math.toDegrees(45));
to achieve the same effect. The transform
method is very generic, as it may receive arbitrary transformation matrices that may have been composed elsewhere - e.g. in some other method, where the Graphics2D
object is simply not available.
If you wonder what the method does internally: That's actually quite simple. It just performs a Matrix Multiplication. It multiplies the AffineTransform
that is contained in the Graphics2D
with the AffineTransform
that is passed to the transform
method.
The multiplication of such transformation matrices can be imagined as applying the respective transformations, in reverse order:
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);
As a result, a rectangle will be drawn, that is
So the result will be a larger, rotated rectangle.
If you had applied the operations like this
g.transform(scalingByFactor2);
g.transform(rotationBy45degrees);
then the rectangle would appear like a large, diamond-like-shaped object.
Edit in response to the comments:
The reason why the objects appear to be different is that the transformations are applied independently. That is, they are applied to the objects that may already have been transformed by other transforms. I think the example of a rectangle (or square) may be rather intuitive here:
This is basically the same as in usual painting or drawing programs, where you apply one transformation to an object, and then another one, and the order of the transformations affects the result.
There have been some guesses about the difference between the transform
and the setTransform
method. And as MadProgrammer finally pointed out:
The transform
method concatenates the current transform of the graphics object with the new one (that is: It multiplies the matrices). You can use it to "compose" several transformations, as shown with the rotation and scaling examples above.
In contrast to that, the Graphics2D#setTransform
method serves an entirely different purpose, and the documentation contains the corresponding warning:
WARNING: This method should never be used to apply a new coordinate transform on top of an existing transform
It should only be used to restore the original transform of the graphics, after a sequence to transform
calls, as shown in the example of the JavaDoc.