Save JPanel as Image HD Quality - java
I am trying to save a JPanel as Image (Png, Jpg, whatever) but with Graphics2D the Quality is very low. My JPanel contains mostly text and I want to print the panel 4 times on a A4 paper size, so that it fills the page.
But the text is like washed-out when i print the image. I tried to create an A3 image then print it to the smaller A4 size. The quality increases very little, almost insignificantly.
Here is the function i use to generate the image, based on this ScreenImage.Class i found here :
public void exportToPNG(JRootPane panel){
Dimension size = panel.getSize();
BufferedImage image = new BufferedImage(
size.width, size.height * 4 /* use the same image 4 times */
, BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = image.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g2.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g2.drawImage(ScreenImage.createImage(panel), 0, 0, size.width, size.height, null);
g2.drawImage(ScreenImage.createImage(panel), 0, size.height, size.width, size.height, null);
g2.drawImage(ScreenImage.createImage(panel), 0, size.height * 2, size.width, size.height, null);
g2.drawImage(ScreenImage.createImage(panel), 0, size.height * 3, size.width, size.height, null);
try{
ImageIO.write(image, "png", new File("D:\\test-image.png"));
}
catch(Exception e)
{
e.printStackTrace();
}
}
Here is a sample image i generated:
click the link..
You should see that washed-out effect on the image even when zooming-in a little.
Is there a way to increase the quality? or a dedicated library?
Let's start with the obvious...
A A4 sheet of paper is 21.0cm x 29.7cm.
At 300dpi, that makes it 2480.315x3507.874 pixels
At 72dpi, that makes it 595.2756x841.8898 pixels
Why's this important? Java renders to the screen at 72dpi, but is capable of printing at 300dpi (above and below, but this is nice figure). This means, roughly, you'd to scale the screen image UP by 4 times. Up scaling is never pleasant, as demonstrated here
A better solution would be work with the printer DPI and scale the image down to the screen.
For convenience sake, you can use something like...
public static final float CM_PER_INCH = 0.393700787f;
public static float cmsToPixel(float cms, float dpi) {
return cmToInches(cms) * dpi;
}
public static float cmToInches(float cms) {
return cms * CM_PER_INCH;
}
To convert from cm's to pixels at a given DPI.
Now to the fun part. You "could" use Swing components to render the basic layout, it might be easier, but you'd have to scale the graphics down, as the core printer API assumes a DPI of 72 (don't ask). Now scaling down generally results in a better output, but there is another solution.
Instead, you could use the Graphics 2D API and generate the output yourself...
public static class Ticket {
public enum TextAlignment {
LEFT,
RIGHT,
CENTRE
}
protected static final int STUB_NUMBER_Y_POS = 12;
private Font plainFont;
private Font boldFont;
private Stroke dashedStroke;
public void paint(Graphics2D g2d, double pageWidth, double pageHeight, int stubNumber) {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
paintLeftStub((Graphics2D) g2d.create(), pageWidth, pageHeight, stubNumber);
paintBody((Graphics2D) g2d.create(), pageWidth, pageHeight, stubNumber);
paintRightStub((Graphics2D) g2d.create(), pageWidth, pageHeight, stubNumber);
paintEndStub((Graphics2D) g2d.create(), pageWidth, pageHeight, stubNumber);
g2d = (Graphics2D) g2d.create();
g2d.setColor(Color.GRAY);
g2d.setStroke(getDashedStroke());
g2d.draw(new Line2D.Double(0, pageHeight - 1, pageWidth, pageHeight - 1));
g2d.dispose();
}
protected void paintLeftStub(Graphics2D graphics, double pageWidth, double pageHeight, int stubNumber) {
graphics.setColor(Color.BLACK);
double stubWidth = pageWidth / 4;
Graphics2D g2d = (Graphics2D) graphics.create();
Font font = getBoldFont().deriveFont(18f);
g2d.setFont(font);
FontMetrics fm = g2d.getFontMetrics();
// Did mention I hate doing inline transformations :P
g2d.rotate(Math.toRadians(-90), stubWidth / 2, pageHeight / 2);
g2d.translate(0, -((stubWidth - pageHeight) / 2));
String lines[] = {"MORATUFIESTA", "", "Sat. 3 Auguest 2015", "Adult - $3"};
double x = 2;
double y = 0;
double maxWidth = 0;
for (String text : lines) {
x = calculateHorizontalCenterPositionFor(text, fm, stubWidth);
maxWidth = Math.max(maxWidth, fm.stringWidth(text));
g2d.drawString(text, (int) Math.round(x), (int) Math.round(y + fm.getAscent()));
y += fm.getHeight();
}
double blockWidth = y;
// Easier then trying to undo the transformation...
g2d.dispose();
g2d = (Graphics2D) graphics.create();
String text = "Low";
font = getPlainFont().deriveFont(6f);
g2d.setFont(font);
fm = g2d.getFontMetrics();
double xPos = calculateHorizontalCenterPositionFor(text, fm, blockWidth) + 2;
double yPos = (pageHeight - maxWidth) / 2;
g2d.drawString(text, (int) Math.round(xPos), (int) Math.round(yPos - fm.getAscent()));
g2d.setStroke(getDashedStroke());
g2d.draw(new Line2D.Double(stubWidth, 0, stubWidth, pageHeight));
g2d.dispose();
g2d = (Graphics2D) graphics.create();
drawStubNumber(g2d, stubWidth - 8 - fm.getHeight() - fm.getAscent(), STUB_NUMBER_Y_POS, stubNumber);
g2d.dispose();
}
protected Stroke getDashedStroke() {
if (dashedStroke == null) {
float dash[] = {10.0f};
dashedStroke = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 10f, dash, 0.0f);
}
return dashedStroke;
}
public Font getPlainFont() {
if (plainFont == null) {
plainFont = UIManager.getFont("Label.font");
}
return plainFont;
}
public Font getBoldFont() {
if (boldFont == null) {
boldFont = getPlainFont();
boldFont = boldFont.deriveFont(Font.BOLD);
}
return boldFont;
}
protected double calculateHorizontalCenterPositionFor(String text, FontMetrics fm, double width) {
return (width - fm.stringWidth(text)) / 2d;
}
protected void paintBody(Graphics2D graphics, double pageWidth, double pageHeight, int stubNumber) {
int padding = 8;
double xOffset = pageWidth / 4d;
graphics.setColor(Color.BLACK);
double bodyWidth = (pageWidth / 2d);
Graphics2D g2d = (Graphics2D) graphics.create();
g2d.translate(xOffset, 0);
g2d.setFont(getPlainFont().deriveFont(12f));
FontMetrics fm = g2d.getFontMetrics();
String text = "Moratu Fiesta";
double xPos = bodyWidth - fm.stringWidth(text) - padding;
double yPos = padding;
g2d.drawString(text, (int) Math.round(xPos), (int) Math.round(yPos + fm.getAscent()));
g2d.dispose();
Font plainFont = getPlainFont().deriveFont(9.5f);
TextLine[] addressLines01 = new TextLine[]{
new TextLine("61 Railway Pde North", plainFont),
new TextLine("Glen Waverley", plainFont),
new TextLine("03 9836 8673", plainFont)
};
TextLine[] addressLines02 = new TextLine[]{
new TextLine("1120, Glen Huntly", plainFont),
new TextLine("Glen Huntly", plainFont),
new TextLine("03 9571 5544", plainFont)
};
TextLine[] sponsorLines = new TextLine[]{
new TextLine("Proudly supported by", plainFont),
new TextLine("Quality Groceries", plainFont.deriveFont(Font.BOLD)),
new TextLine("Visit for all your grocery needs", plainFont)
};
Area area = new Area();
addTo(g2d, area, addressLines01);
addTo(g2d, area, addressLines02);
addTo(g2d, area, sponsorLines);
int height = area.getBounds().height;
double bottomBlockYPos = pageHeight - height - padding;
g2d = (Graphics2D) graphics.create();
g2d.translate(xOffset, 0);
drawTextLines(g2d, padding, bottomBlockYPos, bodyWidth - (padding * 2), TextAlignment.LEFT, addressLines01);
drawTextLines(g2d, padding, bottomBlockYPos, bodyWidth - (padding * 2), TextAlignment.CENTRE, sponsorLines);
drawTextLines(g2d, padding, bottomBlockYPos, bodyWidth - (padding * 2), TextAlignment.RIGHT, addressLines02);
g2d.dispose();
plainFont = getPlainFont().deriveFont(10f);
TextLine[] textLines = new TextLine[]{
new TextLine("On Saturday, August 3, 2013 from 6.30pm till midnight", plainFont.deriveFont(Font.BOLD)),
new TextLine("At 21, Sacred Heart Parish Hall, Johnson Street, Oakleigh", plainFont)
};
int blockHeight = getSizeFor(g2d, textLines).height;
double mainBlockYPos = (pageHeight - blockHeight) / 2;
g2d = (Graphics2D) graphics.create();
g2d.translate(xOffset, 0);
drawTextLines(g2d, 0, mainBlockYPos, bodyWidth, TextAlignment.CENTRE, textLines);
g2d.dispose();
g2d = (Graphics2D) graphics.create();
g2d.translate(xOffset, 0);
Font boldFont = getBoldFont().deriveFont(9f);
double upperYPos = (mainBlockYPos + blockHeight);
double middleBlockYPos = upperYPos + ((bottomBlockYPos - upperYPos) / 2) - (g2d.getFontMetrics(boldFont).getHeight() / 2);
drawTextLines(g2d, padding, middleBlockYPos, bodyWidth - (padding * 2), TextAlignment.LEFT, new TextLine("Melway Ref, 69 FB", boldFont));
drawTextLines(g2d, padding, middleBlockYPos, bodyWidth - (padding * 2), TextAlignment.CENTRE, new TextLine("Music by REDEMPTION", boldFont));
drawTextLines(g2d, padding, middleBlockYPos, bodyWidth - (padding * 2), TextAlignment.RIGHT, new TextLine("Donations $30", boldFont));
g2d.dispose();
g2d = (Graphics2D) graphics.create();
g2d.translate(xOffset, 0);
g2d.setStroke(getDashedStroke());
g2d.draw(new Line2D.Double(bodyWidth, 0, bodyWidth, pageHeight));
g2d.dispose();
g2d = (Graphics2D) graphics.create();
drawStubNumber(g2d, padding + xOffset, STUB_NUMBER_Y_POS, stubNumber);
g2d.dispose();
}
protected void drawStubNumber(Graphics2D g2d, double x, double y, int stubNumber) {
Font font = getBoldFont().deriveFont(18f);
g2d.setFont(font);
FontMetrics fm = g2d.getFontMetrics();
String text = Integer.toString(stubNumber);
g2d.translate(x, y);
g2d.rotate(Math.toRadians(-90), fm.stringWidth(text) / 2, fm.getHeight() / 2);
g2d.drawString(text, 0, fm.getAscent());
g2d.dispose();
}
protected void paintRightStub(Graphics2D graphics, double pageWidth, double pageHeight, int stubNumber) {
graphics.setColor(Color.BLACK);
int padding = 8;
double xOffset = (pageWidth / 4d) * 3;
double stubWidth = (pageWidth / 4d) / 2;
Graphics2D g2d = (Graphics2D) graphics.create();
g2d.translate(xOffset, 0);
g2d.setStroke(getDashedStroke());
g2d.draw(new Line2D.Double(stubWidth, 0, stubWidth, pageHeight));
g2d.dispose();
g2d = (Graphics2D) graphics.create();
drawStubNumber(g2d, padding + xOffset, STUB_NUMBER_Y_POS, stubNumber);
g2d.dispose();
g2d = (Graphics2D) graphics.create();
String text = "Cafe Little Hut";
Font font = getPlainFont().deriveFont(23f);
g2d.setFont(font);
FontMetrics fm = g2d.getFontMetrics();
double x = xOffset + ((stubWidth - fm.stringWidth(text)) / 2d);
double y = ((pageHeight - fm.getHeight()) / 2d);
g2d.translate(x, y);
g2d.rotate(Math.toRadians(-90), fm.stringWidth(text) / 2, fm.getHeight() / 2);
g2d.drawString(text, 0, fm.getAscent());
g2d.dispose();
}
protected void paintEndStub(Graphics2D graphics, double pageWidth, double pageHeight, int stubNumber) {
graphics.setColor(Color.BLACK);
int padding = 8;
double stubWidth = (pageWidth / 4d) / 2;
double xOffset = ((pageWidth / 4d) * 3 + stubWidth);
Graphics2D g2d = (Graphics2D) graphics.create();
g2d.translate(xOffset, 0);
g2d.setStroke(getDashedStroke());
g2d.draw(new Line2D.Double(stubWidth, 0, stubWidth, pageHeight));
g2d.dispose();
g2d = (Graphics2D) graphics.create();
drawStubNumber(g2d, padding + xOffset, STUB_NUMBER_Y_POS, stubNumber);
g2d.dispose();
g2d = (Graphics2D) graphics.create();
String text = "Entrance";
Font font = getBoldFont().deriveFont(32f);
g2d.setFont(font);
FontMetrics fm = g2d.getFontMetrics();
double x = xOffset + ((stubWidth - fm.stringWidth(text)) / 2);
double y = ((pageHeight - fm.getHeight()) / 2);
g2d.translate(x, y);
g2d.rotate(Math.toRadians(-90), fm.stringWidth(text) / 2, fm.getHeight() / 2);
g2d.drawString(text, 0, fm.getAscent());
g2d.dispose();
}
protected Dimension drawTextLines(Graphics2D g2d, double xPos, double yPos, double width, TextAlignment textAlignment, TextLine... textLines) {
Area area = new Area();
for (TextLine textLine : textLines) {
g2d.translate(xPos, yPos);
Dimension size = textLine.getBounds(g2d);
textLine.paint(g2d, width, textAlignment);
area.add(new Area(new Rectangle2D.Double(xPos, yPos, size.width, size.height)));
g2d.translate(-xPos, -yPos);
yPos += size.height;
}
return area.getBounds().getSize();
}
protected void addTo(Graphics2D g2d, Area area, TextLine... textLines) {
area.add(new Area(new Rectangle(getSizeFor(g2d, textLines))));
}
protected Dimension getSizeFor(Graphics2D g2d, TextLine... textLines) {
int yPos = 0;
int width = 0;
for (TextLine textLine : textLines) {
Dimension size = textLine.getBounds(g2d);
yPos += size.height;
width = Math.max(size.width, width);
}
return new Dimension(width, yPos);
}
protected class TextLine {
private String text;
private Font font;
public TextLine(String text, Font font) {
this.text = text;
this.font = font;
}
public String getText() {
return text;
}
public Font getFont() {
return font;
}
public Dimension getBounds(Graphics2D g2d) {
FontMetrics fm = g2d.getFontMetrics(getFont());
return new Dimension(fm.stringWidth(text), fm.getHeight());
}
public void paint(Graphics2D g2d, double width, TextAlignment textAlignment) {
Dimension bounds = getBounds(g2d);
FontMetrics fm = g2d.getFontMetrics(getFont());
g2d.setFont(font);
double x = 0;
switch (textAlignment) {
case CENTRE:
x = (width - bounds.width) / 2;
break;
case RIGHT:
x = width - bounds.width;
break;
}
g2d.drawString(getText(), (int) Math.round(x), fm.getAscent());
}
}
}
Why would you do that? It's easier to wrap a Printable around and print to a printer and you can also "paint" to a component (or image)
For example...
public class TicketPrintable implements Printable {
private Ticket ticket;
public TicketPrintable(Ticket ticket) {
this.ticket = ticket;
}
#Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
int result = NO_SUCH_PAGE;
if (pageIndex == 0) {
Graphics2D g2d = (Graphics2D) graphics;
double width = pageFormat.getImageableWidth();
double height = pageFormat.getImageableHeight();
g2d.translate((int) pageFormat.getImageableX(),
(int) pageFormat.getImageableY());
double ticketHeight = height / 4d;
for (int index = 0; index < 4; index++) {
ticket.paint(g2d, width, ticketHeight, index + 1);
g2d.translate(0, ticketHeight);
}
result = PAGE_EXISTS;
}
return result;
}
}
And to print it, you would use something like...
PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet();
aset.add(MediaSizeName.ISO_A4);
aset.add(new PrinterResolution(300, 300, PrinterResolution.DPI));
aset.add(new MediaPrintableArea(0, 0, 210, 297, MediaPrintableArea.MM));
PrinterJob pj = PrinterJob.getPrinterJob();
pj.setPrintable(new TicketPrintable(new Ticket()));
if (pj.printDialog(aset)) {
try {
pj.print(aset);
} catch (PrinterException ex) {
ex.printStackTrace();
}
}
On the screen it looks like...
On paper it looks like (scaled down for SO)
Now having said that, I would strongly encourage you to learn JasperReports, which makes this all so much simpler...
Updated
So, I was sitting in traffic and thought to myself, I wonder if I can just use scaling to render the Ticket to an image, so I thought I'd give it a try...
The top image is 72dpi, the bottom image is scaled to 300dpi
double pageWidth = cmsToPixel(21.0f, 300f);
double pageHeight = cmsToPixel(29.7f, 300f);
double imageWidth = cmsToPixel(21.0f, 72f);
double imageHeight = cmsToPixel(29.7f, 72f);
double scaleFactor = ImageUtilities.getScaleFactorToFit(
new Dimension((int) Math.round(imageWidth), (int) Math.round(imageHeight)),
new Dimension((int) Math.round(pageWidth), (int) Math.round(pageHeight)));
int width = (int) Math.round(pageWidth);
int height = (int) Math.round(pageHeight);
BufferedImage img = new BufferedImage(
width,
height,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.setColor(Color.WHITE);
g2d.fill(new Rectangle2D.Double(0, 0, img.getWidth(), img.getHeight()));
g2d.scale(scaleFactor, scaleFactor);
Ticket ticket = new Ticket();
ticket.paint(g2d, img.getWidth() / scaleFactor, (img.getHeight() / scaleFactor) / 4, 1);
g2d.dispose();
try {
ImageIO.write(img, "png", new File("Ticket.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
Scale factor algorithms
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) {
double dScale = 1;
if (iMasterSize > iTargetSize) {
dScale = (double) iTargetSize / (double) iMasterSize;
} else {
dScale = (double) iTargetSize / (double) iMasterSize;
}
return dScale;
}
Note: This is only really going to work for text and primitive graphics, you put an image into this and it won't work, the image will be scaled up and look crappy, use demonstrated in the linked answer from before. In that case, you'd have to design the form to render at 300+dpi and scale the image down to 72dpi
Related
Unable to get and set the circular Icon
I am new to programming and I've been trying to get and set the icon to circle but it's not being updated. I want to set every icon that was set to JLabel will become circular. class CircleLabel extends JLabel { private Icon icon; private int borderSize; #Override protected void paintComponent(Graphics g) { if (getIcon() != null) { icon = getIcon(); int width = getWidth(); int height = getHeight(); int diameter = Math.min(width, height); int x = width / 2 - diameter / 2; int y = height / 2 - diameter / 2; int border = borderSize * 2; diameter -= border; Dimension size = getAutoSize(icon, diameter); BufferedImage img = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_ARGB); Graphics2D g2_img = img.createGraphics(); g2_img.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2_img.fillOval(0, 0, diameter, diameter); Composite composite = g2_img.getComposite(); g2_img.setComposite(AlphaComposite.SrcIn); g2_img.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g2_img.drawImage(toImage(icon), 0, 0, size.width, size.height, null); g2_img.setComposite(composite); g2_img.dispose(); Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2.setColor(new Color(255, 255, 255)); diameter += border; g2.fillOval(borderSize, borderSize, diameter, diameter); g2.drawImage(img, x - borderSize, y + borderSize, null); } else { System.out.println("No Icon!"); } super.paintComponent(g); } private Dimension getAutoSize(Icon image, int size) { int w = size; int h = size; int iw = image.getIconWidth(); int ih = image.getIconHeight(); double xScale = (double) w / iw; double yScale = (double) h / iw; System.out.println(xScale); System.out.println(yScale); double scale = Math.max(xScale, yScale); int width = (int) (scale * iw); int height = (int) (scale * ih); if (width < 1) { width = 1; } if (height < 1) { height = 1; } return new Dimension(width, height); } private Image toImage(Icon icon) { return ((ImageIcon) icon).getImage(); } } If I set the icon manually and removed the icon from the label, it will work but when I add the icon to the label through properties, it will not work. icon = new ImageIcon(getClass().getResource("/Attachments/user.png")); I found this on YouTube and wanted to apply it to the application I'm trying to build.
I'm rotating image in java but want to save rotated image [duplicate]
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
Rotate a buffered image in Java
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
Draw Beautiful Speech Bubbles in Swing [closed]
Closed. This question needs details or clarity. It is not currently accepting answers. Want to improve this question? Add details and clarify the problem by editing this post. Closed 8 years ago. Improve this question I am trying to create beautiful speech bubbles in swing but the result is not very good...I mean I want something better and more beautiful! here is the code i'm using: import javax.swing.*; import java.awt.*; import java.awt.geom.Area; import java.awt.geom.RoundRectangle2D; public class BubbleTest { public static void main(String[] args) { try { for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) { if ("Nimbus".equals(info.getName())) { javax.swing.UIManager.setLookAndFeel(info.getClassName()); break; } } } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | javax.swing.UnsupportedLookAndFeelException e) { e.printStackTrace(); } LeftArrowBubble leftArrowBubble = new LeftArrowBubble(); JOptionPane.showMessageDialog(null, leftArrowBubble); RightArrowBubble rightArrowBubble = new RightArrowBubble(); JOptionPane.showMessageDialog(null, rightArrowBubble); } private static class LeftArrowBubble extends JPanel { private int strokeThickness = 5; private int padding = strokeThickness / 2; private int radius = 10; private int arrowSize = 6; #Override protected void paintComponent(final Graphics g) { final Graphics2D graphics2D = (Graphics2D) g; RenderingHints qualityHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); qualityHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); graphics2D.setRenderingHints(qualityHints); graphics2D.setColor(new Color(80, 150, 180)); graphics2D.setStroke(new BasicStroke(strokeThickness)); int x = padding + strokeThickness + arrowSize; int width = getWidth() - arrowSize - (strokeThickness * 2); int height = getHeight() - strokeThickness; graphics2D.fillRect(x, padding, width, height); RoundRectangle2D.Double rect = new RoundRectangle2D.Double(x, padding, width, height, radius, radius); Polygon arrow = new Polygon(); arrow.addPoint(14, 6); arrow.addPoint(arrowSize + 2, 10); arrow.addPoint(14, 12); Area area = new Area(rect); area.add(new Area(arrow)); graphics2D.draw(area); graphics2D.dispose(); } } private static class RightArrowBubble extends JPanel { private int strokeThickness = 5; private int padding = strokeThickness / 2; private int arrowSize = 6; private int radius = 10; #Override protected void paintComponent(final Graphics g) { final Graphics2D graphics2D = (Graphics2D) g; RenderingHints qualityHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); qualityHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); graphics2D.setRenderingHints(qualityHints); graphics2D.setColor(new Color(20, 130, 230)); graphics2D.setStroke(new BasicStroke(strokeThickness)); int width = getWidth() - arrowSize - (strokeThickness * 2); int height = getHeight() - strokeThickness; graphics2D.fillRect(padding, padding, width, height); RoundRectangle2D.Double rect = new RoundRectangle2D.Double(padding, padding, width, height, radius, radius); Polygon arrow = new Polygon(); arrow.addPoint(width, 6); arrow.addPoint(width + arrowSize, 10); arrow.addPoint(width, 12); Area area = new Area(rect); area.add(new Area(arrow)); graphics2D.draw(area); graphics2D.dispose(); } } } any idea guys? I want something like speech bubbles in viber (android version 5.0.0) but I don't know how to draw something like that... can anyone help me? Thanks!
finally I draw it with GeneralPath... close enough? :)) #Override protected void paintComponent(final Graphics g) { final Graphics2D graphics2D = (Graphics2D) g; RenderingHints qualityHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); qualityHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); graphics2D.setRenderingHints(qualityHints); graphics2D.setPaint(new Color(80, 150, 180)); int width = getWidth(); int height = getHeight(); GeneralPath path = new GeneralPath(); path.moveTo(5, 10); path.curveTo(5, 10, 7, 5, 0, 0); path.curveTo(0, 0, 12, 0, 12, 5); path.curveTo(12, 5, 12, 0, 20, 0); path.lineTo(width - 10, 0); path.curveTo(width - 10, 0, width, 0, width, 10); path.lineTo(width, height - 10); path.curveTo(width, height - 10, width, height, width - 10, height); path.lineTo(15, height); path.curveTo(15, height, 5, height, 5, height - 10); path.lineTo(5, 15); path.closePath(); graphics2D.fill(path); }
Understanding Swing code from "Filthy Rich Clients"
Below is the code from "Filthy Rich Clients" by Romain Guy and Chet Haase private class ImageViewer extends JComponent { private BufferedImage image, landscape; private ImageViewer() { try { image = ImageIO.read(new File("picture.png")); landscape = ImageIO.read(new File("landscape.jpg")); } catch (IOException ex) { ex.printStackTrace(); } } #Override protected void paintComponent(Graphics g) { BufferedImage temp = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = temp.createGraphics(); if (shadow.isSelected()) { int x = (getWidth() - image.getWidth()) / 2; int y = (getHeight() - image.getHeight()) / 2; g2.drawImage(image, x + 4, y + 10, null); Composite oldComposite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, 0.75f)); g2.setColor(Color.BLACK); g2.fillRect(0, 0, getWidth(), getHeight()); g2.setComposite(oldComposite); g2.drawImage(image, x, y, null); } else { int x = (getWidth() - image.getWidth()) / 2; int y = (getHeight() - image.getHeight()) / 2; g2.drawImage(image, x, y, null); Composite oldComposite = g2.getComposite(); g2.setComposite(AlphaComposite.SrcIn); x = (getWidth() - landscape.getWidth()) / 2; y = (getHeight() - landscape.getHeight()) / 2; g2.drawImage(landscape, x, y, null); g2.setComposite(oldComposite); } g2.dispose(); g.drawImage(temp, 0, 0, null); } } It is a part of the code to obtain the following output with different states when checkbox is checked:- What i dont understand is 1)what is the line BufferedImage temp = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB); doing there because if I write only the following code and run nothing is obtained in the output. BufferedImage temp = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = temp.createGraphics(); if (jcb.isSelected()) { int x = (getWidth() - image.getWidth()) / 2; int y = (getHeight() - image.getHeight()) / 2; g2.drawImage(image, x + 4, y + 10, null); } 2)What is the need to obtain g2 after creating the 'temp' bufferedImage? Can't I do it directly like Graphics2D g2=(Graphics2D)g.create(); 3)What is practical difference between TYPE_INT_RGB and TYPE_INT_ARGB.?
This is double buffering: http://docs.oracle.com/javase/tutorial/extra/fullscreen/doublebuf.html You first draw on an off-screen image, which in this case is backed by a BufferedImage object referenced in the variable temp; then you do whatever to the off-screen image; finally, you print the result on the component, via g.drawImage(temp, ...). This is not to have the user see glitches or artifacts due to the image being constructed directly on the screen.