Bitmap位圖文件讀取、保存、屏幕截圖

雖然現在網上已經有很多位圖讀取、保存的文章,很多寫的都很詳細,提供的源代碼功能也很強大,但是我仍然要自己重寫一個位圖加載程序。主要是因爲這些大牛們的文章寫的太深奧了,代碼功能太強大了,以至於像我這樣的菜鳥讀不懂。所以,我要力求簡潔。省略掉一些細節,比方說調色板。爲了能夠方便容易操作,我的程序只支持24位以上的位圖文件加載。

        

       首先,瞭解下位圖文件的結構。24位以上的位圖文件包含3個部分:位圖文件頭(BITMAPFILEHEADER)、位圖信息頭(BITMAPINFOHEADER)、位圖數據。

下面是MSDN中兩個信息頭的具體定義:

  1. typedef struct tagBITMAPFILEHEADER {  
  2.     WORD    bfType; //圖像類型:必須是‘BM’(BM的16進制編碼爲:0x4d42)  
  3.     DWORD   bfSize; //位圖文件大小。  
  4.     WORD    bfReserved1; //保留值。必須爲0  
  5.     WORD    bfReserved2; //保留值。必須爲0  
  6.     DWORD   bfOffBits; //從文件開頭到像素數據的偏移量。  
  7. } BITMAPFILEHEADER, *PBITMAPFILEHEADER;  
  8.   
  9. typedef struct tagBITMAPINFOHEADER{  
  10.     DWORD  biSize; //當前結構體的大小。  
  11.     LONG   biWidth; //位圖的寬度。單位是像素  
  12.     LONG   biHeight; //位圖高度。單位是像素  
  13.     WORD   biPlanes; //位圖平面個數。必須是1  
  14.     WORD   biBitCount //位圖的位數,也就是位圖深度。可以是1、4、8、16、24、32。16位以下的位圖文件含有調色板信息。  
  15.         DWORD  biCompression; //壓縮方式。位圖沒有壓縮,賦予BI_RGB參數(也就是0)。  
  16.     DWORD  biSizeImage; //位圖數據佔用的字節數。可以設置爲默認值0。  
  17.     LONG   biXPelsPerMeter; //指定目標設備水平分辨率,單位是每米的像素個數。可設置爲0  
  18.     LONG   biYPelsPerMeter; //指定目標設備垂直分辨率,同上。  
  19.     DWORD  biClrUsed; //指定本圖像實際用到的像素。可設置爲0  
  20.     DWORD  biClrImportant; //指定本圖像重要的顏色個數。可設置爲0,表示所有顏色都重要。  
  21. } BITMAPINFOHEADER, *PBITMAPINFOHEADER;  


接下來就是位圖數據了:

位圖按行存貯,位圖的寬度不等於位圖的行字節數。每個像素佔用 biBitCount/8 個字節,且每行佔用的字節數必須是4字節的整數倍!例如:101*101*24的位圖,行佔用字節數爲:101*3=303,而303%4!=0,所以行佔用字節數需修改爲304,此時位圖數據的實際大小爲304*101,上面的biSizeImage就可設置爲304*101。

下面是保存位圖文件的結構體填充例:

  1. BITMAPFILEHEADER bmheader;  
  2. memset(&bmheader,0,sizeof(bmheader));  
  3. bmheader.bfType=0x4d42;     //圖像格式。必須爲'BM'格式。  
  4. bmheader.bfOffBits = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER); //從文件開頭到數據的偏移量  
  5. bmheader.bfSize = m_nWidthBytes*m_nHeight + bmheader.bfOffBits;//文件大小  
  6.   
  7. BITMAPINFOHEADER bmInfo;  
  8. memset(&bmInfo,0,sizeof(bmInfo));  
  9. bmInfo.biSize = sizeof(bmInfo);  
  10. bmInfo.biWidth = m_nWidth;   
  11. bmInfo.biHeight = m_nHeight;   
  12. bmInfo.biPlanes = 1;   
  13. bmInfo.biBitCount =  m_nDeep;  
  14. bmInfo.biCompression = BI_RGB;  

注:m_nWidthBytes 可這樣計算:int m_nWidthBytes = ((m_nWidth*m_nDeep/8 + 3)/4) * 4;


下面是一個用來創建DIB位圖,獲得HBITMAP句柄一個API。

HBITMAP CreateDIBSection(
  HDC hdc,                 // handle to DC
  CONST BITMAPINFO *pbmi,  // bitmap data
  UINT iUsage,             // data type indicator
  VOID **ppvBits,          // bit values
  HANDLE hSection,         // handle to file mapping object
  DWORD dwOffset           // offset to bitmap bit values
);
(這裏就不詳細介紹了,可以去百科裏看看,如:http://www.hudong.com/wiki/CreateDIBSection


大致說下打開文件文件的流程:

1.讀入文件頭BITMAPFILEHEADER數據。

2.讀入位圖信息頭BITMAPINFOHEADER數據。

3.讀取位圖數據。

4.使用CreateDIBSection創建Dib位圖,將讀取到的數據拷貝給Dib位圖。

然後就可以使用Dib位圖句柄進行位圖的操作了。


保存文件流程:

1.填寫文件頭BITMAPFILEHEADER,並寫入文件。

2.填寫位圖信息頭BITMAPINFOHEADER,並寫入文件。

3.獲得位圖數據(GetBitmapBits,或者是在CreateDIBSection時,將數據地址記錄下來),將數據寫入文件。


截圖功能(參考樣例見文章末尾):

1.創建掩碼DC(CreateCompatibelDC),掩碼位圖(CreateCompatibleBitmap),將位圖選到DC中。

2.將 要截圖的窗口的DC內容拷貝(BitBlt)給掩碼DC(也就是拷貝到了掩碼位圖上了)。

3.接着就是上面說到的保存文件流程了。




附件:我將上面的操作封裝起來。

  1. //DIBBitmap.h  
  2. #pragma once  
  3.   
  4. class CDIBBitmap  
  5. {  
  6. public://方法  
  7.   
  8.     CDIBBitmap(void);  
  9.     ~CDIBBitmap(void);  
  10.   
  11.     bool create(HDC hdc,int width,int height,int deep);//創建位圖  
  12.     bool create(HDC hDC,HBITMAP hBitmap);//通過拷貝來創建位圖  
  13.   
  14.     bool load(HDC hDC,char *szFileName);//從文件中價值位圖  
  15.     bool save(char *szFileName);//保存位圖到文件  
  16.   
  17.     void clear(void);//清空數據  
  18.   
  19.     void sampleRender(HDC hDC,int x,int y);//簡單顯示  
  20.   
  21. public://屬性  
  22.   
  23.     //獲得位圖  
  24.     inline HBITMAP getBitmap(void){ return m_hDibBmp; }  
  25.     //獲得數據  
  26.     inline BYTE * getBmpData(void){ return m_pBmpData; }  
  27.     //獲得寬度  
  28.     inline int  getWidth(void){ return m_nWidth; }  
  29.     //獲得高度  
  30.     inline int  getHeight(void){ return m_nHeight; }  
  31.     //獲得位深度  
  32.     inline int  getDeep(void){ return m_nDeep; }  
  33.     //獲得一行字節數  
  34.     inline int  getWidthBytes(void){ return m_nWidthBytes; }  
  35.      
  36. protected://成員  
  37.     HBITMAP     m_hDibBmp;  
  38.     BYTE        *m_pBmpData;//指向位圖數據  
  39.     int         m_nWidth;   //寬度。(單位:像素pixel)  
  40.     int         m_nHeight;  //高度。pixel  
  41.     int         m_nDeep;    //深度。bit。深度必須大於24位  
  42.     int         m_nWidthBytes;//一行所需的字節數。必須是4字節的整數倍。  
  43. };  


  1. #include <Windows.h>  
  2. #include <stdio.h>  
  3. #include <cassert>  
  4.   
  5. #include "DIBBitmap.h"  
  6.   
  7.   
  8. class FileHoder  
  9. {  
  10.     FILE *m_pFile;  
  11.   
  12. public:  
  13.   
  14.     FileHoder(FILE *pFile)  
  15.         : m_pFile(pFile)  
  16.     {}  
  17.   
  18.     ~FileHoder()  
  19.     {  
  20.         if (m_pFile) fclose(m_pFile);  
  21.     }  
  22. };  
  23.   
  24. //////////////////////////////////////////////////////////////////////////  
  25. CDIBBitmap::CDIBBitmap(void)  
  26. {  
  27.     m_hDibBmp = NULL;  
  28.     m_pBmpData = NULL;  
  29.     m_nWidth = 0;   //寬度。(單位:像素pixel)  
  30.     m_nHeight = 0;  //高度。pixel  
  31.     m_nDeep = 0;    //深度。bit。深度必須大於24位  
  32.     m_nWidthBytes = 0;  
  33. }  
  34.   
  35. CDIBBitmap::~CDIBBitmap(void)  
  36. {  
  37.     clear();  
  38. }  
  39.   
  40. bool CDIBBitmap::create(HDC hdc, int width, int height, int deep)  
  41. {  
  42.     clear();  
  43.   
  44.     m_nWidth = width;  
  45.     m_nHeight = height;  
  46.     m_nDeep = deep;  
  47.     m_nWidthBytes = ((m_nWidth*m_nDeep / 8 + 3) / 4) * 4;  
  48.     m_pBmpData = nullptr;  
  49.   
  50.     BITMAPINFO bmInfo;  
  51.     memset(&bmInfo, 0, sizeof(bmInfo));  
  52.     bmInfo.bmiHeader.biSize = sizeof(bmInfo);  
  53.     bmInfo.bmiHeader.biWidth = m_nWidth;  
  54.     bmInfo.bmiHeader.biHeight = m_nHeight;  
  55.     bmInfo.bmiHeader.biPlanes = 1;  
  56.     bmInfo.bmiHeader.biBitCount = m_nDeep;  
  57.     bmInfo.bmiHeader.biCompression = BI_RGB;  
  58.   
  59.     //如果創建成功,m_pBmpData將指向位圖的像素區域。  
  60.     m_hDibBmp = CreateDIBSection(hdc, &bmInfo, DIB_RGB_COLORS, (void**) &m_pBmpData, NULL, 0);  
  61.     if (m_hDibBmp == NULL)  
  62.     {  
  63.         return false;  
  64.     }  
  65.   
  66.     return true;  
  67. }  
  68.   
  69. bool CDIBBitmap::create(HDC hDC, HBITMAP hBitmap)  
  70. {  
  71.     if (NULL == hDC || NULL == hBitmap)  
  72.     {  
  73.         return false;  
  74.     }  
  75.   
  76.     //獲得位圖信息  
  77.     BITMAP bm;  
  78.     GetObject(hBitmap, sizeof(bm), &bm);  
  79.     //創建位圖  
  80.     if (!create(hDC, bm.bmWidth, bm.bmHeight, bm.bmBitsPixel))  
  81.     {  
  82.         return false;  
  83.     }  
  84.   
  85.     //獲得hBitmap數據,並拷貝給Dib位圖  
  86.     GetBitmapBits(hBitmap, bm.bmWidthBytes*bm.bmHeight, getBmpData());  
  87.     return true;  
  88. }  
  89.   
  90. void CDIBBitmap::clear(void)  
  91. {  
  92.     if (m_hDibBmp != NULL)  
  93.     {  
  94.         DeleteObject(m_hDibBmp);  
  95.     }  
  96.     m_hDibBmp = NULL;  
  97.     m_pBmpData = NULL;  
  98. }  
  99.   
  100.   
  101. void CDIBBitmap::sampleRender(HDC hDC, int x, int y)  
  102. {  
  103.     if (!m_hDibBmp) return;  
  104.   
  105.     HDC memDC = CreateCompatibleDC(0);  
  106.     SelectObject(memDC, m_hDibBmp);  
  107.   
  108.     BitBlt(hDC, x, y, m_nWidth, m_nHeight, memDC, 0, 0, SRCCOPY);  
  109.   
  110.     DeleteDC(memDC);  
  111. }  
  112.   
  113. bool CDIBBitmap::load(HDC hDC, char *szFileName)  
  114. {  
  115.     assert(hDC && szFileName && "CDIBBitmap::load");  
  116.   
  117.     FILE *fp = NULL;  
  118.     fopen_s(&fp, szFileName, "rb");  
  119.     if (NULL == fp)  
  120.     {  
  121.         return false;  
  122.     }  
  123.   
  124.     FileHoder holder(fp);  
  125.   
  126.     //讀入文件頭  
  127.     BITMAPFILEHEADER bmheader;  
  128.     if (fread(&bmheader, sizeof(bmheader), 1, fp) != 1)  
  129.     {  
  130.         return false;  
  131.     }  
  132.   
  133.     //無效的位圖文件  
  134.     if (bmheader.bfType != 0x4d42)  
  135.     {  
  136.         return false;  
  137.     }  
  138.   
  139.     //讀入位圖信息頭  
  140.     BITMAPINFOHEADER bmInfo;  
  141.     if (fread(&bmInfo, sizeof(bmInfo), 1, fp) != 1)  
  142.     {  
  143.         return false;  
  144.     }  
  145.   
  146.     //注:目前程序不支持調色板數據位圖加載。  
  147.     if (bmInfo.biBitCount < 16)  
  148.     {  
  149.         return false;  
  150.     }  
  151.   
  152.     //創建DIB位圖  
  153.     if (!create(hDC, bmInfo.biWidth, bmInfo.biHeight, bmInfo.biBitCount))  
  154.     {  
  155.         return false;  
  156.     }  
  157.   
  158.     //定位到像素數據  
  159.     fseek(fp, bmheader.bfOffBits, SEEK_SET);  
  160.   
  161.     //讀入像素數據  
  162.     if (fread(m_pBmpData, m_nWidthBytes, m_nHeight, fp) != m_nHeight)  
  163.     {  
  164.         return false;  
  165.     }  
  166.   
  167.   
  168.     return true;  
  169. }  
  170.   
  171. bool CDIBBitmap::save(char *szFileName)  
  172. {  
  173.     assert(szFileName && "CDIBBitmap::save");  
  174.   
  175.     if (NULL == m_hDibBmp || NULL == m_pBmpData)  
  176.     {  
  177.         return false;  
  178.     }  
  179.   
  180.     FILE *fp = NULL;  
  181.     fopen_s(&fp, szFileName, "wb");  
  182.     if (NULL == fp)  
  183.     {  
  184.         return false;  
  185.     }  
  186.   
  187.     FileHoder hoder(fp);  
  188.   
  189.     BITMAPFILEHEADER bmheader;  
  190.     memset(&bmheader, 0, sizeof(bmheader));  
  191.     bmheader.bfType = 0x4d42;     //圖像格式。必須爲'BM'格式。  
  192.     bmheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); //從文件開頭到數據的偏移量  
  193.     bmheader.bfSize = m_nWidthBytes*m_nHeight + bmheader.bfOffBits;//文件大小  
  194.   
  195.     BITMAPINFOHEADER bmInfo;  
  196.     memset(&bmInfo, 0, sizeof(bmInfo));  
  197.     bmInfo.biSize = sizeof(bmInfo);  
  198.     bmInfo.biWidth = m_nWidth;  
  199.     bmInfo.biHeight = m_nHeight;  
  200.     bmInfo.biPlanes = 1;  
  201.     bmInfo.biBitCount = m_nDeep;  
  202.     bmInfo.biCompression = BI_RGB;  
  203.   
  204.     fwrite(&bmheader, sizeof(bmheader), 1, fp);  
  205.     fwrite(&bmInfo, sizeof(bmInfo), 1, fp);  
  206.     for (int i = 0; i < m_nHeight; ++i)  
  207.     {  
  208.         int index = m_nWidthBytes*i;  
  209.         fwrite(m_pBmpData + index, m_nWidthBytes, 1, fp);  
  210.     }  
  211.   
  212.     return true;  
  213. }  
  214.   
  215. //////////////////////////////////////////////////////////////////////////  



截圖功能參考代碼:

  1. //獲得設備描述表  
  2. m_hDeviceContext=GetDC(m_hWnd);  
  3. //創建掩碼dc  
  4. m_hBackDC=CreateCompatibleDC(m_hDeviceContext);  
  5. //創建掩碼位圖  
  6. m_bBackBitmap=CreateCompatibleBitmap(m_hDeviceContext,m_nWidth,m_nHeight);  
  7. SelectObject(m_hBackDC,m_bBackBitmap);  
  8. ...  
  9. 將m_hDeviceContext的內容使用BitBlt拷貝給m_hBackDC  
  10. ...  
  11. CDIBBitmap bmp;  
  12. if(!bmp.create(m_hBackDC,m_bBackBitmap))  
  13. {  
  14.     g_log.write("截圖失敗!");  
  15. }  
  16. else  
  17. {  
  18.     CreateDirectory(TEXT("shot"),NULL);  
  19.     char buffer[64];  
  20.     SYSTEMTIME tm;  
  21.     GetLocalTime(&tm);  
  22.     sprintf_s(buffer,64,"shot/%d_%d_%d_%d_%d_%d.bmp",  
  23.         tm.wYear,tm.wMonth,tm.wDay,tm.wHour,tm.wMinute,tm.wSecond);  
  24.     if(!bmp.save(buffer))  
  25.     {  
  26.         g_log.writex("保存位圖【%s】失敗!",buffer);  
  27.     }  
  28. }  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章