FunnyFunkyBuggy
FunnyFunkyBuggy

Reputation: 613

Exporting an image from screen(viewport) in java OpenGL

I am a newbie in OpenGL programming. I am making a java program with OpenGL. I drew many cubes inside. I now wanted to implement a screenshot function in my program but I just couldn't make it work. The situation is as follow :

What should I do? Are there any good suggestions for me to export the screen contents to an image file using OpenGL? I heard that there are several libraries available but I don't know which one is suitable. Any help is appreciated T_T (plz forgive me if I have any grammatical mistakes ... )

Upvotes: 1

Views: 1461

Answers (3)

elect
elect

Reputation: 7190

You can do something like this, supposing you are drawing to the default framebuffer:

    protected void saveImage(GL4 gl4, int width, int height) {

        try {

            GL4 gl4 = GLContext.getCurrentGL().getGL4();

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

            ByteBuffer buffer = GLBuffers.newDirectByteBuffer(width * height * 4);

            gl4.glReadBuffer(GL_BACK);
            gl4.glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);

            for (int h = 0; h < height; h++) {
                for (int w = 0; w < width; w++) {
                    graphics.setColor(new Color((buffer.get() & 0xff), (buffer.get() & 0xff),
                            (buffer.get() & 0xff)));
                    buffer.get();   
                    graphics.drawRect(w, height - h, 1, 1);
                }
            }
            BufferUtils.destroyDirectBuffer(buffer);
            File outputfile = new File("D:\\Downloads\\texture.png");
            ImageIO.write(screenshot, "png", outputfile);
        } catch (IOException ex) {
              Logger.getLogger(EC_DepthPeeling.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

Essentially you create a bufferedImage and a direct buffer. Then you use Graphics to render the content of the back buffer pixel by pixel to the bufferedImage. You need an additional buffer.get(); because that represents the alpha value and you need also height - h to flip the image.

Edit: of course you need to read it when there is what you are looking for.

You have several options:

  • trigger a boolean variable and call it directly from the display method, at the end, when everything you wanted has been rendered
  • disable the automatic buffer swapping, call from the key listener the display() method, read the back buffer and enable the swapping again
  • call from the key listener the same code you would call in the display

Upvotes: 2

Xirema
Xirema

Reputation: 20386

When you use buffers with LWJGL, they almost always need to be directly allocated. The OpenGL library doesn't really understand how to interface with Java Arrays™, and in order for the underlying memory operations to work, they need to be applied on natively-allocated (or, in this context, directly allocated) memory.

If you're using LWJGL 3.x, that's pretty simple:

//Check the math, because for an image array, given that Ints are 4 bytes, I think you can just allocate without multiplying by 4.
IntBuffer ib = org.lwjgl.BufferUtils.createIntBuffer(Constants.PanelSize.width * Constants.PanelSize.height);

And if that function isn't available, this should suffice:

//Here you actually *do* have to multiply by 4.
IntBuffer ib = java.nio.ByteBuffer.allocateDirect(Constants.PanelSize.width * Constants.PanelSize.height * 4).asIntBuffer();

And then you do your normal code:

Constants.gl.glPixelStorei(GL2.GL_UNPACK_ALIGNMENT, 1);
Constants.gl.glReadPixels(0, 0, Constants.PanelSize.width, Constants.PanelSize.height, GL2.GL_RGBA, GL2.GL_UNSIGNED_BYTE, ib);
System.out.println(Constants.gl.glGetError());
int[] bb = new int[Constants.PanelSize.width * Constants.PanelSize.height];
ib.get(bb); //Stores the contents of the buffer into the int array.
ImageExport.savePixelsToPNG(bb, Constants.PanelSize.width, Constants.PanelSize.height, "imageFilename.png");

Upvotes: 0

Krzysztof Cichocki
Krzysztof Cichocki

Reputation: 6414

You could use Robot class to take screenshot:

BufferedImage screenshot = new Robot().createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));
ImageIO.write(screenshot, "png", new File("screenshot.png"));

There are two things to consider:

You take screenshot from screen, you could determine where the cordinates of you viewport are, so you can catch only the part of interest.

Something can reside a top of you viewport(another window), so the viewport could be hided by another window, it is unlikely to occur, but it can.

Upvotes: 0

Related Questions