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

VC++ 實現 BMP 位圖文件結構解析與平滑縮放技術

     編輯:VC++

前言

在 Windows 程式開發中,直接使用 GDI 函數(如 BitBlt、StretchBlt)顯示 BMP 位圖雖然簡單,但存在記憶體佔用大、顯示速度慢、縮放失真嚴重,以及在低色深設備上顯示高色深圖像失真等缺點。本文將詳細解析 BMP 文件結構,並介紹如何利用 Video for Windows (VFW) 的 DrawDib 系列函數實現高效、平滑的位圖縮放顯示。

一、BMP 文件結構深度解析

BMP 文件由四部分組成:文件頭、位圖信息頭、調色板(顏色表)和位圖數據。

1. 文件頭 (BITMAPFILEHEADER)

包含文件類型、大小、保留字及數據偏移量。

typedef struct tagBITMAPFILEHEADER {
    WORD  bfType;      // 必須為 'BM' (0x4D42)
    DWORD bfSize;      // 文件大小(字節)
    WORD  bfReserved1; // 保留,必須為 0
    WORD  bfReserved2; // 保留,必須為 0
    DWORD bfOffBits;   // 位圖數據起始偏移量
} BITMAPFILEHEADER;

2. 位圖信息頭 (BITMAPINFOHEADER)

描述位圖尺寸、色深、壓縮方式、分辨率等核心屬性。

typedef struct tagBITMAPINFOHEADER {
    DWORD biSize;            // 結構體大小
    LONG  biWidth;           // 寬度(像素)
    LONG  biHeight;          // 高度(像素),正值表示底-up DIB
    WORD  biPlanes;          // 色平面數,必須為 1
    WORD  biBitCount;        // 色深:1, 4, 8, 24
    DWORD biCompression;     // 壓縮類型:BI_RGB(0), BI_RLE8(1), BI_RLE4(2)
    DWORD biSizeImage;       // 圖像數據大小
    LONG  biXPelsPerMeter;   // 水平分辨率
    LONG  biYPelsPerMeter;   // 垂直分辨率
    DWORD biClrUsed;         // 實際使用顏色數
    DWORD biClrImportant;    // 重要顏色數
} BITMAPINFOHEADER;

3. 調色板 (RGBQUAD)

僅當 biBitCount 為 1, 4, 8 時存在。每個條目定義一種顏色(BGR 順序 + 保留字)。

typedef struct tagRGBQUAD {
    BYTE rgbBlue;
    BYTE rgbGreen;
    BYTE rgbRed;
    BYTE rgbReserved; // 必須為 0
} RGBQUAD;
  • 1-bit: 2 色 (2 項)
  • 4-bit: 16 色 (16 項)
  • 8-bit: 256 色 (256 項)
  • 24-bit: 無調色板,直接存儲 RGB 數據

4. 位圖數據存儲規則

  • 掃描行順序:從下到上(Bottom-up DIB)
  • 行內像素:從左到右
  • 每行字節數必須是 4 的倍數(DWORD 對齊),不足補 0
  • 行大小計算:((biWidth * biBitCount + 31) / 32) * 4

二、傳統 GDI 顯示方法及其缺陷

  1. GlobalAlloc 分配記憶體讀取文件
  2. CreateDIBitmap 創建 DDB(設備相關位圖)
  3. 創建兼容 DC (CreateCompatibleDC),選入位圖
  4. 使用 BitBlt (1:1) 或 StretchBlt (縮放) 繪製
  5. DeleteObject 釋放資源

主要缺點:

  • 速度慢,特別是 StretchBlt 縮放時
  • 需創建 DDB,佔用大量顯存/內存
  • 縮小圖像嚴重失真(無抗鋸齒/抖動處理)
  • 低色深模式(如 256 色)下顯示真彩色圖像顏色慘不忍睹

三、DrawDib 視頻函數:高性能平滑縮放方案

DrawDib 是 VFW 提供的高性能繪圖 API,支持抖動、色彩映射、高效縮放,直接操作 DIB 數據,無需創建 DDB。

核心流程:

  1. 構造函數中調用 DrawDibOpen() 獲取 HDRAWDIB 句柄
  2. 分配記憶體並讀取 BMP 文件到緩衝區 (mpBitsSrc)
  3. 解析文件頭,獲取 BITMAPINFOHEADER 指針 (mpBitmapInfo) 及數據偏移 (bfOffBits)
  4. OnPaint 中調用 DrawDibRealize 實現調色板映射,隨後調用 DrawDibDraw 繪製
  5. 析構函數中調用 DrawDibClose() 釋放資源

關鍵代碼片段 (GraphicDraw):

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

    if (m_hDrawDib == NULL || mDispMethod == 0) {
        // 傳統 GDI 方式 (略)
    } else {
        // DrawDib 高性能方式
        DrawDibRealize(m_hDrawDib, dc.GetSafeHdc(), TRUE);

        if (mFullViewTog == 0) { // 1:1 顯示
            int w = min(mDispR.Width(), mpBitmapInfo->biWidth);
            int h = min(mDispR.Height(), mpBitmapInfo->biHeight);
            DrawDibDraw(m_hDrawDib, dc.GetSafeHdc(),
                0, 0, w, h,
                mpBitmapInfo, (LPVOID)(mpBitsSrc + bfOffBits),
                mPos.x, mPos.y, w, h, DDF_BACKGROUNDPAL);
        } else { // 全屏縮放顯示
            DrawDibDraw(m_hDrawDib, dc.GetSafeHdc(),
                0, 0, mDispR.Width(), mDispR.Height(),
                mpBitmapInfo, (LPVOID)(mpBitsSrc + bfOffBits),
                0, 0, mpBitmapInfo->biWidth, mpBitmapInfo->biHeight,
                DDF_BACKGROUNDPAL);
        }
    }
}

DrawDib 優勢總結:

  • 繪製速度極快,利用硬件加速及優化算法
  • 內存佔用低,直接繪製 DIB,無需中間 DDB
  • 縮放品質高,內建抖動算法,縮小失真極小
  • 完美解決低色深設備顯示高色深圖像問題(自動色彩映射/抖動)
  • 支持直接操作數據幀,易於製作簡單動畫

四、CViewBitmap 類封裝要點

成員變量:

HDRAWDIB         m_hDrawDib;     // DrawDib 句柄
HANDLE           m_hBitsSrc;     // 文件內存句柄
LPSTR            m_pBitsSrc;     // 文件內存指針
BITMAPINFOHEADER* m_pBitmapInfo; // 信息頭指針

生命周期管理:

  • 構造函數: m_hDrawDib = DrawDibOpen();
  • 析構函數: if(m_hDrawDib) DrawDibClose(m_hDrawDib);

五、實戰應用:在 MFC 專案中集成

  1. 創建 MFC AppWizard (exe) 專案
  2. 添加 Bitmap0.h/.cpp (包含 CViewBitmap) 到專案
  3. 主框架菜單添加 "ViewBitmap" 項 (ID_VIEW_BITMAP),映射命令處理函數 OnViewBitmap:
    void CMainFrame::OnViewBitmap() {
        CViewBitmap* pView = new CViewBitmap(_T("BITMAP.BMP"), this);
        pView->ShowWindow(SW_SHOW);
    }
  4. 包含頭文件 #include "Bitmap0.h"
  5. 準備一張大尺寸真彩色 BMP 重命名為 BITMAP.BMP 放入執行目錄
  6. 運行程序,點擊菜單查看效果

六、功能特性與測試建議

  • 支持水平/垂直滾動條瀏覽大圖
  • 狀態欄顯示:圖片信息、顯示模式切換 (GDI/DrawDib)、縮放模式切換 (1:1/全屏)
  • 支持拖放文件打開
  • 對比測試: 切換到全屏模式縮小顯示大圖,對比 GDI 與 DrawDib 差異;將系統顏色設置為 256 色,再對比兩者顯示真彩色圖片效果

DrawDib 技術雖屬早期 VFW 技術,但其核心思想——直接 DIB 操作、硬件加速抖動縮放——在現代圖形引擎中依然具有參考價值。對於維護舊系統或嵌入式 Windows 開發,這仍是一套經典高效的方案。

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