Isa*_*ler 22 java graphics bufferedimage awt
我正在尝试创建一个不可见的AWT组件的图像(屏幕截图).我无法使用Robot类的屏幕捕获功能,因为该组件在屏幕上不可见.尝试使用以下代码:
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
component.paintAll(g);
Run Code Online (Sandbox Code Playgroud)
有时工作,但如果组件包含诸如文本框或按钮之类的东西,或某种OpenGL/3D组件(这些东西都不在图像中!),则无效.我怎样才能对整个事物进行适当的截图?
aio*_*obe 11
(disclamer:woops ..这似乎不适用于AWT) - :
我无法相信没有人建议SwingUtilities.paintComponent或CellRendererPane.paintComponent为此目的而制造.从前者的文件:
将组件绘制到指定的组件
Graphics.此方法主要用于呈现不作为可见包含层次结构的一部分存在但用于呈现的组件.
下面是一个将不可见组件绘制到图像上的示例方法:
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
以下是测试上述类的代码片段:
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"));
Run Code Online (Sandbox Code Playgroud)
以下是生成的图像:

Component有一个方法paintAll(Graphics)(你已经发现).该方法将在传递的图形上绘制自己.但是我们必须在调用paint方法之前预先配置图形.这就是我在java.sun.com上发现的关于AWT组件渲染的内容:
当AWT调用此方法时,Graphics对象参数已预先配置为在此特定组件上绘制的适当状态:
- Graphics对象的颜色设置为组件的foreground属性.
- Graphics对象的字体设置为组件的font属性.
- 设置Graphics对象的转换,使得坐标(0,0)表示组件的左上角.
- Graphics对象的剪辑矩形设置为需要重新绘制的组件区域.
所以,这是我们得到的方法:
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);
}
Run Code Online (Sandbox Code Playgroud)
这也是更好的方式,而不是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);
}
Run Code Online (Sandbox Code Playgroud)
因此,这也适用于Windows和Mac.另一个答案是在虚拟屏幕上绘制它.但这并不需要它.
很好的问题,我不时地想到这个!
正如您已经写过的那样,将重量较大的组件(如3D和AWT)渲染到图像上是一个很大的问题.这些组件(几乎)直接传输到图形卡,因此无法使用常规paintComponent材料将其重新渲染为图像,您需要操作系统的帮助或自行渲染这些组件.
对于没有图像渲染方法的每个组件,您需要创建自己的组件.例如,使用jogl,您可以使用此方法拍摄屏幕截图(SO帖子).
先决条件:
然后,您可以使用Xvfb将整个程序渲染到虚拟屏幕上,然后从该虚拟屏幕截取屏幕截图,如下所示:
Xvfb :1 &
DISPLAY=:1 java YourMainClass
xwd -display :1 -root -out image.xwd
Run Code Online (Sandbox Code Playgroud)
也许您需要通过将要渲染的程序的大小传递给它来调整Xvfb(-screen 0 1024x768x24).