Search code examples
c#openglscreenshotopentkstl-format

How to save an OpenGL rendering to disk


I'm using this library in order to render an STL:

enter image description here

How do we convert this STL into a BITMAP or IMAGE?

This method is responsible for generating the STL:

private void ReadSelectedFile(string fileName)
{
    STLReader stlReader = new STLReader(fileName);
    TriangleMesh[] meshArray = stlReader.ReadFile();
    modelVAO = new Batu_GL.VAO_TRIANGLES();
    modelVAO.parameterArray = STLExport.Get_Mesh_Vertices(meshArray);
    modelVAO.normalArray = STLExport.Get_Mesh_Normals(meshArray);
    modelVAO.color = Color.Crimson;

    minPos = stlReader.GetMinMeshPosition(meshArray);
    maxPos = stlReader.GetMaxMeshPosition(meshArray);
    orb.Reset_Orientation();
    orb.Reset_Pan();
    orb.Reset_Scale();

    if (stlReader.Get_Process_Error())
    { 
        modelVAO = null;
        /* if there is an error, deinitialize the gl monitor to clear the screen */
        Batu_GL.Configure(GL_Monitor, Batu_GL.Ortho_Mode.CENTER);
        GL_Monitor.SwapBuffers();
    }
}

How do generate an image/bitmap and save it?

I've stumbled upon this, specifically this method:

// Returns a System.Drawing.Bitmap with the contents of the current framebuffer
public static Bitmap GrabScreenshot()
{
    if (GraphicsContext.CurrentContext == null)
        throw new GraphicsContextMissingException();

    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, PixelFormat.Bgr, PixelType.UnsignedByte, data.Scan0);
    bmp.UnlockBits(data);

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

However, I'm getting this issue:

System.Runtime.InteropServices.ExternalException
  HResult=0x80004005
  Message=A generic error occurred in GDI+.
  Source=System.Drawing
  StackTrace:
   at System.Drawing.Image.Save(String filename, ImageCodecInfo encoder, EncoderParameters encoderParams)
   at System.Drawing.Image.Save(String filename, ImageFormat format)
   at System.Drawing.Image.Save(String filename)
   at STLViewer.AppMainForm.ReadSelectedFile(String fileName) in C:\Users\alexg\Source\Repos\STL-Viewer\STL-Viewer\AppMainForm.cs:line 152
   at STLViewer.AppMainForm.FileMenuImportBt_Click(Object sender, EventArgs e) in C:\Users\alexg\Source\Repos\STL-Viewer\STL-Viewer\AppMainForm.cs:line 162
   at System.Windows.Forms.ToolStripItem.RaiseEvent(Object key, EventArgs e)
   at System.Windows.Forms.ToolStripMenuItem.OnClick(EventArgs e)
   at System.Windows.Forms.ToolStripItem.HandleClick(EventArgs e)
   at System.Windows.Forms.ToolStripItem.HandleMouseUp(MouseEventArgs e)
   at System.Windows.Forms.ToolStripItem.FireEventInteractive(EventArgs e, ToolStripItemEventType met)
   at System.Windows.Forms.ToolStripItem.FireEvent(EventArgs e, ToolStripItemEventType met)
   at System.Windows.Forms.ToolStrip.OnMouseUp(MouseEventArgs mea)
   at System.Windows.Forms.ToolStripDropDown.OnMouseUp(MouseEventArgs mea)
   at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
   at System.Windows.Forms.ToolStrip.WndProc(Message& m)
   at System.Windows.Forms.ToolStripDropDown.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
   at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
   at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
   at System.Windows.Forms.Application.Run(Form mainForm)
   at STLViewer.Program.Main() in C:\Users\alexg\Source\Repos\STL-Viewer\STL-Viewer\Program.cs:line 19

Solution

  • Using the Method you provided in your question I just added a SaveFileDialog and a Save Bitmap item to the File menu with this Click event handler code and it seems to work just fine:

    private void saveBitmapToolStripMenuItem_Click(object sender, EventArgs e){
        if(saveFileDialog1.ShowDialog(this) != DialogResult.OK) return;
        var bmp = GrabScreenshot();
        bmp.Save(saveFileDialog1.FileName, ImageFormat.Bmp);
    }
    Bitmap GrabScreenshot(){
        if(GraphicsContext.CurrentContext == null) throw new GraphicsContextMissingException();
        var bmp = new Bitmap(ClientSize.Width, ClientSize.Height);
        var data = bmp.LockBits(ClientRectangle, System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
        GL.ReadPixels(0, 0, ClientSize.Width, ClientSize.Height, PixelFormat.Bgr, PixelType.UnsignedByte, data.Scan0);
        bmp.UnlockBits(data);
        bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
        return bmp;
    }