Search code examples
javaimageswingscreenshotjframe

Swing: Obtain Image of JFrame


How do I obtain a java.awt.Image of a JFrame?

I want to obtain a screen shot of a JFrame (for later use within my application). This is presently accomplished using the robot to take a screen shot specifying the coordinates and dimensions of the JFrame involved.

However, I believe that there is a better way: Swing components, by default, render themselves as images into a double buffer prior to painting themselves onto the screen.

Is there a way to obtain these images from the component?


Solution

  • ComponentImageCapture.java

    import java.awt.BorderLayout;
    import java.awt.Component;
    import java.awt.Image;
    import java.awt.Graphics;
    
    import java.awt.image.BufferedImage;
    
    import java.awt.event.ActionListener;
    import java.awt.event.ActionEvent;
    import java.awt.event.KeyEvent;
    import java.awt.event.InputEvent;
    
    import javax.swing.*;
    
    import javax.swing.border.TitledBorder;
    
    import javax.imageio.ImageIO;
    
    import java.io.File;
    
    /**
    Create a screenshot of a component.
    @author Andrew Thompson
    */
    class ComponentImageCapture {
    
      static final String HELP =
        "Type Ctrl-0 to get a screenshot of the current GUI.\n" +
        "The screenshot will be saved to the current " +
        "directory as 'screenshot.png'.";
    
      public static BufferedImage getScreenShot(
        Component component) {
    
        BufferedImage image = new BufferedImage(
          component.getWidth(),
          component.getHeight(),
          BufferedImage.TYPE_INT_RGB
          );
        // call the Component's paint method, using
        // the Graphics object of the image.
        component.paint( image.getGraphics() ); // alternately use .printAll(..)
        return image;
      }
    
      public static void main(String[] args) {
        Runnable r = new Runnable() {
          public void run() {
            final JFrame f = new JFrame("Test Screenshot");
    
            JMenuItem screenshot =
              new JMenuItem("Screenshot");
            screenshot.setAccelerator(
              KeyStroke.getKeyStroke(
                KeyEvent.VK_0,
                InputEvent.CTRL_DOWN_MASK
              ));
            screenshot.addActionListener(
              new ActionListener(){
                public void actionPerformed(ActionEvent ae) {
                  BufferedImage img = getScreenShot(
                    f.getContentPane() );
                  JOptionPane.showMessageDialog(
                    null,
                    new JLabel(
                      new ImageIcon(
                        img.getScaledInstance(
                          img.getWidth(null)/2,
                          img.getHeight(null)/2,
                          Image.SCALE_SMOOTH )
                        )));
                  try {
                    // write the image as a PNG
                    ImageIO.write(
                      img,
                      "png",
                      new File("screenshot.png"));
                  } catch(Exception e) {
                    e.printStackTrace();
                  }
                }
              } );
            JMenu menu = new JMenu("Other");
            menu.add(screenshot);
            JMenuBar mb = new JMenuBar();
            mb.add(menu);
            f.setJMenuBar(mb);
    
            JPanel p = new JPanel( new BorderLayout(5,5) );
            p.setBorder( new TitledBorder("Main GUI") );
            p.add( new JScrollPane(new JTree()),
              BorderLayout.WEST );
            p.add( new JScrollPane( new JTextArea(HELP,10,30) ),
              BorderLayout.CENTER );
    
            f.setContentPane( p );
            f.pack();
            f.setLocationRelativeTo(null);
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.setVisible(true);
          }
        };
        SwingUtilities.invokeLater(r);
      }
    } 
    

    Screen shot

    GUI with captured image

    See also

    The code shown above presumes the component has been realized on-screen, prior to rendering.

    Rob Camick shows how to paint an unrealized component in the Screen Image class.

    Another thread that might be of relevance, is Render JLabel without 1st displaying, particularly the 'one line fix' by Darryl Burke.

    LabelRenderTest.java

    Here is an updated variant of the code shown on the second link.

    import java.awt.*;
    import java.awt.image.BufferedImage;
    import javax.swing.*;
    
    public class LabelRenderTest {
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater( new Runnable() {
                public void run() {
    
                String title = "<html><body style='width: 200px; padding: 5px;'>"
                    + "<h1>Do U C Me?</h1>"
                    + "Here is a long string that will wrap.  "
                    + "The effect we want is a multi-line label.";
    
                    JFrame f = new JFrame("Label Render Test");
                    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
                    BufferedImage image = new BufferedImage(
                        400,
                        300,
                        BufferedImage.TYPE_INT_RGB);
                    Graphics2D imageGraphics = image.createGraphics();
                    GradientPaint gp = new GradientPaint(
                        20f,
                        20f,
                        Color.red,
                        380f,
                        280f,
                        Color.orange);
                    imageGraphics.setPaint(gp);
                    imageGraphics.fillRect(0, 0, 400, 300);
    
                    JLabel textLabel = new JLabel(title);
                    textLabel.setSize(textLabel.getPreferredSize());
    
                    Dimension d = textLabel.getPreferredSize();
                    BufferedImage bi = new BufferedImage(
                        d.width,
                        d.height,
                        BufferedImage.TYPE_INT_ARGB);
                    Graphics g = bi.createGraphics();
                    g.setColor(new Color(255, 255, 255, 128));
                    g.fillRoundRect(
                        0,
                        0,
                        bi.getWidth(f),
                        bi.getHeight(f),
                        15,
                        10);
                    g.setColor(Color.black);
                    textLabel.paint(g);
                    Graphics g2 = image.getGraphics();
                    g2.drawImage(bi, 20, 20, f);
    
                    ImageIcon ii = new ImageIcon(image);
                    JLabel imageLabel = new JLabel(ii);
    
                    f.getContentPane().add(imageLabel);
                    f.pack();
                    f.setLocationByPlatform(true);
    
                    f.setVisible(true);
                }
            });
        }
    }
    

    Screen shot

    Label rendered on image