Unable to get and set the circular Icon - java

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.

Related

java swing - dynamically fit jlabel to jframe [duplicate]

I have a JPanel with a painted background image and a layout manager holding other smaller images, all of this inside a JFrame. The background image is pretty big and I want to be able to have it maintain its aspect ratio whether its on a big or small monitor.
Eventually, I want to be able to have my LayoutManager and the smaller images in its cells "glued" to the background picture.
I looked around for resources and it seems that many examples use a BufferedImage but I am not; will this pose a problem? I'll post my code below for painting the image, If I lack any information please let me know.
public class MonitorPanel extends JPanel {
Image img;
public MonitorPanel() throws MalformedURLException {
//add components
try {
img = ImageIO.read(new File("src/customer_vlans.jpg"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
protected void paintComponent(Graphics g)
{
//paint background image
super.paintComponent(g);
//g.drawImage(img, 0, 0, getWidth(), getHeight(), this);
g.drawImage(img, 0, 0, this);
}
}
EDIT: I should mention that I know the aspect ratio formula:
original height / original width x new width = new height
However, I do not know how to use that correctly to my advantage.
Well, the quickest and easiest solution is to use Image.getScaledInstance
g.drawImage(img.getScaledInstance(newWidth, -1, Image. SCALE_SMOOTH), x, y, this);
If your wondering about the negative number, the java docs say:
If either width or height is a negative number then a value is
substituted to maintain the aspect ratio of the original image
dimensions. If both width and height are negative, then the original
image dimensions are used.
UPDATE
Just as a side note (my Google was playing up).
getScaledInstance is neither the fastest or highest quality approach, but it is the easiest.
Take a read through The Perils of Image.getScaledInstance for some more ideas
UPDATE
Scaling an image to fit an area is slightly more complicated then simply scaling the aspect ratio. You have to make a choice over if you want the image to "fit" within the area (possibly leaving blank areas around it) or over "fill" the area (so that it's smallest dimension fits the largest dimension of the area).
Fit & Fill
Basically, I work with scale factors
This returns the scaling factor for a particular size. I use this to make decisions about which factor I want to use based which algorithm I need
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;
}
It's used by these two methods. They simply take two Dimensions. The original and the target.
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 getScaleFactorToFill(Dimension masterSize, Dimension targetSize) {
double dScaleWidth = getScaleFactor(masterSize.width, targetSize.width);
double dScaleHeight = getScaleFactor(masterSize.height, targetSize.height);
double dScale = Math.max(dScaleHeight, dScaleWidth);
return dScale;
}
It's relatively simple to pass an image into (either directly or via a support method). So for example, you could call this from within your paint method
double factor getScaledFactorToFit(new Dimension(image.getWidth(), image.getHeight()), getSize());
int scaledWidth = image.getWidth() * scale;
int scaledHeight *= image.getWidth() * scale;
This will automatically take care of the aspect ratio for you ;)
UPDATED with expanded example
public 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;
}
public 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;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
double scaleFactor = Math.min(1d, getScaleFactorToFit(new Dimension(image.getWidth(), image.getHeight()), getSize()));
int scaleWidth = (int) Math.round(image.getWidth() * scaleFactor);
int scaleHeight = (int) Math.round(image.getHeight() * scaleFactor);
Image scaled = image.getScaledInstance(scaleWidth, scaleHeight, Image.SCALE_SMOOTH);
int width = getWidth() - 1;
int height = getHeight() - 1;
int x = (width - scaled.getWidth(this)) / 2;
int y = (height - scaled.getHeight(this)) / 2;
g.drawImage(scaled, x, y, this);
}
Try something like this:
import java.awt.*;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class SG2B2 {
JFrame frame;
public static void main(String[] args) {
SG2B2 gui = new SG2B2();
gui.createUI();
}
public void createUI() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MyDrawPanel drawPanel = new MyDrawPanel();
frame.getContentPane().add(BorderLayout.CENTER, drawPanel);
frame.setSize(300, 400);
frame.setVisible(true);
}
class MyDrawPanel extends JPanel {
Image image;
private final String pic = "Logo.jpg";
public MyDrawPanel() {
image = new ImageIcon(pic).getImage();
image = scaleImage(image);
}
#Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(image, 0, 0, this);
}
private Image scaleImage(Image rawImage) {
Image scaledImage = null;
System.out.println("Scaling");
try {
int rawImageWidth = rawImage.getWidth(this);
int rawImageHeight = rawImage.getHeight(this);
int paneWidth = (int) getWidth();
int paneHeight = (int) getHeight();
System.out.println("Image W = " + rawImageWidth
+ ", H = " + rawImageHeight
+ "; Pane W = " + paneWidth
+ ", H = " + paneHeight);
// preserve the original ratio
float widthRatio = (float) rawImageWidth / (float) paneWidth;
float heightRatio = (float) rawImageHeight / (float) paneHeight;
int widthFactor = -1;
int heightFactor = -1;
if ((widthRatio > heightRatio) && (widthRatio > 1.0)) {
widthFactor = paneWidth;
} else if ((heightRatio > widthRatio) && (heightRatio > 1.0)) {
heightFactor = paneHeight;
}
System.out.println("widthRatio = "
+ String.format("%.3f", widthRatio)
+ ", heightRatio = "
+ String.format("%.3f", heightRatio));
System.out.println("widthFactor = " + widthFactor
+ ", heightFactor = " + heightFactor);
if ((widthFactor < 0) && (heightFactor < 0)) {
scaledImage = rawImage;
} else {
scaledImage = rawImage.getScaledInstance(widthFactor, heightFactor,
Image.SCALE_SMOOTH);
// load the new image, 'getScaledInstance' loads asynchronously
MediaTracker tracker = new MediaTracker(this);
tracker.addImage(scaledImage, 0);
tracker.waitForID(0);
}
} catch (InterruptedException ie) {
System.err.println("load interrupt: " + ie.getMessage());
} catch (Exception e) {
e.printStackTrace();
}
return (scaledImage);
}
}
}
which will ultimately scale the image to the JPanel's size by using getScaledInstance(int width, int height, ImageObserver io)
For anyone interested ammending the PaintComponent method by MadProgrammer as follows allows much quicker display update
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
super.paintComponent(g);
double scaleFactor = Math.min(1d, getScaleFactorToFit(new Dimension(image.getWidth(), image.getHeight()), getSize()));
int scaleWidth = (int) Math.round(image.getWidth() * scaleFactor);
int scaleHeight = (int) Math.round(image.getHeight() * scaleFactor);
//Image scaled = image.getScaledInstance(scaleWidth, scaleHeight, Image.SCALE_SMOOTH);
int width = getWidth() - 1;
int height = getHeight() - 1;
int x = (width - scaleWidth) / 2;
int y = (height - scaleHeight) / 2;
g2d.drawImage(image, x, y, scaleWidth, scaleHeight, this);
}
I came up with this solution:
public class ImageLabel extends JPanel {
private Image image = null;
public void setImage(Image img) {
image = img;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (image != null) {
int imgWidth, imgHeight;
double contRatio = (double) getWidth() / (double) getHeight();
double imgRatio = (double) image.getWidth(this) / (double) image.getHeight(this);
//width limited
if(contRatio < imgRatio){
imgWidth = getWidth();
imgHeight = (int) (getWidth() / imgRatio);
//height limited
}else{
imgWidth = (int) (getHeight() * imgRatio);
imgHeight = getHeight();
}
//to center
int x = (int) (((double) getWidth() / 2) - ((double) imgWidth / 2));
int y = (int) (((double) getHeight()/ 2) - ((double) imgHeight / 2));
g.drawImage(image, x, y, imgWidth, imgHeight, this);
}
}
}

dynamic re-size of a drawing area

I wrote a program that prints coordinates from GPS onto the screen as a scatter points.
here is the code:
class POSCanvas extends JPanel{
int cnt = 1;
private static final long serialVersionUID = 1L;
List<Vector2D> pnts = new ArrayList<Vector2D>();
public void getNewPoint (double xnew, double ynew){
Vector2D pnt = new Vector2D(cnt*xnew, cnt*ynew);
pnts.add(pnt);
cnt++;
}
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
if (!pnts.isEmpty()){
super.paintComponent(g);
g2.setColor(Color.white);
Dimension size = getSize();
Insets insets = getInsets();
int w = size.width - insets.left - insets.right;
int h = size.height - insets.top - insets.bottom;
for (int i = 0; i < pnts.size(); i++ ){
Ellipse2D.Double OV = new Ellipse2D.Double(pnts.get(i).getX() % w, size.height - pnts.get(i).getY() % h, 5, 5);
g2.draw(OV);
repaint();
}
}
}
}
the problem with the drawing is that when the coordinates are bigger then the panel size the drawing makes no sense, the plotting starts from the left side again. here is an example:
I'd like to dynamically re-size the panel so the coordinates will always stay with its boundaries. How can I do that?

Save JPanel as Image HD Quality

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

Image background on JFrame [duplicate]

I have a JPanel with a painted background image and a layout manager holding other smaller images, all of this inside a JFrame. The background image is pretty big and I want to be able to have it maintain its aspect ratio whether its on a big or small monitor.
Eventually, I want to be able to have my LayoutManager and the smaller images in its cells "glued" to the background picture.
I looked around for resources and it seems that many examples use a BufferedImage but I am not; will this pose a problem? I'll post my code below for painting the image, If I lack any information please let me know.
public class MonitorPanel extends JPanel {
Image img;
public MonitorPanel() throws MalformedURLException {
//add components
try {
img = ImageIO.read(new File("src/customer_vlans.jpg"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
protected void paintComponent(Graphics g)
{
//paint background image
super.paintComponent(g);
//g.drawImage(img, 0, 0, getWidth(), getHeight(), this);
g.drawImage(img, 0, 0, this);
}
}
EDIT: I should mention that I know the aspect ratio formula:
original height / original width x new width = new height
However, I do not know how to use that correctly to my advantage.
Well, the quickest and easiest solution is to use Image.getScaledInstance
g.drawImage(img.getScaledInstance(newWidth, -1, Image. SCALE_SMOOTH), x, y, this);
If your wondering about the negative number, the java docs say:
If either width or height is a negative number then a value is
substituted to maintain the aspect ratio of the original image
dimensions. If both width and height are negative, then the original
image dimensions are used.
UPDATE
Just as a side note (my Google was playing up).
getScaledInstance is neither the fastest or highest quality approach, but it is the easiest.
Take a read through The Perils of Image.getScaledInstance for some more ideas
UPDATE
Scaling an image to fit an area is slightly more complicated then simply scaling the aspect ratio. You have to make a choice over if you want the image to "fit" within the area (possibly leaving blank areas around it) or over "fill" the area (so that it's smallest dimension fits the largest dimension of the area).
Fit & Fill
Basically, I work with scale factors
This returns the scaling factor for a particular size. I use this to make decisions about which factor I want to use based which algorithm I need
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;
}
It's used by these two methods. They simply take two Dimensions. The original and the target.
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 getScaleFactorToFill(Dimension masterSize, Dimension targetSize) {
double dScaleWidth = getScaleFactor(masterSize.width, targetSize.width);
double dScaleHeight = getScaleFactor(masterSize.height, targetSize.height);
double dScale = Math.max(dScaleHeight, dScaleWidth);
return dScale;
}
It's relatively simple to pass an image into (either directly or via a support method). So for example, you could call this from within your paint method
double factor getScaledFactorToFit(new Dimension(image.getWidth(), image.getHeight()), getSize());
int scaledWidth = image.getWidth() * scale;
int scaledHeight *= image.getWidth() * scale;
This will automatically take care of the aspect ratio for you ;)
UPDATED with expanded example
public 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;
}
public 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;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
double scaleFactor = Math.min(1d, getScaleFactorToFit(new Dimension(image.getWidth(), image.getHeight()), getSize()));
int scaleWidth = (int) Math.round(image.getWidth() * scaleFactor);
int scaleHeight = (int) Math.round(image.getHeight() * scaleFactor);
Image scaled = image.getScaledInstance(scaleWidth, scaleHeight, Image.SCALE_SMOOTH);
int width = getWidth() - 1;
int height = getHeight() - 1;
int x = (width - scaled.getWidth(this)) / 2;
int y = (height - scaled.getHeight(this)) / 2;
g.drawImage(scaled, x, y, this);
}
Try something like this:
import java.awt.*;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class SG2B2 {
JFrame frame;
public static void main(String[] args) {
SG2B2 gui = new SG2B2();
gui.createUI();
}
public void createUI() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MyDrawPanel drawPanel = new MyDrawPanel();
frame.getContentPane().add(BorderLayout.CENTER, drawPanel);
frame.setSize(300, 400);
frame.setVisible(true);
}
class MyDrawPanel extends JPanel {
Image image;
private final String pic = "Logo.jpg";
public MyDrawPanel() {
image = new ImageIcon(pic).getImage();
image = scaleImage(image);
}
#Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(image, 0, 0, this);
}
private Image scaleImage(Image rawImage) {
Image scaledImage = null;
System.out.println("Scaling");
try {
int rawImageWidth = rawImage.getWidth(this);
int rawImageHeight = rawImage.getHeight(this);
int paneWidth = (int) getWidth();
int paneHeight = (int) getHeight();
System.out.println("Image W = " + rawImageWidth
+ ", H = " + rawImageHeight
+ "; Pane W = " + paneWidth
+ ", H = " + paneHeight);
// preserve the original ratio
float widthRatio = (float) rawImageWidth / (float) paneWidth;
float heightRatio = (float) rawImageHeight / (float) paneHeight;
int widthFactor = -1;
int heightFactor = -1;
if ((widthRatio > heightRatio) && (widthRatio > 1.0)) {
widthFactor = paneWidth;
} else if ((heightRatio > widthRatio) && (heightRatio > 1.0)) {
heightFactor = paneHeight;
}
System.out.println("widthRatio = "
+ String.format("%.3f", widthRatio)
+ ", heightRatio = "
+ String.format("%.3f", heightRatio));
System.out.println("widthFactor = " + widthFactor
+ ", heightFactor = " + heightFactor);
if ((widthFactor < 0) && (heightFactor < 0)) {
scaledImage = rawImage;
} else {
scaledImage = rawImage.getScaledInstance(widthFactor, heightFactor,
Image.SCALE_SMOOTH);
// load the new image, 'getScaledInstance' loads asynchronously
MediaTracker tracker = new MediaTracker(this);
tracker.addImage(scaledImage, 0);
tracker.waitForID(0);
}
} catch (InterruptedException ie) {
System.err.println("load interrupt: " + ie.getMessage());
} catch (Exception e) {
e.printStackTrace();
}
return (scaledImage);
}
}
}
which will ultimately scale the image to the JPanel's size by using getScaledInstance(int width, int height, ImageObserver io)
For anyone interested ammending the PaintComponent method by MadProgrammer as follows allows much quicker display update
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
super.paintComponent(g);
double scaleFactor = Math.min(1d, getScaleFactorToFit(new Dimension(image.getWidth(), image.getHeight()), getSize()));
int scaleWidth = (int) Math.round(image.getWidth() * scaleFactor);
int scaleHeight = (int) Math.round(image.getHeight() * scaleFactor);
//Image scaled = image.getScaledInstance(scaleWidth, scaleHeight, Image.SCALE_SMOOTH);
int width = getWidth() - 1;
int height = getHeight() - 1;
int x = (width - scaleWidth) / 2;
int y = (height - scaleHeight) / 2;
g2d.drawImage(image, x, y, scaleWidth, scaleHeight, this);
}
I came up with this solution:
public class ImageLabel extends JPanel {
private Image image = null;
public void setImage(Image img) {
image = img;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (image != null) {
int imgWidth, imgHeight;
double contRatio = (double) getWidth() / (double) getHeight();
double imgRatio = (double) image.getWidth(this) / (double) image.getHeight(this);
//width limited
if(contRatio < imgRatio){
imgWidth = getWidth();
imgHeight = (int) (getWidth() / imgRatio);
//height limited
}else{
imgWidth = (int) (getHeight() * imgRatio);
imgHeight = getHeight();
}
//to center
int x = (int) (((double) getWidth() / 2) - ((double) imgWidth / 2));
int y = (int) (((double) getHeight()/ 2) - ((double) imgHeight / 2));
g.drawImage(image, x, y, imgWidth, imgHeight, this);
}
}
}

How to add mouse listener to a JPanel image?

I have painted a BufferedImage on a JPanel with the following code.
protected void paintComponent(Graphics g) {
if (image != null) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
double x = (getWidth() - scale * imageWidth) / 2;
double y = (getHeight() - scale * imageHeight) / 2;
AffineTransform at = AffineTransform.getTranslateInstance(x, y);
at.scale(scale, scale);
g2.drawRenderedImage(image, at);
}
}
How can I add a mouse click listener to that image? Additionally, I want to get the click coordinate of the image, not the JPanel.
Add a MouseListener to the pane as per normal.
In the mouseClicked method check to see if the Point is within the rectangle of the image...
public void mouseClicked(MouseEvent evt) {
if (image != null) {
double width = scale * imageWidth;
double height = scale * imageHeight;
double x = (getWidth() - width) / 2;
double y = (getHeight() - height) / 2;
Rectangle2D.Double bounds = new Rectangle2D.Double(x, y, width, height);
if (bounds.contains(evt.getPoint()) {
// You clicked me...
}
}
}

Categories

Resources