Java: Rotating Images

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...

BaseTurret

Tank

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.

Sample Image

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

Advanced

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



Leave a reply



Submit