Search code examples
javarotationbufferedimage

Java: Rotation of a buffered image around its centre is skewed


I am trying to rotate a buffered image in Java (a plane icon on the map) around its centre using help from here: Rotating BufferedImage instances

When I use this code:

AffineTransform at = new AffineTransform();

at.rotate(Math.toRadians(planeHeading),origImage.getWidth() / 2, origImage.getHeight() / 2);

AffineTransformOp op = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);

origImage = op.filter(origImage, null);

g.drawImage(origImage, x-origImage.getWidth() / 2, y-origImage.getHeight() / 2, null);

on rotation of 180-270 degree, the image is placed higher and a bit to the left of its centre:

enter image description here

If I use this code:

AffineTransform at = new AffineTransform();

at.translate(x, y);

at.rotate(Math.toRadians(planeHeading));

at.translate(-origImage.getWidth()/2, -origImage.getHeight()/2);

g.drawImage(origImage, at, null);

the image is rotated correctly, however the image itself gets very pixelated on its edges.

enter image description here

Can someone please help find the culprit?

This is the whole method:

@Override
public void paintWaypoint(Graphics2D g, JXMapViewer viewer, MapPlane w)
{
    g = (Graphics2D)g.create();

    try
    {
        origImage = ImageIO.read(getClass().getResource("/images/map/mapPLANE.png"));

        Point2D point = viewer.getTileFactory().geoToPixel(w.getPosition(), viewer.getZoom());

        // Center coordinates
        int x = (int)point.getX();
        int y = (int)point.getY();

        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        // Get heading of the plane and rotate the image
        String planeHeadingStr = w.getHeading();

        try
        {
            double planeHeading = Double.parseDouble(planeHeadingStr);

            AffineTransform at = new AffineTransform();

            //Do the actual rotation
            at.rotate(Math.toRadians(planeHeading),origImage.getWidth() / 2, origImage.getHeight() / 2);
            AffineTransformOp op = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
            origImage = op.filter(origImage, null);

            // Draw the image
            g.drawImage(origImage, x-origImage.getWidth() / 2, y-origImage.getHeight() / 2, null);

        }
        catch(NumberFormatException e)
        {

        }


        g.dispose();

    }
    catch (Exception ex)
    {
        log.warn("couldn't read mapPLANE.png", ex);
    }

}

Thanks a lot!


Solution

  • To achieve the same bilinear interpolation that you got for your AffineTransformOp in the second case where you draw directly using an AffineTransform, you should set another RenderingHint:

    g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 
                       RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    

    Otherwise, in your case, it defaulted to NEAREST_NEIGHBOUR interpolation.