|
Getting the content of the OpenGL backbuffer is simple - writing
it to a file is also simple - but why does it always take so long ? This
snippet will show you a way how to save the screenshot in a background
thread with only a few lines of code. It uses the java.util.concurrent package.
We start with the easy part - getting the back buffer from OpenGL:
public Texture2D takeScreenShot() {
int width = getWidth();
int height = getHeight();
ByteBuffer bb = ByteBuffer.allocateDirect(width*height*3);
GL11.glReadBuffer(GL11.GL_BACK);
GL11.glReadPixels(0, 0, width, height, GL12.GL_BGR, GL11.GL_UNSIGNED_BYTE, bb);
Texture2D t = new Texture2D(TextureFormat.BGR, width, height);
t.setData(bb);
return t;
}
Now we need to get an ExecutorService - this class is used to execute the file writing in a seperate background thread. Be sure to only create one ExecutorService. I put this into the a screenshot action class.
if(execService == null) {
execService = Executors.newSingleThreadExecutor();
}
This class has a submit(Runnable) method that allows us to queue our TGAWriter class.
execService.submit(new TGAWriter(file, t));
The TGAWriter class implements the Runnable interface and performs the write in it's run() method.
public class TGAWriter implements Runnable {
final File file;
final Texture2D t;
public TGAWriter(File file, Texture2D t) {
this.file = file;
this.t = t;
}
public static void write(FileOutputStream os, Texture2D t) throws IOException {
if(t.getFormat() != TextureFormat.BGR) {
throw new UnsupportedOperationException("Not yet implemented");
}
// duplicate the ByteBuffer to preserve position in case of concurent access
ByteBuffer bb = t.getData()[0].duplicate();
byte[] header = new byte[18];
header[2] = 2; // TGA file version
header[12] = (byte)(t.getWidth() ); // 16 bit width, little endian
header[13] = (byte)(t.getWidth() >> 8);
header[14] = (byte)(t.getHeight() ); // 16 bit heigth, little endian
header[15] = (byte)(t.getHeight() >> 8);
header[16] = 24; // 24 bit color bit depth, little endian
os.write(header);
os.getChannel().write(bb);
}
public static void write(File file, Texture2D t) throws IOException {
FileOutputStream fos = new FileOutputStream(file);
try {
write(fos, t);
} finally {
fos.close();
}
}
public void run() {
try {
write(file, t);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
So I hope this snippet is useful for you.
|