Screenshot: Unterschied zwischen den Versionen
I0n0s (Diskussion | Beiträge) () |
BenBE (Diskussion | Beiträge) |
||
Zeile 1: | Zeile 1: | ||
− | = Allgemeines zum Screenshot erstellen = | + | == Allgemeines zum Screenshot erstellen == |
− | == Einleitung == | + | === Einleitung === |
Ein '''Screenshot''' ist ein Abbild des aktuellen Bildschirm Inhaltes. In dem Falle von OpenGL greifen wir aber nicht auf das Fenster zurück sondern auf den [[Framebuffer]]. Neben dem eigentlichen Vorgang, nämlich der Besorgung der Bilddaten, ist es meistens auch noch sinnvoll die Daten dem Benutzer verfügbar zu machen. Dies geschieht meistens in Form einer Bild-Datei oder eines Eintrages in der Zwischenablage. | Ein '''Screenshot''' ist ein Abbild des aktuellen Bildschirm Inhaltes. In dem Falle von OpenGL greifen wir aber nicht auf das Fenster zurück sondern auf den [[Framebuffer]]. Neben dem eigentlichen Vorgang, nämlich der Besorgung der Bilddaten, ist es meistens auch noch sinnvoll die Daten dem Benutzer verfügbar zu machen. Dies geschieht meistens in Form einer Bild-Datei oder eines Eintrages in der Zwischenablage. | ||
− | == Bilddaten besorgen == | + | === Bilddaten besorgen === |
Mit Hilfe des [[glReadPixels]] Befehls kann der [[Framebuffer]] ausgelesen werden. Für einen Screenshot sind meistens die [[RGB]]-Werte interessant. Bei diesen Werten ergibt sich auch schon das erste Problem: OpenGL speichert die Daten in der Reihenfolge Rot Grün Blau, während es in einigen Bildformaten (nämlich denen die auf Bitmaps aufbauen) üblich ist, die Daten in der Reihenfolge Blau Grün Rot zu speichern. Glücklicherweise kann man in aktuellen OpenGL Versionen [[glReadPixels]] anweisen die Daten auch in dieser Reihenfolge zu liefern. | Mit Hilfe des [[glReadPixels]] Befehls kann der [[Framebuffer]] ausgelesen werden. Für einen Screenshot sind meistens die [[RGB]]-Werte interessant. Bei diesen Werten ergibt sich auch schon das erste Problem: OpenGL speichert die Daten in der Reihenfolge Rot Grün Blau, während es in einigen Bildformaten (nämlich denen die auf Bitmaps aufbauen) üblich ist, die Daten in der Reihenfolge Blau Grün Rot zu speichern. Glücklicherweise kann man in aktuellen OpenGL Versionen [[glReadPixels]] anweisen die Daten auch in dieser Reihenfolge zu liefern. | ||
Zeile 16: | Zeile 16: | ||
Was die einzelnen Parameter bedeuten, könnt ihr im [[glReadPixels]] Artikel nachlesen. Es sei hier nur gesagt das das letzte Argument der Speicherplatz ist, an dem OpenGL die Daten abspeichern soll (meist ein Pointer z.B. auf ein Array). | Was die einzelnen Parameter bedeuten, könnt ihr im [[glReadPixels]] Artikel nachlesen. Es sei hier nur gesagt das das letzte Argument der Speicherplatz ist, an dem OpenGL die Daten abspeichern soll (meist ein Pointer z.B. auf ein Array). | ||
− | == Hinweis == | + | === Hinweis === |
Unter [[SDL]] finden sich die Bilddaten schon in der [[SDL_Surface|Displaysurface]] unter ''pixels''. | Unter [[SDL]] finden sich die Bilddaten schon in der [[SDL_Surface|Displaysurface]] unter ''pixels''. | ||
− | = Speichern | + | == Speichern des Screenshots == |
+ | Die Speicherung der Screenshots ist, sofern man erst einmal die Rohdaten der einzelnen Pixel hat, nicht mehr allzu kompliziert. Da der Computer aber ohne zusätzliche Informationen zu diesen Rohdaten später nichts mehr mit ihnen anfangen könnte, muss man einige zusätzliche Informationen mit in seine Zieldatei schreiben, die oftmals als "Header" bezeichnet werden und Informationen zu Breite, Höhe und "Pixelformat" beinhalten. | ||
− | Eine TGA-Datei hat einen | + | === Speichern im BMP Format === |
+ | |||
+ | Eine BMP-Datei hat einen relativ einfachen Aufbau. Als erstes kommen Header-Informationen (das Zeugs, was die Daten als Bitmap ausweist ;-)), dann FileInfo-Informationen (das, was angibt, wie groß das Bitmap ist) und schließlich die eigentlichen Bilddaten. | ||
+ | |||
+ | Der Header sieht so aus: | ||
+ | <pascal> BITMAPFILEHEADER = packed record | ||
+ | bfType: Word; | ||
+ | bfSize: DWORD; | ||
+ | bfReserved1: Word; | ||
+ | bfReserved2: Word; | ||
+ | bfOffBits: DWORD; | ||
+ | end; | ||
+ | |||
+ | BITMAPINFOHEADER = packed record | ||
+ | biSize: DWORD; | ||
+ | biWidth: Longint; | ||
+ | biHeight: Longint; | ||
+ | biPlanes: Word; | ||
+ | biBitCount: Word; | ||
+ | biCompression: DWORD; | ||
+ | biSizeImage: DWORD; | ||
+ | biXPelsPerMeter: Longint; | ||
+ | biYPelsPerMeter: Longint; | ||
+ | biClrUsed: DWORD; | ||
+ | biClrImportant: DWORD; | ||
+ | end;</pascal> | ||
+ | |||
+ | Nun zur Speicherfunktion für einen Screenshot: | ||
+ | <pascal>procedure ScreenShot(const Name : string); | ||
+ | var F : file; | ||
+ | FileInfo: BITMAPINFOHEADER; | ||
+ | FileHeader : BITMAPFILEHEADER; | ||
+ | pPicData:Pointer; | ||
+ | Viewport : array[0..3] of integer; | ||
+ | begin | ||
+ | //Speicher für die Speicherung der Header-Informationen vorbereiten | ||
+ | ZeroMemory(@FileHeader, SizeOf(BITMAPFILEHEADER)); | ||
+ | ZeroMemory(@FileInfo, SizeOf(BITMAPINFOHEADER)); | ||
+ | |||
+ | //Größe des Viewports abfragen --> Spätere Bildgrößenangaben | ||
+ | glGetIntegerv(GL_VIEWPORT, @Viewport); | ||
+ | |||
+ | //Initialisieren der Daten des Headers | ||
+ | FileHeader.bfType := 19778; //$4D42 = 'BM' | ||
+ | FileHeader.bfOffBits := SizeOf(BITMAPINFOHEADER)+SizeOf(BITMAPFILEHEADER); | ||
+ | |||
+ | //Schreiben der Bitmap-Informationen | ||
+ | FileInfo.biSize := SizeOf(BITMAPINFOHEADER); | ||
+ | FileInfo.biWidth := Viewport[2]; | ||
+ | FileInfo.biHeight := Viewport[3]; | ||
+ | FileInfo.biPlanes := 1; | ||
+ | FileInfo.biBitCount := 32; | ||
+ | FileInfo.biSizeImage := FileInfo.biWidth*FileInfo.biHeight*(FileInfo.biBitCount div 8); | ||
+ | |||
+ | //Größenangabe auch in den Header übernehmen | ||
+ | FileHeader.bfSize := FileHeader.bfOffBits + FileInfo.biSizeImage; | ||
+ | |||
+ | //Speicher für die Bilddaten reservieren | ||
+ | GetMem(pPicData, FileInfo.biSizeImage); | ||
+ | try | ||
+ | |||
+ | //Bilddaten von OpenGL anfordern (siehe oben) | ||
+ | glReadPixels(0, 0, Viewport[2], Viewport[3], GL_BGRA, GL_UNSIGNED_BYTE, pPicData); | ||
+ | |||
+ | //Und den ganzen Müll in die Daten schieben ;-) | ||
+ | //Moderne Leute nehmen dafür auch Streams ... | ||
+ | AssignFile(f, name); | ||
+ | Rewrite( f,1 ); | ||
+ | try | ||
+ | BlockWrite(F, FileHeader, SizeOf(BITMAPFILEHEADER)); | ||
+ | BlockWrite(F, FileInfo, SizeOf(BITMAPINFOHEADER)); | ||
+ | BlockWrite(F, pPicData^, FileInfo.biSizeImage ); | ||
+ | finally | ||
+ | CloseFile(f); | ||
+ | end; | ||
+ | finally | ||
+ | //Und den angeforderten Speicher wieder freigeben ... | ||
+ | FreeMem(pPicData, FileInfo.biSizeImage); | ||
+ | end; | ||
+ | end;</pascal> | ||
+ | |||
+ | === Speichern im TGA Format === | ||
+ | |||
+ | Eine TGA-Datei hat einen ähnlich einfachen Aufbau. Als erstes kommen auch hier Header-Informationen und dannach die eigentlichen Daten. | ||
+ | |||
+ | Der Header sieht diesmal so aus: | ||
<pascal>type | <pascal>type | ||
TTGAHEADER = packed record | TTGAHEADER = packed record | ||
Zeile 47: | Zeile 133: | ||
viewport : Array[0..3] of integer; | viewport : Array[0..3] of integer; | ||
begin | begin | ||
− | + | //Viewport-Größe lesen | |
glGetIntegerv(GL_VIEWPORT, @viewport); | glGetIntegerv(GL_VIEWPORT, @viewport); | ||
width := viewport[2]; | width := viewport[2]; | ||
height := viewport[3]; | height := viewport[3]; | ||
+ | //Größe der Daten berechnen | ||
DataSize := Width * Height * 3; | DataSize := Width * Height * 3; | ||
+ | //Größe des Puffers festlegen (Speicher reservieren) | ||
SetLength(DataBuffer,DataSize); | SetLength(DataBuffer,DataSize); | ||
Zeile 63: | Zeile 151: | ||
tgaHeader.tfBpp := 24; | tgaHeader.tfBpp := 24; | ||
+ | //Daten von OpenGL anfordern | ||
glReadPixels(0,0,Width, Height, GL_BGR, GL_UNSIGNED_BYTE, @DataBuffer[0]); | glReadPixels(0,0,Width, Height, GL_BGR, GL_UNSIGNED_BYTE, @DataBuffer[0]); | ||
Zeile 79: | Zeile 168: | ||
end; | end; | ||
</pascal> | </pascal> | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
[[Kategorie:Anleitung]] [[Kategorie:Technik_oder_Algorithmus]] | [[Kategorie:Anleitung]] [[Kategorie:Technik_oder_Algorithmus]] |
Version vom 21. Dezember 2005, 02:46 Uhr
Inhaltsverzeichnis
Allgemeines zum Screenshot erstellen
Einleitung
Ein Screenshot ist ein Abbild des aktuellen Bildschirm Inhaltes. In dem Falle von OpenGL greifen wir aber nicht auf das Fenster zurück sondern auf den Framebuffer. Neben dem eigentlichen Vorgang, nämlich der Besorgung der Bilddaten, ist es meistens auch noch sinnvoll die Daten dem Benutzer verfügbar zu machen. Dies geschieht meistens in Form einer Bild-Datei oder eines Eintrages in der Zwischenablage.
Bilddaten besorgen
Mit Hilfe des glReadPixels Befehls kann der Framebuffer ausgelesen werden. Für einen Screenshot sind meistens die RGB-Werte interessant. Bei diesen Werten ergibt sich auch schon das erste Problem: OpenGL speichert die Daten in der Reihenfolge Rot Grün Blau, während es in einigen Bildformaten (nämlich denen die auf Bitmaps aufbauen) üblich ist, die Daten in der Reihenfolge Blau Grün Rot zu speichern. Glücklicherweise kann man in aktuellen OpenGL Versionen glReadPixels anweisen die Daten auch in dieser Reihenfolge zu liefern.
Der glReadPixels Befehl für neue Grafikkarten könnte in etwa so aussehen:
// ungünstige Reihenfolge Rot Grün Blau glReadPixels( 0, 0, Breite, Hoehe, GL_RGB, GL_UNSIGNED_BYTE, Daten ); // auf neueren Grafikkarten: glReadPixels( 0, 0, Breite, Hoehe, GL_BGR, GL_UNSIGNED_BYTE, Daten );
Was die einzelnen Parameter bedeuten, könnt ihr im glReadPixels Artikel nachlesen. Es sei hier nur gesagt das das letzte Argument der Speicherplatz ist, an dem OpenGL die Daten abspeichern soll (meist ein Pointer z.B. auf ein Array).
Hinweis
Unter SDL finden sich die Bilddaten schon in der Displaysurface unter pixels.
Speichern des Screenshots
Die Speicherung der Screenshots ist, sofern man erst einmal die Rohdaten der einzelnen Pixel hat, nicht mehr allzu kompliziert. Da der Computer aber ohne zusätzliche Informationen zu diesen Rohdaten später nichts mehr mit ihnen anfangen könnte, muss man einige zusätzliche Informationen mit in seine Zieldatei schreiben, die oftmals als "Header" bezeichnet werden und Informationen zu Breite, Höhe und "Pixelformat" beinhalten.
Speichern im BMP Format
Eine BMP-Datei hat einen relativ einfachen Aufbau. Als erstes kommen Header-Informationen (das Zeugs, was die Daten als Bitmap ausweist ;-)), dann FileInfo-Informationen (das, was angibt, wie groß das Bitmap ist) und schließlich die eigentlichen Bilddaten.
Der Header sieht so aus:
BITMAPFILEHEADER = packed record bfType: Word; bfSize: DWORD; bfReserved1: Word; bfReserved2: Word; bfOffBits: DWORD; end; BITMAPINFOHEADER = packed record biSize: DWORD; biWidth: Longint; biHeight: Longint; biPlanes: Word; biBitCount: Word; biCompression: DWORD; biSizeImage: DWORD; biXPelsPerMeter: Longint; biYPelsPerMeter: Longint; biClrUsed: DWORD; biClrImportant: DWORD; end;
Nun zur Speicherfunktion für einen Screenshot:
procedure ScreenShot(const Name : string); var F : file; FileInfo: BITMAPINFOHEADER; FileHeader : BITMAPFILEHEADER; pPicData:Pointer; Viewport : array[0..3] of integer; begin //Speicher für die Speicherung der Header-Informationen vorbereiten ZeroMemory(@FileHeader, SizeOf(BITMAPFILEHEADER)); ZeroMemory(@FileInfo, SizeOf(BITMAPINFOHEADER)); //Größe des Viewports abfragen --> Spätere Bildgrößenangaben glGetIntegerv(GL_VIEWPORT, @Viewport); //Initialisieren der Daten des Headers FileHeader.bfType := 19778; //$4D42 = 'BM' FileHeader.bfOffBits := SizeOf(BITMAPINFOHEADER)+SizeOf(BITMAPFILEHEADER); //Schreiben der Bitmap-Informationen FileInfo.biSize := SizeOf(BITMAPINFOHEADER); FileInfo.biWidth := Viewport[2]; FileInfo.biHeight := Viewport[3]; FileInfo.biPlanes := 1; FileInfo.biBitCount := 32; FileInfo.biSizeImage := FileInfo.biWidth*FileInfo.biHeight*(FileInfo.biBitCount div 8); //Größenangabe auch in den Header übernehmen FileHeader.bfSize := FileHeader.bfOffBits + FileInfo.biSizeImage; //Speicher für die Bilddaten reservieren GetMem(pPicData, FileInfo.biSizeImage); try //Bilddaten von OpenGL anfordern (siehe oben) glReadPixels(0, 0, Viewport[2], Viewport[3], GL_BGRA, GL_UNSIGNED_BYTE, pPicData); //Und den ganzen Müll in die Daten schieben ;-) //Moderne Leute nehmen dafür auch Streams ... AssignFile(f, name); Rewrite( f,1 ); try BlockWrite(F, FileHeader, SizeOf(BITMAPFILEHEADER)); BlockWrite(F, FileInfo, SizeOf(BITMAPINFOHEADER)); BlockWrite(F, pPicData^, FileInfo.biSizeImage ); finally CloseFile(f); end; finally //Und den angeforderten Speicher wieder freigeben ... FreeMem(pPicData, FileInfo.biSizeImage); end; end;
Speichern im TGA Format
Eine TGA-Datei hat einen ähnlich einfachen Aufbau. Als erstes kommen auch hier Header-Informationen und dannach die eigentlichen Daten.
Der Header sieht diesmal so aus:
type TTGAHEADER = packed record tfType : Byte; tfColorMapType : Byte; tfImageType : Byte; tfColorMapSpec : Array[0..4] of Byte; tfOrigX : Word; //Array [0..1] of Byte; tfOrigY : Word; tfWidth : Word; tfHeight : Word; tfBpp : Byte; tfImageDes : Byte; end;
Nun zur Speicherfunktion für ein Screenshot ohne Alpha Wert
procedure ScreenShot(const Name : string); var DataBuffer : array of Byte; f : file; tgaHeader : TTGAHEADER; width, height : integer; DataSize:Integer; viewport : Array[0..3] of integer; begin //Viewport-Größe lesen glGetIntegerv(GL_VIEWPORT, @viewport); width := viewport[2]; height := viewport[3]; //Größe der Daten berechnen DataSize := Width * Height * 3; //Größe des Puffers festlegen (Speicher reservieren) SetLength(DataBuffer,DataSize); // TGA Kopf mit Daten füllen ZeroMemory(@tgaHeader, SizeOf(tgaHeader)); tgaHeader.tfImageType := 2; // TGA_RGB = 2 tgaHeader.tfWidth := Width; tgaHeader.tfHeight := Height; tgaHeader.tfBpp := 24; //Daten von OpenGL anfordern glReadPixels(0,0,Width, Height, GL_BGR, GL_UNSIGNED_BYTE, @DataBuffer[0]); //Datei erstellen AssignFile(f, Name); Rewrite( f,1 ); try // TGA Kopf in die Datei reinschreiben BlockWrite(F, tgaHeader, SizeOf(tgaHeader)); // Die eigentlichen Bilddaten in die Datei schreiben BlockWrite(f, DataBuffer[0], DataSize ); finally CloseFile(f); end; end;