Search code examples
javaswingbufferedimagepaintcomponent

Only drawing part of bufferedimage


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:

Image of frame and small section of card

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.


Solution

  • 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