Bitmap loader



  • Ich schriebe gerade eine Bmp Loader für ein Spiel.

    Quellcode:

    Headerdatei:

    //////////////////////////////////////////////////////////////////////////
    #include <cstdio>
    #include <cassert>
    
    //////////////////////////////////////////////////////////////////////////
    
    //////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////
    //
    // Speicherleichen vermeiden, TIFE KOPIEN!!!!
    // eventuell Imgae class erstellen
    //////////////////////////////////////////////////////////////////////////
    //////////////////////////////////////////////////////////////////////////
    
    #pragma  pack(1)
    struct ZpBMPFileHeader
    {
    	char			m_cType[2];		// 2 bytes
    	unsigned int	m_wFileSize;	// 4 bytes
    	short int		m_nReserved1;	// 2 bytes
    	short int		m_nReserved2;	// 2 bytes
    	unsigned int	m_wOffsetBits;	// 4 bytes;
    };
    #pragma pack()
    
    #pragma  pack(1)
    struct ZpBMPInfoHeader
    {
    	unsigned int	m_wSizeOfHeader;	// 4 bytes - 40
    	unsigned int	m_wWidth;			// 4 bytes
    	unsigned int	m_wHeight;			// 4 bytes
    	short int		m_nPlanes;			// 2 bytes
    	short int		m_nBitCount;		// 2 bytes
    	unsigned int	m_wCompression;		// 4 bytes
    	unsigned int	m_wSizeOfImage;		// 4 bytes
    	unsigned int	m_wXPelsPerMeter;	// 4 bytes
    	unsigned int	m_wYPelsPerMeter;	// 4 bytes
    	unsigned int	m_wColorUsed;		// 4 bytes;
    	unsigned int	m_wColorImportant;	// 4 bytes
    };
    #pragma pack()
    
    struct ZpImage
    {
    	unsigned int	m_wWidth;
    	unsigned int	m_wHeight;
    	unsigned int*	m_pARGB;
    };
    
    class ZpBmpLoader
    {
    private:
    	ZpBMPFileHeader	m_BmpFileHeader;
    	ZpBMPInfoHeader	m_BmpInfoHeader;
    	ZpImage			m_Image;
    
    public:
    	ZpBmpLoader(void);
    	ZpBmpLoader(const ZpBmpLoader	*Bitmap);
    	ZpBmpLoader(const char			*pFileName);
    
    	~ZpBmpLoader(void);
    
    	bool LoadBmpFile(const char *pFileName);
    	void Release(void);
    
    	void print(void)
    	{
    		printf("%x\n", m_Image.m_pARGB);
    		printf("%d\n", m_Image.m_wWidth);
    	}
    
    	ZpBmpLoader &operator=(const ZpBmpLoader	*Bitmap);
    };
    

    Quellcodedatei:

    //////////////////////////////////////////////////////////////////////////
    #include "ZpBmpLoader.h"
    
    //////////////////////////////////////////////////////////////////////////
    ZpBmpLoader::ZpBmpLoader(void)
    {
    }
    
    ZpBmpLoader::ZpBmpLoader(const ZpBmpLoader *Bitmap)
    {
    	m_Image.m_wWidth	= Bitmap->m_Image.m_wWidth;
    	m_Image.m_wHeight	= Bitmap->m_Image.m_wHeight;
    	m_Image.m_pARGB		= new(unsigned int[m_Image.m_wWidth * m_Image.m_wHeight]);
    
    	//*m_Image.m_pARGB = *Bitmap->m_Image.m_pARGB;
    	// oder
    
    	for(unsigned int h = 0; h < m_Image.m_wHeight; h ++)
    	{
    		for(unsigned int w = 0; w < m_Image.m_wWidth; w ++)
    		{
    			m_Image.m_pARGB[w + h * m_Image.m_wWidth] = Bitmap->m_Image.m_pARGB[w + h * Bitmap->m_Image.m_wWidth];
    		}
    	}
    
    	m_BmpFileHeader = Bitmap->m_BmpFileHeader;
    	m_BmpInfoHeader = Bitmap->m_BmpInfoHeader;
    }
    
    ZpBmpLoader::ZpBmpLoader(const char *pFileName)
    {
    	LoadBmpFile(pFileName);
    }
    
    ZpBmpLoader::~ZpBmpLoader(void)
    {
    	Release();
    }
    
    bool ZpBmpLoader::LoadBmpFile(const char *pFileName)
    {
    	FILE* pFile = fopen(pFileName, "rb");
    
    	if(!pFile)
    		return(false);
    
    	fread(&m_BmpFileHeader, 1, sizeof(ZpBMPFileHeader), pFile);
    
    	if((m_BmpFileHeader.m_cType[0] != 'B') && (m_BmpFileHeader.m_cType[1] != 'M'))
    		return(false);
    
    	fread(&m_BmpInfoHeader, 1, sizeof(ZpBMPInfoHeader), pFile);
    
    	m_Image.m_wWidth  = m_BmpInfoHeader.m_wWidth;
    	m_Image.m_wHeight = m_BmpInfoHeader.m_wHeight;
    
    	fseek(pFile, m_BmpFileHeader.m_wOffsetBits, SEEK_SET);
    
    	m_Image.m_pARGB = new(unsigned int[m_Image.m_wWidth * m_Image.m_wHeight]);
    	assert(!(m_Image.m_pARGB == NULL) && "Error allocating memory for image");
    
    	int iNumPaddedBytes = (m_Image.m_wWidth * 3) % 4;
    
    	unsigned char r, g, b;
    	unsigned char skip[4];
    
    	for(unsigned int h = 0; h < m_Image.m_wHeight; h ++)
    	{
    		for(unsigned int w = 0; w < m_Image.m_wWidth; w ++)
    		{
    			fread(&b, 1, 1, pFile);
    			fread(&g, 1, 1, pFile);
    			fread(&r, 1, 1, pFile);
    
    			m_Image.m_pARGB[w + h * m_Image.m_wWidth] = (r << 16 | g << 8 | b);
    		}
    
    		if(iNumPaddedBytes != 0)
    		{
    			fread(skip, 1, 4 - iNumPaddedBytes, pFile);
    		}
    	}
    
    	fclose(pFile);
    	return(true);
    }
    
    void ZpBmpLoader::Release(void)
    {
    	if(m_Image.m_pARGB != NULL)
    		delete[](m_Image.m_pARGB);
    	printf("release");
    }
    
    ZpBmpLoader &ZpBmpLoader::operator=(const ZpBmpLoader *Bitmap)
    {
    	if(this == Bitmap)
    		return(*this);
    	if(m_Image.m_pARGB)
    		Release();
    	m_Image.m_wWidth	= Bitmap->m_Image.m_wWidth;
    	m_Image.m_wHeight	= Bitmap->m_Image.m_wHeight;
    	m_Image.m_pARGB		= new(unsigned int[m_Image.m_wWidth * m_Image.m_wHeight]);
    
    	//*m_Image.m_pARGB = *Bitmap->m_Image.m_pARGB;
    	// oder
    
    	for(unsigned int h = 0; h < m_Image.m_wHeight; h ++)
    	{
    		for(unsigned int w = 0; w < m_Image.m_wWidth; w ++)
    		{
    			m_Image.m_pARGB[w + h * m_Image.m_wWidth] = Bitmap->m_Image.m_pARGB[w + h * Bitmap->m_Image.m_wWidth];
    		}
    	}
    
    	m_BmpFileHeader = Bitmap->m_BmpFileHeader;
    	m_BmpInfoHeader = Bitmap->m_BmpInfoHeader;
    
    	return(*this);
    }
    

    Der Bmp loader erzeugt leider nur Flache kopien, wenn ich ihm eine andere Instanz
    übergebe, Warum???

    Mein zweites Problem ist die Speicherverwaltung: Wenn ich eine Image löschen will, die gelöscht ist, gibt es einen Fehler und es kommt zum Absturz. Warum reichen folgende Zeilen zur Abfrage nicht aus?

    void ZpBmpLoader::Release(void)
    {
    	if(m_Image.m_pARGB != NULL)
    		delete[](m_Image.m_pARGB);
    	printf("release");
    }
    


  • wo ist der kopierkonstruktor?



  • Also der ZpBmpLoader class nen

    ZpBmpLoader(const ZpBmpLoader *bmp)
    

    verpassen.

    Wie soll der nun genau aussehen? Ähnlich wie der '=' operator?!



  • referenz auf ein objekt, kein zeiger.



  • Soll ich beim '=' operator auch lieber Referenzen verwenden?



  • class Foo
    {
    public:
    Foo(const Foo&);
    Foo& operator=(const Foo&);
    };
    

    erscheint mir sinnvoll.
    referenz bietet sich der einfachheit der übergabe des objektes meiner meinung nach an. (sonst müsstest du ja immer die methode o.operator=( &deinobjekt) aufrufen)



  • Stimmt, dann muss man auch nicht mehr so viele Fehlerchecks in die Methoden implentieren.

    Hast du noch ne Antwort auf das Release Problem?



  • ich frage mich grad, wozu du den kopierkonstruktor eigentlich brauchst.
    ich seh nur instanzen in deiner klasse, keine verweise, und kein new im konstruktor..



  • Warum, es wird doch m_Imgage.ARGB erneuert.
    Soll ich die ganze Klasse erneuern???



  • ein kopierkonstruktor (und ein selbsthergestellter zuweisungsoperator) und ein definierter destruktor sind dann nötig, wenn die klasse wirklich ein tiefe kopie braucht. das ist dann der fall, wenn das objekt über zeiger im konstruktor mit new ein objekt auf dem heap alloziert. da ein objekt für sich sorgen soll, muss es selber auch üblicherweise im destruktor dieses alloziierte stück wieder freigeben.

    wenn du eine kopie erstellst, besteht bei der flachen kopie in diesem von mir beschriebenen obigen fall dann das problem, dass der zeiger auf das im konstruktor allozierte objekt kopiert wird, und damit zwei zeiger auf dasselbe objekt bestehen.
    beim aufruf eines destruktors wird dann das objekt gelöscht, der zweite destruktoraufruf der kopie (zum beispiel) läßt das programm abstürzen, da nichts mehr auf dem heap reserviert ist.
    es ist wie siamesische zwillinge mit einem herzen.

    du musst in diesem fall, also wenn eine klasse über einen zeiger extra nochmal alloziert, für eine tiefe kopie sorgen.

    wenn du in einer methode (nicht konstruktor) allozierst, und den zeiger nicht irgendwie weitergibst, erzeugst du speicherlöcher.

    http://fara.cs.uni-potsdam.de/~kaufmann/?page=GenCppFaqs&faq=BigThree#Answ



  • Ja, dass ist schon klar.



  • sorry, hab dein programm noch nicht ganz durchstiegen.
    der vortrag war voreilig 🙂



  • Sind auch ne ganze menge Zeilen, auf jeden Fall will ich ne Image class erstellen (statt Membervariable), sodass ich auch TGAs, Gifs und Jpgs laden kann.



  • mach erstmal das

    ZpBmpLoader::ZpBmpLoader(void)
    {
    	m_Image.m_pARGB=NULL;
    }
    

    dein m_Image ist einfach nicht angesprungen.

    aber mir ist das alles zu gefährlich, soll man ein könner draufschaun 🙂



  • Wozu soll das gut sein???



  • alleine bei falscheingabe einer datei stürzt dein programm sonst schon ab, bei aufruf von release..

    bool ZpBmpLoader::LoadBmpFile(const char *pFileName)
    {
        FILE* pFile = fopen(pFileName, "rb");
    
        if(!pFile)
            return(false);
    

    du fragst ja in release ab auf

    if(m_Image.m_pARGB != NULL)
    

    aber wo wird der zeiger m_pARGB genullt? überseh ich das?



  • Stimmt, soloche Sachen übersieht man schnell!!!



  • und ebenso hier..

    if((m_BmpFileHeader.m_cType[0] != 'B') && (m_BmpFileHeader.m_cType[1] != 'M'))
            return(false);
    

Anmelden zum Antworten