I'm designing a card game and I want to draw (paint) a card onto a panel. When I paint, though, only a tiny portion of the image is showing. You can sort of see it in this screenshot:
I wrote a wrapper class (CardImage) for a BufferedImage:
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
public class CardImage {
BufferedImage img = null;
public CardImage() {
}
public BufferedImage setImage(Card c) {
try {
img = ImageIO.read(new File("/media/billy/HOME/workspace/Shithead/src/cards/" + toName(c)));
} catch(Exception e) {
System.out.println(e);
}
/*
int scale_factor = 8;
System.out.println(img.getHeight());
Image dimg = img.getScaledInstance((int)img.getWidth()/scale_factor, (int)img.getHeight()/scale_factor, Image.SCALE_SMOOTH);
Graphics g = img.createGraphics();
g.drawImage(dimg, 0, 0, null);
g.dispose();
*/
return img;
}
public String toName(Card c) {
String tmp = c.toString().replaceAll(" ", "_");
tmp = tmp.toLowerCase();
tmp = tmp + ".png";
return tmp;
}
}
and I have a HandPanel that extends JPanel in which I want to draw the CardImage BufferedImage:
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;
public class HandPanel extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
CardImage cardImage = new CardImage();
Card card = new Card(2,2);
BufferedImage img = cardImage.setImage(card);
System.out.println(img.getHeight(null) + " " + img.getWidth(null));
g.drawImage(img, 0, 0, null);
}
}
And I have a another class to contain the HandPanel:
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ShitGUI {
public static void main(String[] args) {
ShitGUI gui = new ShitGUI();
}
JFrame frame = new JFrame();
JPanel mainPanel = new JPanel();
public ShitGUI() {
mainPanel.setPreferredSize(new Dimension(500,500));
HandPanel pan = new HandPanel();
mainPanel.add(pan);
frame.add(mainPanel);
frame.pack();
frame.setVisible(true);
}
}
The images of the cards are fairly big (creating Card(2,2) is just a 2 of Hearts, and I know I acquire the path correctly).
Any help is appreciated. Thanks!
N.B. When I uncomment the section containing dimg, I obtain a scaled down red card (rather than black and white, not scaled image as I otherwise get). I understanding the scaling, but I don't understand why my approach is giving me a black and white image.
The probable immediate issue is the fact that HandPanel
does not provide any sizing hints to allow layout managers to determine the best size for the component, fallback on it's default preferred size of 0x0
I went back and modified you code slightly to make it a little more reusable.
img = ImageIO.read(new File("/media/billy/HOME/workspace/Shithead/src/cards/" + toName(c)));
is a bad idea.
You should never reference any path which contains src
, src
won't exist once your application is built. You should not reference embedded resources by File
, they aren't (or at least won't be when the application is build), instead, you need to use Class#getResource
.
public class CardImage {
BufferedImage img = null;
public CardImage(Card card) {
setImage(card);
}
public BufferedImage getImage() {
return img;
}
public BufferedImage setImage(Card c) {
try {
//img = ImageIO.read(new File("/media/billy/HOME/workspace/Shithead/src/cards/" + toName(c)));
img = ImageIO.read(getClass().getResource("/cards/" + toName(c)));
} catch (Exception e) {
System.out.println(e);
}
/*
int scale_factor = 8;
System.out.println(img.getHeight());
Image dimg = img.getScaledInstance((int)img.getWidth()/scale_factor, (int)img.getHeight()/scale_factor, Image.SCALE_SMOOTH);
Graphics g = img.createGraphics();
g.drawImage(dimg, 0, 0, null);
g.dispose();
*/
return img;
}
public String toName(Card c) {
String tmp = c.toString().replaceAll(" ", "_");
tmp = tmp.toLowerCase();
tmp = tmp + ".png";
return tmp;
}
}
I've modified the CardImage
class so it needs to be setup in advance, this allows you to cache the image and re-use it more efficiently. I'd also consider using a factory of some kind which, when passed a Card
could determine if it needs to generate a new instance of CardImage
or re-use a previously cached version, this would make it faster for your code to execute.
Again, I've modified the HandPanel
, it now requires you to pass a Card
to it and it uses this to make determinations about what information it will use to generate sizing hints
public class HandPanel extends JPanel {
CardImage cardImage;
public HandPanel(Card card) {
cardImage = new CardImage(card);
}
@Override
public Dimension getPreferredSize() {
BufferedImage img = cardImage.getImage();
return img == null ? super.getPreferredSize() : new Dimension(img.getWidth(), img.getHeight());
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
BufferedImage img = cardImage.getImage();
g.drawImage(img, 0, 0, this);
}
}
I'd also encourage you to take a look at Perials of getScaledInstance and Java: maintaining aspect ratio of JPanel background image for some alternatives