12

Is it possible to create a BufferedImage from a JPanel without first rendering it in a JFrame? I've searched everywhere I can think of and cannot find an answer. Can anyone help?

Here is some sample code. If I don't un-comment the JFrame code, my BufferedImage is blank.

    test(){
//      JFrame frame = new JFrame();
        JPanel panel = new JPanel();
        Dimension dim = new Dimension(50,50);
        panel.setMinimumSize(dim);
        panel.setMaximumSize(dim);
        panel.setPreferredSize(dim);
        JLabel label = new JLabel("hello");
        panel.add(label);
//      frame.add(panel);
//      frame.pack();
        BufferedImage bi = getScreenShot(panel);

        //...code that saves bi to a jpg
    }

    private BufferedImage getScreenShot(JPanel panel){
        BufferedImage bi = new BufferedImage(panel.getWidth(), panel.getHeight(), BufferedImage.TYPE_INT_ARGB);
        panel.paint(bi.getGraphics());
        return bi;
    }
4

2 回答 2

8

See this answer to Swing: Obtain Image of JFrame as well as Why does the JTable header not appear in the image? for tips on painting components that have not yet been rendered. I expect the fix to your problem is shown in the label of LabelRenderTest.java.

JLabel textLabel = new JLabel(title);
textLabel.setSize(textLabel.getPreferredSize());

Update

Dimension dim = new Dimension(50,50);
panel.setSize(dim);  // very important!
panel.setMinimumSize(dim);
panel.setMaximumSize(dim);
panel.setPreferredSize(dim);
// ...

Or here is the complete source. The size of the label also needs to be set.

import java.awt.Color;
import java.awt.Dimension;
import java.awt.image.BufferedImage;

import javax.swing.*;

public class RenderTest {

    RenderTest() {
        JPanel panel = new JPanel();
        panel.setBackground(Color.RED);
        Dimension dim = new Dimension(50,50);
        panel.setSize(dim);
        panel.setMinimumSize(dim);
        panel.setMaximumSize(dim);
        panel.setPreferredSize(dim);
        JLabel label = new JLabel("hello");
        label.setSize(label.getPreferredSize());
        panel.add(label);

        BufferedImage bi = getScreenShot(panel);
        JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(bi)));
    }

    private BufferedImage getScreenShot(JPanel panel){
        BufferedImage bi = new BufferedImage(
            panel.getWidth(), panel.getHeight(), BufferedImage.TYPE_INT_ARGB);
        panel.paint(bi.getGraphics());
        return bi;
    }

    public static void main(String[] args) {
        new RenderTest();
    }
}
于 2012-09-19T00:25:59.760 回答
4

EDIT2: Basically Andrew Thompson is right in his answer, a frame is not necessary. At the same time it can be practical to have one, because a call to pack() will make the layout managers work. I deleted the first part of my original answer, left is the memory-related part. Note that calling dispose on the Graphics is still needed even without a frame.

About your running out of heap space when using frames: this should not happen. Probably you only need to call dispose() on the frames when you are done with them. If that does not help, I would suggest asking it in a separate question. From the dispose docs:

Releases all of the native screen resources used by this Window, its subcomponents, and all of its owned children. That is, the resources for these Components will be destroyed, any memory they consume will be returned to the OS, and they will be marked as undisplayable. (...) Note: When the last displayable window within the Java virtual machine (VM) is disposed of, the VM may terminate.

EDIT: some more thoughts:

  • reusing the same JFrame object should also work. Add panel, call pack, create image, remove panel, repeat
  • don't forget to call dispose() on the Graphics objects created by you as well
  • In worst case you can restart the JVM from a script from time to time
于 2012-09-18T17:35:28.947 回答