3

我想在无头模式下将 JPanel 绘制到 BufferedImage 中(屏幕上根本没有 GUI)。

final JPanel panel = createPanel();
panel.setSize(panel.getPreferredSize());
panel.validate();

//  JFrame frame = new JFrame();
//  frame.getContentPane().add(panel);
//  frame.pack();
//  frame.setVisible(true);

final BufferedImage image = new BufferedImage(
            panel.getBounds().width, 
            panel.getBounds().height, 
            BufferedImage.TYPE_INT_ARGB
);

final Graphics2D gc = image.createGraphics();
gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
gc.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

try {
    panel.paint(gc);
    ...save the image somewhere...
} finally {
    gc.dispose();
}

但是我总是得到空白图像,直到我将面板放入重量级组件并将其显示在屏幕上(请参阅注释代码)。我不想展示它,这个应用程序在服务器上运行。

这是SSCCE:

    public class Example {

    private static JPanel createPanel() {
        final JPanel panel = new JPanel(new GridBagLayout());           
        final JLabel label = new JLabel("Yeah, it's working!", SwingConstants.CENTER);
        label.setFont(new Font("Arial", Font.PLAIN, 12));           
        final GridBagConstraints constraints = new GridBagConstraints();
        constraints.fill = GridBagConstraints.BOTH;
        constraints.weightx = 1;
        constraints.weightx = 1;
        panel.add(label, constraints);          
        return panel;
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                final JPanel panel = createPanel();
                panel.setSize(panel.getPreferredSize());
                panel.validate();

    //              JFrame frame = new JFrame();
    //              frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    //              frame.getContentPane().add(panel);
    //              frame.pack();
    //              frame.setVisible(true);

                final BufferedImage image = new BufferedImage(
                        panel.getBounds().width, 
                        panel.getBounds().height, 
                        BufferedImage.TYPE_INT_ARGB
                );    
                final Graphics2D gc = image.createGraphics();
                gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                gc.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);    
                try {
                    panel.paint(gc);
                    ImageIO.write(image, "png", new File("image.png"));
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    gc.dispose();
                }                   
            }
        });
    }    
}
4

4 回答 4

4

在组件实现之前,组件的大小为零,因此绘制方法不起作用。

查看屏幕图像。它将通过在面板上调用 doLayout() 为您处理此问题,以确保所有组件都具有有效的大小。

于 2013-06-10T15:13:09.397 回答
3

这是一个将简单标签绘制到图像文件的片段,然后打开图像文件(如果在台式计算机上)。

import java.awt.Desktop;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JLabel;

public class Test {

    public static void main(String[] args) throws IOException {
        JLabel label = new JLabel("Hello world");
        label.setSize(label.getPreferredSize());
        BufferedImage image = new BufferedImage(label.getWidth(), label.getHeight(), BufferedImage.TYPE_INT_ARGB);
        label.paint(image.getGraphics());
        File output = new File("C:\\test\\hello world.png");
        if (!output.getParentFile().exists()) {
            output.getParentFile().mkdirs();
        }
        ImageIO.write(image, "png", output);
        Desktop.getDesktop().open(output);
    }

}

编辑(使用您的 SSCCE):

不要调用validate(),而是doLayout()在您的面板上调用(如果您有嵌套面板,请确保递归调用它):

import java.awt.Desktop;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;

public class Example {

    private static JLabel label;

    private static JPanel createPanel() {
        final JPanel panel = new JPanel(new GridBagLayout());

        label = new JLabel("Yeah, it's working!", SwingConstants.CENTER);
        label.setFont(new Font("Arial", Font.PLAIN, 12));

        final GridBagConstraints constraints = new GridBagConstraints();
        constraints.fill = GridBagConstraints.BOTH;
        constraints.weightx = 1;
        constraints.weightx = 1;
        panel.add(label, constraints);

        return panel;
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                final JPanel panel = createPanel();
                panel.setSize(panel.getPreferredSize());
                panel.doLayout();
                System.err.println(label.getSize() + "");
                // JFrame frame = new JFrame();
                // frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                // frame.getContentPane().add(panel);
                // frame.pack();
                // frame.setVisible(true);

                final BufferedImage image = new BufferedImage(panel.getBounds().width, panel.getBounds().height,
                        BufferedImage.TYPE_INT_ARGB);

                final Graphics2D gc = image.createGraphics();
                gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                gc.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

                try {
                    panel.paint(gc);
                    File output = new File("image.png");
                    ImageIO.write(image, "png", output);
                    Desktop.getDesktop().open(output);
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    gc.dispose();
                }

            }
        });
    }
}
于 2013-06-10T15:29:22.510 回答
1

调用addNotify您尝试打印/绘制的根组件也可以解决问题。问题的症结似乎是验证调用短路,除非容器有“对等体”。调用addNotify初始化对等体,允许后续调用Component.validate像在非无头场景中通常那样运行。

将此作为替代解决方案提交,因为在由于未布置子组件而导致组件doLayout嵌套更深的情况下调用将不起作用。doLayout(尽管 camickr 的答案中提到的 ScreenImage 类通过递归调用 doLayout 来解决这个问题。)

于 2017-08-30T14:42:06.767 回答
0

尝试BufferedImage使用它的构造函数之一来实例化。GraphicsEnvironment类可能必须与真正的 GraphicsEnvironment(屏幕,...)一起使用

new  BufferedImage(panel.getBounds().width, panel.getBounds().height, BufferedImage.TYPE_INT_ARGB )
于 2013-06-10T15:02:16.873 回答