Search code examples
javascreenshotjogl

JOGL: Taking a screenshot of a GLCanvas in a JFrame using Component.printAll() doesn´t work


I have a JFrame with a GLCanvas on. When I call the JFrame´s Component.printAll() method and then print the Graphics2D object to a png file (using BufferedImage and ImageIO.write()), then the printAll() method haven´t catched the GLCanvas. The JFrame is there on the png image but the GLCanvas is all gray.

What have I missed?

Sample code to reproduce the problem. Look at the generated png file named "image.png" after running the code. You will see everything except the GLCanvas. The code snippet that creates the image is at the bottom of the constructor.

import com.sun.opengl.util.Animator;
import java.awt.Graphics2D;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.*;
import javax.imageio.ImageIO;
import javax.media.opengl.*;
import javax.media.opengl.glu.GLU;
import javax.swing.JFrame;

public class JOGLTest implements GLEventListener {

    GLU glu = new GLU();
    JFrame frame = new JFrame("JOGL");
    GLCanvas canvas = new GLCanvas();    
    public static void main(String[] args) {
        JOGLTest joglTest = new JOGLTest();
    }

    public JOGLTest() {
        canvas.addGLEventListener(this);
        frame.add(canvas);
        frame.setSize(500, 500);
        final Animator animator = new Animator(canvas);
        frame.addWindowListener(new WindowAdapter() {

            @Override
            public void windowClosing(WindowEvent e) {
                new Thread(new Runnable() {

                    public void run() {
                        animator.stop();
                        System.exit(0);
                    }
                }).start();
            }
        });

        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

        animator.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException ex) {
            Logger.getLogger(JOGLTest.class.getName()).log(Level.SEVERE, null, ex);
        }

        BufferedImage image = new BufferedImage(frame.getWidth(), frame.getHeight(), BufferedImage.TYPE_INT_RGB);         
        Graphics2D g = image.createGraphics();        
        frame.printAll(g);        
        image.flush();

        try {
            ImageIO.write(image, "png", new File("image.png"));
        } catch (IOException ex) {
            Logger.getLogger(JOGLTest.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void init(GLAutoDrawable drawable) {
        GL gl = drawable.getGL();
        gl.setSwapInterval(1);
        gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        gl.glShadeModel(GL.GL_SMOOTH);
    }

    public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
        GL gl = drawable.getGL();
        GLU glu = new GLU();
        if (height <= 0) {        
            height = 1;
        }
        final float h = (float) width / (float) height;
        gl.glViewport(0, 0, width, height);
        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glLoadIdentity();
        glu.gluPerspective(20.0f, h, 1.0, 550.0);
        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glLoadIdentity();
    }

    private int eyeX = 0;
    private int eyeY = 0;
    private int eyeZ = 130;
    private int centerX = 0;    

    public void display(GLAutoDrawable drawable) {
        GL gl = drawable.getGL();
        gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
        gl.glLoadIdentity();
        glu.gluLookAt(eyeX, eyeY, eyeZ, centerX, 0.0, 0.0, 0.0, 1.0, -0.0);
        gl.glBegin(GL.GL_LINES);
        gl.glVertex2d(1, 1);
        gl.glVertex2d(20, 20);
        gl.glEnd();
        gl.glFlush();
    }  
    public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {
    }
}

Solution

  • Use GLJPanel instead of GLCanvas:

    GLJPanel panel = new GLJPanel();
    

    Then just replace your canvas variable with panel and it should work. GLCanvas sometimes causes compatibility problems when used inside swing components.

    From GLCanvas api page:

    GLJPanel is provided for compatibility with Swing user interfaces when adding a heavyweight doesn't work either because of Z-ordering or LayoutManager problems.