首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

BMP 位圖檔案結構解析與平滑縮放技術

     編輯:關於C++

傳統方式顯示 BMP 位圖存在記憶體佔用大、速度慢、縮放失真嚴重、低色深裝置顯示高色深圖像失真等缺點。本文詳細解析 BMP 檔案結構,並介紹使用視訊函數 DrawDib 實現高效、低失真的位圖縮放顯示方案,附完整 MFC 類別實作程式碼。

一、BMP 檔案結構深度解析

1. 檔案組成

BMP 檔案由四部分組成:檔案頭、位圖資訊頭、調色盤、圖像資料。

2. 檔案頭

typedef struct tagBITMAPFILEHEADER {
    WORD  bfType;      // 必須為 'BM'
    DWORD bfSize;      // 檔案大小 (bytes)
    WORD  bfReserved1; // 保留,必須為 0
    WORD  bfReserved2; // 保留,必須為 0
    DWORD bfOffBits;   // 位圖資料起始偏移量
} BITMAPFILEHEADER;

3. 位圖資訊頭

typedef struct tagBITMAPINFOHEADER {
    DWORD biSize;           // 結構大小
    LONG  biWidth;          // 寬度 (像素)
    LONG  biHeight;         // 高度 (像素)
    WORD  biPlanes;         // 必須為 1
    WORD  biBitCount;       // 每像素位數:1, 4, 8, 24
    DWORD biCompression;    // 壓縮類型:0=無, 1=RLE8, 2=RLE4
    DWORD biSizeImage;      // 影像大小
    LONG  biXPelsPerMeter;  // 水平解析度
    LONG  biYPelsPerMeter;  // 垂直解析度
    DWORD biClrUsed;        // 實際使用顏色數
    DWORD biClrImportant;   // 重要顏色數
} BITMAPINFOHEADER;

4. 調色盤

僅當 biBitCount 為 1、4、8 時存在,分別含 2、16、256 個 RGBQUAD 項目:

typedef struct tagRGBQUAD {
    BYTE rgbBlue;
    BYTE rgbGreen;
    BYTE rgbRed;
    BYTE rgbReserved; // 必須為 0
} RGBQUAD;

24 位真彩色圖像無調色盤,像素值直接為 RGB 值。

5. 位圖資料

  • 掃描順序:行內左→右,行間下→上
  • 每行位元組數必須為 4 的倍數,不足補 0
  • 計算公式:DataSizePerLine = ((biWidth * biBitCount + 31) / 8) / 4 * 4
  • 資料總大小:DataSize = DataSizePerLine * biHeight

二、傳統顯示方法與缺點

  1. GlobalAlloc 分配記憶體
  2. 讀入檔案至記憶體
  3. CreateDIBitmap 建立顯示用位圖,CreateCompatibleDC 建立相容 DC
  4. BitBlt 或 StretchBlt 繪製
  5. DeleteObject 釋放資源

缺點:速度慢、記憶體佔用大、縮小失真、低色深裝置顯示高色深圖像失真。

三、DrawDib 視訊函數實現平滑縮放

DrawDib 系列函數提供:

  • 顯示速度快、記憶體佔用少
  • 縮放失真小,支援抖動處理
  • 低色深裝置顯示高色深圖像效果佳
  • 可直接操作位圖資料製作簡單動畫

核心流程

  1. 建構函數呼叫 DrawDibOpen()
  2. 同樣分配記憶體、讀入檔案
  3. OnPaint 中呼叫 DrawDibRealize()、DrawDibDraw()
  4. 解構函數呼叫 DrawDibClose()

四、CViewBitmap 類別關鍵實作

成員變數

HDRAWDIB m_hDrawDib;           // 視訊控制代碼
HANDLE   m_hBitsSrc;           // 位圖檔案記憶體控制代碼
LPSTR    m_pBitsSrc;           // 位圖檔案記憶體位址
BITMAPINFOHEADER* m_pBitmapInfo; // 位圖資訊頭指標

建構/解構

CViewBitmap::CViewBitmap() {
    m_hDrawDib = DrawDibOpen();
}

CViewBitmap::~CViewBitmap() {
    if (m_hDrawDib != NULL) {
        DrawDibClose(m_hDrawDib);
        m_hDrawDib = NULL;
    }
}

繪圖核心函數 GraphicDraw

void CViewBitmap::GraphicDraw(void) {
    CClientDC dc(this);
    BITMAPFILEHEADER* pBitmapFileHeader = (BITMAPFILEHEADER*)m_pBitsSrc;
    ULONG bfOffBits = pBitmapFileHeader->bfOffBits;

    if (m_hDrawDib == NULL || mDispMethod == 0) {
        // 傳統 GDI 方式
        HBITMAP hBitmap = ::CreateDIBitmap(dc.m_hDC, m_pBitmapInfo, CBM_INIT,
            m_pBitsSrc + bfOffBits, (LPBITMAPINFO)m_pBitmapInfo, DIB_RGB_COLORS);
        HDC hMemDC = ::CreateCompatibleDC(dc.m_hDC);
        HBITMAP hOld = (HBITMAP)::SelectObject(hMemDC, hBitmap);
        // ... 座標裁切邏輯 ...
        if (mFullViewTog == 0)
            ::BitBlt(dc.m_hDC, 0, 0, mDispR.Width(), mDispR.Height(),
                hMemDC, mPos.x, mPos.y, SRCCOPY);
        else
            ::StretchBlt(dc.m_hDC, 0, 0, mDispR.Width(), mDispR.Height(),
                hMemDC, 0, 0, m_pBitmapInfo->biWidth, m_pBitmapInfo->biHeight, SRCCOPY);
        ::DeleteObject(::SelectObject(hMemDC, hOld));
    } else {
        // DrawDib 視訊函數方式
        DrawDibRealize(m_hDrawDib, dc.GetSafeHdc(), TRUE);
        // ... 座標裁切邏輯 ...
        if (mFullViewTog == 0) {
            CPoint Wid(mDispR.Width(), mDispR.Height());
            if (Wid.x > m_pBitmapInfo->biWidth) Wid.x = m_pBitmapInfo->biWidth;
            if (Wid.y > m_pBitmapInfo->biHeight) Wid.y = m_pBitmapInfo->biHeight;
            DrawDibDraw(m_hDrawDib, dc.GetSafeHdc(), 0, 0, Wid.x, Wid.y,
                m_pBitmapInfo, (LPVOID)(m_pBitsSrc + bfOffBits),
                mPos.x, mPos.y, Wid.x, Wid.y, DDF_BACKGROUNDPAL);
        } else {
            DrawDibDraw(m_hDrawDib, dc.GetSafeHdc(), 0, 0,
                mDispR.Width(), mDispR.Height(),
                m_pBitmapInfo, (LPVOID)(m_pBitsSrc + bfOffBits),
                0, 0, m_pBitmapInfo->biWidth, m_pBitmapInfo->biHeight,
                DDF_BACKGROUNDPAL);
        }
    }
}

五、專案整合步驟

  1. 建立 MFC AppWizard (exe) 專案
  2. 加入 Bitmap0.h、Bitmap0.cpp 至專案
  3. 資源視圖中選單加入 ID_VIEW_BITMAP 項目
  4. ClassWizard 為 CMainFrame 加入 OnViewBitmap 命令處理器
  5. 實作:new CViewBitmap("BITMAP.BMP", this)->ShowWindow(TRUE);
  6. 編譯執行,準備一張大尺寸真彩色 BMP 測試

六、功能特色與測試建議

  • 支援水平/垂直捲軸、狀態列顯示資訊
  • 雙擊狀態列切換:一般/視訊函數顯示、1:1/全螢幕模式
  • 支援拖放檔案開啟
  • 測試重點:全螢幕縮小時比較兩種方式差異;切換至 256 色模式顯示真彩色圖片,體驗視訊函數優勢

結語

DrawDib 視訊函數有效解決了傳統 GDI 顯示 BMP 的諸多痛點,特別適合需要頻繁縮放、跨色深環境顯示的應用場景。完整原始碼可作為 MFC 圖像處理模組的基礎參考。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved