Java: Rotating Images
This is how you can do it. This code assumes the existance of a buffered image called 'image' (like your comment says)
// The required drawing location
int drawLocationX = 300;
int drawLocationY = 300;
// Rotation information
double rotationRequired = Math.toRadians (45);
double locationX = image.getWidth() / 2;
double locationY = image.getHeight() / 2;
AffineTransform tx = AffineTransform.getRotateInstance(rotationRequired, locationX, locationY);
AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
// Drawing the rotated image at the required drawing locations
g2d.drawImage(op.filter(image, null), drawLocationX, drawLocationY, null);
How to rotate Graphics in Java
If you are using plain Graphics
, cast to Graphics2D
first:
Graphics2D g2d = (Graphics2D)g;
To rotate an entire Graphics2D
:
g2d.rotate(Math.toRadians(degrees));
//draw shape/image (will be rotated)
To reset the rotation (so you only rotate one thing):
AffineTransform old = g2d.getTransform();
g2d.rotate(Math.toRadians(degrees));
//draw shape/image (will be rotated)
g2d.setTransform(old);
//things you draw after here will not be rotated
Example:
class MyPanel extends JPanel {
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
AffineTransform old = g2d.getTransform();
g2d.rotate(Math.toRadians(degrees));
//draw shape/image (will be rotated)
g2d.setTransform(old);
//things you draw after here will not be rotated
}
}
How to rotate an image around a point in Java
The rotate
method you’re calling passes in the anchor point as the center of the image:
https://docs.oracle.com/javase/7/docs/api/java/awt/geom/AffineTransform.html#rotate(double,%20double,%20double)
Try just passing in the rotation angle itself and it should rotate around the top-left:
transform.rotate(Math.toRadians(angle));
Java - rotate image in place
Okay, assuming that the turret and base are separate images, and the turret isn't the same size as the tank (cause then it becomes complicated....more then it actually is :P)
You can use a AffineTransform
and compound the transformations...
// This is the x/y position of the top, at the top/left point,
// I've placed it at the center of my screen, but you get the idea
double x = (getWidth() - base.getWidth()) / 2d;
double y = (getHeight() - base.getHeight()) / 2d;
// Translate the location to the x/y, this makes the top/left 0x0...
// much easier to deal with...
AffineTransform at = AffineTransform.getTranslateInstance(x, y);
g2d.setTransform(at);
// Draw the base...
g2d.drawImage(base, 0, 0, this);
// Offset the turret, in my testing, this was 8x8 from the bases
// top/left
at.translate(8, 8);
if (targetPoint != null) {
// Calculate the delta between the mouse and the center point
// of the turret, this is in screen coordinates and not
// translated coordinates
double deltaX = (x + 8) - targetPoint.x;
double deltaY = (y + 8) - targetPoint.y;
// Calculate the rotation required to point at the mouse
// Had to apply an offset to allow for the default orientation
// of the tank...
double rotation = Math.atan2(deltaY, deltaX) + Math.toRadians(180d);
// Rotate around the anchor point of the turret
// Remember, we've translated so the top/left (0x0) is now the
// turrets default position
at.rotate(rotation, 4, 4);
}
// Transform the Graphics context
g2d.setTransform(at);
// Paint the turret
g2d.drawImage(turret, 0, 0, this);
}
g2d.dispose();
And because I went to effort...
My assets...
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.geom.Line2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class FollowMe {
public static void main(String[] args) {
new FollowMe();
}
public FollowMe() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Point targetPoint;
private BufferedImage turret;
private BufferedImage base;
public TestPane() {
addMouseMotionListener(new MouseAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
targetPoint = e.getPoint();
repaint();
}
});
try {
base = ImageIO.read(getClass().getResource("/Base.png"));
turret = ImageIO.read(getClass().getResource("/Turret.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g.drawLine(getWidth() / 2, 0, getWidth() / 2, getHeight());
g.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2);
if (base != null) {
double x = (getWidth() - base.getWidth()) / 2d;
double y = (getHeight() - base.getHeight()) / 2d;
// Test line from center of tank to mouse poisition
if (targetPoint != null) {
g2d.draw(new Line2D.Double((x + 12), (y + 12), targetPoint.x, targetPoint.y));
}
AffineTransform at = AffineTransform.getTranslateInstance(x, y);
g2d.setTransform(at);
g2d.drawImage(base, 0, 0, this);
at.translate(8, 8);
if (targetPoint != null) {
double deltaX = (x + 8) - targetPoint.x;
double deltaY = (y + 8) - targetPoint.y;
double rotation = Math.atan2(deltaY, deltaX) + Math.toRadians(180d);
at.rotate(rotation, 4, 4);
}
g2d.setTransform(at);
g2d.drawImage(turret, 0, 0, this);
}
g2d.dispose();
}
}
}
Have a look at Transforming Shapes, Text, and Images for more details
Rotate 90 degree to right image in java
As discussed here, you can use AffineTransformOp
to rotate an image by Math.PI / 2
; this is equivalent to rotating the image clockwise 90°, as shown here. See also Handling 90-Degree Rotations.
Rotating an image around a reference point
Rotating an image by an angle around the center point:
private BufferedImage rotateImage(BufferedImage buffImage, double angle) {
double radian = Math.toRadians(angle);
double sin = Math.abs(Math.sin(radian));
double cos = Math.abs(Math.cos(radian));
int width = buffImage.getWidth();
int height = buffImage.getHeight();
int nWidth = (int) Math.floor((double) width * cos + (double) height * sin);
int nHeight = (int) Math.floor((double) height * cos + (double) width * sin);
BufferedImage rotatedImage = new BufferedImage(nWidth, nHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = rotatedImage.createGraphics();
graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
graphics.translate((nWidth - width) / 2, (nHeight - height) / 2);
// This is the rotation around the center point - change this line
graphics.rotate(radian, (double) (width / 2), (double) (height / 2));
graphics.drawImage(buffImage, 0, 0, null);
graphics.dispose();
return rotatedImage;
}
To change the origin point of the rotation see javadoc
of the method rotate.
Source: Creating simple captcha.
rotate image in grid in Java
First, you need a Graphics2D
context. In most cases when supplied with a Graphics
it's actually an instance of Graphics2D
so you can simply cast it.
Having said that though, when perform transformations, it's always useful to create a new context (this copies the state only)...
Graphics2D g2d = (Graphics2D) g.create();
Next, you want to translate the origin point. This makes it a lot easier to do things like rotation.....
g2d.translate(50 * cellWidth, 50 * cellHeight);
Then you can rotate the context around the centre point of the cell (remember, 0x0
is now our cell offset)...
g2d.rotate(Math.toRadians(90), cellWidth / 2, cellWidth / 2);
And then we can simply draw the image...
g2d.drawImage(image, 0, 0, cellWidth, cellHeight, Color.WHITE, null);
And don't forget to dispose of the copy when you're done
g2d.dispose();
You might also want to take a look at The 2D Graphics trail, as you could use a AffineTransformation
instead, but it'd be accomplishing the same thing, more or less
Is there a way to actually see the rotating happening (so see the rotation "live")?
Animation is a complex subject, add in the fact that Swing is single threaded and not thread safe and you need to think carefully about it.
Have a look at Concurrency in Swing and How to Use Swing Timers for more details.
Simple animation
The following example makes use of simple Swing Timer
to rotate a image when it's clicked. The example makes use of time based approach (ie the animation runs over a fixed period of time). This produces a better result then a linear/delta approach.
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Simple {
public static void main(String[] args) throws IOException {
new Simple();
}
public Simple() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
Logger.getLogger(Advanced.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public class TestPane extends JPanel {
private List<BufferedImage> images;
private BufferedImage selectedImage;
public TestPane() throws IOException {
images = new ArrayList<>(9);
for (int index = 0; index < 9; index++) {
BufferedImage img = ImageIO.read(getClass().getResource("/images/p" + (index + 1) + ".png"));
images.add(img);
}
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (selectedImage != null) {
return;
}
int col = (e.getX() - 32) / 210;
int row = (e.getY() - 32) / 210;
int index = (row * 3) + col;
selectedImage = images.get(index);
startTimer();
}
});
}
private Timer timer;
private Instant startedAt;
private Duration duration = Duration.ofSeconds(1);
private double maxAngle = 1440;
private double currentAngle = 0;
protected void startTimer() {
if (timer != null) {
return;
}
timer = new Timer(5, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (startedAt == null) {
startedAt = Instant.now();
}
Duration runtime = Duration.between(startedAt, Instant.now());
double progress = runtime.toMillis() / (double)duration.toMillis();
if (progress >= 1.0) {
progress = 1.0;
selectedImage = null;
startedAt = null;
stopTimer();
}
currentAngle = maxAngle * progress;
repaint();;
}
});
timer.start();
}
protected void stopTimer() {
if (timer == null) {
return;
}
timer.stop();
timer = null;
}
@Override
public Dimension getPreferredSize() {
return new Dimension((210 * 3) + 64, (210 * 3) + 64);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.translate(32, 32);
int row = 0;
int col = 0;
for (BufferedImage img : images) {
int x = col * 210;
int y = row * 210;
Graphics2D gc = (Graphics2D) g2d.create();
gc.translate(x, y);
if (selectedImage == img) {
gc.rotate(Math.toRadians(currentAngle), 210 / 2, 210 / 2);
}
gc.drawImage(img, 0, 0, this);
gc.dispose();
col++;
if (col >= 3) {
col = 0;
row++;
}
}
g2d.dispose();
}
}
}
nb: My images are 210x210
in size and I'm been naughty with not using the actual sizes of the images, and using fixed values instead
Advanced animation
While the above example "works", it becomes much more complicated the more you add it. For example, if you want to have multiple images rotate. Towards that end, you will need to keep track of some kind of model for each image which contains the required information to calculate the current rotation value.
Another issue is, what happens if you want to compound the animation? That is, scale and rotate the animation at the same time.
Towards this end, I'd lean towards using concepts like "time lines" and "key frames"
The following example is based on my personal library Super Simple Swing Animation Framework. This is bit more of a playground for me then a fully fledged animation framework, but it embodies many of the core concepts which help make animating in Swing simpler and help produce a much nicer result
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import org.kaizen.animation.Animatable;
import org.kaizen.animation.AnimatableAdapter;
import org.kaizen.animation.AnimatableDuration;
import org.kaizen.animation.DefaultAnimatableDuration;
import org.kaizen.animation.curves.Curves;
import org.kaizen.animation.timeline.BlendingTimeLine;
import org.kaizen.animation.timeline.DoubleBlender;
public class Advanced {
public static void main(String[] args) throws IOException {
new Advanced();
}
public Advanced() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
Logger.getLogger(Advanced.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public class TestPane extends JPanel {
private List<BufferedImage> images;
private Map<BufferedImage, Double> imageZoom = new HashMap<>();
private Map<BufferedImage, Double> imageRotate = new HashMap<>();
private BlendingTimeLine<Double> zoomTimeLine;
private BlendingTimeLine<Double> rotateTimeLine;
public TestPane() throws IOException {
zoomTimeLine = new BlendingTimeLine<>(new DoubleBlender());
zoomTimeLine.addKeyFrame(0, 1.0);
zoomTimeLine.addKeyFrame(0.25, 1.5);
zoomTimeLine.addKeyFrame(0.75, 1.5);
zoomTimeLine.addKeyFrame(1.0, 1.0);
rotateTimeLine = new BlendingTimeLine<>(new DoubleBlender());
rotateTimeLine.addKeyFrame(0d, 0d);
rotateTimeLine.addKeyFrame(0.1, 0d);
// rotateTimeLine.addKeyFrame(0.85, 360.0 * 4d);
rotateTimeLine.addKeyFrame(1.0, 360.0 * 4d);
images = new ArrayList<>(9);
for (int index = 0; index < 9; index++) {
BufferedImage img = ImageIO.read(getClass().getResource("/images/p" + (index + 1) + ".png"));
images.add(img);
}
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
int col = (e.getX() - 32) / 210;
int row = (e.getY() - 32) / 210;
int index = (row * 3) + col;
BufferedImage selectedImage = images.get(index);
if (imageZoom.containsKey(selectedImage)) {
return;
}
animate(selectedImage);
}
});
}
protected void animate(BufferedImage img) {
Animatable animatable = new DefaultAnimatableDuration(Duration.ofSeconds(1), Curves.CUBIC_IN_OUT.getCurve(), new AnimatableAdapter<Double>() {
@Override
public void animationTimeChanged(AnimatableDuration animatable) {
double progress = animatable.getProgress();
Double desiredZoom = zoomTimeLine.getValueAt(progress);
imageZoom.put(img, desiredZoom);
double desiredAngle = rotateTimeLine.getValueAt(progress);
imageRotate.put(img, desiredAngle);
repaint();
}
@Override
public void animationStopped(Animatable animator) {
imageZoom.remove(img);
imageRotate.remove(img);
repaint();
}
});
animatable.start();
}
@Override
public Dimension getPreferredSize() {
return new Dimension((210 * 3) + 64, (210 * 3) + 64);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.translate(32, 32);
int row = 0;
int col = 0;
for (BufferedImage img : images) {
if (!(imageZoom.containsKey(img) || imageRotate.containsKey(img))) {
int x = col * 210;
int y = row * 210;
Graphics2D gc = (Graphics2D) g2d.create();
gc.translate(x, y);
gc.drawImage(img, 0, 0, this);
gc.dispose();
}
col++;
if (col >= 3) {
col = 0;
row++;
}
}
row = 0;
col = 0;
for (BufferedImage img : images) {
if (imageZoom.containsKey(img) || imageRotate.containsKey(img)) {
int x = col * 210;
int y = row * 210;
Graphics2D gc = (Graphics2D) g2d.create();
gc.translate(x, y);
double width = img.getWidth();
double height = img.getHeight();
double zoom = 1;
if (imageZoom.containsKey(img)) {
zoom = imageZoom.get(img);
width = (img.getWidth() * zoom);
height = (img.getHeight() * zoom);
double xPos = (width - img.getWidth()) / 2d;
double yPos = (height - img.getHeight()) / 2d;
gc.translate(-xPos, -yPos);
}
if (imageRotate.containsKey(img)) {
double angle = imageRotate.get(img);
gc.rotate(Math.toRadians(angle), width / 2, height / 2);
}
gc.scale(zoom, zoom);
gc.drawImage(img, 0, 0, this);
gc.dispose();
}
col++;
if (col >= 3) {
col = 0;
row++;
}
}
g2d.dispose();
}
}
}
nb: The paint workflow is a little more complicated (and could be optimised more) as it focuses on painting the images which are been animated onto of the others, which results in a much nicer result
How to implement rotate image algorithm and scale image algorithm?
You haven't passed rotateCW a graphics object. Your code looks like this..
Rotation r = new Rotation();
r.rotateCw(img, null);
Where it needs to look a little like this.. Get a Graphics object from the JFrame to draw the result onto.
Rotation r = new Rotation();
Graphics graphics = f.getGraphics();
r.rotateCw(img, graphics);
graphics.dispose();
I tested your code and added this and it worked :) In your case it would have thrown an exception on the missing Graphics object, leaving just the empty JPanel open.
Related Topics
How to Run Testng from Command Line
How to Implement a Re-Try-Catch
Where Do I Find a Standard Trie Based Map Implementation in Java
How to Convert Java String to Date Object
Why Does the Ternary Operator Unexpectedly Cast Integers
Jlabel - Show Longer Text as Multiple Lines
How to Remove Entity with Manytomany Relationship in JPA (And Corresponding Join Table Rows)
How to Sort an Array of Ints Using a Custom Comparator
Executing Another Application from Java
When Should I Use File.Separator and When File.Pathseparator
Overriding Private Methods in Java
How to Make Threadpoolexecutor's Submit() Method Block If It Is Saturated