Hilfe bei Bitmap-Loader



  • bmario_ schrieb:

    CBitmap::CBitmap( string str ) :
        m_strFileName( str ) // <- kann nicht funktionieren, string* vs. string
    {
    }
    

    mit dem editierten Code ist klar, warum das nicht gehen kann.
    An der Stelle ist es unnötig einen Zeiger auf einen String zu verwenden. Außerdem hat der Dateipfad eigentlich keine Daseinsberechtigung in der Bitmap Klasse, weil er keine Eigenschaft eines Bildes ist.

    greetz KN4CK3R



  • KN4CK3R schrieb:

    mit dem editierten Code ist klar, warum das nicht gehen kann.

    Im Code selbst ist es richtig, nur steht hier das falsche. Entschuldigung, mein Fehler.

    Ich habe den Bitmap-Loader nochmals überarbeitet, jetzt sieht es so aus:
    http://pastebin.com/8yypS2Zu - Bitmap.h
    http://pastebin.com/Pq72JE2h - Bitmap.cpp
    Der Crash tritt direkt beim Initialisieren des 2-dim Arrays auf, der Code müsste aber (wie auch hier beschrieben) richtig sein...



  • im Debugger geschaut ob die ausgelesenen Werte auch stimmen?
    Weil am Code liegts nicht (falls ich nichts übersehen habe)
    http://ideone.com/KRJuj
    Da wir hier allerdings bei C++ sind, lege ich dir einen vector<vector<int> > ans Herz.

    greetz KN4CK3R



  • Welchen wert hat den m_iWidth?
    Edit: zu spät...



  • KN4CK3R schrieb:

    im Debugger geschaut ob die ausgelesenen Werte auch stimmen?

    Eigentlich prüfe ich bei jeder Debugging-Session die Werte, die bis dato immer korrekt waren, (weshalb ich mir das wahrscheinlich unbewusst abgewöhnt habe).
    Bei der letzten Session (Breakpoint direkt an der Zeile mit der Array-Initialisation)

    Name          Value                                                Type
    this          0x00000010 {m_iWidth=??? m_iHeight=??? m_pData=???}  CBitmap * const
      -m_iWidth   C*XX0030: Error: expression cannot be evaluated
      -m_iHeight  C*XX0030: Error: expression cannot be evaluated
      -m_pData    C*XX0030: Error: expression cannot be evaluated
    strFileName   "terrain.bmp"                                        std::basic_string<char,std::char_traits<char>,std::allocator<char>>
    offset        -858993460                                           int
    stream        [ganz viel Gedöns, das ziemlich richtig aussieht]    std::basic_ifstream<char,std::char_traits<char>>
    

    'strFileName' ist richtig. 'offset' ist auch richtig, da die Variable erst später benutzt wird, stream sollte auch richtig sein, da ich keine Lesefehler bekomme, aber was mit 'this' los ist würde ich selbst gerne wissen (zumal this ja automatisch generiert wird)
    Ich bin verwirrt.



  • Um _irgendetwas_ mit den Daten zu machen, fehlt dir übrigens noch eine wichtige Information: der Bitcount, also die Anzahl der Bits, die einen Pixel beschreiben.
    Wenn du nur 24 und 32 Bit zulassen möchtest, brauchst du dich auch nicht um die Farbpaletten kümmern.

    Der Code hat außerdem noch einige logische Fehler: Du möchtest jede Rasterzeile speichern, forderst aber Speicher mit der Größe der Breite an, (müsste die Höhe sein) und im Weiteren für jede Zeile in Abhängigkeit der Höhe.
    Außerdem musst du bedenken, dass die Adresse jeder Zeile eines Bitmaps DWORD (4 Byte)-aligned ist.
    Also die Größe einer Rasterzeile ist

    RowLen = 4*((Width*BitCount+31)/32);
    

    Und die des Gesamtbildes somit

    abs(Height)*RowLen;
    

    abs deshalb, da die Höhe zu allem Überfluss auch negativ sein kann ("top-down-DIB", die Daten stehen dann von oben nach unten im Speicher).

    Ich würde an deiner Stelle Folgendes machen: Anfordern von Speicher für das gesamte Bild (std::vector<unsigned char>!) und die Adressen der Rasterzeilen dann in einem seperaten vector<unsigned char*> speichern (falls sich die Bildgröße ändert, muss dieser neu angepasst werden ⚠ ).

    Und dann einfach die kompletten Daten einlesen, ist das Schnellste und in diesem Fall zugleich das Einfachste. :xmas1:



  • aVoX schrieb:

    KN4CK3R schrieb:

    im Debugger geschaut ob die ausgelesenen Werte auch stimmen?

    Eigentlich prüfe ich bei jeder Debugging-Session die Werte, die bis dato immer korrekt waren, (weshalb ich mir das wahrscheinlich unbewusst abgewöhnt habe).
    Bei der letzten Session (Breakpoint direkt an der Zeile mit der Array-Initialisation)

    Name          Value                                                Type
    this          0x00000010 {m_iWidth=??? m_iHeight=??? m_pData=???}  CBitmap * const
      -m_iWidth   C*XX0030: Error: expression cannot be evaluated
      -m_iHeight  C*XX0030: Error: expression cannot be evaluated
      -m_pData    C*XX0030: Error: expression cannot be evaluated
    strFileName   "terrain.bmp"                                        std::basic_string<char,std::char_traits<char>,std::allocator<char>>
    offset        -858993460                                           int
    stream        [ganz viel Gedöns, das ziemlich richtig aussieht]    std::basic_ifstream<char,std::char_traits<char>>
    

    'strFileName' ist richtig. 'offset' ist auch richtig, da die Variable erst später benutzt wird, stream sollte auch richtig sein, da ich keine Lesefehler bekomme, aber was mit 'this' los ist würde ich selbst gerne wissen (zumal this ja automatisch generiert wird)
    Ich bin verwirrt.

    Wie erstellst du den deine Bitmap Instanz... Zeig mal den Code wo du CBitmap erstellst.



  • @Thorgrim:
    Danke für die Antwort. Wie gesagt, der Bitmap-Loader soll ganz minimalistisch ausfallen, d.h. 24 bit Farbtiefe und nur BMPs (DIBs sind uncool). Auf die Idee, das Bild mit 2 Vektoren zu beschreiben bin ich auch schon gekommen, nur dachte ich mir, dass es einfacher wäre, alles in ein 2-dimensionales Array zu speichern (als ob)

    @Tobias Gerg:
    Der BMP-Loader wird von einer anderen Klasse namens CMesh aufgerufen.
    Relevante Ausschnitte:

    // Mesh.h
    #pragma once
    #include <WinDef.h>
    #include "Bitmap.h"
    class CMesh
    {
    public:
        CMesh( void )
        {
            m_bmp = CBitmap();
        };
        ~CMesh( void );
    
        bool LoadHeightMap( char *szFile, float flScale, float flResolution = (4.0f) );
        // noch ein paar unwichtige Funktions- und Variablendeklarationen
    
        CBitmap     m_bmp;
    
    };
    
    // Mesh.cpp
    bool CMesh::LoadHeightMap( char *szFile, float flScale, float flResolution )
    {
        if ( !m_bmp.Load(string(szFile)) ) // <- Crash
        {
            g_error.szErrMsg = t( "Error loading height map." );
            return false;
        }
        // Ende des bmp-relevanten Abschnitts
    }
    


  • Ist das CMesh Objekt ein Pointer der noch nicht initialisiert wurde wenn du die Loadfunktion aufrufst?



  • Tobias Gerg schrieb:

    Ist das CMesh Objekt ein Pointer der noch nicht initialisiert wurde wenn du die Loadfunktion aufrufst?

    Ja. Verdammt, ich könnt' mich echt manchmal selbst auslachen. Sowas banales und dafür 2 Seiten voll mit Fachsimplereien! Es tut richtig leid um eure Zeit. (wobei ich mich wundere, dass das Programm nicht schon früher abgestürzt ist)

    Jetzt funktioniert alles soweit, bis auf einen Debug Error ("Invalid allocation size"), was wohl am scheinbar komplett falsch geschriebenen Lade-Code liegt.

    Danke an alle PosterInnen!
    aVoX



  • Wegen dem nicht Absturz, dass ist einfach undefined behavior, da darf alles passieren...

    Ansonsten würde ich mal einen Breakpoint in deinen CMesh Konstruktor setzen. Schau im Debugger einfach was passiert, dann findest du den Fehler... :xmas1:

    Greets
    Tobi



  • @Tobias: Fehler bereits behoben, Danke für alles 👍

    @Topic:
    Das hoffentlich letzte Problem (auch Grund für die invalid allocation size): stream << var lest keine Daten aus der Datei aus.
    Aktueller Code:

    bool CBitmap::Load( string strFileName )
    {
        ifstream stream( strFileName, ios_base::binary | ios_base::in ); // Breakpoint 1: m_iWidth und m_iHeight sind beide 0 (im Konstruktor so zugewiesen)
    
        if ( stream.fail() || stream.bad() || !stream.is_open() )
        {
            g_error.szErrMsg = t("Error loading heightmap!");
            return false;
        }
    
        stream.seekg( 18, ios_base::beg );    // Offset 18: 4 Byte speichern die Bildbreite.
        stream >> m_iWidth;                   //            4 Byte (int) einlesen.
    
                                              // Cursor steht bei 22.
                                              // Offset 22: 4 Byte speichern die Bildhöhe.
        stream >> m_iHeight;                  //            4 Byte (int) einlesen.
    
        m_pData = new int *[m_iWidth];        // Breakpoint 2: Laut Debugger sind iWidth und m_iHeight noch immer 0.
    

    Wenn ich die Bitmap in einem Hexeditor öffne und zum Offset 18 springe, steht dort 0400 (1024 dezimal - also richtiger Wert), selbiges gilt für Offset 22.
    Sollten ebenjene Werte nicht auch in den entsprechenden Variablen stehen?



  • Du musst die Datei binär öffnen (std::ios::binary angeben).



  • hat er doch?

    greetz KN4CK3R



  • @KN4CK3R: Sorry, hab das irgendwie übersehen 😮

    Ich würde es dennoch anders machen:

    class CBitmap
    {
    ....
    private:
    	std::vector<unsigned char> m_data; // Daten
    	std::vector<unsigned char*> m_rows; // Adresse jeder Rasterzeile
    	BITMAPINFOHEADER m_infoHeader;   
    }
    
    bool Bitmap::Load(const char* file)
    {
    	std::ifstream stream(file,std::ios::binary);
    	if(!stream.is_open())
    		return false;
    	BITMAPFILEHEADER fileHeader;
    	stream.read(reinterpret_cast<char*>(&fileHeader),sizeof(BITMAPFILEHEADER));
    	stream.read(reinterpret_cast<char*>(&m_infoHeader),sizeof(BITMAPINFOHEADER));
    
    	if(m_infoHeader.biBitCount!=24 || bmi.biHeight<0) // 
    		return false;
    	stream.seekg(fileHeader.bfOffBits); // eigentlich unnötig, Daten sollten direkt anfangen
    	unsigned rowlen = 4*((m_infoHeader.biWidth*m_infoHeader.biBitCount+31)/32);
    	unsigned size = m_infoHeader.biHeight*rowlen;
    	m_data.resize(size);
    	m_rows.resize(m_infoHeader.biHeight);
    	stream.read(reinterpret_cast<char*>(&m_data[0]),m_data.size());
    	for(unsigned y=0;y<m_rows.size();++y) // Zeilen liegen von unten nach oben im Speicher
    		m_rows[y] = &m_data[0] + (m_infoHeader.biHeight - y - 1)*rowlen;
    }
    

    Natürlich sollte die Fehlerüberprüfung noch verbessert werden (z.B. überprüfen, ob es sich überhaupt um eine bmp-Datei handelt, den stream nach jedem Lesen prüfen etc.).
    Wenn dir die Strukturen BITMAPFILEHEADER und BITMAPINFOHEADER nicht zur Verfügung stehen, kannst du sie nachbauen (zu finden bei MSDN), musst allerdings unbedingt das Padding des Compilers auf 2 Byte setzen.

    Achja, der Code nur kurz angetestet.



  • @Thorgrim: OK, du hast mich endgultig von den Vektoren überzeugt! 😉

    Danke an alle für's Mithelfen, auch wenn das eigentliche Problem mein eigener Fehler war. :xmas1:


Anmelden zum Antworten