3

My goal is to draw things with opentk in c# on a GL_Control (a gui control) AND also save it to a Bitmap object everytime the paint event is called. I have this code:

private void glControl1_Paint(object sender, PaintEventArgs e)
{
  // do lots of drawing here
  GL.Finish();
  GL.Flush();
  glControl1.SwapBuffers();
  gl_image = TakeScreenshot();
}

public Bitmap TakeScreenshot()
{
    if (GraphicsContext.CurrentContext == null)
        throw new GraphicsContextMissingException();
    int w = glControl1.ClientSize.Width;
    int h = glControl1.ClientSize.Height;
    Bitmap bmp = new Bitmap(w, h);
    System.Drawing.Imaging.BitmapData data =
        bmp.LockBits(glControl1.ClientRectangle, System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
    GL.ReadPixels(0, 0, w, h, PixelFormat.Bgr, PixelType.UnsignedByte, data.Scan0);
    bmp.UnlockBits(data);

    bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
    return bmp;
}

so inside the paint event after swaping the buffer, I take the schreenshot. The problem is that the captured image is of the state before the drawing. That means if I want to capture the image, I need to run the paint event twice. I've tried GL.flush and finish and swapbuffer. Does any one know how to overcome this problem. Also note that I've tried using an asynchronous way but that failed because you cannot access the opentk image data from another thread.

Chris
  • 681
  • 1
  • 6
  • 16
max
  • 9,708
  • 15
  • 89
  • 144

2 Answers2

2

I had the EXACT same problem, here's how I solved it. When you call glControl1.Invalidate() to refresh the image, OpenTK actually does that at the VERY END. So if you are grabbing the screenshot during that tick, the buffer won't be updated until the next cycle.

What you need to do is to force glControl1 to refresh, here is the code.

public void GLrefresh()
{
    glControl1.Invalidate();
    glControl1.Update();
    glControl1.Refresh();
}

Call this function right before you grab the screenshot

GLrefresh();
Image I = TakeScreenshot();
//Now Image I should be the image of the current buffer
Chris
  • 681
  • 1
  • 6
  • 16
0

Chris. Thanks. Your idea works. I was hoping to make as efficient as possible. Here is an improved version:

bool must_redraw = true;
private void glControl1_Paint(object sender, PaintEventArgs e){
  must_redraw = !must_redraw;
  // ...
}

private void timer1_Tick(object sender, EventArgs e)
{
    if (must_redraw)
    {
        glControl1.Refresh();// redraws and updates
        gl_image = TakeScreenshot();
    }
}

still, it does double the paint operation which slows down drawing by a factor of 2 so if anyone has an alternative idea, please post.

max
  • 9,708
  • 15
  • 89
  • 144