Java: Rotating Images - java

I need to be able to rotate images individually(in java). The only thing I have found so far is g2d.drawImage(image, affinetransform, ImageObserver ). Unfortunately, I need to draw the image at a specific point, and there is no method with an argument that 1.rotates the image separately and 2. allows me to set the x and y. any help is appreciated

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);

AffineTransform instances can be concatenated (added together). Therefore you can have a transform that combines 'shift to origin', 'rotate' and 'shift back to desired position'.

A simple way to do it without the use of such a complicated draw statement:
//Make a backup so that we can reset our graphics object after using it.
AffineTransform backup = g2d.getTransform();
//rx is the x coordinate for rotation, ry is the y coordinate for rotation, and angle
//is the angle to rotate the image. If you want to rotate around the center of an image,
//use the image's center x and y coordinates for rx and ry.
AffineTransform a = AffineTransform.getRotateInstance(angle, rx, ry);
//Set our Graphics2D object to the transform
g2d.setTransform(a);
//Draw our image like normal
g2d.drawImage(image, x, y, null);
//Reset our graphics object so we can draw with it again.
g2d.setTransform(backup);

I struggled a little with the existing answers because my image to be rotated is not always a square, furthermore the accepted answer has a comment asking "Any info on how to circumvent the cutoff problem" that is not answered.
So for those who had the issue of image being croped when rotated here is the code that worked for me :
public static BufferedImage rotate(BufferedImage bimg, Double angle) {
double sin = Math.abs(Math.sin(Math.toRadians(angle))),
cos = Math.abs(Math.cos(Math.toRadians(angle)));
int w = bimg.getWidth();
int h = bimg.getHeight();
int neww = (int) Math.floor(w*cos + h*sin),
newh = (int) Math.floor(h*cos + w*sin);
BufferedImage rotated = new BufferedImage(neww, newh, bimg.getType());
Graphics2D graphic = rotated.createGraphics();
graphic.translate((neww-w)/2, (newh-h)/2);
graphic.rotate(Math.toRadians(angle), w/2, h/2);
graphic.drawRenderedImage(bimg, null);
graphic.dispose();
return rotated;
}

public static BufferedImage rotateCw( BufferedImage img )
{
int width = img.getWidth();
int height = img.getHeight();
BufferedImage newImage = new BufferedImage( height, width, img.getType() );
for( int i=0 ; i < width ; i++ )
for( int j=0 ; j < height ; j++ )
newImage.setRGB( height-1-j, i, img.getRGB(i,j) );
return newImage;
}
from https://coderanch.com/t/485958/java/Rotating-buffered-image

Here is a solution for rotations of 90, 180 & 270 degrees.
For these cases, the AffineTransform can introduce some loss/interpolation.
This solution is lossless & can also handle (esoteric?) Colour Models with more than 8 Bits/Pixel which BufferedImage.getRGB(int x, int y) cannot.
The solution proceeds on a pixel-by-pixel basis, which has the advantage of being simple to code.
It is possible to read the original Image row-by-row to gain performance, but its more complex, so I've left that out.
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
/**
* N.B. this example uses the new switch/case/Arrow notation, which requires Java 14.
*/
public enum Rotation {
CLOCKWISE_90,
CLOCKWISE_180,
CLOCKWISE_270;
public BufferedImage rotate(final BufferedImage original) {
final int oW = original.getWidth();
final int oH = original.getHeight();
final BufferedImage rotated =
switch (this) {
case CLOCKWISE_180 -> new BufferedImage(oW, oH, original.getType());
default -> new BufferedImage(oH, oW, original.getType());
};
final WritableRaster rasterOriginal = original.copyData(null);
final WritableRaster rasterRotated = rotated .copyData(null);
/*
* The Data for 1 Pixel...
*/
final int[] onePixel = new int[original.getSampleModel().getNumBands()];
/*
* Copy the Pixels one-by-one into the result...
*/
for (int x = 0; x < oW; x++) {
for (int y = 0; y < oH; y++) {
; rasterOriginal.getPixel( x, y, onePixel);
switch (this) {
case CLOCKWISE_90 -> rasterRotated .setPixel(oH - 1 - y, x, onePixel);
case CLOCKWISE_270 -> rasterRotated .setPixel( y, oW - 1 - x, onePixel);
default -> rasterRotated .setPixel(oW - 1 - x, oH - 1 - y, onePixel);
};
}
}
rotated.setData(rasterRotated);
return rotated;
}
}

Sorry, but all the answers are difficult to understand for me as a beginner in graphics...
After some fiddling, this is working for me and it is easy to reason about.
#Override
public void draw(Graphics2D g) {
AffineTransform tr = new AffineTransform();
// X and Y are the coordinates of the image
tr.translate((int)getX(), (int)getY());
tr.rotate(
Math.toRadians(this.rotationAngle),
img.getWidth() / 2,
img.getHeight() / 2
);
// img is a BufferedImage instance
g.drawImage(img, tr, null);
}
I suppose that if you want to rotate a rectangular image this method wont work and will cut the image, but I thing you should create square png images and rotate that.

I think the sturdiest and easiest approach is to not only rotate the image, but also the coordinates you're working with. This code will turn a bufferedImage around the topleft corner and draw it accordingly without cropping, and the returned image will be drawn in the right place. If you know what a vector and matrix are and look at the wikipedia article for rotation matrix you will understand this code easily. I also added a little hand drawing to it. In the 2nd part of the algorithm we determine the position and dimension of the bigger rectangle, which contains our rotated bufferedImage. The first 4 points we define are technically not used, but they still help you understand where the points are initially.
public void drawRotated(final Graphics g, final BufferedImage bufferedImage, final int x, final int y, final int width, final int height, final double angle) {
final Rectangle collision = new Rectangle(x, y, width, height);
final BufferedImage resize = resize(bufferedImage, collision.width, collision.height);
final BufferedImage rotate = rotate(resize, angle, collision);
g.drawImage(rotate, collision.x, collision.y, collision.width, collision.height, null);
}
public static BufferedImage resize(final BufferedImage bufferedImage, final int newWidth, final int newHeight) {
final BufferedImage resized = new BufferedImage(newWidth, newHeight, bufferedImage.getType());
final Graphics g = resized.createGraphics();
g.drawImage(bufferedImage, 0, 0, newWidth, newHeight, null);
return resized;
}
public static BufferedImage rotate(final BufferedImage bufferedImage, final double angle, final Rectangle collision) {
final double sin = Math.sin(Math.toRadians(angle));
final double cos = Math.cos(Math.toRadians(angle));
final int x1 = collision.x;
final int y1 = collision.y;
final int x2 = collision.x+collision.width;
final int y2 = collision.y;
final int x3 = collision.x;
final int y3 = collision.y+collision.height;
final int x4 = collision.x+collision.width;
final int y4 = collision.y+collision.height;
//turn all 4 points around the top left point
final int newx1 = collision.x;
final int newy1 = collision.y;
//the y component is 0
final int newx2 = (int) (collision.x+collision.width*cos);
final int newy2 = (int) (collision.y+collision.width*sin);
//the x component is 0
final int newx3 = (int) (collision.x-collision.height*sin);
final int newy3 = (int) (collision.y+collision.height*cos);
final int newx4 = (int) (collision.x+collision.width*cos-collision.height*sin);
final int newy4 = (int) (collision.y+collision.width*sin+collision.height*cos);
//determine the new position of our bigger rectangle containing our image
collision.x = Math.min(Math.min(newx1, newx2), Math.min(newx3, newx4));
collision.y = Math.min(Math.min(newy1, newy2), Math.min(newy3, newy4));
//determine the new dimensions of our bigger rectangle containing our image
collision.width = Math.max(Math.max(newx1, newx2), Math.max(newx3, newx4))-collision.x;
collision.height = Math.max(Math.max(newy1, newy2), Math.max(newy3, newy4))-collision.y;
final BufferedImage rotated = new BufferedImage(collision.width, collision.height, bufferedImage.getType());
final Graphics2D g2d = rotated.createGraphics();
g2d.translate(newx1- collision.x, newy1- collision.y);
g2d.rotate(Math.toRadians(angle), 0, 0);
g2d.drawRenderedImage(bufferedImage, null);
g2d.dispose();
return rotated;
}

Related

how to rotate an image by "alpha" degrees in java language [duplicate]

I need to be able to rotate images individually(in java). The only thing I have found so far is g2d.drawImage(image, affinetransform, ImageObserver ). Unfortunately, I need to draw the image at a specific point, and there is no method with an argument that 1.rotates the image separately and 2. allows me to set the x and y. any help is appreciated
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);
AffineTransform instances can be concatenated (added together). Therefore you can have a transform that combines 'shift to origin', 'rotate' and 'shift back to desired position'.
A simple way to do it without the use of such a complicated draw statement:
//Make a backup so that we can reset our graphics object after using it.
AffineTransform backup = g2d.getTransform();
//rx is the x coordinate for rotation, ry is the y coordinate for rotation, and angle
//is the angle to rotate the image. If you want to rotate around the center of an image,
//use the image's center x and y coordinates for rx and ry.
AffineTransform a = AffineTransform.getRotateInstance(angle, rx, ry);
//Set our Graphics2D object to the transform
g2d.setTransform(a);
//Draw our image like normal
g2d.drawImage(image, x, y, null);
//Reset our graphics object so we can draw with it again.
g2d.setTransform(backup);
I struggled a little with the existing answers because my image to be rotated is not always a square, furthermore the accepted answer has a comment asking "Any info on how to circumvent the cutoff problem" that is not answered.
So for those who had the issue of image being croped when rotated here is the code that worked for me :
public static BufferedImage rotate(BufferedImage bimg, Double angle) {
double sin = Math.abs(Math.sin(Math.toRadians(angle))),
cos = Math.abs(Math.cos(Math.toRadians(angle)));
int w = bimg.getWidth();
int h = bimg.getHeight();
int neww = (int) Math.floor(w*cos + h*sin),
newh = (int) Math.floor(h*cos + w*sin);
BufferedImage rotated = new BufferedImage(neww, newh, bimg.getType());
Graphics2D graphic = rotated.createGraphics();
graphic.translate((neww-w)/2, (newh-h)/2);
graphic.rotate(Math.toRadians(angle), w/2, h/2);
graphic.drawRenderedImage(bimg, null);
graphic.dispose();
return rotated;
}
public static BufferedImage rotateCw( BufferedImage img )
{
int width = img.getWidth();
int height = img.getHeight();
BufferedImage newImage = new BufferedImage( height, width, img.getType() );
for( int i=0 ; i < width ; i++ )
for( int j=0 ; j < height ; j++ )
newImage.setRGB( height-1-j, i, img.getRGB(i,j) );
return newImage;
}
from https://coderanch.com/t/485958/java/Rotating-buffered-image
Here is a solution for rotations of 90, 180 & 270 degrees.
For these cases, the AffineTransform can introduce some loss/interpolation.
This solution is lossless & can also handle (esoteric?) Colour Models with more than 8 Bits/Pixel which BufferedImage.getRGB(int x, int y) cannot.
The solution proceeds on a pixel-by-pixel basis, which has the advantage of being simple to code.
It is possible to read the original Image row-by-row to gain performance, but its more complex, so I've left that out.
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
/**
* N.B. this example uses the new switch/case/Arrow notation, which requires Java 14.
*/
public enum Rotation {
CLOCKWISE_90,
CLOCKWISE_180,
CLOCKWISE_270;
public BufferedImage rotate(final BufferedImage original) {
final int oW = original.getWidth();
final int oH = original.getHeight();
final BufferedImage rotated =
switch (this) {
case CLOCKWISE_180 -> new BufferedImage(oW, oH, original.getType());
default -> new BufferedImage(oH, oW, original.getType());
};
final WritableRaster rasterOriginal = original.copyData(null);
final WritableRaster rasterRotated = rotated .copyData(null);
/*
* The Data for 1 Pixel...
*/
final int[] onePixel = new int[original.getSampleModel().getNumBands()];
/*
* Copy the Pixels one-by-one into the result...
*/
for (int x = 0; x < oW; x++) {
for (int y = 0; y < oH; y++) {
; rasterOriginal.getPixel( x, y, onePixel);
switch (this) {
case CLOCKWISE_90 -> rasterRotated .setPixel(oH - 1 - y, x, onePixel);
case CLOCKWISE_270 -> rasterRotated .setPixel( y, oW - 1 - x, onePixel);
default -> rasterRotated .setPixel(oW - 1 - x, oH - 1 - y, onePixel);
};
}
}
rotated.setData(rasterRotated);
return rotated;
}
}
Sorry, but all the answers are difficult to understand for me as a beginner in graphics...
After some fiddling, this is working for me and it is easy to reason about.
#Override
public void draw(Graphics2D g) {
AffineTransform tr = new AffineTransform();
// X and Y are the coordinates of the image
tr.translate((int)getX(), (int)getY());
tr.rotate(
Math.toRadians(this.rotationAngle),
img.getWidth() / 2,
img.getHeight() / 2
);
// img is a BufferedImage instance
g.drawImage(img, tr, null);
}
I suppose that if you want to rotate a rectangular image this method wont work and will cut the image, but I thing you should create square png images and rotate that.
I think the sturdiest and easiest approach is to not only rotate the image, but also the coordinates you're working with. This code will turn a bufferedImage around the topleft corner and draw it accordingly without cropping, and the returned image will be drawn in the right place. If you know what a vector and matrix are and look at the wikipedia article for rotation matrix you will understand this code easily. I also added a little hand drawing to it. In the 2nd part of the algorithm we determine the position and dimension of the bigger rectangle, which contains our rotated bufferedImage. The first 4 points we define are technically not used, but they still help you understand where the points are initially.
public void drawRotated(final Graphics g, final BufferedImage bufferedImage, final int x, final int y, final int width, final int height, final double angle) {
final Rectangle collision = new Rectangle(x, y, width, height);
final BufferedImage resize = resize(bufferedImage, collision.width, collision.height);
final BufferedImage rotate = rotate(resize, angle, collision);
g.drawImage(rotate, collision.x, collision.y, collision.width, collision.height, null);
}
public static BufferedImage resize(final BufferedImage bufferedImage, final int newWidth, final int newHeight) {
final BufferedImage resized = new BufferedImage(newWidth, newHeight, bufferedImage.getType());
final Graphics g = resized.createGraphics();
g.drawImage(bufferedImage, 0, 0, newWidth, newHeight, null);
return resized;
}
public static BufferedImage rotate(final BufferedImage bufferedImage, final double angle, final Rectangle collision) {
final double sin = Math.sin(Math.toRadians(angle));
final double cos = Math.cos(Math.toRadians(angle));
final int x1 = collision.x;
final int y1 = collision.y;
final int x2 = collision.x+collision.width;
final int y2 = collision.y;
final int x3 = collision.x;
final int y3 = collision.y+collision.height;
final int x4 = collision.x+collision.width;
final int y4 = collision.y+collision.height;
//turn all 4 points around the top left point
final int newx1 = collision.x;
final int newy1 = collision.y;
//the y component is 0
final int newx2 = (int) (collision.x+collision.width*cos);
final int newy2 = (int) (collision.y+collision.width*sin);
//the x component is 0
final int newx3 = (int) (collision.x-collision.height*sin);
final int newy3 = (int) (collision.y+collision.height*cos);
final int newx4 = (int) (collision.x+collision.width*cos-collision.height*sin);
final int newy4 = (int) (collision.y+collision.width*sin+collision.height*cos);
//determine the new position of our bigger rectangle containing our image
collision.x = Math.min(Math.min(newx1, newx2), Math.min(newx3, newx4));
collision.y = Math.min(Math.min(newy1, newy2), Math.min(newy3, newy4));
//determine the new dimensions of our bigger rectangle containing our image
collision.width = Math.max(Math.max(newx1, newx2), Math.max(newx3, newx4))-collision.x;
collision.height = Math.max(Math.max(newy1, newy2), Math.max(newy3, newy4))-collision.y;
final BufferedImage rotated = new BufferedImage(collision.width, collision.height, bufferedImage.getType());
final Graphics2D g2d = rotated.createGraphics();
g2d.translate(newx1- collision.x, newy1- collision.y);
g2d.rotate(Math.toRadians(angle), 0, 0);
g2d.drawRenderedImage(bufferedImage, null);
g2d.dispose();
return rotated;
}

How can I create a non-square BufferedImage from a non-square image file?

I'm trying to create a BufferedImage from an arbitrary image file and then center that image in the background of a JPanel. I don't have any problems with square images, but I can't figure out how to handle non-square images.
Some debugging indicates that the (immediate) problem is that when I use ImageIO to create a BufferedImage from a rectangular input file, say one that's 256x128, BufferedImage.getHeight() returns 256 rather than 128.
Here's a snippet approximating my code:
class ExtendedPanel extends JPanel {
static final int WIDTH = 400;
static final int HEIGHT = 400;
BufferedImage image;
public ExtendedPanel(File f) {
super();
setPreferredSize(new Dimension(WIDTH,HEIGHT));
image = ImageIO.read(f);
}
#Override
public void paintComponent(Graphics g) {
int x = (WIDTH - image.getWidth())/2;
int y = (HEIGHT - image.getHeight())/2;
Graphics2D g2d = (Graphics2d)g;
g2d.drawRenderedImage(image,AffineTransform.getTranslateInstance(x,y));
}
}
As I said, this is fine for square image files. But with rectangular images that are wider than they are tall, the image is displayed higher than it should be. I haven't tried it yet with images taller than they are wide but I'm afraid that it that case the image would be displayed too far to the left. What can I do?
It is more a problem of (understanding) the right calculation.
public void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2d)g;
// How to scale the image:
double xscale = ((double)WIDTH) / image.getWidth();
double yscale = ((double)HEIGHT) / image.getHeight());
// When scaling proportionally:
double scale = Math.min(xscale, yscale); // max for covering entire panel.
xscale = scale;
yscale = scale;
double w = scalex * image.getWidth();
double h = scaley * image.getHeight();
double x = (getWidth() - w) / 2;
double y = (getHeight() - h) / 2;
g.drawImage(img, (int)x, (int)y, (int)w, (int)h, Color.BLACK, null);
//g2d.translate(x, y);
//g2d.scale(xscale, yscale);
//g2d.draw...;
}
Using the simple (scaling) version of drawImage what is needed is entirely clear.
To be considered is proportionally scaling, filling entirely (loss of image part) or upto maximal size (seeing background).

How can I create identicons using Java or Android?

I've seen many questions about this, but all of them are C#. None of them are Java, and I couldn't find a proper library for this.
What library can do this for me programmatically by giving it a string/hash? This algorithm is actually implemented on StackExchange.
You can look at this link. There is a code that you could use to generate your identicons http://www.davidhampgonsalves.com/Identicons
The code for Java is the following one:
public static BufferedImage generateIdenticons(String text, int image_width, int image_height){
int width = 5, height = 5;
byte[] hash = text.getBytes();
BufferedImage identicon = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
WritableRaster raster = identicon.getRaster();
int [] background = new int [] {255,255,255, 0};
int [] foreground = new int [] {hash[0] & 255, hash[1] & 255, hash[2] & 255, 255};
for(int x=0 ; x < width ; x++) {
//Enforce horizontal symmetry
int i = x < 3 ? x : 4 - x;
for(int y=0 ; y < height; y++) {
int [] pixelColor;
//toggle pixels based on bit being on/off
if((hash[i] >> y & 1) == 1)
pixelColor = foreground;
else
pixelColor = background;
raster.setPixel(x, y, pixelColor);
}
}
BufferedImage finalImage = new BufferedImage(image_width, image_height, BufferedImage.TYPE_INT_ARGB);
//Scale image to the size you want
AffineTransform at = new AffineTransform();
at.scale(image_width / width, image_height / height);
AffineTransformOp op = new AffineTransformOp(at, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
finalImage = op.filter(identicon, finalImage);
return finalImage;
}
I solved the problem.
I used Gravatar. I first got the link of the image and stored it as a String like this:
String identiconURL = "http://www.gravatar.com/avatar/" + userID + "?s=55&d=identicon&r=PG";
Then, I used Glide:
Glide.with(ProfilePictureChooserActivity.this)
.load(identiconURL)
.centerCrop()
.into(imageView);

Rotating Images and keeping track of point [duplicate]

I need to be able to rotate images individually(in java). The only thing I have found so far is g2d.drawImage(image, affinetransform, ImageObserver ). Unfortunately, I need to draw the image at a specific point, and there is no method with an argument that 1.rotates the image separately and 2. allows me to set the x and y. any help is appreciated
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);
AffineTransform instances can be concatenated (added together). Therefore you can have a transform that combines 'shift to origin', 'rotate' and 'shift back to desired position'.
A simple way to do it without the use of such a complicated draw statement:
//Make a backup so that we can reset our graphics object after using it.
AffineTransform backup = g2d.getTransform();
//rx is the x coordinate for rotation, ry is the y coordinate for rotation, and angle
//is the angle to rotate the image. If you want to rotate around the center of an image,
//use the image's center x and y coordinates for rx and ry.
AffineTransform a = AffineTransform.getRotateInstance(angle, rx, ry);
//Set our Graphics2D object to the transform
g2d.setTransform(a);
//Draw our image like normal
g2d.drawImage(image, x, y, null);
//Reset our graphics object so we can draw with it again.
g2d.setTransform(backup);
I struggled a little with the existing answers because my image to be rotated is not always a square, furthermore the accepted answer has a comment asking "Any info on how to circumvent the cutoff problem" that is not answered.
So for those who had the issue of image being croped when rotated here is the code that worked for me :
public static BufferedImage rotate(BufferedImage bimg, Double angle) {
double sin = Math.abs(Math.sin(Math.toRadians(angle))),
cos = Math.abs(Math.cos(Math.toRadians(angle)));
int w = bimg.getWidth();
int h = bimg.getHeight();
int neww = (int) Math.floor(w*cos + h*sin),
newh = (int) Math.floor(h*cos + w*sin);
BufferedImage rotated = new BufferedImage(neww, newh, bimg.getType());
Graphics2D graphic = rotated.createGraphics();
graphic.translate((neww-w)/2, (newh-h)/2);
graphic.rotate(Math.toRadians(angle), w/2, h/2);
graphic.drawRenderedImage(bimg, null);
graphic.dispose();
return rotated;
}
public static BufferedImage rotateCw( BufferedImage img )
{
int width = img.getWidth();
int height = img.getHeight();
BufferedImage newImage = new BufferedImage( height, width, img.getType() );
for( int i=0 ; i < width ; i++ )
for( int j=0 ; j < height ; j++ )
newImage.setRGB( height-1-j, i, img.getRGB(i,j) );
return newImage;
}
from https://coderanch.com/t/485958/java/Rotating-buffered-image
Here is a solution for rotations of 90, 180 & 270 degrees.
For these cases, the AffineTransform can introduce some loss/interpolation.
This solution is lossless & can also handle (esoteric?) Colour Models with more than 8 Bits/Pixel which BufferedImage.getRGB(int x, int y) cannot.
The solution proceeds on a pixel-by-pixel basis, which has the advantage of being simple to code.
It is possible to read the original Image row-by-row to gain performance, but its more complex, so I've left that out.
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
/**
* N.B. this example uses the new switch/case/Arrow notation, which requires Java 14.
*/
public enum Rotation {
CLOCKWISE_90,
CLOCKWISE_180,
CLOCKWISE_270;
public BufferedImage rotate(final BufferedImage original) {
final int oW = original.getWidth();
final int oH = original.getHeight();
final BufferedImage rotated =
switch (this) {
case CLOCKWISE_180 -> new BufferedImage(oW, oH, original.getType());
default -> new BufferedImage(oH, oW, original.getType());
};
final WritableRaster rasterOriginal = original.copyData(null);
final WritableRaster rasterRotated = rotated .copyData(null);
/*
* The Data for 1 Pixel...
*/
final int[] onePixel = new int[original.getSampleModel().getNumBands()];
/*
* Copy the Pixels one-by-one into the result...
*/
for (int x = 0; x < oW; x++) {
for (int y = 0; y < oH; y++) {
; rasterOriginal.getPixel( x, y, onePixel);
switch (this) {
case CLOCKWISE_90 -> rasterRotated .setPixel(oH - 1 - y, x, onePixel);
case CLOCKWISE_270 -> rasterRotated .setPixel( y, oW - 1 - x, onePixel);
default -> rasterRotated .setPixel(oW - 1 - x, oH - 1 - y, onePixel);
};
}
}
rotated.setData(rasterRotated);
return rotated;
}
}
Sorry, but all the answers are difficult to understand for me as a beginner in graphics...
After some fiddling, this is working for me and it is easy to reason about.
#Override
public void draw(Graphics2D g) {
AffineTransform tr = new AffineTransform();
// X and Y are the coordinates of the image
tr.translate((int)getX(), (int)getY());
tr.rotate(
Math.toRadians(this.rotationAngle),
img.getWidth() / 2,
img.getHeight() / 2
);
// img is a BufferedImage instance
g.drawImage(img, tr, null);
}
I suppose that if you want to rotate a rectangular image this method wont work and will cut the image, but I thing you should create square png images and rotate that.
I think the sturdiest and easiest approach is to not only rotate the image, but also the coordinates you're working with. This code will turn a bufferedImage around the topleft corner and draw it accordingly without cropping, and the returned image will be drawn in the right place. If you know what a vector and matrix are and look at the wikipedia article for rotation matrix you will understand this code easily. I also added a little hand drawing to it. In the 2nd part of the algorithm we determine the position and dimension of the bigger rectangle, which contains our rotated bufferedImage. The first 4 points we define are technically not used, but they still help you understand where the points are initially.
public void drawRotated(final Graphics g, final BufferedImage bufferedImage, final int x, final int y, final int width, final int height, final double angle) {
final Rectangle collision = new Rectangle(x, y, width, height);
final BufferedImage resize = resize(bufferedImage, collision.width, collision.height);
final BufferedImage rotate = rotate(resize, angle, collision);
g.drawImage(rotate, collision.x, collision.y, collision.width, collision.height, null);
}
public static BufferedImage resize(final BufferedImage bufferedImage, final int newWidth, final int newHeight) {
final BufferedImage resized = new BufferedImage(newWidth, newHeight, bufferedImage.getType());
final Graphics g = resized.createGraphics();
g.drawImage(bufferedImage, 0, 0, newWidth, newHeight, null);
return resized;
}
public static BufferedImage rotate(final BufferedImage bufferedImage, final double angle, final Rectangle collision) {
final double sin = Math.sin(Math.toRadians(angle));
final double cos = Math.cos(Math.toRadians(angle));
final int x1 = collision.x;
final int y1 = collision.y;
final int x2 = collision.x+collision.width;
final int y2 = collision.y;
final int x3 = collision.x;
final int y3 = collision.y+collision.height;
final int x4 = collision.x+collision.width;
final int y4 = collision.y+collision.height;
//turn all 4 points around the top left point
final int newx1 = collision.x;
final int newy1 = collision.y;
//the y component is 0
final int newx2 = (int) (collision.x+collision.width*cos);
final int newy2 = (int) (collision.y+collision.width*sin);
//the x component is 0
final int newx3 = (int) (collision.x-collision.height*sin);
final int newy3 = (int) (collision.y+collision.height*cos);
final int newx4 = (int) (collision.x+collision.width*cos-collision.height*sin);
final int newy4 = (int) (collision.y+collision.width*sin+collision.height*cos);
//determine the new position of our bigger rectangle containing our image
collision.x = Math.min(Math.min(newx1, newx2), Math.min(newx3, newx4));
collision.y = Math.min(Math.min(newy1, newy2), Math.min(newy3, newy4));
//determine the new dimensions of our bigger rectangle containing our image
collision.width = Math.max(Math.max(newx1, newx2), Math.max(newx3, newx4))-collision.x;
collision.height = Math.max(Math.max(newy1, newy2), Math.max(newy3, newy4))-collision.y;
final BufferedImage rotated = new BufferedImage(collision.width, collision.height, bufferedImage.getType());
final Graphics2D g2d = rotated.createGraphics();
g2d.translate(newx1- collision.x, newy1- collision.y);
g2d.rotate(Math.toRadians(angle), 0, 0);
g2d.drawRenderedImage(bufferedImage, null);
g2d.dispose();
return rotated;
}

Resizing an image in swing

I have snippet of code that I am using for the purpose of resizing an image to a curtain size (I want to change the resolution to something like 200 dpi). Basically the reason I need it is because I want to display the image that the user have picked (somewhat large) and then if the user approves I want to display the same image in a different place but using a smaller resolution. Unfortunately, if I give it a large image nothing appears on the screen. Also, if I change
imageLabel.setIcon(newIcon);
to
imageLabel.setIcon(icon);
I get the image to display but not in the correct resolution that's how I know that I have a problem inside this snipper of code and not somewhere else.
Image img = icon.getImage();
BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB);
BufferedImage bi = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB);
Graphics g = bi.createGraphics();
boolean myBool = g.drawImage(img, 0, 0, 100, 100, null);
System.out.println(myBool);
ImageIcon newIcon = new ImageIcon(bi);
imageLabel.setIcon(newIcon);
submitText.setText(currentImagePath);
imageThirdPanel.add(imageLabel);
You don't really have to care about the details of scaling images. The Image class has already a method getScaledInstance(int width, int height, int hints) designed for this purpose.
Java documentation says:
Creates a scaled version of this image. A new Image object is returned
which will render the image at the specified width and height by
default. The new Image object may be loaded asynchronously even if the
original source image has already been loaded completely. If either
the width or height is a negative number then a value is substituted
to maintain the aspect ratio of the original image dimensions.
And you can use it like this:
// Scale Down the original image fast
Image scaledImage = imageToScale.getScaledInstance(newWidth, newHighth, Image.SCALE_FAST);
// Repaint this component
repaint();
Check this for a complete example.
Here is my solution:
private BufferedImage resizeImage(BufferedImage originalImage, int width, int height, int type) throws IOException {
BufferedImage resizedImage = new BufferedImage(width, height, type);
Graphics2D g = resizedImage.createGraphics();
g.drawImage(originalImage, 0, 0, width, height, null);
g.dispose();
return resizedImage;
}
Try this CODE to resize image :
public static Image scaleImage(Image original, int newWidth, int newHeight) {
//do nothing if new and old resolutions are same
if (original.getWidth() == newWidth && original.getHeight() == newHeight) {
return original;
}
int[] rawInput = new int[original.getHeight() * original.getWidth()];
original.getRGB(rawInput, 0, original.getWidth(), 0, 0, original.getWidth(), original.getHeight());
int[] rawOutput = new int[newWidth * newHeight];
// YD compensates for the x loop by subtracting the width back out
int YD = (original.getHeight() / newHeight) * original.getWidth() - original.getWidth();
int YR = original.getHeight() % newHeight;
int XD = original.getWidth() / newWidth;
int XR = original.getWidth() % newWidth;
int outOffset = 0;
int inOffset = 0;
for (int y = newHeight, YE = 0; y > 0; y--) {
for (int x = newWidth, XE = 0; x > 0; x--) {
rawOutput[outOffset++] = rawInput[inOffset];
inOffset += XD;
XE += XR;
if (XE >= newWidth) {
XE -= newWidth;
inOffset++;
}
}
inOffset += YD;
YE += YR;
if (YE >= newHeight) {
YE -= newHeight;
inOffset += original.getWidth();
}
}
return Image.createRGBImage(rawOutput, newWidth, newHeight, false);
}
Another example is given here :
2D-Graphics/LoadImageandscaleit.htm">http://www.java2s.com/Tutorial/Java/0261_2D-Graphics/LoadImageandscaleit.htm
http://www.java2s.com/Code/JavaAPI/java.awt/ImagegetScaledInstanceintwidthintheightinthints.htm

Categories

Resources