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

256 色位圖 (BMP) 格式解析與 C++ Builder 繪圖實作

     編輯:關於C++

前言

本文以一個在 Borland C++ Builder 6.0 編譯通過的完整範例 DrawBmp.cpp 為基礎,詳細解析 256 色 BMP 檔案結構,並示範如何手動解析檔案標頭、調色盤與像素資料,直接繪製至視窗畫布。

BMP 檔案結構速覽 (256 色)

位移大小內容
014 Bytes檔案標頭
1440 Bytes資訊標頭
54256 × 4 Bytes調色盤 (RGB + 保留)
1078Width × Height Bytes像素索引資料 (每 Byte 一像素)

關鍵欄位位移 (資訊標頭)

  • 18-21:圖寬 (4 Bytes, 有號長整數)
  • 22-25:圖高 (4 Bytes)
  • 28-29:色深 (2 Bytes, 8 表 256 色)

完整實作程式碼


#include 
#pragma hdrstop
#include "DrawBitmap.h"

#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

typedef enum {
    OK, FORMATERROR, FILENOTFOUND, OVERFLOW, READERROR
} RESULTCODE;

RESULTCODE DrawBmp(int start_x, int start_y, char filename[]);
AnsiString filen;

__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) {
    filen = InputBox("256 位圖顯示程式", "輸入 256 位圖檔名...", filen);
}

void __fastcall TForm1::FormPaint(TObject *Sender) {
    RESULTCODE res = DrawBmp(0, 0, filen.c_str());
}

//------------------------------------------------------------
#include 
unsigned char DAC[256][3]; // 調色盤 RGB

RESULTCODE DrawBmp(int start_x, int start_y, char filename[]) {
    int i, j, k, n, r, g, b, width, height;
    int xcnt = 0, ycnt = 0;
    FILE *fp;

    if ((fp = fopen(filename, "rb")) == NULL) {
        ShowMessage("檔案開啟失敗");
        return FILENOTFOUND;
    }

    // 檢查色深
    fseek(fp, 28, SEEK_SET);
    fread(&i, 2, 1, fp);
    if (i != 8) { fclose(fp); return FORMATERROR; }

    // 讀取寬高
    fseek(fp, 18, SEEK_SET);
    fread(&width, 4, 1, fp);
    fread(&height, 4, 1, fp);

    // 讀取調色盤 (54 位移之後)
    fseek(fp, 54, SEEK_SET);
    for (i = 0; i < 256; i++) {
        DAC[i][0] = fgetc(fp); // B
        DAC[i][1] = fgetc(fp); // G
        DAC[i][2] = fgetc(fp); // R
        fgetc(fp);             // 保留位
    }

    // 調整視窗大小置中 (假設 1024x768)
    Form1->Height = height + 30;
    Form1->Width  = width;
    Form1->Left   = (1024 - Form1->Width)  / 2;
    Form1->Top    = (768  - Form1->Height) / 2;

    ycnt = start_y + height;
    unsigned char *buffer = (unsigned char*)malloc(width);
    if (!buffer) return OVERFLOW;

    // BMP 為自下而上儲存,需反轉 Y 軸
    while (ycnt > 0) {
        xcnt = 0;
        if (fread(buffer, width, 1, fp) != 1) { free(buffer); fclose(fp); return READERROR; }
        while (xcnt < width) {
            unsigned char data = buffer[xcnt];
            // 注意 DAC 存 BGR,Canvas->Pixels 需 RGB
            Form1->Canvas->Pixels[xcnt][ycnt] = RGB(DAC[data][2], DAC[data][1], DAC[data][0]);
            xcnt++;
        }
        ycnt--;
    }

    free(buffer);
    fclose(fp);
    return OK;
}

關鍵技術點解析

1. 調色盤序列為 BGR

BMP 檔案中每筆調色盤記錄為 Blue, Green, Red, Reserved,而 VCL RGB() 宏需 R, G, B,故存取時必須交換:RGB(DAC[i][2], DAC[i][1], DAC[i][0])。

2. 座標系翻轉

BMP 像素資料由左下角開始向上掃描,螢幕座標原點在左上角。程式以 ycnt = start_y + height 起始,每行繪製後 ycnt-- 實現垂直翻轉。

3. 逐行緩衝

配置一行寬度的緩衝區 malloc(width),逐行讀取、逐像素轉色繪製,記憶體佔用極小,適合大圖。

4. 錯誤代碼枚舉

使用 RESULTCODE 明確回傳狀態,便於上層處理。

效能優化建議

  • 使用 ScanLine 直接寫入位圖記憶體,避免 Pixels[][] 的高呼叫開銷。
  • 若頻繁重繪,建議先載入 TBitmap 或 Graphics::TBitmap 快取。
  • 可改用 StretchDIBits 或 SetDIBitsToDevice 直接繪製 DIB,完全跳過 VCL 封裝層。

總結

理解 BMP 檔案結構、調色盤序列、座標系差異,即可不依賴高層封裝直接繪製 256 色位圖。此技術對嵌入式圖形、舊系統遷移、特殊格式處理仍具參考價值。

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