C: TGA-Screenshots aus einer OpenGL Anwendung

dxx

Newbie
Registriert
Jan. 2006
Beiträge
4
Hallo

zuerst ne kleine Vorstellung:
Ich bin ja nun neu hier und fange an ein wenig C zu lernen. Dafür habe ich mich natürlich gleich übernommen und ein altes Softwareprojekt übernommen - bzw. ein auf SDL und OpenGL basierendes Spiel: D2X.

Dort gibt es eine Option um Screenshots zu erzeugen. dies funktioniert alles relativ gut, solange ich Windows jedoch nu auf 16Bit Farbtiefe betreibe. Auf 32Bit werden die Bilder jedoch total verzerrt dargestellt.

Den Fehler vermute ich irgendwo im TGA-Header. Doch da bin ich sehr überfordert. Also wende ich mich mal an euch, da ein sehr guter Freund sehr gute Erfahrungen mit diesem Forum gemacht hat.

Natürlich wäre es zu viel verlangt euch den gesamten Quellcode aufzudrücken. Daher geb ich euch mal die entsprechende Funktion, die das TGA dann kalkuliert und schreibt. Ich hoffe, dass dort der Fehler irgendwo versteckt ist.

Code:
void write_bmp(char *savename,int w,int h,unsigned char *buf){
	int f;
#ifdef _WIN32
	f=open(savename,O_CREAT|O_EXCL|O_WRONLY,S_IREAD|S_IWRITE);
#else
	f=open(savename,O_CREAT|O_EXCL|O_WRONLY,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
#endif
	if (f>=0){
		GLubyte    targaMagic[12] = { 0, //no identification field
			 0,//no colormap
			 2,//RGB image (well, BGR, actually)
			 0, 0, 0, 0, 0, 0, 0, 0, 0 };//no colormap or image origin stuff.
		GLubyte blah;
		int r;
		GLubyte *s;
 		int x,y;
		
		//write .TGA header.
		write (f,targaMagic,sizeof(targaMagic));
		blah=w%256;write (f,&blah,1);//w, low
		blah=w/256;write (f,&blah,1);//w, high
		blah=h%256;write (f,&blah,1);//h, low
		blah=h/256;write (f,&blah,1);//h, high
		blah=24;write (f,&blah,1);//24 bpp
		blah=0;write (f,&blah,1);//no attribute bits, origin is lowerleft, no interleave
		
		s=buf;
		for (y=0;y<h;y++){//TGAs use BGR ordering of data.
			for (x=0;x<w;x++){
				blah=s[0];
				s[0]=s[2];
				s[2]=blah;
				s+=3;				
			}
		}
		x=0;y=w*h*3;
		while (y > 0)
		{
			r=write(f,buf+x,y);
			if (r<=0){
				mprintf((0,"screenshot error, couldn't write to %s (err %i)\n",savename,errno));
				break;
			}
			x+=r;y-=r;
		}
		close(f);
	}else{
		mprintf((0,"screenshot error, couldn't open %s (err %i)\n",savename,errno));
	}
}

Wie ihr seht, ist das nicht der allerbeste Code...
Das Projekt läuft übrigens, wie ihr am ifdef sehen könnt plattformunabhängig. Unter Linux läuft es auch, und da scheint es relativ egal zu sein, in welcher Farbtiefe ich Screenshots mache.

Ich hoffe, dass jemand eine Idee hat. Falls jemand doch mehr Code sehen möchte, kann ich diesen gerne komplet zur Verfügung stellen (natürlich nicht in einem Post ;) )

EDIT: hier ein Beispielbild: http://mitglied.lycos.de/zicoc/dxx/scrn0000.tga

Vielen Dank im Vorraus
Christian
 
Zuletzt bearbeitet: (anmerkung - erweiterung)
Hallo dxx,

das Problem wird wohl darin liegen, das in 16 Bit Farbtiefe der Bildschirm anders organisiert ist als in 32 Bit Farbtiefe.

In 32 Bit Farbtiefe wird ein Pixel in 32 Bit (RGBA) dargestellt. In 16 Bit Farbtiefe reichen dazu 16 Bit. Das gilt zumindest für den Windows Desktop. In OpenGL müsste das aus Performancegründen eigentlich auch so sein.

Mit OpenGl kenn ich mich jetzt nicht so aus. Da Deine Anwendung anscheinend portabel sein soll könnte es schwieirg werden. Es gibt wohl Win32 spezifische OpenGl Erweiterungen.

Am besten wühlst Du dich mal durch die MSDN Hilfe Seiten.

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/opengl/ntopnglo_47eb.asp

MfG

Arnd
 
Hallo und Danke für die Antwort :)

Das Stichwort RGBA von dir ist sehr gut, dann mir ist aufgefallen, dass legiglich GL_RGB übermittelt wird, anstatt GL_RGBA. Setz ich dies auf GL_RGBA , stürzt das Programm jedoch direkt bei Aufruf der Funktion ab. Ich vermute deshalb fast, dass das Problem tiefer liegen könnte..... Ich hoffe jedoch, dass ich mich da irre, denn sonst liegt der "bug" viel tiefer. :)
 
Hall dxx,

die Frage ist wo das Programm abstürzt. Das Problem ist sicher nicht einfach mit dem Austausch eines Parameters zu lösen.

Wenn die Bitformate des Quellbildes unterschiedlich sind und die Bilddatei manuell erzeugt wird, dann musst Du schon auch in die Decodierung des Quellbildes eingreifen.

Vor allem schreibst Du in den TGA Header fix 24 Bit Farbtiefe rein. Wenn jetzt aber 32 Bit Bilder die Quelle sind, sind Bildstörungen vorprogrammiert. Vielleicht stimmt ja auch einfach der Offset nicht, mit dem Du durch die Quelldaten gehst. Also nicht immer +3 Byte (für RGB), sondern +4Byte( für RGBA). Und den Alpha Kanal einfach ignorieren.

MfG

Arnd
 
So. Habe mich weiter ins Thema reingearbeitet und die Funktion komplett umgeschrieben (mit einiger Hilfe von Google :) ).

Ich befinde mich immer noch im Lernvorgang und muss gestehen, dass mir einiges immer noch unklar ist, doch funktioniert es nun:

Code:
typedef struct {
      unsigned char TGAheader[12];
      unsigned char header[6];
} TGA_header;

//writes out an uncompressed RGB .tga file
//if we got really spiffy, we could optionally link in libpng or something, and use that.
void write_bmp(char *savename,int w,int h,unsigned char *buf){ 
	FILE* TGAFile;
	TGA_header TGA;
	GLbyte HeightH,HeightL,WidthH,WidthL;

	buf = (unsigned char*)calloc(w*h*3,sizeof(unsigned char));

	glReadPixels(0,0,w,h,GL_BGR_EXT,GL_UNSIGNED_BYTE,buf);

	TGAFile = fopen(savename, "wb");

	HeightH = (GLbyte)(h / 256);
	HeightL = (GLbyte)(h % 256);
	WidthH  = (GLbyte)(w / 256);
	WidthL  = (GLbyte)(w % 256);
	// Write TGA Header
	TGA.TGAheader[0] = 0;
	TGA.TGAheader[1] = 0;
	TGA.TGAheader[2] = 2;
	TGA.TGAheader[3] = 0;
	TGA.TGAheader[4] = 0;
	TGA.TGAheader[5] = 0;
	TGA.TGAheader[6] = 0;
	TGA.TGAheader[7] = 0;
	TGA.TGAheader[8] = 0;
	TGA.TGAheader[9] = 0;
	TGA.TGAheader[10] = 0;
	TGA.TGAheader[11] = 0;
	TGA.header[0] = (GLbyte) WidthL;
	TGA.header[1] = (GLbyte) WidthH;
	TGA.header[2] = (GLbyte) HeightL;
	TGA.header[3] = (GLbyte) HeightH;
	TGA.header[4] = (GLbyte) 24;
	TGA.header[5] = 0;
	fwrite(&TGA,sizeof(TGA_header),1,TGAFile);
	fwrite(buf,w*h*3*sizeof(unsigned char),1,TGAFile);
	fclose(TGAFile);
	d_free(buf);
}
 
Hallo dxx,

prima das es geht. Nur noch als Anmerkung. Das Programm läuft stabiler wenn Du die Rückgabewerte von calloc, fopen, ... prüfen würdest und auch nur dann weitermachst, wenn die Aufrufe erfolgreich waren.

Die Speicherallokierung kann fehlschlagen, das anlegen der Datei kann schiefgehen, ...

MfG

Arnd
 
Hi Arnd

Danke für den Hinweis.
Aber das wurde bereits erledigt. :)
Im weiteren Quellcode befindet sich eine weitere Funktion, welche bereits die bildschirmdaten einliest und schon den entsprechenden Zähler (ebenso für Stereobrillen/Monitore) setzt. Diese Funktion rief ursprünglich die write_bmp auf. Dortwurde auch der Buffer eingelesen und die werte später auc wieder dort hin übergeben wo sie gegengecheckt wurden.

Ich weiss nicht, warum das so gemacht wurde, aber ich hab das nun etwas umgelagert. :)
 
Zurück
Oben