I am using Java's Graphics2D to draw on a component using AffineTransform's to manipulate my drawing. Graphics2D offers an method transform for this, which takes an AffineTransform.
Sometimes I need to manipulate a point manually without using the builtin-transformation. But when I try to transform a point using the same transformation I gave to Graphics2D.transform sometimes the resulting point is not the same.
The following code reproduces the problem (It's Scala code, but I think you can imagine the Java code.):
var transformationMatrix = new AffineTransform()
/*
* transformationMatrix is modified throughout the program
* ...
*/
override def paintComponent(g: Graphics2D) = {
super.paintComponent(g)
/* 1. transform using graphics transform */
g.transform(transformationMatrix)
g.setColor(Color.RED)
g.fill(new Rectangle(0, 0, 1, 1))
/* 2. transform point manually */
g.setTransform(new AffineTransform) // reset transformation to standard
val p0 = new Point(0, 0)
val pDest = new Point()
transformationMatrix.transform(p0, pDest)
g.setColor(Color.BLUE)
g.fill(new Rectangle(pDest.x, pDest.y, 1, 1)
}
Expected behaviour
The blue rectangle (manually calculated) overdraws the red one (calculated by transform).
Experienced behaviour
I admit that my transformationMatrix is not really integer, but that should'nt be the problem, should it?
affineTransform = 1.1, 0.0, 520.55
0.0, 1.1, 182.54999999999995
0.0, 0.0, 1.0
Is this a bug or am I missing some deep insight?
Edit: You can reproduce the bug, if you set transformationMatrix to
transformationMatrix = new AffineTransform(1.1, 0.0, 0.0, 1.1, 521.55, 183.54999999999995)
at the beginning of paintComponent. Please note, that g is of type Graphics2D.
Well, you are doing two different things.
In (1) you are subjecting a shape (and it is irrelevant that it is Rectangle
and not Rectangle2D.Double
) to a transform that yields fractional coordinates. It only is painted aliased, because you haven't set specific rendering hints (RenderingHints.KEY_ANTIALIASING
-> RenderingHints.VALUE_ANTIALIAS_ON
, and RenderingHints.KEY_STROKE_CONTROL
-> RenderingHints.VALUE_STROKE_PURE
).
In (2) you are subjecting a point to the transform, and coerce it into aliased coordinates (Point
instead of Point2D.Double
). Then successively construct a rectangle from that point.
Clearly there may be very different things happening under the hood, and I wouldn't expect at all that transforming into an integer point versus painting floating point shapes in an aliasing graphics context yield the same results.
(Without testing) I would guess that a valid equivalent statement for (1) would be
g.fill(transformationMatrix.createTransformedShape(new Rectangle(0, 0, 1, 1)))