weidagang2046的專欄

          物格而后知致
          隨筆 - 8, 文章 - 409, 評論 - 101, 引用 - 0
          數據加載中……

          位圖文件讀寫綜述

          位圖文件讀寫綜述
          作者: 吉林大學 胡卓瑋

          一、位圖文件結構

          1. 位圖文件頭
          2. 位圖信息
            2.1 位圖信息頭
            2.2 顏色表
          3. 位圖數據

          二、位圖文件讀寫操作

          1. 類的聲明
          2. 位圖的讀取
          3. 位圖讀取過程中的調色板的創建和調用
          4. 位圖的顯示
          5. 位圖的存儲
          6. 新位圖的創建
          7. 其它問題

          三、 CFG_DIB 的使用


          下載本文配套代碼


          關于位圖文件操作的資料很多。為了方便開發人員的工作,寫下本文,介紹了位圖文件結構,在此基礎之上設計了通用類 CFG_DIB ,用于進行位圖文件的讀寫操作。

          一、位圖文件結構

          位圖文件由三部分組成:文件頭 + 位圖信息 + 位圖像素數據

          1 、位圖文件頭 。位圖文件頭主要用于識別位圖文件。以下是位圖文件頭結構的定義:

          typedef struct tagBITMAPFILEHEADER { // bmfh

          ??? WORD??? bfType;

          ??? DWORD?? bfSize;

          ??? WORD??? bfReserved1;

          ??? WORD??? bfReserved2;

          ??? DWORD?? bfOffBits;

          } BITMAPFILEHEADER;

          其中的 bfType 值應該是 “BM” 0x4d42 ),標志該文件是位圖文件。 bfSize 的值是位圖文件的大小。
          2 、位圖信息 中所記錄的值用于分配內存,設置調色板信息,讀取像素值等。
          以下是位圖信息結構的定義:

          typedef struct tagBITMAPINFO {

          ??? BITMAPINFOHEADER??? bmiHeader;

          ??? RGBQUAD????????? ???bmiColors[1];

          } BITMAPINFO;

          可見位圖信息也是由兩部分組成的:位圖信息頭 + 顏色表



          2.1 位圖信息頭。 位圖信息頭包含了單個像素所用字節數以及描述顏色的格式,此外還包括位圖的寬度、高度、目標設備的位平面數、圖像的壓縮格式。以下是位圖信息頭結構的定義:

          typedef struct tagBITMAPINFOHEADER{ // bmih

          ??? DWORD? biSize;

          ??? LONG?? biWidth;

          ??? LONG?? biHeight;

          ? ??WORD?? biPlanes;

          ??? WORD?? biBitCount

          ??? DWORD? biCompression;

          ??? DWORD? biSizeImage;

          ??? LONG?? biXPelsPerMeter;

          ??? LONG?? biYPelsPerMeter;

          ??? DWORD? biClrUsed;

          ??? DWORD? biClrImportant;

          } BITMAPINFOHEADER;

          下表是對結構體當中各個成員的說明:

          結構成員

          biSize

          結構 BITMAPINFOHEADER 的字節數,即 sizeof(BITMAPINFOHEADER)*

          biWidth

          以像素為單位的圖像寬度 *

          biHeight

          以像素為單位的圖像長度 *

          biplanes

          目標設備的位平面數

          biBitCount

          每個像素的位數 * 1

          biCompression

          圖像的壓縮格式(這個值幾乎總是為 0

          biSizeImage

          以字節為單位的圖像數據的大小(對 BI_RGB 壓縮方式而言)

          biXPelsPermeter

          水平方向上的每米的像素個數

          biYpelsPerMeter

          垂直方向上的每米的像素個數

          biClrused

          調色板中實際使用的顏色數( 2

          biClrImportant

          顯示位圖時必須的顏色數( 3

          說明: * 是需要加以注意的部分,因為它們是我們在進行位圖操作時經常參考的變量
          1 )對于每個像素的字節數,分別有一下意義:
          0
          ,用在 JPEG 格式中
          1
          ,單色圖,調色板中含有兩種顏色,也就是我們通常說的黑白圖片
          4
          16 色圖
          8
          256 色圖,通常說的灰度圖
          16
          64K 圖,一般沒有調色板,圖像數據中每兩個字節表示一個像素, 5 個或 6 個位表示一個 RGB 分量
          24
          16M 真彩色圖,一般沒有調色板,圖像數據中每 3 個字節表示一個像素,每個字節表示一個 RGB 分量
          32
          4G 真彩色,一般沒有調色板,每 4 個字節表示一個像素,相對 24 位真彩圖而言,加入了一個透明度,即 RGBA 模式

          2 )這個值通常為 0 ,表示使用 biBitCount 確定的全部顏色,例外是使用的顏色數目小于指定的顏色深度的顏色數目的最大值。

          3 )這個值通常為 0 ,表示所有的顏色都是必需的

          2.2 顏色表。 顏色表一般是針對 16 位以下的圖像而設置的,對于 16 位和 16 位以上的圖像,由于其位圖像素數據中直接對對應像素的 RGB(A) 顏色進行描述,因而省卻了調色板。而對于 16 位以下的圖像,由于其位圖像素數據中記錄的只是調色板索引值,因而需要根據這個索引到調色板去取得相應的 RGB(A) 顏色。顏色表的作用就是創建調色板。

          下圖是帶調色板和不帶調色板的位圖的簡單示意圖

          1 帶調色板和不帶調色板位圖之間的區別

          顏色表是由顏色表項組成的,顏色表項結構的定義如下:

          typedef struct tagRGBQUAD { // rgbq

          ??? BYTE??? rgbBlue;

          ??? BYTE??? rgbGreen;

          ??? BYTE??? rgbRed;

          ??? BYTE??? rgbReserved;

          } RGBQUAD;

          其中需要注意的問題是, RGBQUAD 結構中的顏色順序是 BGR ,而不是平常的 RGB

          3 、位圖數據。 最后,在位圖文件頭、位圖信息頭、位圖顏色表之后,便是位圖的主體部分:位圖數據。根據不同的位圖,位圖數據所占據的字節數也是不同的,比如,對于 8 位位圖,每個字節代表了一個像素,對于 16 位位圖,每兩個字節代表了一個像素,對于 24 位位圖,每三個字節代表了一個像素,對于 32 位位圖,每四個字節代表了一個像素。

          二、位圖文件讀寫操作

          認識了位圖文件的結構以后,對特定位圖文件進行讀寫操作就顯得簡單了。本文附帶的源代碼中包含了一個能夠方便進行位圖讀寫操作的 C++ 類。以下給出該類的使用參考,對于實現代碼中的關鍵部分做出了講解。

          1 、類的聲明

          class CFG_DIB : public CObject

          {

          public:

          ??????????? // 默認構造函數

          ??????????? CFG_DIB();

          ??????????? // 構造函數 , 根據圖象寬和高 , 以及記錄每個象素所需字節數來初始化

          ??????????? CFG_DIB(int width, int height, int nBitCounts);

          ??????????? virtual ~CFG_DIB();

          ?

          public:

          ??????????? HBITMAP m_hBitmap;

          ??????????? LPBYTE m_lpDIBits; //DIB 位的起始位置

          ??????????? LPBITMAPINFOHEADER m_lpBMPHdr;???????????????????? //BITMAPINFOHEADER 信息

          ??????????? LPVOID m_lpvColorTable; // 顏色表信息

          ??????????? HPALETTE m_hPalette; // 調色板

          ?

          private:

          ??????????? DWORD m_dwImageSize; // BITMAPINFOHEADER BITMAPFILEHEADER 的位

          ??????????? int m_nColorEntries; // 顏色表項的個數

          ?

          // 顯示參數

          public:

          ??????????? CPoint m_Dest; // 目的矩形域的左上角坐標

          ??????????? CSize m_DestSize; // 顯示矩形的寬度和高度

          ??????????? CPoint m_Src; // 原矩形左下角坐標

          ??????????? CSize m_SrcSize; // 原矩形寬度和高度

          ?

          public:

          ??????????? void InitDestroy();// 初始化變量

          ??????????? void ComputePaletteSize(int nBitCounts); // 計算調色板大小

          ??????????? void ComputeImage();// 計算圖象大小

          ?

          ??????????? // BMP 文件中讀入 DIB 信息

          ??????????? BOOL ReadFile(CFile* pFile);

          ?

          ??????????? // BMP 文件中讀入 DIB 信息 , ReadFile 不同的是使用 CreateSection 創建位圖位

          ??????????? BOOL ReadSection(CFile* pFile, CDC* pDC = NULL);

          ?

          ??????????? // DIB 寫入文件,保存成 BMP 圖片格式

          ??????????? BOOL WriteFile(CFile* pFile);

          ?

          ??????????? // 創建新的位圖文件,根據參數 width,height,nBitCounts 分配內存空間

          ??????????? BOOL NewFile(int width, int height, int nBitCounts);

          ??????????? // 關閉位圖文件

          ??????????? BOOL CloseFile();

          ?

          ??????????? // 顯示位圖

          ??????????? BOOL Display(CDC* pDC);

          ?

          ??????????? HBITMAP CreateBitmap(CDC* pDC);?????????????????????????????????????????????? // DIB 創建 DDB

          ??????????? HBITMAP CreateSection(CDC* pDC = NULL);????????????????????? // 創建位圖位數據,即象素數據

          ??????????? // 如果 DIB 沒有顏色表,可以用邏輯調色板

          ??????????? BOOL SetLogPalette(CDC* pDC);

          ??????????? // 如果 DIB 有顏色表,可以創建系統調色板

          ??????????? BOOL SetWinPalette();

          ??????????? // DIB 對象的邏輯調色板選進設備環境里,然后實現調色板

          ??????????? UINT UseLogPalette(CDC* pDC);

          ?

          ??????????? // 得到 BitmapInfoHeader 的大小,包含顏色表數據

          ??????????? int GetHeaderSize()

          ??????????? {

          ??????????????????????? return sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * m_nColorEntries;

          ??????????? }

          ??????????? // 得到圖像的高度

          ??????????? int GetHeight()

          ??????????? {

          ??????????????????????? if(m_lpBMPHdr == NULL) return 0;

          ??????????????????????? return m_lpBMPHdr->biHeight;

          ??????????? }

          ??????????? // 得到圖像的寬度

          ??????????? int GetWidth()

          ??????????? {

          ??????????????????????? if(m_lpBMPHdr == NULL) return 0;

          ??????????????????????? return m_lpBMPHdr->biWidth;

          ??????????? }

          ??????????? // 得到圖像的大小

          ??????????? int GetImageSize()

          ??????????? {

          ??????????????????????? return m_dwImageSize;

          ??????????? }

          ??????????? long GetLineBit();?????????????????????? // 得到一行的象素數

          };

          2 、位圖的讀取。
          CFG_DIB
          提供了兩個從位圖文件讀取位圖數據的方法: ReadFile ReadSection ,二者不同之處,前者使用動態分配內存的方法初始化存儲位位圖數據的指針,后者則使用 API 函數,根據位圖信息初始化存儲位圖數據的指針。

          方法 1

          m_lpDIBits = (LPBYTE) new char[m_dwImageSize];

          方法 2

          m_hBitmap = ::CreateDIBSection(pDC->GetSafeHdc(),

          ???????? (LPBITMAPINFO) m_lpBMPHdr, DIB_RGB_COLORS,

          ???????? (LPVOID*) &m_lpDIBits, NULL, 0);

          3 、位圖讀取過程中的調色板的創建和調用。
          關于調色板的詳細情況,本文不作詳細介紹,只是對讀取位圖的過程中需要調用的對調色板進行操作的相關函數進行說明。

          讀取文件的過程中,計算出調色板大小,然后調用創建調色板函數:

          ComputePaletteSize(m_lpBMPHdr->biBitCount);

          SetWinPalette();

          在顯示位圖之前,設置調色板:

          if(m_hPalette != NULL) {

          ???? ::SelectPalette(pDC->GetSafeHdc(), m_hPalette, TRUE);

          }

          4 、位圖的顯示。
          位圖的顯示還是調用 Windows API 函數來進行,需要傳遞的參數包括當前位圖信息頭,位圖數據等:

          ::StretchDIBits(pDC->GetSafeHdc(), m_Dest.x, m_Dest.y,

          ???????????????? ????????????m_DestSize.cx, m_DestSize.cy,

          ???????????????????????????? m_Src.x, m_Src.y,

          ???????????????????????????? m_SrcSize.cx, m_SrcSize.cy,

          ???????????????????????????? m_lpDIBits, (LPBITMAPINFO) m_lpBMPHdr,

          ???????????????????????????? DIB_RGB_COLORS, SRCCOPY);

          其中的 m_Dest m_DestSize m_Src m_SrcSize 分別代表了圖像在當前設備上顯示的左上角坐標和范圍以及需要顯示的源圖像的左下角坐標和范圍。此處需要說明的是,位圖數據的字節數組是從圖像的最下面一行開始逐行向上存儲的,所以用戶在選取源位圖的現實范圍的時候需要特別注意!
          m_Dest
          m_DestSize m_Src m_SrcSize 需要在現實之前設置好。

          5 、位圖的存儲。 位圖的存儲用 WriteFile 實現。
          6 、新位圖的創建。 新位圖的創建由 NewFile 實現。需要的參數是位圖的寬度、高度、以及位圖像素占用的位數。
          7 、其它問題。 存取位圖數據的字節數組有個問題需要引起開發人員的注意:字節數組中每個掃描行的字節數必需是 4 的倍數,如果不足要用 0 補齊。
          以下是處理的辦法:

          DWORD dwBytes = ((DWORD) m_lpBMPHdr->biWidth * m_lpBMPHdr->biBitCount) / 32;

          if(((DWORD) m_lpBMPHdr->biWidth * m_lpBMPHdr->biBitCount) % 32) {

          ???? dwBytes++;

          }

          dwBytes *= 4;

          m_dwImageSize = dwBytes * m_lpBMPHdr->biHeight;

          這段代碼按照要求算出了用于記錄圖像數據的字節數組的大小。

          三、 CFG_DIB 的使用

          以下是 CFG_DIB 的使用示例代碼。

          #include "fg_dib.h"

          ?

          CFG_DIB m_fgdib;

          ?

          //new file

          m_fgdib.NewFile(width, height, nbitnum);

          ?

          //open file

          CFile* pf;

          pf = new CFile;

          pf->Open(sFileName, CFile::modeRead);

          m_fgdib.ReadFile(pf);

          pf->Close();

          delete pf;

          ?

          //draw BMP

          m_fgdib.m_Dest.x = 0;

          m_fgdib.m_Dest.y = 0;

          m_fgdib.m_DestSize.cx = m_fgdib.GetWidth();

          m_fgdib.m_DestSize.cy = m_fgdib.GetHeight();

          m_fgdib.m_Src.x = 0;

          m_fgdib.m_Src.y = 0;

          m_fgdib.m_SrcSize.cx = m_fgdib.GetWidth();

          m_fgdib.m_SrcSize.cy = m_fgdib.GetHeight();

          CDC* pDC = GetDC();

          m_fgdib.Display(pDC);

          ?

          //close BMP

          m_fgdib.CloseFile();


          如果您在閱讀文章和使用代碼過程中遇到的問題,請與作者聯系:

          吉林省長春市西民主大街 6 號地球探測科學與技術學院 2001 級碩士研究生 (130026)
          歡迎訪問作者的主頁 :Forevergis.6to23.com

          posted on 2006-10-01 17:07 weidagang2046 閱讀(1388) 評論(0)  編輯  收藏 所屬分類: Windows

          主站蜘蛛池模板: 泾源县| 麻城市| 昌宁县| 凌云县| 镇巴县| 广德县| 察隅县| 镇沅| 延长县| 丰城市| 濮阳市| 镇雄县| 华坪县| 综艺| 勃利县| 友谊县| 仙桃市| 金寨县| 盐城市| 扎鲁特旗| 福州市| 积石山| 澳门| 德阳市| 隆安县| 勐海县| 霍山县| 阳曲县| 乐山市| 乌兰浩特市| 商水县| 柳河县| 永兴县| 育儿| 新建县| 西充县| 南投市| 四平市| 青岛市| 麻城市| 伊川县|