I am trying to rotate a buffered image in java. Here is the code I am using:
public static BufferedImage rotate(BufferedImage bimg, double angle) {
int w = bimg.getWidth();
int h = bimg.getHeight();
Graphics2D graphic = bimg.createGraphics();
graphic.rotate(Math.toRadians(angle), w / 2, h / 2);
graphic.drawImage(bimg, null, 0, 0);
graphic.dispose();
return bimg;
}
I have looked a numerous stack overflow questions and answers on this topic and not been able to figure out why the image is chopped up the way it is when I try to rotate it. Here is an example showing a loaded image:
loaded image
After I click the rotate button which calls the above function with the buffered image and a 90.0 for the angle:
chopped up image
Can someone help me understand what is happening and how to fix it?
As always, the Internet to the rescue. So, this is some code which I hobbled together from other resources/post/blogs which will return a new image which is sized so it will contain the rotated image
public BufferedImage rotateImageByDegrees(BufferedImage img, double angle) {
double rads = Math.toRadians(angle);
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);
BufferedImage 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 = w / 2;
int y = h / 2;
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();
return rotated;
}
Updated
So, using this PNG:
And this code...
package javaapplication1.pkg040;
import java.awt.Color;
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.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.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
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 BufferedImage master;
private BufferedImage rotated;
public TestPane() {
try {
master = ImageIO.read(new File("/Volumes/Disk02/Dropbox/MegaTokyo/Miho_Small.png"));
rotated = rotateImageByDegrees(master, 0.0);
} catch (IOException ex) {
ex.printStackTrace();
}
Timer timer = new Timer(40, new ActionListener() {
private double angle = 0;
private double delta = 1.0;
#Override
public void actionPerformed(ActionEvent e) {
angle += delta;
rotated = rotateImageByDegrees(master, angle);
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return master == null
? new Dimension(200, 200)
: new Dimension(master.getWidth(), master.getHeight());
}
#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.dispose();
}
}
public BufferedImage rotateImageByDegrees(BufferedImage img, double angle) {
double rads = Math.toRadians(angle);
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);
BufferedImage 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 = w / 2;
int y = h / 2;
at.rotate(rads, x, y);
g2d.setTransform(at);
g2d.drawImage(img, 0, 0, this);
g2d.dispose();
return rotated;
}
}
}
I can generate something like...
You get jumbled image result because you are drawing the rotated image into the input image itself. Instead you need to create graphic from a new BufferedImage.
public static BufferedImage rotate(BufferedImage bimg, double angle) {
int w = bimg.getWidth();
int h = bimg.getHeight();
BufferedImage rotated = new BufferedImage(w, h, bimg.getType());
Graphics2D graphic = rotated.createGraphics();
graphic.rotate(Math.toRadians(angle), w/2, h/2);
graphic.drawImage(bimg, null, 0, 0);
graphic.dispose();
return rotated;
}
Note that if you want to avoid getting cropped corners, you need to adjust width & height of the output BufferedImage.
This code reads an image from a file, rotates it by a certain angle, and writes to another file. It works fine with png images with transparency:
public static void main(String[] args) throws IOException {
BufferedImage image = ImageIO.read(
Test.class.getResourceAsStream("/resources/image.png"));
BufferedImage rotated = rotateImage(image, 45);
ImageIO.write(rotated, "png",
new FileOutputStream("resources/rotated.png"));
}
private static 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);
// rotation around the center point
graphics.rotate(radian, (double) (width / 2), (double) (height / 2));
graphics.drawImage(buffImage, 0, 0, null);
graphics.dispose();
return rotatedImage;
}
image.png
rotated.png (45º) ⟳
rotated.png (-45º) ⟲
You'll have to account for resizing and new width and height for the output. See: https://stackoverflow.com/a/4787898/5420880
Related
This question already has answers here:
Java2D - How to rotate an image and save the result
(3 answers)
Closed 5 years ago.
I'm using AffineTransform to rotate the image and I had tried to save the rotated image by ImageIo.write it is saving the image but not the rotated image it's saving the original image. so please tell me how to correct it Thanks in advance.
This is my code for rotation and saving the image
public class Image {
public static void main(String[] args) {
new Image();
}
public Image() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
private JSlider slider;
private BufferedImage image;
public TestPane() {
setLayout(new BorderLayout());
try {
File imagefile = new File("C:/pics/1206.jpg");
image = ImageIO.read(imagefile);
ImageIO.write(image, "jpg",new File("C:/pics"));
ImageIO.write(image, "bmp",new File("C:/pics"));
ImageIO.write(image, "gif",new File("C:/picsf"));
ImageIO.write(image, "png",new File("C:/pics"));
} catch (IOException ex) {
ex.printStackTrace();
}
slider = new JSlider();
slider.setMinimum(0);
slider.setMaximum(360);
slider.setMinorTickSpacing(5);
slider.setMajorTickSpacing(10);
slider.setValue(0);
add(slider, BorderLayout.SOUTH);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
repaint();
}
});
}
#Override
public Dimension getPreferredSize() {
return image == null ? new Dimension(200, 200) : new Dimension(image.getWidth(), image.getHeight());
}
public double getAngle() {
return Math.toRadians(slider.getValue());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
g2d.drawLine(getWidth() / 2, 0, getWidth() / 2, getHeight());
g2d.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2);
g2d.setColor(Color.BLACK);
int x = (getWidth() - image.getWidth()) / 2;
int y = (getHeight() - image.getHeight()) / 2;
AffineTransform at = new AffineTransform();
at.setToRotation(getAngle(), x + (image.getWidth() / 2), y + (image.getHeight() / 2));
at.translate(x, y);
g2d.setTransform(at);
g2d.drawImage(image, 0, 0, this);
g2d.dispose();
}
}
}
Let's start with, this isn't rotating the image, it's rotating the Graphics context which is used to display the image, it doesn't affect the original image at all
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
g2d.drawLine(getWidth() / 2, 0, getWidth() / 2, getHeight());
g2d.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2);
g2d.setColor(Color.BLACK);
int x = (getWidth() - image.getWidth()) / 2;
int y = (getHeight() - image.getHeight()) / 2;
AffineTransform at = new AffineTransform();
at.setToRotation(getAngle(), x + (image.getWidth() / 2), y + (image.getHeight() / 2));
at.translate(x, y);
g2d.setTransform(at);
g2d.drawImage(image, 0, 0, this);
g2d.dispose();
}
And then this...
try {
File imagefile = new File("C:/pics/1206.jpg");
image = ImageIO.read(imagefile);
ImageIO.write(image, "jpg",new File("C:/pics"));
ImageIO.write(image, "bmp",new File("C:/pics"));
ImageIO.write(image, "gif",new File("C:/picsf"));
ImageIO.write(image, "png",new File("C:/pics"));
} catch (IOException ex) {
ex.printStackTrace();
}
which just saves the original image to a number of different formats ... to the same file 😕, but doesn't even react to any changes to the angle
Instead, you need to use something which generates a new image from the original, rotated by the amount you want...
public BufferedImage rotateImageByDegrees(BufferedImage img, double degrees) {
double rads = Math.toRadians(degrees);
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);
BufferedImage 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();
return rotated;
}
Then you can save it...
File imagefile = new File("C:/pics/1206.jpg");
image = ImageIO.read(imagefile);
BufferedImage rotated = rotateImageByDegrees(image, 22.5);
ImageIO.write(rotated, "png", new File("RotatedBy225.png"));
So, the next time you use one of my previous examples and I tell you it's not doing what you think/want it to, I hope you will understand my meaning better 😉 and look more closly at the other examples we show you
The following code works correctly, but I'm having trouble understanding some of the details. Can somebody help me understand how the AffineTransform is working to rotate the image?
package pks;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
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;
public class rotate {
public static void main(String[] args) {
new rotate();
}
public rotate() {
EventQueue.invokeLater(new Runnable() {
public void run() {
final RotationPane rotationPane = new RotationPane(); // initilize object of RotationPane class
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(rotationPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class RotationPane extends JPanel {
private BufferedImage img;
private BufferedImage rotated;
private double angle;
public RotationPane() {
try {
img = ImageIO.read(new File("C:\\Users\\pardeep\\Desktop\\tomb1.jpg")); // path of the image to be rotated
setAngle(45);
}
catch (IOException ex) {
}
}
public void setAngle(double angle) {
this.angle = angle;
// Using Affine transform we will calculate the new values
//x=vcos (theta)+wsin(theta)
//y=vcos(theta)+ wsin(theta)
double rads = Math.toRadians(angle); // calculating angle in radian
double sin = Math.abs(Math.sin(rads)), //calculating sin theta
cos = Math.abs(Math.cos(rads)); // calculating cos theta
int w = img.getWidth();
int h = img.getHeight();
int newWidth = (int) Math.floor(w * cos + h * sin); //using affine transform
int newHeight = (int) Math.floor(h * cos + w * sin);
rotated = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = rotated.createGraphics(); //rotating planes.....
AffineTransform plane = new AffineTransform();
plane.translate((newWidth - w) / 2, (newHeight - h) / 2);
int x=w/2;
int y=h/2;
plane.rotate(Math.toRadians(45), x, y);
g2d.setTransform(plane);
g2d.drawImage(img, 0, 0, this);
}
public Dimension getPreferredSize() {
return new Dimension(800, // setting the window size
800);
}
protected void paintComponent(Graphics g) {
// super.paintComponent(g); no need for it
if (rotated != null) { // drawing image on 2 dimentional size surface
Graphics2D g2d = (Graphics2D) g.create();
int x = (getWidth() - rotated.getWidth()) / 2;
int y = (getHeight() - rotated.getHeight()) / 2;
g2d.drawImage(rotated, x, y, this); // overriding the method......
}
}
}
}
What exactly don't you understand? plane.rotate(Math.toRadians(45), x, y); rotates the image around the center point clockwise by 45 degrees. plane.translate((newWidth - w) / 2, (newHeight - h) / 2); is the same as plane.translate(newWidth/2 - w/2, newHeight/2 - h/2); so it calculates the difference between the new center and the old center and moves the entire picture by that difference.
I should mention that AffineTransform applies the transformations in the opposite order.
So if you write this
plane.translate((newWidth - w) / 2, (newHeight - h) / 2);
int x=w/2;
int y=h/2;
plane.rotate(Math.toRadians(45), x, y);
Then every pixel of of the image is first rotated by 45 degrees clockwise and then shifted, not the other way around. It's done this way because the transformations are supposed to be applied to the coordinate system. Applying transformation A followed by tranformation B to the coordinate system is mathematically equivalent to applying B followed by A to the pixels of the image.
I need to print a 1800 x 1200 pixels, 300 dpi image on 4" x 6" paper (also known as 4r)
What I have Tried
I have created a PrintRequestAttributeSet which takes care of my PrintableArea(4 x 6), Printer print DPI, Orientation. I have attached a MCVE at the bottom.
Problem
While the code works, and I get a PageFormat with the following attributes(for my printer) :
x= 12.0
y= 12.32
w= 276.0
h= 419.67
The width and height are little less, because my printer doesn't support Zero Margin. (This is what I have considered. If anyone is aware of a way other than this through which I can force zero margin, please let me know)
I am supplying the margin as 0, because these images will be printed via printers which support zero margin(Photobooth Printers).
aset.add(new MediaPrintableArea(0, 0, 4, 6, MediaPrintableArea.INCH));
The printable area including the margin is roughly 4 x 6 as required. The problem occurs when I scale the image to print inside the printable area.
Since image is 1800 x 1200, it supports an aspect ratio of 3:2, which means the image is created to get printed on a 4 x 6 paper(after getting rotated and scaled). For Reference.
Now, since the pageWidth and pageHeight of the PageFormat are not exactly divisible by the ImageWidth and ImageHeight. I am getting scaling issues.
Note : I rotate the image because it has to be printed on 4 x 6 and not 6 x 4.
The image which is supposed to take 4 x 6 space is taking somewhere close to 4 x 5. The image size is also reduced drastically.
How do I overcome this issue?
Code
Please find the MCVE here :
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.print.attribute.standard.MediaPrintableArea;
import javax.print.attribute.standard.OrientationRequested;
import javax.print.attribute.standard.PrintQuality;
import javax.print.attribute.standard.PrinterResolution;
public class ImgPrinter implements Printable {
Image img;
#Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex)
throws PrinterException {
Graphics2D g2d = (Graphics2D) graphics;
g2d.translate((int) (pageFormat.getImageableX()),
(int) (pageFormat.getImageableY()));
if (pageIndex == 0) {
double pageWidth = pageFormat.getImageableWidth();
double pageHeight = pageFormat.getImageableHeight();
/**
* Swapping width and height, coz the image is later rotated
*/
double imageWidth = img.getHeight(null);
double imageHeight = img.getWidth(null);
double scaleX = pageWidth / imageWidth;
double scaleY = pageHeight / imageHeight;
g2d.scale(scaleX, scaleY);
g2d.rotate(Math.toRadians(90), img.getWidth(null) / 2,
img.getHeight(null) / 2);
g2d.drawImage(img, 0, 0, null);
return Printable.PAGE_EXISTS;
}
return Printable.NO_SUCH_PAGE;
}
public void printPage(String file, String size) {
try {
Image img = ImageIO.read(new File(file));
this.img = img;
PrintRequestAttributeSet aset = createAsetForMedia(size);
PrinterJob pj = PrinterJob.getPrinterJob();
PageFormat pageFormat = pj.getPageFormat(aset);
pj.setPrintable(this, pageFormat);
pj.print();
} catch (PrinterException ex) {
ex.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private PrintRequestAttributeSet createAsetForMedia(String size) {
PrintRequestAttributeSet aset = null;
try {
aset = new HashPrintRequestAttributeSet();
aset.add(PrintQuality.NORMAL);
aset.add(OrientationRequested.PORTRAIT);
/**
* Suggesting the print DPI as 300
*/
aset.add(new PrinterResolution(300, 300, PrinterResolution.DPI));
/**
* Setting the printable area and the margin as 0
*/
if (size.equals("3r")) {
aset.add(new MediaPrintableArea(0, 0, 3, 5,
MediaPrintableArea.INCH));
} else if (size.equals("4r")) {
aset.add(new MediaPrintableArea(0, 0, 4, 6,
MediaPrintableArea.INCH));
} else if (size.equals("5r")) {
aset.add(new MediaPrintableArea(0, 0, 5, 7,
MediaPrintableArea.INCH));
} else if (size.equals("6r")) {
aset.add(new MediaPrintableArea(0, 0, 6, 8,
MediaPrintableArea.INCH));
}
} catch (Exception e) {
e.printStackTrace();
}
return aset;
}
public static void main(String[] args) {
new ImgPrinter().printPage("/Some_URL/sam.jpg",
"4r");
}
}
To run the program, just supply a 1800x1200 image path to the main program and it will print to the default printer.
Things that worry me...
Changing the scale/rotation of the Graphics context without either first making a copy of it or resetting it after the fact. This could actually affect subsequent renderings, as the printable may be called multiple times...
Using Graphics2D#scale. This really isn't the best quality nor is it generally that fast. See The Perils of Image.getScaledInstance(). I also prefer to use AffineTransform, but that's just me...
Not buffering the result. Okay, this relates to the previous comment, but your print method may be called multiple times to print a single page, scaling the image each time is costly, instead, you should scale it once and re-use the scaled result.
Unless you're going to physically rotate the image, you probably want to rotate about the center of the page, not the image itself, this will affect the location where 0x0 becomes.
Now remember, when you rotate the Graphics context, the origin point changes, so instead of been in the top/left corner, in this case, it will become the top/right corner. And now you know why I would have rotated the image in isolation and not tried messing around with the Graphics context :P
What I "think" is happening is that between the scaling, rotating and manipulations of the coordinates (swapping the height and width), things are getting screwed...but frankly, I wasn't going to mess around with it when I have better solutions...
The following example makes use of a bunch of personal library code, so some of might be a little convoluted, but I use the separate functionality for other things, so it binds well together...
So, starting with an image of 7680x4800, this generates a scaled image of 423x264
(the red border are visual guides only, used when dumping the result to PDF to save paper ;))
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.print.attribute.standard.MediaPrintableArea;
import javax.print.attribute.standard.OrientationRequested;
import javax.print.attribute.standard.PrintQuality;
import javax.print.attribute.standard.PrinterResolution;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ImgPrinter implements Printable {
BufferedImage img;
BufferedImage scaled;
#Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex)
throws PrinterException {
int result = NO_SUCH_PAGE;
Graphics2D g2d = (Graphics2D) graphics.create();
g2d.translate((int) (pageFormat.getImageableX()), (int) (pageFormat.getImageableY()));
if (pageIndex == 0) {
double pageWidth = pageFormat.getImageableWidth();
double pageHeight = pageFormat.getImageableHeight();
if (scaled == null) {
// Swap the width and height to allow for the rotation...
System.out.println(pageWidth + "x" + pageHeight);
scaled = getScaledInstanceToFit(
img,
new Dimension((int)pageHeight, (int)pageWidth));
System.out.println("In " + img.getWidth() + "x" + img.getHeight());
System.out.println("Out " + scaled.getWidth() + "x" + scaled.getHeight());
}
double imageWidth = scaled.getWidth();
double imageHeight = scaled.getHeight();
AffineTransform at = AffineTransform.getRotateInstance(
Math.toRadians(90),
pageWidth / 2d,
pageHeight / 2d
);
AffineTransform old = g2d.getTransform();
g2d.setTransform(at);
double x = (pageHeight - imageWidth) / 2d;
double y = (pageWidth - imageHeight) / 2d;
g2d.drawImage(
scaled,
(int)x,
(int)y,
null);
g2d.setTransform(old);
// This is not affected by the previous changes, as those were made
// to a different copy...
g2d.setColor(Color.RED);
g2d.drawRect(0, 0, (int)pageWidth - 1, (int)pageHeight - 1);
result = PAGE_EXISTS;
}
g2d.dispose();
return result;
}
public void printPage(String file, String size) {
try {
img = ImageIO.read(new File(file));
PrintRequestAttributeSet aset = createAsetForMedia(size);
PrinterJob pj = PrinterJob.getPrinterJob();
PageFormat pageFormat = pj.getPageFormat(aset);
pj.setPrintable(this, pageFormat);
if (pj.printDialog()) {
pj.print();
}
} catch (PrinterException ex) {
ex.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private PrintRequestAttributeSet createAsetForMedia(String size) {
PrintRequestAttributeSet aset = null;
try {
aset = new HashPrintRequestAttributeSet();
aset.add(PrintQuality.NORMAL);
aset.add(OrientationRequested.PORTRAIT);
/**
* Suggesting the print DPI as 300
*/
aset.add(new PrinterResolution(300, 300, PrinterResolution.DPI));
/**
* Setting the printable area and the margin as 0
*/
if (size.equals("3r")) {
aset.add(new MediaPrintableArea(1, 1, 3, 5,
MediaPrintableArea.INCH));
} else if (size.equals("4r")) {
aset.add(new MediaPrintableArea(1, 1, 4, 6,
MediaPrintableArea.INCH));
} else if (size.equals("5r")) {
aset.add(new MediaPrintableArea(1, 1, 5, 7,
MediaPrintableArea.INCH));
} else if (size.equals("6r")) {
aset.add(new MediaPrintableArea(1, 1, 6, 8,
MediaPrintableArea.INCH));
}
} catch (Exception e) {
e.printStackTrace();
}
return aset;
}
public static BufferedImage getScaledInstanceToFit(BufferedImage img, Dimension size) {
double scaleFactor = getScaleFactorToFit(img, size);
return getScaledInstance(img, scaleFactor);
}
public static BufferedImage getScaledInstance(BufferedImage img, double dScaleFactor) {
return getScaledInstance(img, dScaleFactor, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
}
public static double getScaleFactorToFit(BufferedImage img, Dimension size) {
double dScale = 1;
if (img != null) {
int imageWidth = img.getWidth();
int imageHeight = img.getHeight();
dScale = getScaleFactorToFit(new Dimension(imageWidth, imageHeight), size);
}
return dScale;
}
public static double getScaleFactorToFit(Dimension original, Dimension toFit) {
double dScale = 1d;
if (original != null && toFit != null) {
double dScaleWidth = getScaleFactor(original.width, toFit.width);
double dScaleHeight = getScaleFactor(original.height, toFit.height);
dScale = Math.min(dScaleHeight, dScaleWidth);
}
return dScale;
}
public static double getScaleFactor(int iMasterSize, int iTargetSize) {
return (double) iTargetSize / (double) iMasterSize;
}
protected static BufferedImage getScaledInstance(BufferedImage img, double dScaleFactor, Object hint) {
BufferedImage imgScale = img;
int iImageWidth = (int) Math.round(img.getWidth() * dScaleFactor);
int iImageHeight = (int) Math.round(img.getHeight() * dScaleFactor);
if (dScaleFactor <= 1.0d) {
imgScale = getScaledDownInstance(img, iImageWidth, iImageHeight, hint);
} else {
imgScale = getScaledUpInstance(img, iImageWidth, iImageHeight, hint);
}
return imgScale;
}
protected static BufferedImage getScaledDownInstance(BufferedImage img,
int targetWidth,
int targetHeight,
Object hint) {
// System.out.println("Scale down...");
int type = (img.getTransparency() == Transparency.OPAQUE)
? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage) img;
if (targetHeight > 0 || targetWidth > 0) {
int w = img.getWidth();
int h = img.getHeight();
do {
if (w > targetWidth) {
w /= 2;
if (w < targetWidth) {
w = targetWidth;
}
}
if (h > targetHeight) {
h /= 2;
if (h < targetHeight) {
h = targetHeight;
}
}
BufferedImage tmp = new BufferedImage(Math.max(w, 1), Math.max(h, 1), type);
Graphics2D g2 = tmp.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
g2.drawImage(ret, 0, 0, w, h, null);
g2.dispose();
ret = tmp;
} while (w != targetWidth || h != targetHeight);
} else {
ret = new BufferedImage(1, 1, type);
}
return ret;
}
protected static BufferedImage getScaledUpInstance(BufferedImage img,
int targetWidth,
int targetHeight,
Object hint) {
int type = BufferedImage.TYPE_INT_ARGB;
BufferedImage ret = (BufferedImage) img;
int w = img.getWidth();
int h = img.getHeight();
do {
if (w < targetWidth) {
w *= 2;
if (w > targetWidth) {
w = targetWidth;
}
}
if (h < targetHeight) {
h *= 2;
if (h > targetHeight) {
h = targetHeight;
}
}
BufferedImage tmp = new BufferedImage(w, h, type);
Graphics2D g2 = tmp.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
g2.drawImage(ret, 0, 0, w, h, null);
g2.dispose();
ret = tmp;
tmp = null;
} while (w != targetWidth || h != targetHeight);
return ret;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
new ImgPrinter().printPage("/Volumes/Disk02/Dropbox/Wallpapers/animepaper.net_wallpaper_art_anime_aria_duanwu_festival_205050_wonderngo_7680x4800-a8aecc9c.jpg",
"4r");
}
});
}
}
You know what would MUCH easier, printing the page in landscape mode to start with :P
I would say you need proportional scaling. Like this
double scaleX = pageWidth / imageWidth;
double scaleY = pageHeight / imageHeight;
double scale = Math.min(scaleX, scaleY);
g2d.scale(scale, scale);
UPDATE:
One more suggestions as mKorbel mentioned would be separate scaling.
Try use public Image getScaledInstance(int width, int height, int hints) method of BufferedImage
passing Image.SCALE_SMOOTH as the hint.
I have a rectangle that rotates around it's middle and I have another rectangle that I want to connect to the upper right corner of the rotating rectangle. The problem is that I have no idea how to get the corner so that the second rectangle always will be stuck to that corner.
This is my sample code. Right now the second rectangle will be at the same place all the time which is not the result that I'm after.
package Test;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
class Test{
public static void main(String[] args){
new Test();
}
public Test(){
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new Graphic());
frame.setSize(1000,700);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
class Graphic extends JPanel{
private int x, y, windowW, windowH;
private double angle;
private Rectangle rect1, rect2;
private Path2D path;
private Timer timer;
private AffineTransform rotation;
public Graphic(){
windowW = (int) Toolkit.getDefaultToolkit().getScreenSize().getWidth();
windowH = (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight();
path = new Path2D.Double();
rotation = new AffineTransform();
angle = 0;
x = windowW / 2;
y = windowH / 2;
timer = new Timer(100, new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
angle += .1;
if(angle > 360) angle -= 360;
repaint();
}
});
timer.start();
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
rotation.setToTranslation(500, 200);
rotation.rotate(angle, 32, 32);
rect1 = new Rectangle(0, 0, 64, 64);
path = new Path2D.Double(rect1, rotation);
rect2 = new Rectangle(path.getBounds().x, path.getBounds().y, 10, 50);
g2d.fill(path);
g2d.fill(rect2);
}
}
Mathematical solution :)
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
rotation.setToTranslation(500, 200);
rotation.rotate(angle, 32, 32);
rect1 = new Rectangle(0, 0, 64, 64);
path = new Path2D.Double(rect1, rotation);
double r = 32.0 * Math.sqrt(2);
// (532, 232) - coordinates of rectangle center |
// you can change position of second rectangle by this V substraction (all you need to know is that the full circle corresponds to 2Pi)
int x2 = (int) Math.round(532 + r * Math.cos(angle - Math.PI / 4));
int y2 = (int) Math.round(232 + r * Math.sin(angle - Math.PI / 4));
rect2 = new Rectangle(x2, y2, 10, 50);
g2d.fill(path);
g2d.fill(rect2);
}
Of course, some constants should be class fields, not method variables.
I can't test this code to be sure but I believe it is the proper working code that you want
int hw = -width / 2;
int hh = -height / 2;
int cos = Math.cos( theta );
int sin = Math.sin( theta );
int x = hw * cos - hh * sin;
int y = hw * sin + hh * cos;
This will get you the top left corner based on the theta, rotation, of the square. To get the other corners you just use change the hw and hh values:
//top right corner
hw = width / 2
hh = -height / 2
//bottom right corner
hw = width / 2
hh = height / 2
//bottom left corer
hw = -width / 2
hh = height / 2
I hope this helps
I have image like this:
and I need deform it to image like this:
I tried make it with a lot of codes, but I didn't solve it. There is my last attempt, but final image doesn't look nice
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
class MyPanel2 extends JPanel {
private final static double DEG_TO_RAD = Math.PI / 360;
private BufferedImage imageA;
private BufferedImage imageB;
static String IMG_URL1 = "img/upg.png";
public MyPanel2() {
try {
imageA = ImageIO.read(new File(IMG_URL1));
} catch (IOException e) {
System.err.println("Couldn't find input file. ");
System.exit(1);
}
double rotationRequired = Math.toRadians(90);
double locationX = imageA.getWidth() / 2;
double locationY = imageA.getHeight() / 2;
AffineTransform tx = AffineTransform.getRotateInstance(rotationRequired, locationX, locationY);
AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
BufferedImage pom_2 = imageA;
imageA = new BufferedImage(imageA.getWidth(), imageA.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = imageA.createGraphics();
g2.setStroke(new BasicStroke(5));
g2.setColor(Color.BLACK);
g2.drawImage(op.filter(pom_2, null),10,10, null);
BufferedImage pom = new BufferedImage(imageA.getWidth()*2, imageA.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g3 = pom.createGraphics();
g3.setColor(Color.white);
g3.drawImage(imageA, imageA.getWidth()/5, 0, null);
imageA = pom;
imageB = new BufferedImage(imageA.getWidth(), imageA.getWidth(), BufferedImage.TYPE_INT_ARGB);
// pruchod obrazkem pixel po pixelu
for (int i = imageB.getWidth(); i > 0; i--) {
for (int j = imageB.getHeight(); j > 0; j--) {
int r = (int) (Math.sqrt(i * i + j *j));
int fi = (int) (Math.atan2(j, i) / DEG_TO_RAD);
if (r < pom.getWidth() && fi < pom.getHeight()) {
imageB.setRGB(i, j, pom.getRGB(r, fi));
}
}
}
this.setPreferredSize(new Dimension(800, 600));
imageA = pom_2;
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D)g;
g2.drawImage(imageA,0,0, null);
double rotationRequired = Math.toRadians(-135);
double locationX = imageB.getWidth() / 2;
double locationY = imageB.getHeight() / 2;
AffineTransform tx = AffineTransform.getRotateInstance(rotationRequired, locationX, locationY);
AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
g2.drawImage(op.filter(imageB, null), 0, -imageB.getHeight()/2, null);
}
}
Using this code look final image like this:
I post my solution. Maybe it isn't elegant but it works:
class MyPanel2 extends JPanel {
private BufferedImage imageA;
private BufferedImage imageB;
private double k = 3.0;
String IMG_URL1 = "img/upg.png";
public MyPanel2() {
try {
imageA = ImageIO.read(new File(IMG_URL1));
} catch (IOException e) {
System.err.println("Couldn't find input file. ");
System.exit(1);
}
double radius = k*imageA.getHeight();
int xC = (int)radius, yC = (int)radius;
imageB = new BufferedImage(xC*2,yC, BufferedImage.TYPE_INT_ARGB);
double r, i, j;
for(int y = 0; y < imageB.getHeight(); y++) {
for (int x = 0; x < imageB.getWidth(); x++) {
r = Math.sqrt((xC-x)*(xC-x)+(yC-y)*(yC-y));
i = (radius-r);
j = (-k*imageA.getWidth()/2*(xC-x))/r + imageA.getWidth()/2;
if (i>=0 && i < imageA.getHeight() && j>=0 && j < imageA.getWidth()) {
imageB.setRGB(x, y, imageA.getRGB((int)j, (int)i));
}
}
}
this.setPreferredSize(new Dimension(800, 600));
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D)g;
g2.drawImage(imageA, 0, 0, null);
g2.drawImage(imageB, -imageA.getWidth(), 0, null);
}
}
You are on the right way, cause you got the polar to rectangular coordinate transform.
You just have to scale/translate your source image, and your generated image (or equivalently, just transform the relative coordinates).
Intuitively, in your generated image, the fi just spans too much, like PI/2, while in your desired image, the angle span very very little (do you see the curvature is much smaller?)
Hope this helps.