Swing: Obtain Image of JFrame
Asked Answered
D

1

65

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?

Disruptive answered 2/5, 2011 at 5:55 Comment(2)
Yes Swing components can render themselves but a JFrame is not a Swing component so you need to use the Robot if you want to capture the title bar as well. Of course if you use: JFrame.setDefaultLookAndFeelDecorated(true); then you should be able to render the entire frame.Guddle
For future readers: JFrame is and has always been a swing component. The above comment is wrong.Simpson
H
75

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

Hypoplasia answered 2/5, 2011 at 6:15 Comment(8)
+1 @Andrew : Thanks for the answer, this is certainly better than using the robot. H/w component.paint( image.getGraphics() ); still involves making a copy of the Image. I was looking for a way to obtain the Image directly from the buffer, if indeed that were possible. I'll accept your answer if no-one else answers with this!Disruptive
just curious: why the p.validate?Huge
@kleopatra: The best explanation I can think of (wrote that code a long while ago) is that my first attempts were obtaining an image of the component before it was realized. As far as I can infer, that did not work. Removed that line, added the 'See also' section that addresses that corner case.Hypoplasia
I have a doubt. I tried removing the label.setSize() method, the text on the JLabel didn't appear? Why did this happen? (Thank you. +1)Sounder
@Sounder The size of a label is typically set (appropriate to the layout) when the code calls pack() or validate(). Those methods will often base the size on the preferred size. Because no layouts are involved here, we need to set the size explicitly. More info. in the Oracle thread linked in the answer.Hypoplasia
And also you have set g.setColor(Color.BLACK) before textLabel.paint(g); I tried removing it and nothing changed!Sounder
print or printAll instead of paint would be more efficient as they're not double bufferedConcerning
how to take include the window decorations ?Eunaeunice

© 2022 - 2024 — McMap. All rights reserved.