Bitmap Laden



  • Guten Abend,
    Ich habe mir heute einen Bitmap Loader geschrieben - und er treibt mich in die Verzweiflung - leider macht er nicht ganz, was er soll.
    nachdem ich e eine 150x200 Pixel große Bitmap geladen und gespeichert habe, ist die Differenz der Dateien am Ende knapp 1KB, ...
    Wenn ich das gespeicherte BMP mit Irfant View angucke, fehlt auch eine halbe Zeile - der Windows Interne Bildanzeiger streikt ganz. 😞

    class Image
    {
    public:
    	Image();
    	Image(const Image& other);
    	~Image();
    	Image& operator =(const Image& other);
    	virtual void Release();
    	bool IsEmpty() const;
    
    	// Dreckig, da keine Datenkappslung
    public:
    	unsigned long width;
    	unsigned long height;
    	unsigned long bits_per_pixel;
    	unsigned char* data;
    };
    
    ////////////////////////////////////////////////////////////////////////////////
    #include "BMPLoader.hpp"
    
    BMPLoader::BMPLoader()
    {
    }
    
    BMPLoader::~BMPLoader()
    {
    
    }
    
    BMPLoader::BMPLoader(const std::string& filename, Image& image)
    {
    	LoadImage(filename, image);
    }
    
    bool BMPLoader::LoadImage(const std::string& filename, Image& image)
    {
    	if(filename.empty())
    		return false;
    
    	FILE* file = NULL;
    
    	if((file = fopen(filename.c_str(), "rb")) == NULL)
    		return false;
    
    	// Bitmap File Header auslesen
    	BMPFileHeader bfh;
    	fread((void*) &bfh.bm_type[0],	1, 1, file);
    	fread((void*) &bfh.bm_type[1],	1, 1, file);
    	fread((void*) &bfh.file_size,	4, 1, file);
    	fread((void*) &bfh.reserved1,	2, 1, file);
    	fread((void*) &bfh.reserved2,	2, 1, file);
    	fread((void*) &bfh.offset_bits,	4, 1, file);
    
    	if(bfh.bm_type[0] != 'B' && bfh.bm_type[1] != 'M')
    		return false;
    
    	// Bitmap Infor Header auslesen
    	BMPInfoHeader bih;
    	fread((void*) &bih.size_header,			4, 1, file);
    	fread((void*) &bih.width,				4, 1, file);
    	fread((void*) &bih.height,				4, 1, file);
    	fread((void*) &bih.planes,				2, 1, file);
    	fread((void*) &bih.bits_per_pixel,		2, 1, file);
    	fread((void*) &bih.compression,			4, 1, file);
    	fread((void*) &bih.size_image,			4, 1, file);
    	fread((void*) &bih.x_pels_per_meter,	4, 1, file);
    	fread((void*) &bih.y_pels_per_meter,	4, 1, file);
    	fread((void*) &bih.color_used,			4, 1, file);
    	fread((void*) &bih.color_important,		4, 1, file);
    
    #if defined _DEBUG
    	printf("magic number     %c\n", bfh.bm_type[0]);
    	printf("magic number     %c\n", bfh.bm_type[1]);
    	printf("file size        %u\n", bfh.file_size);
    	printf("reserved1        %d\n", bfh.reserved1);
    	printf("reserved2        %d\n", bfh.reserved2);
    	printf("offset bits      %u\n", bfh.offset_bits);
    
    	printf("size header      %u\n", bih.size_header);
    	printf("widt             %d\n", bih.width);
    	printf("height           %d\n", bih.height);
    	printf("planes           %d\n", bih.planes);
    	printf("bits per pixel   %d\n", bih.bits_per_pixel);
    	printf("compression      %u\n", bih.compression);
    	printf("size image       %u\n", bih.size_image);
    	printf("x pels per meter %d\n", bih.x_pels_per_meter);
    	printf("y pels per meter %d\n", bih.y_pels_per_meter);
    	printf("color used       %u\n", bih.color_used);
    	printf("color important  %u\n", bih.color_important);
    #endif
    
    	// TODO: Andere Kompressionsverfahren implementieren
    	if(bih.compression != COMPRESSION_RGB)
    		return false;
    
    	// TODO: Andere Farbarten implementieren
    	if(bih.bits_per_pixel != 24)
    		return false;
    
    	image.Release();
    
    	image.width				= bih.width;
    	image.height			= bih.height;
    	image.bits_per_pixel	= bih.bits_per_pixel;
    
    	// Speicher anlegen
    	image.data				= new unsigned char[image.width * image.height * 3];
    
    	// Farbwerte auslesen
    	fread((void*) image.data, 3, image.width * image.height, file);
    	fclose(file);
    
    	return(true);
    }
    
    bool BMPLoader::SaveImage(const std::string& filename, const Image& image)
    {
    	if(filename.empty() || image.IsEmpty())
    		return false;
    
    	FILE* file = NULL;
    
    	if((file = fopen(filename.c_str(), "wb")) == NULL)
    		return false;
    
    	// TODO: Andere Farbarten implementieren
    	if(image.bits_per_pixel != 24)
    		return false;
    
    	// Bitmap File Header erstellen
    	BMPFileHeader bfh;
    	bfh.bm_type[0]	= 'B';
    	bfh.bm_type[1]	= 'M';
    	// Breite x Höhe x Farbkanalanzahl (je 1 byte) + (Größe File Header + Größe Info Header?) oder offset?!
    	bfh.file_size	= image.width * image.height * 3 + 54;
    	bfh.reserved1	= 0;
    	bfh.reserved2	= 0;
    	bfh.offset_bits	= 54; // bei allen 24 Bit Bildern!
    
    	// Bitmap File Header speichern
    	fwrite((void*) &bfh.bm_type[0],	1, 1, file);
    	fwrite((void*) &bfh.bm_type[1],	1, 1, file);
    	fwrite((void*) &bfh.file_size,	4, 1, file);
    	fwrite((void*) &bfh.reserved1,	2, 1, file);
    	fwrite((void*) &bfh.reserved2,	2, 1, file);
    	fwrite((void*) &bfh.offset_bits,4, 1, file);
    
    	// Bitmap Info Header erstellen
    	BMPInfoHeader bih;
    	bih.size_header			= 40;
    	bih.width				= image.width;
    	bih.height				= image.height;
    	bih.planes				= 1;
    	bih.bits_per_pixel		= image.bits_per_pixel;
    	bih.compression			= COMPRESSION_RGB;
    	// Breite x Höhe x Farbkanalanzahl (je 1 byte) + Größe File Header + Größe Info Header
    	bih.size_image			= image.width * image.height * 3;
    	bih.x_pels_per_meter	= 0;
    	bih.y_pels_per_meter	= 0;
    	bih.color_used			= 0;
    	bih.color_important		= 0;
    
    	// Bitmap Info Header speichern
    	fwrite((void*) &bih.size_header,		4, 1, file);
    	fwrite((void*) &bih.width,				4, 1, file);
    	fwrite((void*) &bih.height,				4, 1, file);
    	fwrite((void*) &bih.planes,				2, 1, file);
    	fwrite((void*) &bih.bits_per_pixel,		2, 1, file);
    	fwrite((void*) &bih.compression,		4, 1, file);
    	fwrite((void*) &bih.size_image,			4, 1, file);
    	fwrite((void*) &bih.x_pels_per_meter,	4, 1, file);
    	fwrite((void*) &bih.y_pels_per_meter,	4, 1, file);
    	fwrite((void*) &bih.color_used,			4, 1, file);
    	fwrite((void*) &bih.color_important,	4, 1, file);
    
    	// Farbwerte speichern
    	fwrite((void*) image.data, 3, image.width * image.height, file);
    	fclose(file);
    
    	return true;
    }
    
    ////////////////////////////////////////////////////////////////////////////////
    class BMPLoader : public ImageLoader
    {
    public:
    	BMPLoader();
    	BMPLoader(const std::string& filename, Image& image);
    	virtual ~BMPLoader();
    
    public:
    	virtual bool LoadImage(const std::string& filename, Image& image);
    	virtual bool SaveImage(const std::string& filename, const Image& image);
    
    public:
    	struct BMPFileHeader
    	{
    		char			bm_type[2];
    		unsigned long	file_size;
    		short int		reserved1;
    		short int		reserved2;
    		unsigned long	offset_bits;
    	};
    
    	struct BMPInfoHeader
    	{
    		unsigned int	size_header;
    		long			width;
    		long			height;
    		short int		planes;
    		short int		bits_per_pixel;
    		unsigned long	compression;
    		unsigned long	size_image;
    		long			x_pels_per_meter;
    		long			y_pels_per_meter;
    		unsigned long	color_used;
    		unsigned long	color_important;
    	};
    
    	enum BitCompression
    	{
    		// keine Kompression
    		COMPRESSION_RGB			= 0,
    		COMPRESSION_RLE8,
    		COMPRESSION_RLE4,
    		COMPRESSION_BITFIELDS
    	};
    };
    

    MfG Mandy



  • Die einzelnen Zeilen eines Bitmaps liegen auf Doppelwort-Bereichen, also immer ohne Rest durch 4 teilbaren Adressen. Also werden maximal 3 Bytes (ich gehe jetzt mal nicht von einem exotischen System aus) hinzugefügt, die normalerweise auf 0 gesetzt werden (müssen sie aber nicht).

    Und das ist denke ich das Problem, z.B. ist die Größe der Bilddaten nicht

    w*h*3 // wenn man von 24 bit ausgeht
    // sondern
    h*((w*(bitcount >> 3) + 3) & ~3)
    

    .
    Diese Größen müssen dann beim Laden und Speichern beachtet, und biSizeImage auch angepasst werden.

    Edit: Klammern vergessen



  • was du da machst ist echt schönes C...

    ...mit klassen.

    mach das besser mit den stream operatoren, da kannst du am ende auch sicher sein, dass alles da ist


Anmelden zum Antworten