22

非表示の AWT コンポーネントの画像 (スクリーンショット) を作成しようとしています。Robotコンポーネントが画面に表示されないため、クラスのスクリーン キャプチャ機能を使用できません。次のコードを使用しようとしています:

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

時々動作しますが、コンポーネントにテキスト ボックスやボタン、またはある種の OpenGL / 3D コンポーネントが含まれている場合は動作しません (これらは画像から除外されています!)。全体の適切なスクリーンショットを撮るにはどうすればよいですか?

4

5 に答える 5

13

(免責事項:おっと..これは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);
        }
    }
}

上記のクラスをテストするコードのスニペットを次に示します。

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"));

結果の画像は次のとおりです。

                      ここに画像の説明を入力

于 2010-11-11T12:47:39.503 に答える
5

ComponentpaintAll(Graphics)(すでに見つけたように)メソッドがあります。そのメソッドは、渡されたグラフィックにそれ自体をペイントします。ただし、paintメソッドを呼び出す前に、グラフィックを事前に構成する必要があります。これが、 java.sun.comでのAWTコンポーネントのレンダリングについて私が見つけたものです。

AWTがこのメソッドを呼び出すと、Graphicsオブジェクトのパラメーターは、この特定のコンポーネントを描画するための適切な状態で事前構成されます。

  • Graphicsオブジェクトの色は、コンポーネントのフォアグラウンドプロパティに設定されます。
  • 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);
}

これは、表示されているコンポーネントに使用する代わりに、より良い方法でもあり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でも機能します。もう1つの答えは、仮想画面に描画することでした。しかし、これはそれを必要としません。

于 2010-11-07T13:13:28.387 に答える
4

素晴らしい質問です。私は時々これについて自分自身で考えました!


すでに書いたように、3D や AWT などの重いコンポーネントを画像にレンダリングすることは大きな問題です。これらのコンポーネントは (ほとんど)グラフィック カードに直接転送されるため、通常のものを使用して画像に再レンダリングすることはできませんpaintComponent。オペレーティング システムの助けを借りるか、これらのコンポーネントを独自にレンダリングする必要があります。


1. 画像レンダラーを自作する

to image レンダリング メソッドを持たないコンポーネントごとに、独自のメソッドを作成する必要があります。たとえば、joglを使用すると、この方法( SO post )を使用して画面外のスクリーンショットを撮ることができます。


2. 仮想画面へのレンダリング

前提条件:

  1. ヘッドレス環境でプログラム/コンポーネントを起動できますか?
  2. Linux を使用していますか?

次に、Xvfbを使用してプログラム全体を仮想画面にレンダリングし、その仮想画面から次のようにスクリーンショットを取得できます。

Xvfb :1 &
DISPLAY=:1 java YourMainClass
xwd -display :1 -root -out image.xwd

レンダリングしたいプログラムのサイズを Xvfb に渡して、Xvfb を少し調整する必要があるかもしれません ( -screen 0 1024x768x24)。

于 2010-11-12T08:04:45.003 に答える
2

Screen Imageクラスは、Swing コンポーネントでこれを行う方法を示しています。AWTコンポーネントで試したことはありませんが、コンセプトは同じだと思います。

于 2010-10-27T01:02:14.490 に答える
1

このようなものはどうですか。すべてのコンポーネントを保持するJFrameは表示されません。


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 ); } }

}

于 2010-11-10T19:19:00.673 に答える