1. 程式人生 > >如何將點陣圖物件儲存為BMP檔案

如何將點陣圖物件儲存為BMP檔案

  GDI中點陣圖物件是很常見的GDI物件,但是無論是SDK,還是MFC都沒有提供現在的函式或是方法來將一個位圖物件儲存為一個BMP檔案,這裡介紹一下儲存方法。 點陣圖檔案格式: DIB檔案有四個主要部分: 檔案表頭(BITMAPFILEHEADER) 資訊表頭  (BITMAPINFOHEADER) 調色盤(不一定有)  點陣圖圖素位 而一個位圖物件和上述唯一不同在於它沒有檔案表頭。 相關資料結構: (1)檔案表頭
typedef struct tagBITMAPFILEHEADER { 
  WORD    bfType; //BMP檔案型別,總是字元BM,十六進位制為0x4d42
 DWORD   bfSize; //BMP檔案大小,包含這個結構在內。
 WORD    bfReserved1; 
  WORD    bfReserved2; //以上均保留為0
 DWORD   bfOffBits; //
是一個偏移量,指出了檔案中圖素位開始位置的位元組偏移量
} BITMAPFILEHEADER, *PBITMAPFILEHEADER; 
2)資訊表頭
typedef struct tagBITMAPINFOHEADER{  DWORD biSize; //結構的大小  LONG   biWidth; //點陣圖的寬度  LONG   biHeight; //點陣圖的高度  WORD   biPlanes; //必須是1  WORD   biBitCount; //指出每一個畫素要用的bit位。  DWORD biCompression; //指出是否是壓縮的,以及壓縮方式  DWORD biSizeImage; //
指出影象的尺寸  LONG   biXPelsPerMeter; //水平基線  LONG   biYPelsPerMeter; //堅直基線  DWORD biClrUsed; //被用的顏色數  DWORD biClrImportant; //重要的顏色數 } BITMAPINFOHEADER, *PBITMAPINFOHEADER;
3)調色盤結構:
typedef struct tagRGBQUAD // rgb {            BYTE rgbBlue ;     // blue level            BYTE rgbGreen ;    // green level            BYTE rgbRed ;      // red level
           BYTE rgbReserved ; // = 0 }RGBQUAD ; 注意這個結構應該是一個數組,在256色及以下的BMP檔案中存在,陣列的長度關鍵看顏色數。 BITMAP定義了一個位圖的型別、長度、寬度、顏色格式等,這個結構一般用GetObject來獲得。定義如下:typedef struct tagBITMAP {  LONG   bmType; //型別,不過總是為0  LONG   bmWidth; //寬度,總是大於0  LONG   bmHeight; //高度,總是大於0  LONG   bmWidthBytes; //MSDN上解釋說是指定每一個掃描行的位元組數。  WORD   bmPlanes; //指定調色盤數目  WORD   bmBitsPixel; //指示一個畫素所要求的byte位  LPVOID bmBits; //指定一個數組指標,這個陣列大約應該是儲存點陣圖資料的。 } BITMAP, *PBITMAP 一個位圖物件也就是存在記憶體中的點陣圖,它與存在硬碟上的BMP檔案相比,唯一的區別就是它沒有BITMAPFILEHEADER這個檔案資訊頭,其餘部分是完全相同的,所以我們要做的就是先構造一個檔案資訊頭,寫入檔案中,然後將記憶體中的點陣圖寫入檔案。 原始碼如下:(只寫主要部分) WORD wbitsCount;//點陣圖中每個畫素所佔位元組數。        DWORD dwpalettelsize=0;//調色盤大小        DWORD dwbmdibitsize,dwdibsize,dwwritten;        BITMAP bitmap;//定義了點陣圖的各種的資訊。        BITMAPFILEHEADER bmfhdr;//定義了大小、型別等BMP檔案的資訊。        BITMAPINFOHEADER bi;        LPBITMAPINFOHEADER lpbi;        HANDLE fh,fdib; GetObject(hBitmap,sizeof(BITMAP),(void *)&bitmap);//得到BITMAP結構。        //以下程式碼是用BITMAP的資訊填充BITMAPINFOHEADER結構        wbitsCount=bitmap.bmBitsPixel; bi.biSize=sizeof(BITMAPINFOHEADER);        bi.biWidth=bitmap.bmWidth;        bi.biHeight=bitmap.bmHeight;        bi.biPlanes=1;        bi.biBitCount= bitmap.bmBitsPixel ;        bi.biClrImportant=0;        bi.biClrUsed=0;        bi.biCompression=BI_RGB;        bi.biSizeImage=0;        bi.biYPelsPerMeter=0;        bi.biXPelsPerMeter=0; //以下程式碼是獲取調色盤的長度,調色盤現在的用處很少,因為256色的點陣圖已經不多了。        if(wbitsCount<=8)               dwpalettelsize=(1<<wbitsCount)*sizeof(RGBQUAD); //計算點陣圖的大小,並分配相應的記憶體空間,注意的是沒有分配BITMAPFILEHEADER。        dwbmdibitsize=((bitmap.bmWidth*wbitsCount+31)/8)*bitmap.bmHeight;        fdib=GlobalAlloc(GHND,dwbmdibitsize+dwpalettelsize+sizeof(BITMAPINFOHEADER));        lpbi=(LPBITMAPINFOHEADER)::GlobalLock(fdib);        *lpbi=bi;//將bi中的資料寫入分配的記憶體中。        hdc=::GetDC(NULL);        GetDIBits(hdc,hBitmap,0,(UINT)bitmap.bmHeight,(LPSTR)lpbi+sizeof(BITMAPINFOHEADER)+dwpalettelsize,(BITMAPINFO *)lpbi,DIB_RGB_COLORS); /*GetDIBits是最重要的函式,真正獲得點陣圖資料的工作就由它完成,它第一個引數為HDC,第二個引數為點陣圖控制代碼,第三個引數為掃描行的開始行,一般為0,第四個為結束行,一般就是高度,第四個引數最重要,它表示接收資料的起始地址,這個地址一般是在調色盤之後。第五個引數指的是接收BITMAPINFO結構的地址,這個結構上面沒有寫,它其實就是BITMAPINFO結構加上調色盤資訊。最後一個引數是格式。一般是DIB_RGB_COLORS*/ //建立檔案以及檔案資訊頭 fh=CreateFile(FileName,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN,NULL);        if(fh==INVALID_HANDLE_VALUE)               return FALSE; bmfhdr.bfType=0x4d42;//BMP型別,一定要這樣寫        dwdibsize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+dwbmdibitsize+dwpalettelsize;//檔案總長,由幾個部分組成        bmfhdr.bfSize=dwdibsize;        bmfhdr.bfReserved1=0;        bmfhdr.bfReserved2=0; bmfhdr.bfOffBits=(DWORD)sizeof(BITMAPFILEHEADER)+(DWORD)sizeof(BITMAPINFOHEADER)+dwpalettelsize;//點陣圖資料相對於檔案頭的偏移量 //將檔案資訊頭寫入檔案 WriteFile(fh,(LPSTR)&bmfhdr,sizeof(BITMAPFILEHEADER),&dwwritten,NULL); //將資料寫入檔案,包含BITMAPINFO結構、調色盤、資料 WriteFile(fh,(LPSTR)lpbi,dwdibsize,&dwwritten,NULL); //關閉相關控制代碼 ::GlobalUnlock(fdib);        ::GlobalFree(fdib);        ::CloseHandle(fh);        return TRUE;