问题
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