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 * hoeheoder 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 * hoeheoder 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
