Rotating Image with AffineTransform

只愿长相守 提交于 2019-12-17 02:32:00

问题


I have got class called Airplane. Inside this class i have got variable img which is a BufferedImage type. What is more i have got class WorldMap which overrides function paintComponent(Graphics g):

@Override
public void paintComponent(Graphics g) {
    Graphics2D g2d = (Graphics2D) g;
    g2d.drawImage(mapa, 0, 0, getWidth(), getHeight(), null); 
    drawAirplanes(g2d);
}

Function drawAirplanes() look like this:

private void drawAirplane(Graphics2D g){
    for(Samolot i: s){
        i.rotateAirplane();
        g.drawImage(i.getImg(),i.getX(),i.getY(),(int)i.getDim().getWidth(),(int)i.getDim().getHeight(),  null);
    }
}

It simply need to 1) rotate airplane (BufferedImage inside Airplane object) 2) draw him.

My Airplane.rotateAirplane() function looks like this:

 public void rotateSamolot() {
   AffineTransform tx = new AffineTransform();

   tx.translate(10,10); //10, 10 is height and width of img divide by 2
   tx.rotate(Math.PI / 2);
   tx.translate(-10,-10); 

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

   BufferedImage newImage =new BufferedImage(20, 20, img.getType()); //20, 20 is a height and width of img ofc
   op.filter(img, newImage);

       this.img = newImage;
 }

ofc when im running my program only mapa object is drawn. when im deleting this lane

this.img = newImage;

i have got ofc my airplane too but not rotated.


回答1:


The major problem (that I can see) is the translation of the Graphics context which is offset the position that the rotation will take place.

I "think" rotation by default occurs at the top/left corner of the Graphics context (where it's 0x0 position is, which you've translated to something else), this could be causing the image to be rotated out of frame (or viewable area)

You should provide a "anchor" point where the rotation takes place, typically, the centre is my personal preference.

The following example simply has a master image (due to size constraints I had to scale it, but you may not need this). I then use this to generate a "rotated" instance which is sized to allow the image to fit within in. This is a lot of fun with trig - I stole the code from somewhere, so credit to that developer.

The example allows you to click any where and it will change the rotation pivot, so you can see what's going on. The default position is the centre of the pane...

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class SampleRotation {

    public static void main(String[] args) {
        new SampleRotation();
    }

    public SampleRotation() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                final RotationPane rotationPane = new RotationPane();
                final JSlider slider = new JSlider(0, 100);
                slider.addChangeListener(new ChangeListener() {
                    @Override
                    public void stateChanged(ChangeEvent e) {
                        double angle = 720d * (slider.getValue() / 100d);
                        rotationPane.setAngle(angle);
                    }
                });
                slider.setValue(0);

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(rotationPane);
                frame.add(slider, BorderLayout.SOUTH);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class RotationPane extends JPanel {

        private BufferedImage img;
        private BufferedImage rotated;
        private double angle;
        private Point clickPoint;

        public RotationPane() {
            try {
                img = ImageIO.read(new File("/Users/swhitehead/Dropbox/MegaTokyo/issue459.jpg"));
                BufferedImage scaled = new BufferedImage(img.getWidth() / 2, img.getHeight() / 2, BufferedImage.TYPE_INT_ARGB);
                Graphics2D g2d = scaled.createGraphics();
                g2d.setTransform(AffineTransform.getScaleInstance(0.5d, 0.5d));
                g2d.drawImage(img, 0, 0, this);
                g2d.dispose();
                img = scaled;
                setAngle(0d);
            } catch (IOException ex) {
                ex.printStackTrace();
            }

            addMouseListener(new MouseAdapter() {

                @Override
                public void mouseClicked(MouseEvent e) {
                    clickPoint = e.getPoint();
                    repaint();
                }

            });

        }

        public void setAngle(double angle) {
            this.angle = angle;

            double rads = Math.toRadians(getAngle());
            double sin = Math.abs(Math.sin(rads)), cos = Math.abs(Math.cos(rads));
            int w = img.getWidth();
            int h = img.getHeight();
            int newWidth = (int) Math.floor(w * cos + h * sin);
            int newHeight = (int) Math.floor(h * cos + w * sin);

            rotated = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = rotated.createGraphics();
            AffineTransform at = new AffineTransform();
            at.translate((newWidth - w) / 2, (newHeight - h) / 2);

            int x = clickPoint == null ? w / 2 : clickPoint.x;
            int y = clickPoint == null ? h / 2 : clickPoint.y;

            at.rotate(rads, x, y);
            g2d.setTransform(at);
            g2d.drawImage(img, 0, 0, this);
            g2d.setColor(Color.RED);
            g2d.drawRect(0, 0, newWidth - 1, newHeight - 1);
            g2d.dispose();

            repaint();
        }

        public double getAngle() {
            return angle;
        }

        @Override
        public Dimension getPreferredSize() {
            return img == null ? new Dimension(200, 200) : new Dimension(img.getWidth(this), img.getHeight(this));
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (rotated != null) {
                Graphics2D g2d = (Graphics2D) g.create();

                int x = (getWidth() - rotated.getWidth()) / 2;
                int y = (getHeight() - rotated.getHeight()) / 2;
                g2d.drawImage(rotated, x, y, this);

                g2d.setColor(Color.RED);

                x = clickPoint == null ? getWidth() / 2 : clickPoint.x;
                y = clickPoint == null ? getHeight()/ 2 : clickPoint.y;

                x -= 5;
                y -= 5;

                g2d.drawOval(x, y, 10, 10);
                g2d.dispose();
            }
        }        
    }    
}



回答2:


This is what worked for me (kinda copying from here and there):

public BufferedImage rotateImag (BufferedImage imag, int n) { //n rotation in gradians

        double rotationRequired = Math.toRadians (n);
        double locationX = imag.getWidth() / 2;
        double locationY = imag.getHeight() / 2;
        AffineTransform tx = AffineTransform.getRotateInstance(rotationRequired, locationX, locationY);
        AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);         
       BufferedImage newImage =new BufferedImage(imag.getWidth(), imag.getHeight(), imag.getType()); //20, 20 is a height and width of imag ofc
       op.filter(imag, newImage);

           //this.img = newImage;
       return(newImage);
     }


来源:https://stackoverflow.com/questions/20275424/rotating-image-with-affinetransform

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!