This is a problem that disturbs me for few hours now and I\'m not able to find a solution by myself...
I\'ve found similar topics all around the net, but I couldn\'t fin
Personally, I would either resize it before applying it to the label or use a JPanel
to perform the painting. JLabel
has to much functionality dragging around with it.
Case in point, the problem you're having is you're actually using setIcon
to set the image, but using paintComponent
to paint another (the initial) image over the top of it
Your custom label takes a ImageIcon
as a inital parameter and paints it as such...
private static class MyJLabel extends JLabel {
private ImageIcon img = null;
public MyJLabel(ImageIcon img) {
super();
this.img = img;
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(img.getImage(), 0, 0, getWidth(), getHeight(), this);
}
}
You initialise it as such...
label = new MyJLabel(new ImageIcon(img));
It should be noted that if you used the Icon
support of JLabel
, this...
label.setPreferredSize(dims);
Would be irrelevant as the JLabel
would use the icon size to determine it's preferred size...but any way...
Then you update the icon using this..
img = image;
if (label != null) {
label.setIcon(new ImageIcon(img));
label.repaint();
}
It should be pointed out, that based on your example, this is actually been called outside of the EDT, which is dangerous and could lead to a dirty paint
But setIcon
never changes the value of img
within MyLabel
, so when your paintComponent
method is called, you are actually painting over the icon you have supplied in the update...
// Paint the new Icon
super.paintComponent(g);
// Paint the old/initial image...
g.drawImage(img.getImage(), 0, 0, getWidth(), getHeight(), this);
Updated
Personally, what I would do is create a custom component, using something like a JPanel
and scale the original image based on the current size of the panel, for example...
Now, normally, when performing image scaling I prefer to use a divide and conqure approach as demonstrated in Java: maintaining aspect ratio of JPanel background image, but for this example, I've simply used and AffineTransform
for simplicity sake
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.HeadlessException;
import java.awt.Image;
import java.awt.geom.AffineTransform;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ScalableImageExample {
public static void main(String[] args) {
new ScalableImageExample();
}
public ScalableImageExample() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
try {
ResizableImagePane pane = new ResizableImagePane();
pane.setImage(...);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(pane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException exp) {
exp.printStackTrace();
}
}
});
}
public class ResizableImagePane extends JPanel {
private Image img;
public ResizableImagePane() {
}
public void setImage(Image value) {
if (img != value) {
Image old = img;
this.img = value;
firePropertyChange("image", old, img);
revalidate();
repaint();
}
}
public Image getImage() {
return img;
}
@Override
public Dimension getPreferredSize() {
return img == null ? new Dimension(200, 200) : new Dimension(img.getWidth(this), img.getHeight(this));
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
Graphics2D g2d = (Graphics2D) g.create();
int width = getWidth();
int height = getHeight();
double scaleFactor = getScaleFactorToFit(new Dimension(img.getWidth(this), img.getHeight(this)), getSize());
int x = (int)((width - (img.getWidth(this) * scaleFactor)) / 2);
int y = (int)((height - (img.getHeight(this) * scaleFactor)) / 2);
AffineTransform at = new AffineTransform();
at.translate(x, y);
at.scale(scaleFactor, scaleFactor);
g2d.setTransform(at);
g2d.drawImage(img, 0, 0, this);
g2d.dispose();
}
}
public double getScaleFactor(int iMasterSize, int iTargetSize) {
return (double) iTargetSize / (double) iMasterSize;
}
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;
}
}
}
This is what I came up with:
private static class MyJPanel extends JPanel {
private Image img = null;
public MyJPanel() {}
public void setImage(Image value) {
if (img != value) {
Image old = img;
this.img = value;
firePropertyChange("image", old, img);
revalidate();
repaint();
}
}
public Image getImage() {
return img;
}
@Override
public Dimension getPreferredSize() {
return img == null ? new Dimension(200, 200) : new Dimension(img.getWidth(this), img.getHeight(this));
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
Graphics2D g2d = (Graphics2D) g.create();
int width = getWidth();
int height = getHeight();
double scaleFactor = getScaleFactorToFit(new Dimension(img.getWidth(this), img.getHeight(this)), getSize());
int x = (int) ((width - (img.getWidth(this) * scaleFactor)) / 2);
int y = (int) ((height - (img.getHeight(this) * scaleFactor)) / 2);
AffineTransform at = new AffineTransform();
at.translate(x, y);
at.scale(scaleFactor, scaleFactor);
g2d.setTransform(at);
g2d.drawImage(img, 0, 0, this);
g2d.dispose();
}
}
public double getScaleFactor(int iMasterSize, int iTargetSize) {
return (double) iTargetSize / (double) iMasterSize;
}
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;
}
}
private static class MyJFrame extends JFrame implements Runnable {
private BufferedImage img = null;
private MyJPanel panel = null;
public MyJFrame(BufferedImage image, String title) {
super(title);
img = image;
}
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {}
panel = new MyJPanel();
panel.setImage(img);
setLayout(new BorderLayout());
add(BorderLayout.CENTER, panel);
setLocation(200, 200);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
pack();
setVisible(true);
}
public void changeImage(BufferedImage image) {
if ((panel != null) && (panel.getImage() != image)) panel.setImage(image);
}
}
It's pretty much straightforward copy-paste from example that @MadProgrammer provided.
The only thing left is EDT
usage, which is rather magic for me.
I'm still invoking this code using dirty way:
MyJFrame mjf = null;
javax.swing.SwingUtilities.invokeLater(mjf = new MyJFrame(buffer, "RDP"));
...
mjf.changeImage(buffer);
My question is: how do I use changeImage method with EDT
?