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

DOS 時代的經典:VGA 256 色模式下 BMP 位圖顯示完整源碼解析

     編輯:關於C語言

歷史背景與技術棧

16 位實模式 DOS(Turbo C / Borland C++),直接操作硬體:

  • 視頻內存映射:0xA000:0000 (VGA 模式 13h,320×200×256 色)
  • I/O 端口:調色板寄存器 0x3C6-0x3C9
  • BIOS 中斷:INT 0x10 設置視頻模式
  • 文件 I/O:open/read/lseek/close (低級 POSIX 風格)

核心數據結構:BMP 文件格式解析

// 位圖文件頭 (14 字節)
typedef struct {
    uint16_t bfType;      // 'BM' (0x4D42)
    uint32_t bfSize;      // 文件大小
    uint16_t bfReserved1; // 0
    uint16_t bfReserved2; // 0
    uint32_t bfOffBits;   // 數據偏移
} BITMAPFILEHEADER;

// 位圖信息頭 (40 字節,BITMAPINFOHEADER)
typedef struct {
    uint32_t biSize;          // 40
    int32_t  biWidth;         // 寬
    int32_t  biHeight;        // 高 (正數=底部優先)
    uint16_t biPlanes;        // 1
    uint16_t biBitCount;      // 色深:1/4/8/24/32
    uint32_t biCompression;   // 0=BI_RGB 無壓縮
    uint32_t biSizeImage;     // 數據大小
    int32_t  biXPelsPerMeter;
    int32_t  biYPelsPerMeter;
    uint32_t biClrUsed;       // 實際調色板顏色數
    uint32_t biClrImportant;
} BITMAPINFOHEADER;

// 調色板項 (4 字節,BGR 順序 + 保留)
typedef struct {
    uint8_t rgbBlue;
    uint8_t rgbGreen;
    uint8_t rgbRed;
    uint8_t rgbReserved;
} RGBQUAD;

關鍵算法:加載並顯示 8-bit BMP

  1. 驗證:bfType == 'BM'、biBitCount == 8、biCompression == 0。
  2. 讀取調色板:256 個 RGBQUAD,順序 BGR。需將 8-bit 分量縮放至 6-bit (VGA DAC):outp(0x3C9, color >> 2)。
  3. 設置調色板:
    outp(0x3C6, 0xFF);        // 掩碼寄存器
    outp(0x3C8, index);       // 寫索引端口
    outp(0x3C9, r>>2); outp(0x3C9, g>>2); outp(0x3C9, b>>2);
  4. 讀取像素數據並繪製:
    • BMP 掃描行**從下到上**存儲(biHeight > 0)。
    • 每行字節數 = width (8-bit 索引),**無填充**(8-bit 索引天然字節對齊)。
    • 視頻內存線性:video_mem[y * 320 + x] = index。
    • 代碼採用 lseek(fp, -(height-y)*width, SEEK_END) 從文件末尾倒序讀取每行,直接寫入視頻內存對應行。

完整流程代碼精簡版

#include <stdio.h>, <dos.h>, <conio.h>, <fcntl.h>, <io.h>, <stdlib.h>

#define VGA_256 0x13
#define TEXT_MODE 0x03
#define SCREEN_W 320
#define SCREEN_H 200

unsigned char far *vram = (unsigned char far *)0xA0000000L;

void set_mode(int mode) {
    union REGS r; r.h.ah = 0; r.h.al = mode; int86(0x10, &r, &r);
}

void set_palette(int idx, RGBQUAD *c) {
    outp(0x3C6, 0xFF);
    outp(0x3C8, idx);
    outp(0x3C9, c->rgbRed >> 2);
    outp(0x3C9, c->rgbGreen >> 2);
    outp(0x3C9, c->rgbBlue >> 2);
}

void load_bmp(const char *file) {
    int fp = open(file, O_RDONLY | O_BINARY);
    BITMAPFILEHEADER fh; read(fp, &fh, sizeof(fh));
    BITMAPINFOHEADER ih; read(fp, &ih, sizeof(ih));
    if (fh.bfType != 0x4D42 || ih.biBitCount != 8 || ih.biCompression != 0) { close(fp); exit(1); }

    RGBQUAD pal[256];
    for (int i = 0; i < 256; i++) {
        read(fp, &pal[i].rgbBlue, 1);
        read(fp, &pal[i].rgbGreen, 1);
        read(fp, &pal[i].rgbRed, 1);
        read(fp, &pal[i].rgbReserved, 1);
        set_palette(i, &pal[i]);
    }

    unsigned char line[SCREEN_W];
    for (int y = SCREEN_H - 1; y >= 0; y--) {
        lseek(fp, -(long)(SCREEN_H - y) * SCREEN_W, SEEK_END);
        read(fp, line, SCREEN_W);
        _fmemcpy(vram + y * SCREEN_W, line, SCREEN_W); // far memcpy
    }
    close(fp);
}

int main() {
    set_mode(VGA_256);
    load_bmp("256.bmp");
    getch();
    set_mode(TEXT_MODE);
    return 0;
}

BMP 格式深度補充:掃描行填充與色深

  • 掃描行填充:Windows 要求每行字節數為 4 的倍數。
    行字節 = ((寬 * 色深位數 + 31) / 32) * 4
    // 1-bit: 8像素/字節
    // 4-bit: 2像素/字節
    // 8-bit: 1像素/字節 (無填充)
    // 24-bit: 3字節/像素 (需填充)
  • 色深對應:
    • 1-bit:單色,2 色調色板
    • 4-bit:16 色,16 項調色板
    • 8-bit:256 色,256 項調色板 (本文重點)
    • 24-bit:真彩色,無調色板,數據直接存 BGR
    • 32-bit:真彩色 + Alpha,無調色板
  • 調色板判斷:bfOffBits > 14 + 40 則存在調色板區塊。

現代視角的啟示

  1. 幀緩衝區直接寫入:vram[y*width+x] = color 即現代 GPU 繪圖管線的片段著色器邏輯雛形。
  2. 調色板 = 查找表 (LUT):索引色模式本質是「壓縮 + LUT 解壓」,現代纹理壓縮 (BC1-7) 同理。
  3. 文件格式解析器:嚴格按規範讀取、驗證、處理填充/端序,是所有圖形/多媒體開發的基礎功夫。
  4. 硬體抽象層 (HAL):當年 int86/outp 直接操作硬體,今有 DirectX/Vulkan/Metal、framebuffer、DRM/KMS 統一抽象。

總結

這段代碼承載了 PC 圖形編程的啟蒙記憶:**模式設置 → 調色板編程 → 線性幀緩衝區寫入 → 文件格式解析**。雖然 DOS 早已退出歷史舞台,但「零拷貝視頻內存寫入、調色板 LUT、掃描行翻轉、二進制格式解析」等核心概念,在現代圖形引擎、視頻解碼器、嵌入式顯示驅動中依然熠熠生輝。

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