Swing: Obtain Image of JFrame
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
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
Comments
-
bguiz almost 4 years
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 theJFrame
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?
-
bguiz about 13 years+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 theImage
. I was looking for a way to obtain theImage
directly from the buffer, if indeed that were possible. I'll accept your answer if no-one else answers with this! -
kleopatra about 13 yearsjust curious: why the p.validate?
-
Andrew Thompson about 13 years@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.
-
JavaTechnical almost 11 yearsI have a doubt. I tried removing the
label.setSize()
method, the text on theJLabel
didn't appear? Why did this happen? (Thank you. +1) -
Andrew Thompson almost 11 years@JavaTechnical The size of a label is typically set (appropriate to the layout) when the code calls
pack()
orvalidate()
. 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. -
JavaTechnical almost 11 yearsAnd also you have set
g.setColor(Color.BLACK)
beforetextLabel.paint(g);
I tried removing it and nothing changed! -
MadProgrammer almost 10 yearsprint or printAll instead of paint would be more efficient as they're not double buffered