OpenGL -> Dargestelltes als Bild speichern in c#



  • Moin zusammen.
    Ich suche gerade nach einem Weg, wie ich mit ReadPixel das OpenGL Bild als jpg, bmp, png und tga speichern kann.
    .Net bietet ja ein paar Funktionen an um etwas als Bild zu speichern, aber wenn ich versuche ein IntPtr zu speichern, dann krieg ich Errors...

    Mein Speichercode:

    IntPtr picture = new IntPtr();
    GL.ReadPixels(0, 0, Width, Height, PixelFormat.Rgb, PixelType.Bitmap, picture);
    Image.FromHbitmap(picture).Save("MeinBild.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
    

    Das Ganze ist erst mal nur für JPG, die anderen Formate werden ja ähnlich abgearbeitet.
    Leider kriege ich Fehlermeldungen. Die Fehlermeldung bezieht sich auf die letzte Zeile und lautet: Allgemeiner Fehler in GDI+.

    Wenn jemand mir helfen kann, wär froh darum, evt n Beispielcode posten wenn ich alles ganz falsch gemacht habe. 😉

    Grüsse
    Chiller



  • Hallo

    Es erstaunt nicht, dass du beliebige Fehlermeldungen bekommst, denn dein Zeiger zeigt ins nichts. In C# ist ein Statement var Picture = new IntPtr(); in etwa äquivalent zu einem C++ Statement à la void *Pointer = nullptr; . Du hast zwar einen Zeiger, aber noch keinen Speicher, auf den gezeigt wird.

    Also: Berechnen, wie viel Speicher ein komplettes Frame aus deinem OpenGL Kontext benötigt und dann entsprechend viel reservieren. Nach dem Gebrauch bitte nicht die Freigabe des Speichers vergessen 😉

    MfG



  • Wie kann ich dem Pointer Speicher zuordnen? Hab bislang Pointer nur in der Theorie behandelt und nie selber verwendet... 😞
    Gibt es irgendwo ein Beispiel wie so was aussehen würde?

    Grüsse
    Chiller



  • Ja, spätestens hier wird die Programmierung mit C# und .NET sehr mühsamm. Es gibt zwei Möglichkeiten, die hier in Frag kommen: unsafe-Blöcke oder halt explizites Marshaling (eigentlich hat das Thema nur entfernt mit Marshaling zu tun).

    Bezgl. unsafe schau mal hier.



  • Hallo, Du verwendest wahrscheinlich OpenTK.

    Für ein Projekt verwende ich folgenden Code für "Screenshots" eines glControls:

    public Bitmap GrabScreenshot()
    {
    	if (GraphicsContext.CurrentContext == null)
    		return null;
    
    	glControl1.MakeCurrent();
    	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.OpenGL.PixelFormat.Bgr, PixelType.UnsignedByte, data.Scan0);
    	bmp.UnlockBits(data);
    	bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
    	return bmp;
    }
    

    Der Code liegt in einer von UserControl abgeleiteten Klasse und beinhaltet ein glControl mit Dock = DockStyle.Fill. Deswegen wird ClientSize/ClientRectangle für die Größen verwendet.
    Das sollte mit kleinen Anpassungen auch problemlos für den Fullscreen-Modus (also ohne glControl) funktionieren.



  • Hey super, endlich kann ich speichern. Vielen Dank! 😃
    Nur sehe ich jetzt gerade, dass Antialiasing nicht übernommen wird, auch wenn es im Window angezeigt wird.

    Wenn ich das ganze Sauber und unverpixelt haben will, muss ich dann ne render-Engine einbauen, welche das für mich erledigt oder gibt es dafür auch eine Funktion. Immerhin, wenn ich mein Bild drucken will, sollte es nicht verpixelt sein.

    Im Moment reicht das aber noch vollkommen.

    Edit:
    Wenn ich das Bild speichere, ist es irgendwie viel breiter als im Programm.
    Ich verwende folgende Funktion zum speichern:

    Image.FromHbitmap(GrabScreenshot().GetHbitmap()).Save("MeinBild.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
    

    Grüsse
    Chiller



  • Das hier speichert ein jpg in wahlfreier Qualität und unverzerrt:

    GrabScreenshot().SaveCompressed(@"C:\hurrdurr.jpg", 99);
    
    public static void SaveCompressed(this Image image, string path, long quality)
    {
    	EncoderParameters eps = new EncoderParameters(1);
    	eps.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);
    
    	ImageCodecInfo ici = GetCodecInfo("image/jpeg");
    
    	if (ici != null)
    		image.Save(path, ici, eps);
    	else
    	{
    		//Fehlerbehandlung
    	}
    }
    
    public static ImageCodecInfo GetCodecInfo(String mimeType)
    {
    	ImageCodecInfo[] encoders;
    	encoders = ImageCodecInfo.GetImageEncoders();
    	for (int i = 0; i < encoders.Length; ++i)
    	{
    		if (encoders[i].MimeType == mimeType)
    			return encoders[i];
    	}
    	return null;
    }
    


  • Ich merke gerade, dass wenn ich das Bild speichere, dass ich dann eine veraltete Ansicht speichere. Das heisst, das beim speichern des Bildes, ein Abbild erstellt wird, wie es vor ein paar Sekunden ausgesehen hat.

    Kann es daran liegen, dass ich den glControll1.Invalidate() nur dann aufrufe wenn es nötig ist (bei änderung des Inhaltes z.B.)?

    Grüsse
    Chiller


Anmelden zum Antworten