1
\$\begingroup\$

Hope this is a better place to ask things like this. I have a 2D scene with some sprites drawn in Swing frame. I need them to be saved as an image. The problem is every tutorial I found seem to be obsolete.

I found using glReadPixels and putting result to a BufferedImage should help but all those snippets look stale, API has changed. I'm using JOGL 2.1.5 that is current at the moment.

I have a feeling this should be a widely known use-case for professionals. Any help would be appreciated!

Updated: I adopted some stale snippets (please, don't tell me API hasn't changed significantly, there're dozens of movings and renamings) No the problem is I'm getting black screen in png file. It comes from byte buffer being read to with zeros only.

   public static void main(String[] args) {
        TestPNGSaver saver = new TestPNGSaver();
        saver.run();
    }

    private void writeBufferToFile(GLAutoDrawable drawable, File outputFile) {
        int width = drawable.getWidth();
        int height = drawable.getHeight();

        ByteBuffer pixelsRGB = Buffers.newDirectByteBuffer(width * height * 3);

        GL2 gl = drawable.getGL().getGL2();

        gl.glReadBuffer(GL.GL_BACK);
        gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, 1);

        gl.glReadPixels(0, 0, width, height, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, pixelsRGB);

        int[] pixels = new int[width * height];

        int firstByte = width * height * 3;
        int sourceIndex;
        int targetIndex = 0;
        int rowBytesNumber = width * 3;

        for (int row = 0; row < height; row++) {
            firstByte -= rowBytesNumber;
            sourceIndex = firstByte;
            for (int col = 0; col < width; col++) {
                if (pixelsRGB.get(sourceIndex) != 0) {
                    System.out.println(sourceIndex);
                }

                int iR = pixelsRGB.get(sourceIndex++);
                int iG = pixelsRGB.get(sourceIndex++);
                int iB = pixelsRGB.get(sourceIndex++);

                pixels[targetIndex++] = 0xFF000000
                        | ((iR & 0x000000FF) << 16)
                        | ((iG & 0x000000FF) << 8)
                        | (iB & 0x000000FF);
            }

        }

        BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);

        bufferedImage.setRGB(0, 0, width, height, pixels, 0, width);

        try {
            ImageIO.write(bufferedImage, "PNG", outputFile);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void display(GLAutoDrawable drawable) {
        GL2 gl = drawable.getGL().getGL2();
        gl.glClear(GL.GL_COLOR_BUFFER_BIT);
        gl.glBegin(GL.GL_TRIANGLES);
        gl.glColor3f(1, 0, 0);
        gl.glVertex2f(-1, -1);
        gl.glColor3f(0, 1, 0);
        gl.glVertex2f(0, 1);
        gl.glColor3f(0, 0, 1);
        gl.glVertex2f(1, -1);
        gl.glEnd();
    }

    private void run() {
        GLProfile.initSingleton();
        GLProfile glp = GLProfile.getDefault();
        GLCapabilities caps = new GLCapabilities(glp);
        final GLCanvas canvas = new GLCanvas(caps);
        canvas.addGLEventListener(this);

        Frame frame = new Frame();
        frame.setSize(400, 400);
        frame.add(canvas);
        frame.setVisible(true);

        frame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                writeBufferToFile(canvas, new File("/whatever/text.png"));
                System.exit(0);
            }
        });
    }
\$\endgroup\$
  • 2
    \$\begingroup\$ You should try to give people a starting point. What have you done so far? What part of it are you getting hung up on? \$\endgroup\$ – MichaelHouse Apr 3 '14 at 15:57
  • \$\begingroup\$ I'm sorry. I've updated it. Will be able to provide snippets later. \$\endgroup\$ – Viktor Stolbin Apr 3 '14 at 16:05
  • \$\begingroup\$ Are you sure you want to read the back buffer, not the front? Maybe you are taking the screenshot before things got drawn into framebuffer? Maybe try first a scenario when you grab screenshot during the application runing instead of when it closes. That's only my suggestions. \$\endgroup\$ – luke1985 Apr 4 '14 at 8:57
2
\$\begingroup\$

OK, you seem to be pretty confused so here is some code I used with LWJGL which is very similar to JOGL. Probably you will have to swap the buffers with arrays, but the code will be almost identicall.

Here is the method that takes screenshot by writing the screen data to a BufferedImage (the width and height are the dimensions of the screen, the buffer get gets the next element in the buffer - next byte in this case):

public BufferedImage makeScreenshot() {
    BufferedImage screenshot = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    Graphics graphics = screenshot.getGraphics();

    ByteBuffer buffer = BufferUtils.createByteBuffer(width * height * 3);

    glReadPixels(0, 0, width, height, GL_RGB, GL_BYTE, buffer);


    for (int h = 0; h < height; h++) {
        for (int w = 0; w < width; w++) {
            // The color are the three consecutive bytes, it's like referencing
            // to the next consecutive array elements, so we got red, green, blue..
            // red, green, blue, and so on..
            graphics.setColor(new Color( buffer.get()*2, buffer.get()*2, buffer.get()*2 ));
            graphics.drawRect(w,height - h, 1, 1); // height - h is for flipping the image
        }
    }
    return screenshot;
}

And here how to write that data to a file:

      try {

            BufferedImage screenshot = makeScreenshot();

            ImageIO.write(screenshot, "png", new File("D:/screen.png"));
        } catch (IOException ex) {
             // You know ... what to do here :P
        }

Hope I've helped :)

\$\endgroup\$
  • \$\begingroup\$ Appreciated! This really helped. I managed to adoped some old stuff. I've updated my question \$\endgroup\$ – Viktor Stolbin Apr 4 '14 at 1:28
  • \$\begingroup\$ I must say you actually helped me to start using LWJGL and Slick2D instead. Nicely done =) It works now but screenshot seems to look greyed. Colors look like saturation gets down. What might be wrong with it? \$\endgroup\$ – Viktor Stolbin Apr 4 '14 at 22:50
  • \$\begingroup\$ I found this and it works fine for me lwjgl.org/wiki/index.php?title=Taking_Screen_Shots \$\endgroup\$ – Viktor Stolbin Apr 4 '14 at 23:02
  • \$\begingroup\$ the issue is only to multiply each buffer.get() by 2, so you should have to write: "new Color(buffer.get() * 2 , buffer.get() *2, buffer.get() * 2" in the first line of loop. It's due to the fact that glReadPixels with the current params returns only values between 0- 127, while the Color constructor takes values in ragne 0- 256. Sorry for not mentioning that. \$\endgroup\$ – luke1985 Apr 5 '14 at 10:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.