Frage bezüglich des Schreibens einer BMP datei in Datei



  • Hallo, versuche gerade eine BMP datei via fstream in eine andere Datei zu schreiben. Zwar erzeugt der Code keinen Fehler und IRGENDWAS wird auch in die Datei geschrieben/kopiert, jedoch scheinbar nur Teile. Das Laden in die Bitmapklasse dürfte aufjedenfall funktionieren.

    Hiermal der Code, sowie ein Auszug aus der Klasse die zum laden des Bitmaps benutzt wird(nicht von mir):

    #include <fstream>
    #include <iostream>
    #include "Bitmap.h"
    using namespace std;
    
    int main () 
    {
      Bitmap test("test.bmp");
     cout << endl;
     fstream myfile("temp.bmp",ios::out | ios::binary);
     myfile.write((char*)&(test.bmfh),sizeof(BitmapFileHeader));
     myfile.write((char*)&(test.bmih),sizeof(BitmapInfoHeader));
     myfile.write((char*)(test.colours),sizeof(RGBQuad)*256);
     myfile.write(test.data,test.dataSize);
    
     /*
      // open it for output then write to it
      fstream myfile;
      myfile.open("test.txt",ios::out | ios::trunc);
    
      if (myfile.is_open())   {
         myfile << "This outputting a line.\n";
         myfile.close();
      }
     */
     myfile.close();
     getchar();
      return 0;
    }
    
    //load a bitmap from a file and represent it correctly
    //in memory
    bool Bitmap::loadBMP(char* file) {
        FILE *in;                  //file stream for reading
        char *tempData;       //temp storage for image data
        int numColours;            //total available colours
    
        //bitmap is not loaded yet
        loaded=false;
        //make sure memory is not lost
        if(colours!=0) {
            delete[] colours;
        }
        if(data!=0) {
            delete[] data;
        }
    
        //open the file for reading in binary mode
        in=fopen(file,"rb");
    
        //if the file does not exist return in error
        if(in==NULL) {
            error="File not found";
            fclose(in);
            return false;
        }
    
        //read in the entire BITMAPFILEHEADER
        fread(&bmfh,sizeof(BitmapFileHeader),1,in);
    	cout << "sizeof(BitmapFileHeader)=" << sizeof(BitmapFileHeader) << endl;
        //check for the magic number that says this is a bitmap
        if(bmfh.bfType!=BITMAP_MAGIC_NUMBER) {
            error="File is not in DIB format";
            fclose(in);
            return false;
        }
    
        //read in the entire BITMAPINFOHEADER
        fread(&bmih,sizeof(BitmapInfoHeader),1,in);
    	cout << "sizeof(BitmapInfoHeader)=" << sizeof(BitmapInfoHeader) << endl;
    
        //save the width, height and bits per pixel for external use
        width=bmih.biWidth;
        height=bmih.biHeight;
        bpp=bmih.biBitCount;
    	cout << "biBitCount      =" << bmih.biBitCount << endl;
    	cout << "biClrImportant  =" << bmih.biClrImportant << endl;
    	cout << "biClrUsed       =" << bmih.biClrUsed << endl;
    	cout << "biCompression   =" << bmih.biCompression << endl;
    	cout << "biHeight        =" << bmih.biHeight << endl;
    	cout << "biPlanes        =" << bmih.biPlanes << endl;
    	cout << "biSize          =" << bmih.biSize << endl;
    	cout << "biSizeImage     =" << bmih.biSizeImage << endl;
    	cout << "biWidth         =" << bmih.biWidth << endl;
    	cout << "biXPelsPerMeter =" << bmih.biXPelsPerMeter << endl;
    	cout << "biYPelsPerMeter =" << bmih.biYPelsPerMeter << endl;
    
        //calculate the size of the image data with padding
        dataSize=(width*height*(unsigned int)(bmih.biBitCount/8.0));
    
        //calculate the number of available colours
        numColours=1<<bmih.biBitCount;
        cout << numColours << endl;
        //if the bitmap is not 8 bits per pixel or more
        //return in error
        if(bpp<8) {
            error="File is not 8 or 24 bits per pixel";
            fclose(in);
            return false;
        }
    
        //load the palette for 8 bits per pixel
        if(bpp==8) {
        	colours=new RGBQuad[numColours];
        	fread(colours,sizeof(RGBQuad),numColours,in);
        }
    
        //set up the temporary buffer for the image data
        tempData=new char[dataSize];
    
        //exit if there is not enough memory
        if(tempData==NULL) {
            error="Not enough memory to allocate a temporary buffer";
            fclose(in);
            return false;
        }
    
        //read in the entire image
        fread(tempData,sizeof(char),dataSize,in);
    
        //close the file now that we have all the info
        fclose(in);
    
        //calculate the witdh of the final image in bytes
        byteWidth=padWidth=(int)((float)width*(float)bpp/8.0);
    
        //adjust the width for padding as necessary
        while(padWidth%4!=0) {
            padWidth++;
        }
    
        //change format from GBR to RGB
        if(bpp==8) {
        	loaded=convert8(tempData);
       	}
        else if(bpp==24) {
        	loaded=convert24(tempData);
       	}
    
        //clean up memory
        delete[] tempData;
    
        //bitmap is now loaded
        error="Bitmap loaded";
    
        //return success
        return loaded;
    }
    

    Das Ergebnis sieht so aus:
    http://up.picr.de/4198297.jpg

    Im Original natürlich ohne die komische Huntergrundfarbe sowie mit weiteren schwarzen Linien.

    Ich vermute, dass es an der Umwandlung von BGR zu RGB liegt, aber wenn ich die Ausschalte ist in der Ausgabedatei gar nichts mehr 😕

    Hoffe man kann den Post einigermaßen lesen, ich weiß ist sehr viel 😕
    Vielen Dank für eure Hilfe 🙂

    Ps: Wenn das hier in n anderes Forum muss, dann bitte verschieben, Danke.



  • (char*)&(test.bmfh)
    

    BitmapFileHeader ist doch ein struct/class, oder? Wie kommst du darauf, dass Der Adressoperator da das richtige macht?
    Kannst du denn die Schreib/Leseoperationen nicht in vernünftige Methoden auslagern? Dass man ein Bitmap öffnen und speichern kann?

    class Bitmap {
    public:
        void open( const char* file );
        bool write( const char* file );
    };
    

    Dein BitmapFileHeader kann dann z.B. ein writeTo(ostream& stream); bekommen, womit du den Header in einen Stream schreiben kannst. Dann ist nömlich die jeweilige Klasse für ihre eigenen Infos verantwortlich, und nicht am Ende der User selber!



  • Na klar könnte ich das, aber darum gehts mir ja (zunächst) gar nicht. Ich will erstmal nur den Bitmap in eine anderee Datei schreiben. Wenn das funktioniert, gehts weiter.

    Ich hab das so in einer Anleitung gesehen, in der komplexe Datenstrukturen(im Beispiel eine struktur) in eine binarydatei geschrieben wurden und zwar mit dem adressoperator. Ohne gibt es einen Compilerfehler
    Ist auch sinniger als ohne?! Die Struktur kann ich ja schlecht zu nem Zeiger auf char umwandeln.



  • Du könntest jetzt bitte die Klassendefinition von (z.B.) BitmapFileHeader posten.
    Und nur weil du es so gesehen hast, heißt das nicht dass es bei deiner BitmapFileHeader-Klasse klappt. Es ist eher unwahrscheinlich, dass die Klasse so im Speicher liegt, dass dein Objekt korrekt in einen char* umgewandelt wird, nur durch den cast, so dass der korrekte Bitmap-Header rauskommt!



  • #ifndef _BITMAP_H
    #define _BITMAP_H
    //File: Bitmap.h
    //Written by:     Mark Bernard
    //on GameDev.net: Captain Jester
    //e-mail: mark.bernard@rogers.com
    //Please feel free to use and abuse this code as much
    //as you like.  But, please give me some credit for
    //starting you off on the right track.
    //
    //The file Bitmap.cpp goes along with this file
    //
    #include <iostream>
    #include <cstdio>
    #include <string>
    using namespace std;
    
    const short BITMAP_MAGIC_NUMBER=19778;
    const int RGB_BYTE_SIZE=3;
    
    #pragma pack(push,bitmap_data,1)
    
    typedef struct tagRGBQuad {
    	char rgbBlue;
    	char rgbGreen;
    	char rgbRed;
    	char rgbReserved;
    } RGBQuad;
    
    typedef struct tagBitmapFileHeader {
    	unsigned short bfType;
    	unsigned int bfSize;
    	unsigned short bfReserved1;
    	unsigned short bfReserved2;
    	unsigned int bfOffBits;
    } BitmapFileHeader;
    
    typedef struct tagBitmapInfoHeader {
    	unsigned int biSize;
    	int biWidth;
    	int biHeight;
    	unsigned short biPlanes;
    	unsigned short biBitCount;
    	unsigned int biCompression;
    	unsigned int biSizeImage;
    	int biXPelsPerMeter;
    	int biYPelsPerMeter;
    	unsigned int biClrUsed;
    	unsigned int biClrImportant;
    } BitmapInfoHeader;
    
    #pragma pack(pop,bitmap_data)
    
    class Bitmap {
    public:
        //variables
        RGBQuad *colours;
        char *data;
        bool loaded;
        int width,height;
        unsigned short bpp;
        string error;
        //methods
        Bitmap(void);
        Bitmap(char *);
        ~Bitmap();
        bool loadBMP(char *);
    //private:
        //variables
        BitmapFileHeader bmfh;
        BitmapInfoHeader bmih;
        int byteWidth;            //the width in bytes of the image
        int padWidth;             //the width in bytes of the added image
        unsigned int dataSize;                //size of the data in the file
        //methods
        void reset(void);
        bool convert24(char *);		//convert to 24bit RGB bottom up data
        bool convert8(char *);		//convert to 24bit RGB bottom up data
    };
    
    #endif //_BITMAP_H
    

    Woher weiß ich denn, wie ich die Struktur übergeben muss, wenn das nicht immer so geht?



  • Hallo, ich hab mir jetzt ne zeitlang den Code des BMP-Laders angeguckt und meine den Fehler gefunden zu haben(bzw. den Fehler in meinem Verständnis des Codes).
    Würde aber gerne Bestätigung meiner Vermutung bekommen.
    Also ich denke es liegt an der Methode convert8.
    Diese sieht wie folgt aus:

    bool Bitmap::convert8(char* tempData) {
    	int offset,diff;
    
    	diff=width*height*RGB_BYTE_SIZE;
        //allocate the buffer for the final image data
        data=new char[diff];
    
        //exit if there is not enough memory
        if(data==NULL) {
            error="Not enough memory to allocate an image buffer";
            delete[] data;
            return false;
        }
    
        if(height>0) {
            offset=padWidth-byteWidth;
            int j=0;
            //count backwards so you start at the front of the image
            for(int i=0;i<dataSize*RGB_BYTE_SIZE;i+=3) {
                //jump over the padding at the start of a new line
                if((i+1)%padWidth==0) {
                    i+=offset;
                }
                //transfer the data
                *(data+i)=colours[*(tempData+j)].rgbRed;
                *(data+i+1)=colours[*(tempData+j)].rgbGreen;
                *(data+i+2)=colours[*(tempData+j)].rgbBlue;
                j++;
            }
        }
    
        //image parser for a forward image
        else {
            offset=padWidth-byteWidth;
            int j=dataSize-1;
            //count backwards so you start at the front of the image
            for(int i=0;i<dataSize*RGB_BYTE_SIZE;i+=3) {
                //jump over the padding at the start of a new line
                if((i+1)%padWidth==0) {
                    i+=offset;
                }
                //transfer the data
                *(data+i)=colours[*(tempData+j)].rgbRed;
                *(data+i+1)=colours[*(tempData+j)].rgbGreen;
                *(data+i+2)=colours[*(tempData+j)].rgbBlue;
                j--;
            }
        }
    
        return true;
    }
    

    In der Schleife wird aus einem ehemaligen Pixel quasi 3 Verschiedene gemacht, jeweils mit dem Rot-,Grün- und Blauanteil.
    Dies geschieht hier:

    *(data+i)=colours[*(tempData+j)].rgbRed;
                *(data+i+1)=colours[*(tempData+j)].rgbGreen;
                *(data+i+2)=colours[*(tempData+j)].rgbBlue;
    

    Dann muss ich natürlich vor dem Speichern in eine andere Datei diese 3 verschiedenen Pixel zu einem einzigen zusammenfügen.
    Ist das richtig?



  • Hallo, Ich bins nochmal.
    Also das war in der Tat der Fehler und nachdem ich die inverse Operation dazu ausgeführt hatte ging es.
    JEDOCH:
    Das Bild erscheint nicht als Symbol in den Windowsordnern, die erscheinen wenn man die Dateien mit großen bzw. kleinen Symbolen anzeigen lässt.
    Woran könnte das liegen? Öffnen lässt sich das ganze ohne Probleme und Grafikfehler, nur das Symbol erscheint nicht?!



  • Vllt. liegt es an einem fehlerhaften BitmapHeader? Entweder erkennt der Windows-Thumbnailer nicht, dass das ein Bitmap sein soll, oder der holt sich den Thumb aus dem Bitmap (embedded Preview). Oder du hast Preview für .bmp deaktiviert 😉
    Kannst du nicht mal ein .bmp, bei dem die Vorschau klappt, im Hexeditor anschauen? Und dort mit einem deiner generierten Bitmaps vergleichen?


Anmelden zum Antworten