0

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.

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
Billy
  • 357
  • 1
  • 5
  • 14
  • 3
    You should not be creating new instances of Card, CardImage, or BufferedImage in your paintComponent() method.thaty method has the potential of being called hundreds of times - whenever the system thinks it needs a refresh. – FredK Feb 01 '17 at 20:27
  • 1
    The likely cause of your immediate problem is the HandPanel isn't returning enough sizing hints to allow the layout manager to lay it out, its defaults preferred size is 0x0 – MadProgrammer Feb 01 '17 at 20:42
  • 3
    *Shithead/src/cards* - you should never reference "src", it won't exist once the app is built, nor should you be referencing embedded resources as via File – MadProgrammer Feb 01 '17 at 20:47
  • 1
    @Marco13 A `JPanel`'s preferred size is `0x0`, the `FlowLayout` adds in some additional spacing of it's own. Please, don't recommend using `setPreferredSize`, this is a bad, short term, solution which just causes more issues than it solves – MadProgrammer Feb 01 '17 at 21:17
  • @MadProgrammer Yeah, `setPreferredSize` is usually not the best solution. But often, you see `getPreferredSize` methods that are overridden, but not implemented properly (which may be even worse). And... no offense, but this also refers to your proposed solution. (I deleted my comment, but see http://stackoverflow.com/a/23503703/3182664 for what I meant with `getPreferredSize`) (EDIT: But I'm **not** the downvoter, of course!) – Marco13 Feb 02 '17 at 02:17
  • 1
    @Marco13 You don't always want to maintain the functionality of getPreferredSize, as the component it self should have last say - it requires context, but in over 16 years of professional swing development, I've found setting the preferred size causes more issues then overriding get preferred size :P – MadProgrammer Feb 02 '17 at 02:22
  • @MadProgrammer Sure, we don't have to argue about that. But I think that **if** someone calls `handPanel.setPreferredSize(someSize)` (regardless of whether it is "good" to do this), then any subsequent call to `getPreferredSize` should return `someSize`, because that's the *specified* behavior of these methods (and in ~16 years of development, I've found that disregarding specifications can cause lots of issues as well ;-) ) – Marco13 Feb 02 '17 at 12:13

1 Answers1

3

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

Community
  • 1
  • 1
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • 2
    Since no new down or close votes have been applied to the question, then the question must be acceptable, this means the downvote is related to the answer directly. I what ways does the answer to answer the OPs question? What has been missed? What could be done to improve the question? Since you've failed to provide an alternative answer, I can only assume you don't know how to resolve the issue yourself which begs the question of how'd you know that mine doesn't? At this point, I can only assume you have a personal issue with me answer questions and trying to help people :( – MadProgrammer Feb 02 '17 at 00:31
  • @Billy Glad it could help ;) – MadProgrammer Feb 02 '17 at 21:34