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

BMP 圖像文件讀寫全解析:C 語言實現位圖加載與存儲(含 4 字節對齊)

     編輯:關於C語言

BMP 格式核心結構與讀寫關鍵點

BMP (Bitmap) 是 Windows 標準圖像格式,結構簡單、無壓縮,適合學習圖像處理基礎。核心難點在於行數據 4 字節對齊、顏色表處理(灰度圖 vs 真彩色)、以及文件頭/信息頭的正確填充。

關鍵數據結構

// 全局變量
unsigned char *gPBmpBuf;      // 像素數據指針
int gBmpWidth, gBmpHeight;    // 寬高
RGBQUAD *gPColorTable;        // 調色盤指針 (8位圖)
int gBiBitCount;              // 位深:8/24/32

// 4字節對齊宏:(bits+31)/32*4 等價於 ((bits+31)&~31)/8
#define WIDTHBYTES(bits) (((bits) + 31) & (~31)) / 8

讀取 BMP 文件 loadBmp

bool loadBmp(char *bmpName) {
    FILE *fp = fopen(bmpName, "rb");
    if (!fp) return false;

    // 1. 跳過文件頭 (14字節)
    fseek(fp, sizeof(BITMAPFILEHEADER), SEEK_SET);

    // 2. 讀取信息頭 (40字節)
    BITMAPINFOHEADER infoHead;
    fread(&infoHead, sizeof(BITMAPINFOHEADER), 1, fp);

    gBmpWidth = infoHead.biWidth;
    gBmpHeight = infoHead.biHeight;
    gBiBitCount = infoHead.biBitCount;

    // 3. 計算行字節數 (4字節對齊)
    int lineByte = (gBmpWidth * gBiBitCount / 8 + 3) / 4 * 4;

    // 4. 讀取調色盤 (僅 8位圖)
    if (8 == gBiBitCount) {
        gPColorTable = (RGBQUAD*)malloc(sizeof(RGBQUAD) * 256);
        fread(gPColorTable, sizeof(RGBQUAD), 256, fp); // 修正: 讀256項
    }

    // 5. 讀取像素數據
    gPBmpBuf = (unsigned char*)malloc(gBmpHeight * lineByte);
    fread(gPBmpBuf, 1, gBmpHeight * lineByte, fp);

    fclose(fp);
    return true;
}

存儲 BMP 文件 storeBmp

bool storeBmp(char *bmpName, unsigned char *imgBuf, int width, int height, 
              int bitCount, RGBQUAD *colorTable) {
    if (!imgBuf) return false;

    int lineByte = (width * bitCount / 8 + 3) / 4 * 4;
    int colorTableSize = (bitCount == 8) ? 1024 : 0; // 256 * 4

    FILE *fp = fopen(bmpName, "wb");
    if (!fp) return false;

    // 1. 文件頭
    BITMAPFILEHEADER fileHead = {0};
    fileHead.bfType = 0x4D42; // 'BM'
    fileHead.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) 
                    + colorTableSize + lineByte * height;
    fileHead.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) 
                       + colorTableSize;
    fwrite(&fileHead, sizeof(BITMAPFILEHEADER), 1, fp);

    // 2. 信息頭
    BITMAPINFOHEADER infoHead = {0};
    infoHead.biSize = 40;
    infoHead.biWidth = width;
    infoHead.biHeight = height;
    infoHead.biPlanes = 1;
    infoHead.biBitCount = bitCount;
    infoHead.biCompression = 0; // BI_RGB 無壓縮
    infoHead.biSizeImage = lineByte * height;
    fwrite(&infoHead, sizeof(BITMAPINFOHEADER), 1, fp);

    // 3. 調色盤
    if (bitCount == 8 && colorTable) {
        fwrite(colorTable, sizeof(RGBQUAD), 256, fp);
    }

    // 4. 像素數據
    fwrite(imgBuf, lineByte * height, 1, fp);
    fclose(fp);
    return true;
}

主程序測試

int main() {
    char *src = "E:/test_pic.bmp";
    char *dst = "E:/save_pic.bmp";
    
    if (loadBmp(src) && storeBmp(dst, gPBmpBuf, gBmpWidth, gBmpHeight, gBiBitCount, gPColorTable)) {
        printf("BMP 讀寫測試成功!\n");
    } else {
        printf("失敗\n");
    }
    
    // 內存釋放
    if (gBiBitCount == 8) free(gPColorTable);
    free(gPBmpBuf);
    return 0;
}

關鍵技術點總結表

項目8位灰度圖24位真彩色32位帶Alpha
biBitCount82432
調色盤有 (256*RGBQUAD)無無
像素存儲索引值 (1字節)BGR (3字節)BGRA (4字節)
行對齊必須必須天然對齊 (4的倍數)

常見坑位與避坑指南

  • 行對齊計算:(width * bitCount / 8 + 3) / 4 * 4 或位運算版 ((width * bitCount + 31) & ~31) / 8,切勿直接用 width * bytesPerPixel。
  • 調色盤讀取項數:fread(colorTable, sizeof(RGBQUAD), 256, fp),第三參數是項數非字節數。
  • 內存釋放順序:先釋放像素緩衝,再釋放調色盤,防止野指針。
  • 高度正負號:biHeight > 0 為底-up (掃描線從底部開始),讀取時注意行序;biHeight < 0 為頂-down。
  • 結構體對齊:編譯器可能對結構體填充,建議用 #pragma pack(1) 或逐字段讀寫。

總結

BMP 讀寫雖屬基礎,但涵蓋了文件格式解析、內存對齊、調色盤管理、二進制 I/O 等核心系統編程技能。掌握此流程,可輕鬆擴展至圖像旋轉、縮放、濾鏡、格式轉換 (BMP↔JPG/PNG) 等高級圖像處理任務。

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