Question

Je suis en train de créer une image (écran-shot) d'un composant AWT non visible. Je ne peux pas utiliser la fonctionnalité de capture d'écran de classes Robot car le composant n'est pas visible à l'écran. Essayer d'utiliser le code suivant:

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

Travaux parfois, mais ne fonctionne pas si le composant contient des choses comme une zone de texte ou un bouton, ou une sorte de composant OpenGL / 3D (ces choses sont laissées sur l'image!). Comment puis-je prendre une capture d'écran appropriée de la chose?

Était-ce utile?

La solution

Excellente question, je l'ai pensé à moi-même de temps en temps!


Comme vous l'avez déjà écrit, que déchirante composants de poids lourds tels que 3D et AWT sur une image est un gros problème. Ces composants sont (presque) directement transférées sur la carte graphique afin qu'ils ne peuvent pas être re-rendu à une image en utilisant la substance de paintComponent normale, vous avez besoin d'aide du système d'exploitation ou de faire votre propre rendu de ces composants .


1. Faire votre propre à l'image renderer

Pour chaque composant qui n'a pas de la méthode de rendu d'image, vous devez créer votre propre. Par exemple en utilisant jogl vous pouvez prendre une capture d'écran hors écran en utilisant cette méthode (SO après ).


2. Rendu sur un écran virtuel

Pré-requis:

  1. Pouvez-vous lancer le programme / composant dans un environnement sans tête?
  2. Vous utilisez Linux?

Ensuite, vous pouvez utiliser Xvfb pour rendre l'ensemble du programme sur un écran virtuel puis prendre une capture d'écran de cet écran virtuel comme ceci:

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

Peut-être que vous avez besoin de Tweek Xvfb un peu en passant la taille du programme que vous voulez rendre à ce (-screen 0 1024x768x24).

Autres conseils

(disclamer: woops .. cela ne semble pas fonctionner pour AWT) -:

Je ne peux pas croire que personne ne l'a suggéré SwingUtilities.paintComponent ou CellRendererPane.paintComponent qui sont faites dans ce but. De la documentation de l'ancien:

  

Produits de revêtement d'un composant aux Graphics spécifié. Cette méthode est surtout utile pour rendre des composants qui n'existent pas dans le cadre de la hiérarchie de confinement visible, mais sont utilisés pour le rendu.


Voici un exemple de procédé que les peintures un composant non visible sur une image:

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

Voici un extrait de code qui teste la classe ci-dessus:

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

Et voici l'image résultante:

entrer image description ici

Component a une méthode paintAll(Graphics) (comme vous l'avez déjà trouvé). Cette méthode va se peindre sur les graphiques transmis. Mais nous devons préconfigurer les graphiques avant d'appeler la méthode de peinture. Voilà ce que je trouve sur le rendu des composants AWT java.sun.com :

  

Lorsque AWT invoque cette méthode, la   paramètre d'objet graphique est   préconfiguré avec la approprié   état pour le dessin sur ce point particulier   composant:

     
      
  • La couleur de l'objet graphique est défini sur la propriété de premier plan du composant.
  •   
  • La police de l'objet Graphics est défini sur la propriété de la police du composant.
  •   
  • La traduction de l'objet graphique est réglé de telle sorte que la coordonnée (0,0) représente le coin supérieur gauche du composant.
  •   
  • rectangle clip de l'objet graphique est défini sur la zone du composant qui est dans le besoin de repeindre.
  •   

Alors, ceci est notre méthode résultante:

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

Ceci est aussi la meilleure façon au lieu d'utiliser Robot pour les composants visibles.


EDIT:

Il y a longtemps je le code que je posté ci-dessus, et cela a fonctionné, mais pas maintenant. Donc, je fouillèrent. J'ai un moyen de travail testé,. Il est sale, mais fonctionne. L'idée de ce fait un JDialog, mettre quelque part hors des limites de l'écran, réglez-le visible, puis tirer sur les graphiques.

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

Alors, cela fonctionnera également sous Windows et Mac. L'autre réponse a été de dessiner sur un écran virtuel. Mais cela ne veut pas besoin.

image écran affiche de classe comment cela peut être fait pour composants Swing. Je ne l'ai jamais essayé avec des composants AWT, j'acheter devinait le concept serait le même.

Que diriez-vous quelque chose comme ça. Le JFrame qui contient tous les composants ne sont pas visibles.


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

}

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top