Bitmap speichern



  • Ich habe hier eine funktion zum speichern von Bitmaps.
    Das Bitmap wird zwar erstellt ich kann es aber mit keinem Bildbetrachtungprogramm öffnen.

    Was mach ich falsch?

    void save(int height, int width, HDC hdc)
    {
        BITMAPINFOHEADER bmih;
        BITMAPFILEHEADER bmfh;
        BITMAPINFO bi;
        HBITMAP aBmp;
        void *dibvalues;
        HGDIOBJ OldObj;
        HDC hdc2;
        DWORD bytes_write;
        DWORD bytes_written;
        HANDLE fileHandle;
    
        hdc2=CreateCompatibleDC(hdc);
    
        ZeroMemory(&bmih,sizeof(BITMAPINFOHEADER));
        bmih.biSize=sizeof(BITMAPINFOHEADER);
        bmih.biHeight=height;
        bmih.biWidth=width;
        bmih.biPlanes=1;
        bmih.biBitCount=16;
        bmih.biCompression=BI_RGB; 
        bmih.biSizeImage = ((((bmih.biWidth * bmih.biBitCount) + 31) & ~31) >> 3) * bmih.biHeight;
        bmih.biXPelsPerMeter = 0;
        bmih.biYPelsPerMeter = 0;
        bmih.biClrImportant = 0;
    
        bi.bmiHeader=bmih;
    
        aBmp=CreateDIBSection(hdc,&bi,DIB_RGB_COLORS,(void**)&dibvalues,NULL,NULL);
    
        OldObj=SelectObject(hdc2,aBmp);
        BitBlt(hdc2,0,0,width,height,hdc,0,0,SRCCOPY);
    
        ZeroMemory(&bmfh,sizeof(BITMAPFILEHEADER));
        bmfh.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
        bmfh.bfSize=(3*bmih.biHeight*bmih.biWidth)+sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
        bmfh.bfType=0x4d42;
        bmfh.bfReserved1 = 0;
        bmfh.bfReserved2 = 0;
    
            fileHandle=CreateFile("test.bmp",GENERIC_READ | GENERIC_WRITE,(DWORD)0,NULL,
                                                        CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,(HANDLE) NULL);
        if (fileHandle==INVALID_HANDLE_VALUE)
        {
            OutputDebugString("CreateFile failed!\n");
        }
    
            // Write the BITMAPFILEHEADER
            bytes_write=sizeof(BITMAPFILEHEADER);
        if (!WriteFile(fileHandle,(void*)&bmfh,bytes_write,&bytes_written,NULL))
        {
            OutputDebugString("WriteFile failed!\n");
        }
    
            //Write the BITMAPINFOHEADER
        bytes_write=sizeof(BITMAPINFOHEADER);
        if (!WriteFile(fileHandle,(void*)&bmih,bytes_write,&bytes_written,NULL))
        {
            OutputDebugString("WriteFile failed!\n");
        }
    
            //Write the Color Index Array???
            bytes_write=bmih.biSizeImage;//3*bmih.biHeight*bmih.biWidth;
        if (!WriteFile(fileHandle,(void*)dibvalues,bytes_write,&bytes_written,NULL))
        { 
            OutputDebugString("WriteFile failed!\n");
        }
    
        CloseHandle(fileHandle);
    
        DeleteObject(SelectObject(hdc2,OldObj));
        DeleteDC(hdc2);
    }
    


  • Kann mir denn Niemand helfen? Hier ich kann euch auch den gesammten code geben:

    #include <windows.h>
    
    void save(int,int,HDC);
    
    LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
    int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                        PSTR szCmdLine, int iCmdShow)
    {
         static TCHAR szAppName [] = TEXT ("Stretch") ;
         HWND         hwnd ;
         MSG          msg ;
         WNDCLASS     wndclass ;
    
         wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
         wndclass.lpfnWndProc   = WndProc ;
         wndclass.cbClsExtra    = 0 ;
         wndclass.cbWndExtra    = 0 ;
         wndclass.hInstance     = hInstance ;
         wndclass.hIcon         = LoadIcon (NULL, IDI_INFORMATION) ;
         wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
         wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
         wndclass.lpszMenuName  = NULL ;
         wndclass.lpszClassName = szAppName ;
    
         if (!RegisterClass (&wndclass))
         {    // UNICODE-Compilierung ist die einzige realistische Fehlermöglichkeit 
              MessageBox (NULL, TEXT ("Programm arbeitet mit Unicode und setzt Windows NT voraus!"), 
                          szAppName, MB_ICONERROR) ;
              return 0 ;
         }
    
         hwnd = CreateWindow (szAppName, TEXT ("StretchBlt-Demo"), 
                              WS_OVERLAPPEDWINDOW, 
                              CW_USEDEFAULT, CW_USEDEFAULT,
                              CW_USEDEFAULT, CW_USEDEFAULT,
                              NULL, NULL, hInstance, NULL) ;
    
         ShowWindow (hwnd, iCmdShow) ;
         UpdateWindow (hwnd) ;
    
         while (GetMessage (&msg, NULL, 0, 0))
         {
              TranslateMessage (&msg) ;
              DispatchMessage (&msg) ;
         }
         return msg.wParam ;
    }
    
    LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
         static int  cxClient, cyClient, cxSource, cySource ;
         HDC         hdcClient, hdcWindow ;
         HWND        hwnd2=GetDesktopWindow();
         PAINTSTRUCT ps ;
    
         switch (message)
         {
         case WM_CREATE:
              cxSource = GetSystemMetrics (SM_CXSCREEN);
              cySource = GetSystemMetrics (SM_CYSCREEN) ;
              return 0 ;
    
         case WM_SIZE:
              cxClient = LOWORD (lParam) ;
              cyClient = HIWORD (lParam) ;
              return 0 ;
    
         case WM_PAINT:
              hdcClient = BeginPaint (hwnd, &ps) ;
              hdcWindow = GetWindowDC (hwnd2) ;
    
              StretchBlt (hdcClient, 0, 0, cxClient, cyClient,
                          hdcWindow, 0, 0, cxSource, cySource, SRCCOPY) ;
    
              save(cyClient,cxClient,hdcWindow);
              ReleaseDC (hwnd, hdcWindow) ;
              EndPaint (hwnd, &ps) ;
              return 0 ;
    
         case WM_DESTROY:
              PostQuitMessage (0) ;
              return 0 ;
         }
         return DefWindowProc (hwnd, message, wParam, lParam) ;
    }
    
    void save(int height, int width, HDC hdc)
    {
        BITMAPINFOHEADER bmih;
        BITMAPFILEHEADER bmfh;
        BITMAPINFO bi;
        HBITMAP aBmp;
        void *dibvalues;
        HGDIOBJ OldObj;
        HDC hdc2;
        DWORD bytes_write;
        DWORD bytes_written;
        HANDLE fileHandle;
    
        hdc2=CreateCompatibleDC(hdc);
    
        ZeroMemory(&bmih,sizeof(BITMAPINFOHEADER));
        bmih.biSize=sizeof(BITMAPINFOHEADER);
        bmih.biHeight=height;
        bmih.biWidth=width;
        bmih.biPlanes=1;
        bmih.biBitCount=16;
        bmih.biCompression=BI_RGB; 
        bmih.biSizeImage = ((((bmih.biWidth * bmih.biBitCount) + 31) & ~31) >> 3) * bmih.biHeight;
        bmih.biXPelsPerMeter = 0;
        bmih.biYPelsPerMeter = 0;
        bmih.biClrImportant = 0;
    
        bi.bmiHeader=bmih;
    
        aBmp=CreateDIBSection(hdc,&bi,DIB_RGB_COLORS,(void**)&dibvalues,NULL,NULL);
    
        OldObj=SelectObject(hdc2,aBmp);
        BitBlt(hdc2,0,0,width,height,hdc,0,0,SRCCOPY);
    
        ZeroMemory(&bmfh,sizeof(BITMAPFILEHEADER));
        bmfh.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
        bmfh.bfSize=(3*bmih.biHeight*bmih.biWidth)+sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
        bmfh.bfType=0x4d42;
        bmfh.bfReserved1 = 0;
        bmfh.bfReserved2 = 0;
    
            fileHandle=CreateFile("test.bmp",GENERIC_READ | GENERIC_WRITE,(DWORD)0,NULL,
                                                        CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,(HANDLE) NULL);
        if (fileHandle==INVALID_HANDLE_VALUE)
        {
            OutputDebugString("CreateFile failed!\n");
        }
    
            // Write the BITMAPFILEHEADER
            bytes_write=sizeof(BITMAPFILEHEADER);
        if (!WriteFile(fileHandle,(void*)&bmfh,bytes_write,&bytes_written,NULL))
        {
            OutputDebugString("WriteFile failed!\n");
        }
    
            //Write the BITMAPINFOHEADER
        bytes_write=sizeof(BITMAPINFOHEADER);
        if (!WriteFile(fileHandle,(void*)&bmih,bytes_write,&bytes_written,NULL))
        {
            OutputDebugString("WriteFile failed!\n");
        }
    
            //Write the Color Index Array???
            bytes_write=bmih.biSizeImage;//3*bmih.biHeight*bmih.biWidth;
        if (!WriteFile(fileHandle,(void*)dibvalues,bytes_write,&bytes_written,NULL))
        { 
            OutputDebugString("WriteFile failed!\n");
        }
    
        CloseHandle(fileHandle);
    
        DeleteObject(SelectObject(hdc2,OldObj));
        DeleteDC(hdc2);
    }
    


  • Der Code ist eigentlich identisch mit dem aus den FAQ, nur hast du bmih.biBitCount von 24 auf 16 abgeändert - evtl. ist das der Grund 🙂



  • Leider nicht. Trotzdem schonmal danke.



  • Also bei mir wird ein bmp erzeugt, was ich dann nachher auch ohne Probleme laden kann (Paint hab ich nicht drauf, hab's aber mit IrfanView getestet)



  • Jetzt geht es auch bei.



  • Aber leider nur bei dem Compiler "Visual c++ Book Edition" bei "Borland C++ Builder 6" geht es nicht kann mir jemand dort weiterhelfen.
    Muss ich vielleicht eine Einstellung ändern.



  • Evtl. dasselbe Problem wie hier ?
    http://www.c-plusplus.net/forum/viewtopic-var-t-is-116386-and-postdays-is-0-and-postorder-is-asc-and-start-is-0.html

    ...da hat el Clio geschrieben:

    el Clio schrieb:

    Das Problem liegt beim BCB6. Beim Byte-weisen schreiben von Strukturen hat der "Kollege" einen Bug, nämlich dass jede Struktur 2 Byte länger geschrieben wird, als sie ist. Wurde im Update 4 behandelt, hab ich aber nicht drauf. 😉 Lässt sich aber auch über einen #pragma lösen.



  • Ich poste hier nochmal meinen funktionierenden Code. Vielleicht wäre das ganze was für die FAQ, z.B. mit dem Titel "Bitmaps selbst gemacht" oder so.

    Drei Sachen sind wichtig:

    1. bmih.biHeight darf NICHT negativ sein (obwohl die MSDN was anderes sagt), da einige Betrachtungsprogs damit nicht klar kommen und "Bad Picture size"-Fehler ausgeben. In der Konsequenz folgt Punkt 2.

    2. Wegen Punkt eins müssen die Bitmapdaten (sofern benötigt) von hinten nach vorne geschrieben werden. Also das "erste" Pixel links oben ist im Datenarray das letzte. 😉

    3. für BCB6: wenn man nicht das Update 4 drauf hat muss man die eigenen Strukturen benutzen. Die Deklaration mit Erklärung weiter unten!

    Der Code:

    int width         = 640;
       int height        = 480;
       int bytesPerPixel = 3;  // we need 24 bit (= 3x8 bit) for 24-Bit-RGB
    
       // for bcb6 users (update 3 or below) only
       myBITMAPFILEHEADER bmfh;
       myBITMAPINFOHEADER bmih;
    
       /* the others
       BITMAPFILEHEADER bmfh;
       BITMAPINFOHEADER bmih;
       */
    
       BYTE *data = new BYTE[width*height*bytesPerPixel];
    
       bmfh.bfType       = 0x4D42; // must be "BM"
       bmfh.bfSize       = sizeof(bmfh) + sizeof(bmih) + sizeof(*data);
       bmfh.bfReserved1  = 0;
       bmfh.bfReserved2  = 0;
       bmfh.bfOffBits    = sizeof(bmfh) + sizeof(bmih);
    
       bmih.biSize          = sizeof(bmih); // self size
       bmih.biWidth         = width;
       bmih.biHeight        = height;  // negative to be a top-down-image
       bmih.biPlanes        = 1;  // must be 1
       bmih.biBitCount      = 8*bytesPerPixel; // we want a 24-bit-RGB-Image
       bmih.biCompression   = BI_RGB;   // uncompressed
       bmih.biSizeImage     = 0;  // because of BI_RGB in biCompression
       bmih.biXPelsPerMeter = 183; // pixels per meter? Microsoft...
       bmih.biYPelsPerMeter = 183; // pixels per meter? Microsoft...
       bmih.biClrUsed       = 0;  // unimportant for now
       bmih.biClrImportant  = 0;  // unimportant for now too
    
       for(int index = height*width*bytesPerPixel-1; index > 0; index--)
       {
          switch((index%3))
          {
             // Lets make a red image
             case 2: data[index] = 255; break;   // Red; Image should be red
             case 1: data[index] = 0; break;     // Green; Image should be red
             case 0: data[index] = 0; break;     // Blue; Image should be red
          }
    
       }
    
       FILE *bmpFile = fopen("test.bmp", "wb");  // open file
       if(bmpFile)
       {
          // write Bitmapfile
          fwrite(&bmfh, sizeof(bmfh), 1, bmpFile);
          fwrite(&bmih, sizeof(bmih), 1, bmpFile);
          fwrite(data, height*width*bytesPerPixel, 1, bmpFile);
    
          fclose(bmpFile); // close file
       }
    

    Die Strukturen

    Besagte BCB6 Benutzer ohne Update 4 werden das Problem haben, dass beim Byte-weisen schreiben Strukturen generell mit 2 Byte zuviel geschrieben werden, da aus dem Stack zuviel gepopt wird. 😉
    Abhilfe schaffen eigene Strukturen (analog zu denen aus der MSDN) und die Befehle "#pragma pack(push,1)" und "#pragma pack(pop)". Diese verlangsamen den Spass zwar etwas (unmerklich), beheben aber das Problem!

    Und los:

    #pragma pack(push,1)
    struct myBITMAPFILEHEADER
    {
      WORD    bfType;
      DWORD   bfSize;
      WORD    bfReserved1;
      WORD    bfReserved2;
      DWORD   bfOffBits;
    };
    
    struct myBITMAPINFOHEADER
    {
      DWORD  biSize; 
      LONG   biWidth; 
      LONG   biHeight; 
      WORD   biPlanes; 
      WORD   biBitCount; 
      DWORD  biCompression; 
      DWORD  biSizeImage; 
      LONG   biXPelsPerMeter; 
      LONG   biYPelsPerMeter; 
      DWORD  biClrUsed; 
      DWORD  biClrImportant; 
    };
    #pragma pack(pop)
    

    Und raus kommt ein wunderschönes Bitmap! 🙂

    Hoffe, ich hab nicht zuviel Unsinn erzählt! 😉



  • el Clio schrieb:

    2. Wegen Punkt eins müssen die Bitmapdaten (sofern benötigt) von hinten nach vorne geschrieben werden. Also das "erste" Pixel links oben ist im Datenarray das letzte. 😉

    Die Scanlines werden von unten nach oben bearbeitet aber trotzdem noch von links nach rechts. Das Ganze ist also nicht vollständig umgedreht und deswegen ist es etwas aufwändiger als ein einfaches Rückwärtsiterieren.

    Du hast deine Bitmapgröße übrigens immer noch fehlerhaft berechnet. Die Scanlines werden nicht an 4-Byte-Grenzen ausgerichtet und dein Algorithmus würde also bei einer Auflösung von 639x480 ein kaputtes Bitmap schreiben - probier's aus 😉 . Also die Bitmapgrößenberechnung folgendermaßen:

    bitmapgröße in bytes = ( (breite * bpp) + ( (breite*bpp)%32) ) ) / 8 * hoehe
    

    oder bereits voroptimiert wie auch in der MSDN zu finden:

    bmih.biSizeImage = ((((bmih.biWidth * bmih.biBitCount) + 31) & ~31) >> 3) * bmih.biHeight;
    


  • masterofx32 schrieb:

    el Clio schrieb:

    2. Wegen Punkt eins müssen die Bitmapdaten (sofern benötigt) von hinten nach vorne geschrieben werden. Also das "erste" Pixel links oben ist im Datenarray das letzte. 😉

    Die Scanlines werden von unten nach oben bearbeitet aber trotzdem noch von links nach rechts. Das Ganze ist also nicht vollständig umgedreht und deswegen ist es etwas aufwändiger als ein einfaches Rückwärtsiterieren.

    Du hast deine Bitmapgröße übrigens immer noch fehlerhaft berechnet. Die Scanlines werden nicht an 4-Byte-Grenzen ausgerichtet und dein Algorithmus würde also bei einer Auflösung von 639x480 ein kaputtes Bitmap schreiben - probier's aus 😉 . Also die Bitmapgrößenberechnung folgendermaßen:

    bitmapgröße in bytes = ( (breite * bpp) + ( (breite*bpp)%32) ) ) / 8 * hoehe
    

    oder bereits voroptimiert wie auch in der MSDN zu finden:

    bmih.biSizeImage = ((((bmih.biWidth * bmih.biBitCount) + 31) & ~31) >> 3) * bmih.biHeight;
    

    Ups! Vergessen! Muss natürlich auch noch mit rein 🙂


Anmelden zum Antworten