18

Is there a way to convert a JPanel (that has not yet been displayed) to a BufferedImage?

thanks,

Jeff

Jeff Storey
  • 56,312
  • 72
  • 233
  • 406
  • Well, I give up. I've given you two answers in your last two postings that you've complelely ignore. Good luck with future postings. – camickr Aug 29 '09 at 01:02
  • I appreciate the responses and I have not been ignoring them. On this post, ScreenImage wasn't exactly what I was looking for and this method of just painting onto other graphics seemed to be the right direction for me. On the dialog box post, it turned out to be a non-issue and I've been swamped so I haven't been replying to everything. I do appreciate your responses. – Jeff Storey Aug 29 '09 at 15:59

4 Answers4

35

From the BufferedImage you can create a graphics object, which you can use to call paint on the JPanel, something like:

public BufferedImage createImage(JPanel panel) {

    int w = panel.getWidth();
    int h = panel.getHeight();
    BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
    Graphics2D g = bi.createGraphics();
    panel.paint(g);
    g.dispose();
    return bi;
}

You may need to make sure you set the size of the panel first.

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
Tom
  • 43,583
  • 4
  • 41
  • 61
  • nice, thanks. is there a way i can figure out what the panel size should be (maybe its preferred size)? – Jeff Storey Aug 28 '09 at 20:49
  • I believe it's preferred size, or current size, as long as it has been rendered already. There are problems if it has not been rendered already - I cannot remember the exact specifics, but I remember running into similar properties when implementing a printing system. – aperkins Aug 28 '09 at 20:59
  • Yep, the preferred size works, but as you said, it doesn't render if the panel isn't displayed yet, which doesn't really help me too much. Is there a way to "render" it but not display it on the screen? Basically I'm building a component that needs to get written to an image but not displayed. – Jeff Storey Aug 28 '09 at 21:09
  • Hmm..that printAll still doesn't work if the component hasn't been made visible yet. – Jeff Storey Aug 28 '09 at 22:40
  • @JeffStorey How u made the stuff present on panel visible, in the image created?? – Xara Apr 12 '12 at 18:46
  • Call panel.doLayout() as the first thing in the method. – serg.nechaev Jun 27 '12 at 05:05
  • The proposed answer would work correctly in most cases. But what if my panel has an overridden `paint` method, and that method returns different picture each time it is called? In that case the saved image would not be the same as the one which is on the screen at the moment. I wonder, is there a way to save the current image, exactly as it appears on the screen? – Vilius Normantas Jan 30 '15 at 20:00
  • Using `panel.setSize(w,h)` before painting/printing made the picture the correct size even if the component hasn't been visible yet. – Karl Jan 25 '17 at 00:40
  • @Karl, call `addNotify()` so the component thinks it has a parent (and can therefore be displayed). – Matthieu Jan 31 '19 at 18:10
  • @ViliusNormantas is that a behaviour you noticed or just an assumption? I fail to see the use can where the paint would be *that* random. In that case though, use [`java.awt.Robot`](https://stackoverflow.com/questions/58305/is-there-a-way-to-take-a-screenshot-using-java-and-save-it-to-some-sort-of-image). And you're not supposed to override `paint()`, but `paintComponent()`. – Matthieu Jan 31 '19 at 18:13
4

Basically I'm building a component that needs to get written to an image but not displayed

ScreenImage explains how to do what you want.


Relevant section of ScreenImage.java (slightly edited). layoutComponent forces all buttons appear in the image.

/**
 * @return Renders argument onto a new BufferedImage
 */
public BufferedImage createImage(JPanel panel, int width, int height) {
    BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
    Graphics2D g = bi.createGraphics();
    panel.setSize(width, height); // or panel.getPreferedSize()
    layoutComponent(panel);
    panel.print(g);
    g.dispose();
    return bi;
}

private void layoutComponent(Component component) {
    synchronized (component.getTreeLock()) {
        component.doLayout();

        if (component instanceof Container) {
            for (Component child : ((Container) component).getComponents()) {
                layoutComponent(child);
            }
        }
    }
}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
camickr
  • 321,443
  • 19
  • 166
  • 288
3

The answer from Tom is basically correct, but invoke paint() directly is not recommended, as it is a synchronous call and can interrupt with other operation on the swing thread. Instead of using paint(), we should use print() instead

public BufferedImage createImage(JPanel panel) {

    int w = panel.getWidth();
    int h = panel.getHeight();
    BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
    Graphics2D g = bi.createGraphics();
    panel.print(g);
    g.dispose();
    return bi;
}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
Pham Trung
  • 11,204
  • 2
  • 24
  • 43
0

Take a look at BasicTableUI. The cell renderer is drawn on image without showing and then drawn on visible table component.

Rastislav Komara
  • 1,711
  • 8
  • 17