java - Render OS-specific windows to a BufferedImage

As part of a game idea, I need to procedurally generate images of a desktop cluttered with randomly generated windows. Desirably the frames of the windows will match to the L&F of the program. To fit this need, I am using a JDesktopPane with JInternalFrames to render the windows and paint them onto a BufferedImage. My code is as such:

JDesktopPane desktopPane = new JDesktopPane();
desktopPane.setSize(1920, 1080);
JInternalFrame internalFrame = new JInternalFrame();
internalFrame.setVisible(true);
internalFrame.setBounds(100, 100, 500, 500);
desktopPane.add(internalFrame);

BufferedImage image = new BufferedImage(1920, 1080, BufferedImage.TYPE_INT_ARGB);
desktopPane.paintAll(image.getGraphics());

ImageIO.write(image, "png", new File("./desktop.png"));

However this code does not create an image of a window in the program L&F, instead it generates a clear and empty image of the appropriate size. Yet if the JDesktopPane is added to a JFrame and the frame made visible, calling myJFrame.paintAll(image.getGraphics()) renders perfectly fine. So, how can I paint components that are not visible / displayable or added to a JFrame? Or is there a simpler way to render windows in a cross-platform way?

1 Answer

  1. Martin- Reply

    2019-11-16

    Okay, after some playing around, a system look and feel provides the decorations for the frames, this means, that until the component is attached to a native peer, the frame decorations can't be painted...

    This means you need to use a JFrame, now the good news is, you can use pack to do just that...

    Desktop

    import java.awt.Dimension;
    import java.awt.Graphics2D;
    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.IOException;
    import javax.imageio.ImageIO;
    import javax.swing.JDesktopPane;
    import javax.swing.JFrame;
    import javax.swing.JInternalFrame;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class TestPaint {
    
        public static void main(String[] args) {
            try {
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                ex.printStackTrace();
            }
    
            JFrame frame = new JFrame();
    
            JDesktopPane desktopPane = new JDesktopPane();
            desktopPane.setPreferredSize(new Dimension(1920, 1080));
            JInternalFrame internalFrame = new JInternalFrame("Testing");
            internalFrame.setVisible(true);
            internalFrame.setBounds(100, 100, 500, 500);
            desktopPane.add(internalFrame);
    
            frame.add(desktopPane);
            frame.pack();
    
            BufferedImage image = new BufferedImage(1920, 1080, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = image.createGraphics();
            desktopPane.printAll(g2d);
            g2d.dispose();
    
            try {
                ImageIO.write(image, "png", new File("./desktop.png"));
            } catch (IOException ex) {
                ex.printStackTrace();
            }
    
            frame.dispose();
        }
    
    }
    

    You should also use printAll over paintAll, mostly because it disables double buffering

Leave a Reply

Your email address will not be published. Required fields are marked *

You can use these HTML tags and attributes <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>