Screen Capture To Texture (OpenGL / Slick2D)

Alright, so I’m trying to capture the current screen to a Texture object that I can render in all kinds of fun ways… I’m using the LWJGL tutorial on taking a screenshot, but rather than save that ByteBuffer to a file, I’m trying to create a Slick2D Texture from it.


	public static org.newdawn.slick.opengl.Texture getTextureFromScreen()
	{
		// Copies the current screen.
		GL11.glReadBuffer(GL11.GL_FRONT);
		int width = Display.getDisplayMode().getWidth();
		int height= Display.getDisplayMode().getHeight();
		
		java.nio.ByteBuffer buffer = org.lwjgl.BufferUtils.createByteBuffer(width * height * 4);		
		GL11.glReadPixels(0, 0, width, height, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer);				
		
		int textureId = GL11.glGenTextures();
		TextureImpl tex = new TextureImpl("screen", GL11.GL_TEXTURE_2D, textureId);
		
		// produce a texture from the byte buffer
		GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureId);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);				
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D,GL11.GL_TEXTURE_MIN_FILTER,GL11.GL_LINEAR);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D,GL11.GL_TEXTURE_MAG_FILTER,GL11.GL_LINEAR);
		GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, width, height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer);				
		
		return tex;			
	}

	public static void drawTexture(float x, float y, Texture texture, int width, int height, int srcX, int srcY, int srcWidth, int srcHeight, float red, float green, float blue, float alpha, float angle) 
	{		
		// If the provided texture string is null or empty, don't try to draw it. :)
		if(texture != null)
		{											
			texture.bind();
			
			float fSrcX = ((float)srcX / texture.getTextureWidth());
			float fSrcY = ((float)srcY / texture.getTextureHeight());
			float fSrcWidth = (((float)srcX + (float)srcWidth) / texture.getTextureWidth());
			float fSrcHeight = (((float)srcY + (float)srcHeight) / texture.getTextureHeight());			
			
			GL11.glPushMatrix();				
							
			// Rotation
			if(angle != 0)
			{
				GL11.glTranslatef(x + (width / 2), y + (height / 2), 0); // move to the proper position
				GL11.glRotatef(angle, 0, 0, 1); // now rotate
				GL11.glTranslatef(-1 *(x+ (width / 2)), -1 * (y+(height  / 2)), 0);				
			}
			
			GL11.glColor4f(red, green, blue, alpha);
			GL11.glBegin(GL11.GL_QUADS);
				// Top Left
				GL11.glTexCoord2f(fSrcX, fSrcY);
				GL11.glVertex2f(x,y);
				// Top Right
				GL11.glTexCoord2f(fSrcWidth, fSrcY);
				GL11.glVertex2f(x + width, y);
				// Bottom Right
				GL11.glTexCoord2f(fSrcWidth,fSrcHeight);
				GL11.glVertex2f(x + width,y + height);
				// Bottom Left
				GL11.glTexCoord2f(fSrcX,fSrcHeight);
				GL11.glVertex2f(x,y + height);
			GL11.glEnd();
			
			GL11.glPopMatrix();
		}			
	}

I’ve had some weird results thus far including the resulting texture only capturing one pixel’s color (at least that’s how it renders) and getting some errors about the ByteBuffer size. Any ideas??

  • Steve

I tried the drawTexture method with another Texture already loaded in and it worked fine, so the issue is definitely with the getTextureFromScreen method.

  • Steve

Just render the scene to a texture using an FBO instead of reading back the data to RAM and then reuploading it again, which is a magnitude or so slower.

I need a way to call back to the captured screen image though while I’m in a screen transition loop, so I figured having a Texture object to reference would be the best way.

Capturing the screen basically helps me avoid redrawing every object each step through the transition loop; instead I just draw the captured screen.

  • Steve

I’m also wondering if this issue has anything to do with the resulting texture’s dimensions not being a power of 2.

??? ??? ???

  • Steve

You’re missing my point. You don’t have to “capture” the screen to a texture. You can render everything onto a texture in the first place instead of rendering to the screen.

I understand what you’re saying, but I’ve already got a ton of code in place for this and the most pain-free way to integrate this functionality in is to copy the screen to a Texture. Otherwise, I’ll have to completely change how everything I’m doing is rendered.

Thanks for the advice either way.

  • Steve

Do you know how FBOs work?
You just bind the FBO and then do everything as you would normally do, but the output will be rendered into the FBO’s texture instead of the back buffer. No extra code needed for anything. :slight_smile:

Infact, Slick should do most of that for you. Just bind to an offscreen graphics context.

Cheers

Kev

Hey guys! I got it! Thank you! :slight_smile:

  • Steve