I'm trying to get some images to scale for a chess program I'm writing. I tried writing up a small program just to get a feel for scaling images and I ran into a problem with the image displaying. Basically the image will only display properly if that ImageIcon (icon) is there and it is above pawn in the code. If it doesn't exist, it is below pawn in the code, or the image sources are different pawn displays as a black square. If I don't try and scale pawn then the whole thing works just fine regardless of whether icon is there or not. This whole thing just really perplexes me because the only association pawn and icon have is that they share a source image. Can anyone shed some light on this?
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class resizeImg extends JFrame{
public void generateGui(){
JPanel mainPanel=new JPanel();
getContentPane().add(mainPanel);
JPanel main=new JPanel();
main.setBorder(BorderFactory.createLineBorder(new Color(0,0,0)));
main.setSize(400,400);
mainPanel.add(main);
mainPanel.setLayout(null);
ImageIcon icon=new ImageIcon(Toolkit.getDefaultToolkit().getImage("resources/pawn.jpg"));
//if you remove the above ImageIcon the image just displays as a black square
Image pawn=scale(Toolkit.getDefaultToolkit().getImage("resources/pawn.jpg"));
JLabel pawnContainer=new JLabel(new ImageIcon(pawn));
main.add(pawnContainer);
setSize(400,400);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public BufferedImage scale(Image i){
BufferedImage resized=new BufferedImage(50,50,BufferedImage.TYPE_INT_RGB);
Graphics2D g=resized.createGraphics();
g.drawImage(i,0,0,50,50,null);
g.dispose();
return resized;
}
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable(){
public void run(){
resizeImg imgObject=new resizeImg();
imgObject.generateGui();
imgObject.setVisible(true);
}
});
}
}
The original image loading code was designed to load to images over slow networks, so it uses a background thread to load the image. This means that when Toolkit.getImage returns, the image may not have actually begin loaded.
When you included the ImageIcon call, ImageIcon is using a MediaTracker to waitFor the image to become loaded. Because of the caching mechanism of Toolkit.getImage, repeated calls for the same image can use the cached image instead.
This is, also, why it's important to use an ImageObserver when painting images, but it may not help you in this case.
Instead, use ImageIO
public class Scale extends JFrame {
public void generateGui() {
JPanel main = new JPanel();
main.setBorder(BorderFactory.createLineBorder(new Color(0, 0, 0)));
main.setLayout(new BorderLayout());
try {
BufferedImage image = ImageIO.read(new File("path/to/image"));
Image pawn = scale(image);
JLabel pawnContainer = new JLabel(new ImageIcon(pawn));
main.add(pawnContainer);
} catch (IOException exp) {
exp.printStackTrace();
}
add(main);
setSize(400, 400);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public BufferedImage scale(Image i) {
BufferedImage resized = new BufferedImage(50, 50, BufferedImage.TYPE_INT_RGB);
Graphics2D g = resized.createGraphics();
g.drawImage(i, 0, 0, 50, 50, this);
g.dispose();
return resized;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
Scale imgObject = new Scale();
imgObject.generateGui();
imgObject.setVisible(true);
}
});
}
}
Related
when I try to render a png image in Java Swing, it is cut off/zoomed on all sides. Here is my code:
this.setSize(500, 500);
this.setResizable(false);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
this.getGraphics().drawImage(getImage("IMAGE PATH"), 0, 0, this);
And this is my getImage function
public static Image getImage(String path) {
File file = new File(path);
try {
return ImageIO.read(file);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
The image I want to render is 500x500, just like my frame, but it still comes out wrong. This also applies to when I use the function like this:
Image image = getImage("Image Path");
assert image != null;
JLabel picLabel = new JLabel(new ImageIcon(image));
Here is one of the images I am trying to render:
Here is the outcome:
It is kind of hard to tell from this example, but the ground should be lower and the trees higher.
Don't set the size of the frame
If your image is 500x500 then how can you expect the image size and frame size to be the same when the frame also has a title bar and borders?
Don't do custom painting`
Instead:
create a JLabel with an ImageIcon.
Add the label to the frame
invoke pack() before making the frame visible.
All the components will get the proper size.
Read the Swing tutorial on How to Use Labels for more information and examples.
Even though #camikr found a solution, I found a better one for my needs.
What I did, was I created a new JPanel and ran setPreferredSize with the dimensions of my JFrame (500x500). Then I overrided the paintComonent function to render the image using the Graphics2D class.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.Objects;
public class MyPanel extends JPanel {
public Image loadedImage = getImage("Image");
public LoadState() {
setPreferredSize(new Dimension(500,500));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g.create();
g2.drawImage(loadedImage, 0, 0, null);
g2.dispose();
}
}
EDIT: Fix what #Harald K suggested
I noticed that every JFrame I create doesnt show a few pixels - ~10px at the right side.
I dont know why that happens, but it could be very problematic for my game if I dont fix that.
Here is the code with which I am experimenting:
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class Resizer {
int width = 500;
int height = 500;
JFrame frame;
JLabel screen;
BufferedImage image;
ImageIcon icon;
public static void main(String[] args) {
Resizer r = new Resizer();
r.runCode();
}
private void runCode() {
createFrame();
javax.swing.Timer t = new javax.swing.Timer(1000/60, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
checkResize();
drawSomething();
}
});
t.start();
}
private void createFrame() {
frame = new JFrame("Resize Experiment");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
screen = new JLabel();
screen.setSize(width, height);
image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
icon = new ImageIcon(image);
screen.setIcon(icon);
frame.add(screen);
frame.pack();
frame.setVisible(true);
}
private void checkResize() {
if (frame.getWidth() != width || frame.getHeight() != height) {
screen.setSize(frame.getWidth(), frame.getHeight());
image = new BufferedImage(frame.getWidth(), frame.getHeight(), BufferedImage.TYPE_INT_ARGB);
frame.pack();
width = frame.getWidth();
height = frame.getHeight();
}
}
private void drawSomething() {
Graphics2D pen = image.createGraphics();
pen.setColor(Color.BLACK);
pen.fillRect(0, 0, width, height);
pen.setColor(Color.RED);
pen.drawLine(width, height/2, width-10, height/2);
addImage();
}
private void addImage() {
icon = new ImageIcon(image);
screen.setIcon(icon);
}
}
I noticed it because of the following statement:
pen.drawLine(width, height/2, width-10, height/2);
It should draw a line from the right side of the JFrame to a place 10 pixel further to the left. In reality, I can't see any line at all. It appears once I raise the distance value.
My question is: Why does this happen, and how can I fix this?
I noticed that every JFrame I create doesnt show a few pixels - ~10px at the right side
That is because you are attempting to make your BufferedImage the size of the frame.
The problem is the frame contains "borders". Your image can only be painted inside the borders.
You should NOT be attempting to make the BufferedImage the size of the frame.
As suggested above the custom painting should be done in the paintComponent() method of a JPanel. Then you add the panel to the frame. Inside the paintComponent() method you can use the getWidth() and getHeight() methods of the panel to make sure you paint on the complete area.
I really like how with Graphics2D things aren't stuffed in one method but can be split up
The custom painting done in the paintComponent() method is done with a Graphics2D object, so you can do anything you want. All Swing components are painted in the paintComponent() method.
i am setting frame's background image when i run program my other components are invisible only image is visible in frame
class ImagePanel extends JComponent {
private Image image;
public ImagePanel(Image image) {
this.image = image;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, this);
}
In the main class I call the above class as shown below:
BufferedImage myImage = ImageIO.read(new File("cal.jpg"));
frame.setContentPane(new ImagePanel(myImage));
You have this code:
BufferedImage myImage = ImageIO.read(new File("cal.jpg"));
frame.setContentPane(new ImagePanel(myImage));
but you appear to be creating the ImagePanel instance inline, and don't appear to be adding any components to this ImagePanel instance, so I'm not surprised that you're not seeing any components. You also don't seem to be adding any components to it in the ImagePanel constructor.
Consider adding components to the ImagePanel class within its constructor, or in the class that uses it, create an ImagePanel instance, assign it to a variable, add components to it, and then place it into the JFrame's contentPane.
Side recommendations:
Consider getting your image as a Jar resource and not as a File, since likely you will Jar the classes at some point, and if you continue using File, your image might not be reachable.
Make sure to give your ImagePanel a decent layout manager. I believe that JComponents use null layouts by default, something that you don't want to use.
For example, this worked for me:
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Image;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
public class TestImagePanel {
private static void createAndShowGui() {
String resource = "/imgFolder/PlanetEarth.jpg";
Image image = null;
try {
image = ImageIO.read(TestImagePanel.class.getResource(resource));
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
ImagePanel mainPanel = new ImagePanel(image);
mainPanel.setLayout(new FlowLayout());
mainPanel.add(new JButton("Fubars Rule!"));
JFrame frame = new JFrame("TestImagePanel");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
#SuppressWarnings("serial")
class ImagePanel extends JComponent {
private Image image;
public ImagePanel(Image image) {
this.image = image;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, this);
}
#Override
public Dimension getPreferredSize() {
Dimension superSize = super.getPreferredSize();
int w = image == null ? superSize.width : Math.max(superSize.width, image.getWidth(null));
int h = image == null ? superSize.height : Math.max(superSize.height, image.getHeight(null));
Dimension d = new Dimension(w, h);
return d;
}
}
and showed this GUI:
I am trying to paint an image onto a panel, that is contained by a frame.
Let us say I have a 320 x 480 image.
When i try to create a frame with size 320x480 and add the panel into it, I encounter a problem.
In different operating systems, the JFrame of 320x480 is of different sizes due to title bar.
Thus my correct fit image in windows XP will not be properly painted in Windows8 or Ubuntu.
A grey patch is visible because the image was not properly placed.
I tried overriding paint method and using ImageIcon.
Please do offer a solution.
TIA
Code Snippet
CLASS PA CONTENTS
setPreferredSize(new Dimension(500,500));
.
.
JLabel image= new JLabel();
ImageIcon background = new ImageIcon(getClass().getClassLoader().getResource("Flower.jpg"));
image.setBounds(0, 0, 500, 500);
image.setIcon(background);
this.add(image); //where "this" is extending from JPanel
CLASS PB CONTENTS
frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
inserting(frame.getContentPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setResizable(false);
private void inserting(Container pane)
{
cardPanel=new JPanel();
CardLayout cards=new CardLayout();
cardPanel.setLayout(cards);
PA home= new PA();
cardPanel.add(home,"homeScreen");
pane.add(cardPanel);
}
Don't call setSize at all, call pack (as VGR stated in his comment). pack will size your JFrame based on size's of component's within it, and gaps between those component's.
Now.. issue you will encounter is that your JFrame will be small at startup. So override getPreferredSize method for your JPanel to return dimensions of your image:
public void getPreferredSize() {
return new Dimension(image.getWidth(), image.getHeight());
}
Now your image will fit perfectly and your application will be fully OS independent.
And also, do not override paint method. Instead, override paintComponent.
Here is a small demo I made in cases like yours:
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public class Drawing {
JFrame frame = new JFrame();
public Drawing() {
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(new Panel());
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Drawing();
}
});
}
class Panel extends JPanel {
BufferedImage image = null;
Panel() {
try {
image = ImageIO.read(new File("path-to-your-image"));
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, this);
}
#Override
public Dimension getPreferredSize() {
// Panel will be sizes based on dimensions of image
return new Dimension(image.getWidth(), image.getHeight());
}
}
}
It seems to be the layout problem. The most obvious solution is to wrap you image panel into another container with proper layout, so your panel will always have the same size.
I have problem with displaying the image on JPanel when I rescaled the image according to the size of the JPanel. The image did not appear.
public class createGUII extends JFrame{
String [] background = {"c1.jpg","c2.jpg","c3.jpg","c4.jpg"};
ArrayList<String> bgPicturesFiles = new ArrayList<String>(Arrays.asList(background));
JPanel panel;
ImagePanel imgBg;
public createGUII(){
GridBagLayout m = new GridBagLayout();
Container c = getContentPane();
c.setLayout (m);
GridBagConstraints con = new GridBagConstraints();
//Panel for background
panel = new JPanel();
panel.setSize(600, 600);
con = new GridBagConstraints();
con.anchor=GridBagConstraints.CENTER;
con.gridy = 1; con.gridx = 0;
con.gridwidth = 1; con.gridheight = 1;
m.setConstraints(panel, con);
c.add(panel);
//randomized the image files
Random r = new Random();
int random = r.nextInt(bgPicturesFiles.size());
//rescale the image according to the size of the JPanel
imgBg = new ImagePanel(new ImageIcon(bgPicturesFiles.get(random)).getImage().getScaledInstance(panel.getHeight(), panel.getWidth(),Image.SCALE_SMOOTH));
panel.add(imgBg);
setResizable(false);
setVisible(true);
setExtendedState(getExtendedState()|JFrame.MAXIMIZED_BOTH);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new createGUII();
}
});
}
}
class ImagePanel extends JPanel {
private Image img;
public ImagePanel(String img) {
this(new ImageIcon(img).getImage());
}
public ImagePanel(Image img) {
this.img = img;
Dimension size = new Dimension(img.getWidth(null), img.getHeight(null));
setPreferredSize(size);
setMinimumSize(size);
setMaximumSize(size);
setSize(size);
setLayout(null);
}
public void paintComponent(Graphics g) {
g.drawImage(img, 0, 0, null);
}
}
Have a look at this: JPanel background image, JPanel with background image, with other panels overlayed
The second link mentions that you have to do some custom painting for scaling. This is a problem. I wouldn't scale the image every single time in the paintComponent method, but do it once if the width and height have been changed since the last call, and in that case, recreate a BufferedImage containing the image which you blit every single time before calling the superclass paintComponent, scaled up to the right size (use something like Image scaling does not work when original image height & width is smaller the scaling height & width). I can see an issue where it might try to fill the panel with a colour when you call the superclass paintComponent method, but you'll have to experiment.
The problem is that in Java images get loaded asynchronously. There are several issues with the above code because of that:
The image doesn't get loaded, so it's dimensions are (-1, -1). Thus, ImagePanel's size is invalid
Even if the dimensions get set manually (i.e. changing it to new Dimension(600, 600)), the image itself may not be loaded.
JFrame resizing is disabled. If you allow it, you would be able to get the image drawn with the above code by artificially making Swing load the image when the JFrame is resized
To ensure loading, add the following:
new ImageIcon(img).getImage();
after this.img = img; in ImagePanel's constructor.
Note that this is partially a hack - I'm not a GUI expert, but I get the idea the above could be written much better. Maybe somebody else might be able to shed more light.
Here are some links that might be helpful:
http://www.exampledepot.com/egs/java.awt.image/Image2Buf.html
http://webcache.googleusercontent.com/search?q=cache:Ho0L4KoL44AJ:java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html+java+getscaledinstance&cd=1&hl=en&ct=clnk&gl=us&client=ubuntu (yes, it's from Google cache, the original link doesn't work...)
Hope this helps.
I don't see where you're actually reading the image, as suggested in this example.
Addendum: I've added an example of scaling. See also Don't Use getScaledInstance() and The Perils of Image.getScaledInstance().
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.Image;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
/** #see http://stackoverflow.com/questions/4170463 */
public class LoadImage extends JPanel {
private Image image;
public LoadImage() {
super(new GridLayout());
try {
image = ImageIO.read(new File("image.jpg"));
} catch (IOException ex) {
ex.printStackTrace(System.err);
}
int w = image.getWidth(null) / 2;
int h = image.getHeight(null) / 2;
this.add(new JLabel(new ImageIcon(
image.getScaledInstance(w, h, Image.SCALE_SMOOTH))));
}
private void display() {
JFrame f = new JFrame("LoadImage");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new LoadImage().display();
}
});
}
}
Here is a link that should help. However I found a way that better suited the problem I had this was what I found, and the following is what I took from that.
I hope this helps.
Container con = getContentPane();
final String backgroundPath = "C:\\background.jpg";
ImageIcon imh = new ImageIcon(backgroundPath);
setSize(imh.getIconWidth(), imh.getIconHeight());
JPanel pnlBackground = new JPanel()
{
public void paintComponent(Graphics g)
{
Image img = new ImageIcon(backgroundPath).getImage();
g.drawImage(img, 0, 0, null);
}
};
con.add(pnlBackground);
pnlBackground.setBounds(0, 0, imh.getIconWidth(), imh.getIconHeight());