Graphics2D conversion result does not match manual conversion

I am using Java Graphics2D to draw on a component using AffineTransform to control my drawing. Graphics2D offers a method conversion for this that accepts AffineTransform.

Sometimes I need to manipulate a point manually without using the inline transform. But when I try to transform a point using the same transformation that I gave Graphics2D.transform, sometimes the resulting point is not the same.

The following code reproduces the problem (Scala code, but I think you can submit 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 Behavior

The blue rectangle (calculated manually) crosses out the red (calculated by conversion).

Experienced behavior

The blue rectangle has an offset of 1

I admit that my transformMatrix is ​​not an integer, but that should not be a problem, right?

  affineTransform = 1.1, 0.0, 520.55 0.0, 1.1, 182.54999999999995 0.0, 0.0, 1.0 

Is this a mistake or am I lacking in-depth understanding?

Change You can reproduce the error if you set the transformMatrix parameter to

 transformationMatrix = new AffineTransform(1.1, 0.0, 0.0, 1.1, 521.55, 183.54999999999995) 

at the beginning of paintComponent. Note that g is of type Graphics2D.

+6
source share
4 answers

Well, you do two different things.

In (1), you undergo a form (and it does not matter that it is a Rectangle , not Rectangle2D.Double ), a transformation that gives fractional coordinates. It is only drawn as an alias because you did not specify specific rendering hints ( RenderingHints.KEY_ANTIALIASINGRenderingHints.VALUE_ANTIALIAS_ON and RenderingHints.KEY_STROKE_CONTROLRenderingHints.VALUE_STROKE_PURE ).

In (2), you transform the point and force it to smoothed coordinates ( Point instead of Point2D.Double ). Then build a rectangle from this point in sequence.

It is clear that a variety of things can happen under the hood, and I would not expect that turning into floating-point figures in a floating-point figure in a graphical context with an alias gives the same results.

(Without testing) I would suggest that a valid equivalent operator for (1) would be

 g.fill(transformationMatrix.createTransformedShape(new Rectangle(0, 0, 1, 1))) 
+4
source

Your conversion is just a translation into (520.55, 182.55) . And since it has fractional pixel values, it is really sensitive to the choice of rounding. If you have anti-aliasing, you will actually get a 4-pixel red blob covering pixels that overlap. Otherwise, the behavior (disagreement) that you see is reasonable, given the ambiguity between rounding to the integer and truncating to the integer.

+5
source

When you complete the first step, g.transform (transformMatrix), the graphics stack with existing transforms. In the second step, you rewind it with g.setTransform (the new AffineTransform), thereby losing the previous transformation, if any. You assume you are back to the beginning, but this may not be true.

Do getTransform () before step 1 and the other after step 2 to verify that they are the same.

+1
source

Whenever you work with floating point coordinates, you should use the “2D” version of the graphic objects if you want to get the correct results. I have not read it from the "book", so I can not quote, it's just an experience with it.

Here is my ugly Java code that gives the result you expect.

 AffineTransform transformationMatrix = AffineTransform.getTranslateInstance(520.55, 182.54999999999995); transformationMatrix.scale(1.1, 1.1); ((Graphics2D) previewGraphics).transform(transformationMatrix); previewGraphics.setColor(Color.RED); ((Graphics2D) previewGraphics).fill(new Rectangle(0,0,1,1)); ((Graphics2D) previewGraphics).setTransform(new AffineTransform()); Point2D p0 = new Point2D.Double(0, 0); Point2D pDest = new Point2D.Double(); transformationMatrix.transform(p0, pDest); previewGraphics.setColor(Color.BLUE); ((Graphics2D) previewGraphics).fill((Shape) new Rectangle2D.Double(pDest.getX(), pDest.getY(), 1, 1)); 
0
source

Source: https://habr.com/ru/post/920072/


All Articles