Java 是否从不可见的AWT组件创建图像?

Java 是否从不可见的AWT组件创建图像?,java,graphics,awt,bufferedimage,Java,Graphics,Awt,Bufferedimage,我正在尝试创建一个不可见AWT组件的图像(屏幕截图)。我无法使用Robotclasses的屏幕捕获功能,因为该组件在屏幕上不可见。正在尝试使用以下代码: BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D g = image.createGraphics(); component.paintAll(g); 有时可以工作,但如果组件包含文本框、按钮或某种Ope

我正在尝试创建一个不可见AWT组件的图像(屏幕截图)。我无法使用
Robot
classes的屏幕捕获功能,因为该组件在屏幕上不可见。正在尝试使用以下代码:

BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
component.paintAll(g);

有时可以工作,但如果组件包含文本框、按钮或某种OpenGL/3D组件(这些内容不在图像中!),则无法工作。如何拍摄整个过程的正确屏幕截图?

本课程展示了如何对Swing组件进行截图。我从未尝试过AWT组件,但我可以猜到它的概念是一样的。

组件
有一个方法
paintAll(Graphics)
(正如您已经发现的那样)。该方法将在传递的图形上绘制自身。但是在调用绘制方法之前,我们必须预先配置图形。这就是我在以下位置发现的有关AWT组件渲染的内容:

当AWT调用此方法时 图形对象参数为 使用适当的 说明如何利用这一特殊情况 组成部分:

  • 图形对象的颜色设置为组件的前景属性
  • 图形对象的字体设置为组件的字体属性
  • 图形对象的平移设置为坐标(0,0)表示组件的左上角
  • 图形对象的剪辑矩形设置为需要重新绘制的组件区域
因此,这是我们得出的方法:

public static BufferedImage componentToImage(Component component, Rectangle region)
{
    BufferedImage img = new BufferedImage(component.getWidth(), component.getHeight(), BufferedImage.TYPE_INT_ARGB_PRE);
    Graphics g = img.getGraphics();
    g.setColor(component.getForeground());
    g.setFont(component.getFont());
    component.paintAll(g);
    g.dispose();
    if (region == null)
    {
        return img;
    }
    return img.getSubimage(region.x, region.y, region.width, region.height);
}
对于可见组件,这也是一种更好的方法,而不是使用
Robot


编辑:

很久以前,我使用了我在上面发布的代码,它工作了,但现在不行了。所以我进一步搜索。我有一种经过测试的工作方式。它很脏,但能用。它的想法是制作一个JDialog,将它放在屏幕边界之外的某个地方,将它设置为可见,然后在图形上绘制它

代码如下:

public static BufferedImage componentToImageWithSwing(Component component, Rectangle region) {
    BufferedImage img = new BufferedImage(component.getWidth(), component.getHeight(), BufferedImage.TYPE_INT_RGB);
    Graphics g = img.createGraphics();

    // Real render
    if (component.getPreferredSize().height == 0 && component.getPreferredSize().width == 0)
    {
        component.setPreferredSize(component.getSize());
    }

    JDialog f = new JDialog();
    JPanel p = new JPanel();
    p.add(component);
    f.add(p);
    f.pack();
    f.setLocation(-f.getWidth() - 10, -f.getHeight() -10);
    f.setVisible(true);
    p.paintAll(g);
    f.dispose();
    // ---

    g.dispose();
    if (region == null) {
        return img;
    }
    return img.getSubimage(region.x, region.y, region.width, region.height);
}

因此,这也适用于Windows和Mac。另一个答案是在虚拟屏幕上绘制。但是这不需要它。

像这样的东西怎么样。容纳所有组件的JFrame不可见


导入java.awt.Color;
导入java.awt.Component;
导入java.awt.Container;
导入java.awt.Dimension;
导入java.awt.Graphics2D;
导入java.awt.Rectangle;
导入java.awt.image.buffereImage;
导入java.io.File;
导入java.io.IOException;
导入java.util.array;
导入java.util.List;
导入java.util.logging.Level;
导入java.util.logging.Logger;
导入javax.imageio.imageio;
导入javax.swing.JButton;
导入javax.swing.JComponent;
导入javax.swing.JFrame;
导入javax.swing.JPanel;
导入javax.swing.JTextArea

/** *捕获不可见的awt组件 *@author-dvargo */ 公共类截图 {

private static List types=Arrays.asList(ImageIO.getWriterFileSuffix());
/**
*构建GUI
*@param args
*/
公共静态void main(字符串[]args)
{
JFrame InvisibleName=新JFrame();
InvisibleName.setSize(300300);
JPanel colorPanel=新的JPanel();
彩色面板。背景(颜色。红色);
colorPanel.setSize(InvisibleName.getSize());
JTextArea textBox=新的JTextArea(“这里有一些文本”);
colorPanel.add(文本框);
添加(彩色面板);
JButton theButton=新JButton(“单击我”);
颜色面板。添加(按钮);
按钮。设置可见(真);
textBox.setVisible(true);
colorPanel.setVisible(真);
//截屏
尝试
{
BuffereImage屏幕截图=createImage((JComponent)colorPanel,新矩形(InvisibleName.getBounds());
writeImage(屏幕截图,“文件路径”);
} 
捕获(IOEX异常)
{
Logger.getLogger(screenscapture.class.getName()).log(Level.SEVERE,null,ex);
}
}
/**
*为Swing组件创建缓冲区映像。
*可以将组件的全部或部分捕获到图像中。
*
*@param component用于创建图像的组件
*@param region要捕捉到图像的组件区域
*@returnimage指定区域的图像
*/
公共静态缓冲区图像createImage(组件,矩形区域){
//确保组件有一个尺寸,并且已经布置好。
//(对未添加到已实现框架的组件进行必要检查)
如果(!component.isDisplayable()){
维度d=组件.getSize();
如果(d.width==0 | | d.height==0){
d=组件。getPreferredSize();
组件。设置大小(d);
}
布局组件(组件);
}
BuffereImage=新的BuffereImage(region.width、region.height、BuffereImage.TYPE_INT_RGB);
Graphics2D g2d=image.createGraphics();
//为非不透明组件绘制背景,
//否则背景将为黑色
如果(!component.isOpaque()){
g2d.setColor(component.getBackground());
g2d.fillRect(region.x,region.y,region.width,region.height);
}
平移(-region.x,-region.y);
成分油漆(g2d);
g2d.dispose();
返回图像;
}
公共静态无效布局组件(组件组件){
已同步(component.getTreeLock()){
component.doLayout();
if(容器的组件实例){
对于(组件子组件:((容器)组件).getComponents()){
布局组件(子组件);
}
}
}
}
/**
*将BuffereImage写入文件。
*
*@param image要写入的图像
*@param fileName要创建的文件的名称
*@exception IOException如果在写入过程中发生错误
*/
公共静态void writeImage(BuffereImage图像,字符串文件名)
抛出IOException
{
如果(fileName==null)返回;
int offset=fileName.lastIndexOf(“.”);
如果(偏移量==-1)
{
字符串消息

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;

/** * Captures an invisible awt component * @author dvargo */ public class ScreenCapture {

private static List<String> types = Arrays.asList( ImageIO.getWriterFileSuffixes() );

/**
 * Build GUI
 * @param args
 */
public static void main(String [] args)
{
    JFrame invisibleFrame = new JFrame();
    invisibleFrame.setSize(300, 300);
    JPanel colorPanel = new JPanel();
    colorPanel.setBackground(Color.red);
    colorPanel.setSize(invisibleFrame.getSize());
    JTextArea textBox = new JTextArea("Here is some text");
    colorPanel.add(textBox);
    invisibleFrame.add(colorPanel);
    JButton theButton = new JButton("Click Me");
    colorPanel.add(theButton);

    theButton.setVisible(true);
    textBox.setVisible(true);
    colorPanel.setVisible(true);

    //take screen shot
    try 
    {
        BufferedImage screenShot = createImage((JComponent) colorPanel, new Rectangle(invisibleFrame.getBounds()));
        writeImage(screenShot, "filePath");
    } 
    catch (IOException ex)
    {
        Logger.getLogger(ScreenCapture.class.getName()).log(Level.SEVERE, null, ex);
    }

}


/**
 *  Create a BufferedImage for Swing components.
 *  All or part of the component can be captured to an image.
 *
 *  @param  component component to create image from
 *  @param  region The region of the component to be captured to an image
 *  @return image the image for the given region
 */
public static BufferedImage createImage(Component component, Rectangle region) {
    //  Make sure the component has a size and has been layed out.
    //  (necessary check for components not added to a realized frame)

    if (!component.isDisplayable()) {
        Dimension d = component.getSize();

        if (d.width == 0 || d.height == 0) {
            d = component.getPreferredSize();
            component.setSize(d);
        }

        layoutComponent(component);
    }

    BufferedImage image = new BufferedImage(region.width, region.height, BufferedImage.TYPE_INT_RGB);
    Graphics2D g2d = image.createGraphics();

    //  Paint a background for non-opaque components,
    //  otherwise the background will be black

    if (!component.isOpaque()) {
        g2d.setColor(component.getBackground());
        g2d.fillRect(region.x, region.y, region.width, region.height);
    }

    g2d.translate(-region.x, -region.y);
    component.paint(g2d);
    g2d.dispose();
    return image;
}

public static void layoutComponent(Component component) {
    synchronized (component.getTreeLock()) {
        component.doLayout();

        if (component instanceof Container) {
            for (Component child : ((Container) component).getComponents()) {
                layoutComponent(child);
            }
        }
    }
}

/**
 *  Write a BufferedImage to a File.
 *
 *  @param   image image to be written
 *  @param   fileName name of file to be created
 *  @exception IOException if an error occurs during writing
*/
public static void writeImage(BufferedImage image, String fileName)
    throws IOException
{
    if (fileName == null) return;

    int offset = fileName.lastIndexOf( "." );

    if (offset == -1)
    {
        String message = "file suffix was not specified";
        throw new IOException( message );
    }

    String type = fileName.substring(offset + 1);

    if (types.contains(type))
    {
        ImageIO.write(image, type, new File( fileName ));
    }
    else
    {
        String message = "unknown writer file suffix (" + type + ")";
        throw new IOException( message );
    }
}
import java.awt.*;
import java.awt.image.BufferedImage;

import javax.swing.*;

public class ComponentPainter {

    public static BufferedImage paintComponent(Component c) {

        // Set it to it's preferred size. (optional)
        c.setSize(c.getPreferredSize());
        layoutComponent(c);

        BufferedImage img = new BufferedImage(c.getWidth(), c.getHeight(),
                BufferedImage.TYPE_INT_RGB);

        CellRendererPane crp = new CellRendererPane();
        crp.add(c);
        crp.paintComponent(img.createGraphics(), c, crp, c.getBounds());    
        return img;
    }

    // from the example of user489041
    public static void layoutComponent(Component c) {
        synchronized (c.getTreeLock()) {
            c.doLayout();
            if (c instanceof Container)
                for (Component child : ((Container) c).getComponents())
                    layoutComponent(child);
        }
    }
}
JPanel p = new JPanel();
p.add(new JButton("Button 1"));
p.add(new JButton("Button 2"));
p.add(new JCheckBox("A checkbox"));

JPanel inner = new JPanel();
inner.setBorder(BorderFactory.createTitledBorder("A border"));
inner.add(new JLabel("Some label"));
p.add(inner);

BufferedImage img = ComponentPainter.paintComponent(p);

ImageIO.write(img, "png", new File("test.png"));
Xvfb :1 &
DISPLAY=:1 java YourMainClass
xwd -display :1 -root -out image.xwd