Frage

Ich versuche, ein Bild (Screenshot) von einer nicht sichtbaren AWT-Komponente zu erstellen. Ich kann nicht die Robot Klassen Screen-Capture-Funktionalität verwenden, da die Komponente nicht auf dem Bildschirm sichtbar ist. Der Versuch, den folgenden Code zu verwenden:

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

Arbeiten manchmal, aber nicht funktioniert, wenn die Komponente Dinge wie ein Textfeld oder Schaltfläche enthält, oder irgendeine Art von OpenGL / 3D-Komponente (diese Dinge sind aus dem Bild links aus!). Wie kann ich einen richtigen Screenshot von der ganzen Sache nehmen?

War es hilfreich?

Lösung

Ausgezeichnete Frage, ich habe darüber nachgedacht, mich von Zeit zu Zeit!


Wie Sie bereits geschrieben haben, dass zerreißend Schwergewichtskomponenten wie 3D und AWT auf ein Bild ist ein großes Problem. Diese Komponenten sind (fast) direkt auf die Grafikkarte übertragen , so dass sie nicht auf ein Bild mit der normalen paintComponent Sachen wieder gemacht werden, Sie brauchen Hilfe aus dem operativen System oder tun Sie Ihre eigene Rendering dieser Komponenten .


1. Machen Sie Ihre eigene Bild Renderer

Für jede Komponente, die keine Rendering-Methode zum Bild, das Sie benötigen, muss eigene erstellen. Zum Beispiel unter Verwendung von jogl Sie einen Off-Screen-Screenshot mit diesem Methode (SO Post ).


2. Rendering auf einem virtuellen Bildschirm

Voraussetzungen:

  1. Können Sie das Programm / Komponente in einer Headless-Umgebung starten?
  2. Sie Linux verwenden?

Dann können Sie verwenden Xvfb das ganze Programm auf einem virtuellen Bildschirm zu machen und dann nehmen ein Screenshot von dem virtuellen Bildschirm wie folgt aus:

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

Vielleicht brauchen Sie Xvfb ein wenig, indem man die Größe des Programms tweek Sie es machen wollen (-screen 0 1024x768x24).

Andere Tipps

(disclamer: woops .. das scheint nicht für AWT zur Arbeit) -:

Ich kann nicht glauben, dass niemand hat vorgeschlagen, SwingUtilities.paintComponent oder CellRendererPane.paintComponent , die für diesen Zweck hergestellt werden. Aus der Dokumentation des ehemaligen:

  

Paints eine Komponente an den angegebenen Graphics. Diese Methode in erster Linie nützlich ist Komponenten zu machen, die nicht als Teil der sichtbaren Container-Hierarchie existiert, sind aber für Rendering verwendet.


Hier ist ein Beispiel Verfahren, dass Farben eine nicht sichtbare Komponente auf ein Bild:

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

Hier ist ein Ausschnitt von Code, der die oben genannte Klasse testet:

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

Und hier ist das resultierende Bild:

eingeben Bild Beschreibung hier

Component hat eine Methode paintAll(Graphics) (wie Sie bereits gefunden haben). Diese Methode wird malen sich auf den übergebenen Grafiken. Aber wir müssen vorkonfigurieren die Grafik, bevor wir die Farbe Methode aufrufen. Das ist, was ich über das AWT Component-Rendering gefunden unter java.sun.com

  

Wenn AWT ruft diese Methode auf, die   Graphics-Objekt-Parameter ist   vorkonfiguriert mit dem entsprechenden   Zustand zum Zeichnen auf diesem speziellen   Komponente:

     
      
  • Die Farbe des Graphics-Objekt wird in den Vordergrund Eigenschaft der Komponente.
  •   
  • Die Schrift des Graphics-Objekt wird auf die Font-Eigenschaft der Komponente.
  •   
  • die Übersetzung des Graphics-Objekt wird so eingestellt, dass der (0,0) Koordinate stellt die obere linke Ecke des Bauteils.
  •   
  • Das Clip Rechteck Graphics Objekt wird in den Bereich der Komponente festgelegt, die in Not von Neulackierung ist.
  •   

Also, das ist unsere resultierende Methode:

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

Dies ist auch der bessere Weg, statt Robot für die sichtbaren Komponenten.


EDIT:

Vor langer Zeit ich den Code habe ich hier oben geschrieben, und es funktionierte, aber jetzt nicht mehr. Also suchte ich weiter. Ich habe einen getestet, arbeiten Art und Weise. Es ist schmutzig, aber funktioniert. Die Idee ist ein JDialog machen, legte es irgendwo aus dem Bildschirm Grenzen, stellen Sie es sichtbar, und es dann auf die Grafik ziehen.

Hier ist der Code:

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

So wird diese Arbeit auch auf Windows und Mac. Die andere Antwort war es auf einem virtuellen Bildschirm zu zeichnen. Aber das es nicht braucht.

Die Bildschirmdaten Klasse zeigt, wie dies für getan werden Swing-Komponenten. Ich habe nie versucht, es mit AWT-Komponenten, kaufen konnte ich das Konzept denke, das gleiche wäre.

Wie wäre es so etwas wie dieses. Die JFrame, die alle Komponenten hält, ist nicht sichtbar.


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

}

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top