无头画

时间:2013-06-10 14:55:29

标签: java swing jpanel bufferedimage javax.imageio

我想在无头模式下将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 个答案:

答案 0 :(得分:3)

组件的大小为零,直到组件被实现,因此绘制方法不起作用。

结帐Screen Image。它将通过在面板上调用doLayout()来确保所有组件都具有有效大小,从而为您解决此问题。

答案 1 :(得分: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();
                }

            }
        });
    }
}

答案 2 :(得分:1)

在您尝试打印/绘制的根组件上调用addNotify也可以解决问题。这个问题的症结似乎是验证要求短路,除非容器有“同行”。调用addNotify初始化对等体,允许对Component.validate的后续调用按照正常情况在非无头情况下运行。

将此作为备用解决方案提交,因为doLayout由于doLayout未布置子组件而导致组件嵌套更深,因此调用DataTable dt = new DataTable(); foreach (DataRow dataRow in dt.Rows) { var value = dataRow.Field<int>("ColumnName"); } 将不起作用。 (虽然camickr的答案中提到的ScreenImage类通过递归调用doLayout来解决这个问题。)

答案 3 :(得分:0)

尝试使用其中一个构造函数来实例化BufferedImageGraphicsEnvironment类可能必须与真正的GraphicsEnvironment(屏幕,...)

一起使用
new  BufferedImage(panel.getBounds().width, panel.getBounds().height, BufferedImage.TYPE_INT_ARGB )