PDA

View Full Version : Capture OpenGL screen C#



Livijn
11-05-2008, 03:08 AM
Hello!

I have a problem with capture my screen using OpenGL. I want to take screenshots when i'm ingame (Counter-Strike 1.6). This is my code and I'm using OpenTK.

public Bitmap GrabScreenshot()
{
Bitmap bmp = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);
System.Drawing.Imaging.BitmapData data =
bmp.LockBits(this.ClientRectangle, System.Drawing.Imaging.ImageLockMode.WriteOnly,
System.Drawing.Imaging.PixelFormat.Format24bppRgb) ;
GL.ReadPixels(0, 0, this.ClientSize.Width, this.ClientSize.Height, OpenTK.Graphics.PixelFormat.Bgr, PixelType.UnsignedByte,
data.Scan0);
bmp.UnlockBits(data);
bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
return bmp;
}
Calling the function with this.

Bitmap bmp = new Bitmap(GrabScreenshot());
bmp.Save("data/" + textBox1.Text + "/" + DateTime.Now.Minute + DateTime.Now.Second + ".jpg", System.Drawing.Imaging.ImageFormat.Jpeg);

The screenshots is all black.

_NK47
11-05-2008, 04:25 AM
can you check the memory output in data.Scan0 after the ReadPixels call? should have meaningful values instead bunch of zeros (black).
check the GetError() as well afterwards.

Livijn
11-05-2008, 04:51 AM
How do i check the memory output?
Well, I do get an error message now.
"Objektreferensen har inte angetts till en instans av ett objekt."
It's Swedish so I'll translate.
"The object's referens is not defined to an instance of an object."
The title of my error is in English though.
"NullReferenceException"

The error wants me to put a "new" before my code. This is the code giving me the error:

GL.ReadPixels(0, 0, this.ClientSize.Width, this.ClientSize.Height, OpenTK.Graphics.PixelFormat.Bgr, PixelType.UnsignedByte, data.Scan0);

_NK47
11-05-2008, 05:36 AM
im not familiar with C#. isnt it possible to see the inside the memory like whats the memory holding under a pointer? this would be the first step to see if ReadPixels returns black pixels or the Bitmap class is messing up. also you sure that data.Scan0 is pointing to pixels inside the Bitmap class (makes sense but who knows)?

Livijn
11-05-2008, 06:19 AM
I'm a beginner in C# so I don't really know the answer of those questions. Although, the Bitmap class is not the problem here i beleive. But, let's aim at my error, and it might get solved.

What should I do?

_NK47
11-05-2008, 06:31 AM
ok, guess this will be a looong thread. though i only could help in the OpenGL part here. if that.
check first if any errors are generated by OpenGL. after GL.ReadPixels call GL.GetError and see if its anything other then GL_NO_ERROR.

Livijn
11-05-2008, 06:57 AM
I can't do GL.GetError() because it won't gewt executed because of my error. :p

_NK47
11-05-2008, 07:16 AM
you should solve your error first. i really can't help on that. not into C#, sorry.
ask some C# forums maybe.

http://www.velocityreviews.com/forums/t101600-c-systemnullreferenceexception.html
http://www.daniweb.com/forums/thread127013.html

Warshade
11-05-2008, 09:22 AM
Use it like this:


Bitmap MyBitmap = GrabScreenshot();

Another problem could be that bmp.Unlockbits() is executed before GL.ReadPixels() is done writing to it. Simply add GL.Finish() after GL.ReadPixels() to ensure this is not the case.

Livijn
11-05-2008, 01:49 PM
Well, adding GL.Finish() after the GL.ReadPixels() didn't make any difference. Thanks though for trying!!

Livijn
11-06-2008, 12:40 PM
bump :D

Livijn
11-08-2008, 06:30 PM
I've solved 1 part. :) I added a glControl, and now i don't get any errors, not even with GL.GetError(). Although, i've got some other difficulties now. The screenshots is still all black and the screenshots is only as big as my application is. I want them to be as big as the screen is.

I'd really appreciate help! Really!

_NK47
11-10-2008, 03:15 AM
the code looks actually ok, the only thing i can think of is, do you grab the screenshot after drawing the scene?

Livijn
11-10-2008, 11:09 AM
I capture the screen before saving it.


public Bitmap GrabScreenshot()
{
Bitmap bmp = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);
System.Drawing.Imaging.BitmapData data = bmp.LockBits(this.ClientRectangle, System.Drawing.Imaging.ImageLockMode.WriteOnly,
System.Drawing.Imaging.PixelFormat.Format24bppRgb) ;
GL.ReadPixels(0, 0, 800, 600, OpenTK.Graphics.PixelFormat.Bgr, PixelType.UnsignedByte, data.Scan0);
GL.Finish();
bmp.UnlockBits(data);
bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
return bmp;
}
private void capScreen(object sender, EventArgs eArgs)
{
Bitmap bmp = GrabScreenshot();
bmp.Save("data/" + textBox1.Text + "/" + DateTime.Now.Minute + DateTime.Now.Second + ".jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
getPList();
}

And i create the surface in the beginning of my program.

_NK47
11-11-2008, 02:09 AM
no. do you capture the screen after you render the scene? i.e. you clear the backbuffer with black and capture it you will have a black bitmap. my thought was that you maybe accidentally capturing it in the wrong place.

Livijn
11-11-2008, 07:06 AM
ooh, nope, I don't do it like that.

_NK47
11-11-2008, 08:54 AM
last thing i would try is setting the read buffer with GL.ReadBuffer(GL_BACK). it's on GL_BACK by default but again, who knows what the problem is.

Livijn
11-12-2008, 02:01 AM
Where? Should i put it before GL.ReadPixels() or after?

_NK47
11-12-2008, 02:34 AM
before GL.ReadPixels(). it's a state and once set is applied to the rest of those calls until set otherwise. it's been a while i done glReadPixels() mainly because this is slow on some Intels, i switched to textures. but no doubt this is the only way to capture screenshots. though it is not that bigger deal to to use it i recommend you go over the documentation and check for some hints:
http://opengl.org/documentation/specs/man_pages/hardcopy/GL/html/gl/readpixels.html

Warshade
11-12-2008, 10:59 AM
the order of operations to obtain a screenshot must be like this:

1. draw scene
2. take the screenshot
3. swapbuffers

You're using an event to take the screenshot and if this is triggered after swapbuffers the backbuffer contents is undefined.

A simple workaround would be



private bool TakeScreenShot = false;

private void capScreen(object sender, EventArgs eArgs)
{
TakeScreenShot = true;
}

private void capScreenBeforeNextSwap()
{
Bitmap bmp = GrabScreenshot();
bmp.Save("data/" + textBox1.Text + "/" + DateTime.Now.Minute + DateTime.Now.Second + ".jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
getPList();
}

private void Render()
{
// draw scene

if (TakeScreenShot)
{
capScreenBeforeNextSwap()
TakeScreenShot = false;
}
this.SwapBuffers();
}


you should probably also change the line

GL.ReadPixels(0, 0, 800, 600, OpenTK.Graphics.PixelFormat.Bgr, PixelType.UnsignedByte, data.Scan0);

to

GL.ReadPixels(0, 0, this.ClientSize.Width, this.ClientSize.Height, OpenTK.Graphics.PixelFormat.Bgr, PixelType.UnsignedByte, data.Scan0);

Livijn
11-13-2008, 05:13 PM
I'm getting a little confused now. For example, you made a function (capScreen) which you don't use. Could you specify your could a little more so I can fully understand the structure and such.

Thanks for helping, btw!

_NK47
11-14-2008, 02:30 AM
can't believe we spend so many posts on fixing this little problem. :)
anyways, capScreen() just sets the bool to true and indicates that a screenshot should be taken. this value is then set to false if that happend. so you call capScreen() whenever you want a screenshot. really man, there is something wrong in your code. if you post more of your code it would be easier to imagine. did you at least checked the function link i posted?