t7.reyeslua's picture

Save Rendering to disk ??

Hello,

I'm trying to save the rendering to disk and found the OpenTK tutorial that supposedly tells how to do it (http://www.opentk.com/doc/graphics/save-opengl-rendering-to-disk). I changed it a little bit so it could work with glControl1 of Windows.Forms but it throws me an Argument Exception that according to the MSDN website (http://msdn.microsoft.com/en-us/library/5ey6h79d.aspx) it's because there's something wrong with "System.Drawing.Imaging.PixelFormat.Format24bppRgb". It tells that: "The PixelFormat is not a specific bits-per-pixel value. -or- The incorrect PixelFormat is passed in for a bitmap."

What do I have wrong?..What should I do?

Thanks! :)

This is my code:

      private void buttonSavePlot_Click(object sender, EventArgs e)
        {
            Bitmap img = GrabScreenshot();
            img.Save("C:\\plot.bmp");
            img.Dispose();
        }
 
        public Bitmap GrabScreenshot()
        {
            if (GraphicsContext.CurrentContext == null)
                throw new GraphicsContextMissingException();
 
            Rectangle r = new Rectangle(glControl1.Location, glControl1.Size);
            Bitmap bmp = new Bitmap(glControl1.Width, glControl1.Height);
 
            BitmapData data = bmp.LockBits(r, System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
 
           GL.ReadPixels(0, 0, glControl1.Width, glControl1.Height, OpenTK.Graphics.OpenGL.PixelFormat.Bgr, PixelType.UnsignedByte, data.Scan0);
            bmp.UnlockBits(data);
 
            bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
            return bmp;            
        }

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
the Fiddler's picture

System.Drawing is a little strange in its handling of pixel formats. Try using OpenTK.Graphics.OpenGL.PixelFormat.Bgra and System.Drawing.Imaging.PixelFormat.Format32bppRgba - these formats have always worked for me.

t7.reyeslua's picture

Hey,

thanks for the reply :) but it still doesn't work for me. I changed it to OpenTK.Graphics.OpenGL.PixelFormat.Bgra and to System.Drawing.Imaging.PixelFormat.Format32bppArgb and still nothing. Actually I couldn't find "Format32bppRgba" as you said. VS told me that there was no definition for it in System.Drawing.Imaging.PixelFormat, that's why I used "Format32bppArgb". I thought it would be the most similar since both "A" stand for "alpha channel"...I guess :S

Thanks

       private void buttonSavePlot_Click(object sender, EventArgs e)
        {
 
            Bitmap img = GrabScreenshot();
            img.Save("C:\\plot.bmp");
            img.Dispose();
 
        }
 
        public Bitmap GrabScreenshot()
        {
            if (GraphicsContext.CurrentContext == null)
                throw new GraphicsContextMissingException();
 
            Rectangle r = new Rectangle(glControl1.Location, glControl1.Size);
            Bitmap bmp = new Bitmap(glControl1.Width, glControl1.Height);
 
            BitmapData data = bmp.LockBits(r, System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            GL.ReadPixels(0, 0, glControl1.Width, glControl1.Height, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);
            bmp.UnlockBits(data);
 
 
            bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
            return bmp;
 
        }
the Fiddler's picture

Yeah, that's the one (Format32bppArgb).

  • Which is the actual line that throws the exception?
  • Do you really have write access to the root of the C:\ drive?

Note that "C:\\plot.bmp" is not portable, not even on different windows installations (there's no guarantee that a C: drive exists). Better use a safe location, such as "My Pictures":

// The 'using' pattern ensures img.Dispose() is called even if an exception is thrown.
using (Bitmap img = GrabScreenshot())
{
    string path = System.IO.Path.Combine(
        Environment.GetFolderPath(Environment.SpecialFolders.MyPictures),
        "plot.bmp");
    img.Save(path);
}
t7.reyeslua's picture

Hi,

The actual line that throws the exception is
BitmapData data = bmp.LockBits(r, System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

It says the t the Format32bppArgb is an invalid parameter. And yes, I do have write access to the root of C:\, but actually C:\\plot.bmp was not my actual path. I put it that way just to make the code clearer..I thought :P .

Thanks for your help!! :)

the Fiddler's picture

Now that's strange.

Try passing the PixelFormat to the Bitmap constructor, i.e. new Bitmap(glControl1.Width, glControl1.Height System.Drawing.Imaging.PixelFormat.Format32bppArgb).

Mincus's picture

The problem is your rectangle, you need to switch the glControl1.Location to just a new Point(). Currently you're specifying an area partially outside the bitmap and LockBits doesn't clip the rectangle (for obvious reasons).

t7.reyeslua's picture

Yeah..I know.

Still the same error with new Bitmap(glControl1.Width, glControl1.Height System.Drawing.Imaging.PixelFormat.Format32bppArgb) :S

I have changed my GrabScreenshot() to this and it "sort of" works:

        public Bitmap GrabScreenshot()
        {
            if (GraphicsContext.CurrentContext == null)
                throw new GraphicsContextMissingException();
 
            Rectangle r = new Rectangle(glControl1.Location, glControl1.Size);
 
            //this.ClientSize instead of glControl1.....This way it doesn't throw any exception
            Bitmap bmp = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);
            //Bitmap bmp = new Bitmap(glControl1.Width, glControl1.Height,System.Drawing.Imaging.PixelFormat.Format32bppArgb);
 
            BitmapData data = bmp.LockBits(r, System.Drawing.Imaging.ImageLockMode.WriteOnly,System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            GL.ReadPixels(0, 0, glControl1.Width, glControl1.Height, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);
 
            bmp.UnlockBits(data);            
            bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
            return bmp;
        }

The problem now is that the image saved is obviously not the size of glControl1 and it doesn' show correctly the actual render. I attach hereby the 2 images: the saved render and a printScreen of how it should actually look.

I don't know if the problem is related to the place in the code wehre I'm saving the render or what. I think the place is fine. I used the advices from this thread: http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Numb... . I save the render before glControl1.SwapBuffers();

My code then looks something like this:

  private void Render()
        {
            // Clear the back buffer.
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
 
            GL.MatrixMode(MatrixMode.Modelview);
            GL.LoadIdentity();           
 
            //Camera
            GL.Translate(0f, 0f, CameraZoom);
            GL.Rotate(CameraRotX, Vector3.UnitY);
            GL.Rotate(CameraRotY, Vector3.UnitX);
 
            GL.CallList(index);
 
            DrawPoints();                   
 
            if (takeScreenshot)
            {
                takeScreenshot = false;
                using (Bitmap img = GrabScreenshot())
                {
                    string screenshotFilename = "PlotImages\\plot_" + 
                                                DateTime.Now.Day.ToString("00")   + "-" + 
                                                DateTime.Now.Month.ToString("00") + "_" +
                                                DateTime.Now.Hour.ToString("00")  +
                                                DateTime.Now.Minute.ToString("00")+
                                                DateTime.Now.Second.ToString("00")+ ".png";
 
                    string path = System.IO.Path.Combine(Environment.CurrentDirectory,screenshotFilename);                                    
                    img.Save(path, System.Drawing.Imaging.ImageFormat.Png);
                }
            }      
 
            // Display the final rendering to the user
            glControl1.SwapBuffers();
 
        }
 
        private bool takeScreenshot = false;
        private void buttonSavePlot_Click(object sender, EventArgs e)
        {
            takeScreenshot = true;   
        }

..and the GrabScreenshot() that is written above.

Thanks once again for your help!!! :)

AttachmentSize
plot_27-04_134705.png18.7 KB
plot_like_it_should_be_saved.PNG9.62 KB
douglas125's picture

Well, this works for me:
(sOGL is my glControl)

            Bitmap bmp = new Bitmap(sOGL.Width, sOGL.Height);
            System.Drawing.Imaging.BitmapData data = bmp.LockBits(new Rectangle(0, 0, sOGL.Width, sOGL.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly,
                System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            GL.ReadPixels(0, 0, sOGL.Width, sOGL.Height, PixelFormat.Bgr, PixelType.UnsignedByte, data.Scan0);
            GL.Finish();
            bmp.UnlockBits(data);
            bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
 
            bmp.Save("c:\\teste.png", System.Drawing.Imaging.ImageFormat.Png);

Maybe it's the pixel format?

the Fiddler's picture

You need to pass ClientSize.Width/Height to both GL.ReadPixels and the Bitmap constructor. If you pass ClientSize to one and Size to the other (as you do here you'll get the corruption you are seeing.

t7.reyeslua's picture

Hey,

That`s right!! I discovered the mistake yesterday and tried to post it here to let future OpenTK newbies like me know about it :P The problem was that yesterday the website was down :S .

       public Bitmap GrabScreenshot()
        {
            if (GraphicsContext.CurrentContext == null)
                throw new GraphicsContextMissingException();
 
            Bitmap bmp = new Bitmap(glControl1.ClientSize.Width, glControl1.ClientSize.Height);
            BitmapData data = bmp.LockBits(glControl1.ClientRectangle, System.Drawing.Imaging.ImageLockMode.WriteOnly,System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            GL.ReadPixels(0, 0, glControl1.ClientSize.Width, glControl1.ClientSize.Height, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);
 
            bmp.UnlockBits(data);            
            bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
            return bmp;
        }

By the way, I read in the tutorial that it should be easy to save a video once you can save the images but I can't find how. Does anyone knows a way? or better yet, Have some code to do it?? :D

Thanks, Fiddler! ..you´re truly the OpenTK master ;)