BMP(Bitmap)文件是一种常见的 无损位图图像格式,主要用于存储 像素数据,支持单色、16色、256色和真彩色(24/32位)等模式。以下是其详细结构解析:
一、BMP文件整体结构
BMP文件由 4个主要部分 组成,按顺序排列如下:
- 文件头(Bitmap File Header)
- 文件类型、大小等信息。
- 信息头(Bitmap Information Header/DIB Header)
- 图像的尺寸、颜色格式等元数据。
- 调色板(Color Palette,可选)
- 仅存在于 颜色索引模式(如8位/256色)。
- 像素数据(Pixel Data)
- 实际的图像像素阵列,存储顺序为 从下到上(左下角为起点)。
二、详细结构(以24位真彩色BMP为例)
1. 文件头(14字节)
#pragma pack(push, 1) // 禁用内存对齐
typedef struct {
uint16_t bfType; // 文件标识,必须为 "BM"(0x4D42)
uint32_t bfSize; // 文件总大小(字节)
uint16_t bfReserved1; // 保留字段(必须为0)
uint16_t bfReserved2; // 保留字段(必须为0)
uint32_t bfOffBits; // 像素数据偏移量(从文件头到像素数据的字节数)
} BITMAPFILEHEADER;
#pragma pack(pop)
2. 信息头(40字节,BITMAPINFOHEADER版本)
typedef struct {
uint32_t biSize; // 本结构体大小(通常为40)
int32_t biWidth; // 图像宽度(像素)
int32_t biHeight; // 图像高度(像素),正数表示从下到上存储
uint16_t biPlanes; // 颜色平面数(必须为1)
uint16_t biBitCount; // 每像素位数(1/4/8/16/24/32)
uint32_t biCompression; // 压缩方式(0表示不压缩)
uint32_t biSizeImage; // 像素数据大小(字节),压缩时需填写
int32_t biXPelsPerMeter; // 水平分辨率(像素/米)
int32_t biYPelsPerMeter; // 垂直分辨率(像素/米)
uint32_t biClrUsed; // 实际使用的调色板颜色数(0表示全部)
uint32_t biClrImportant; // 重要颜色数(0表示所有颜色均重要)
} BITMAPINFOHEADER;
3. 调色板(仅索引颜色模式需要)
- 颜色表(Color Table):每个条目为4字节(BGR+保留字节),例如8位BMP有256个条目:
typedef struct { uint8_t rgbBlue; // 蓝色分量 uint8_t rgbGreen; // 绿色分量 uint8_t rgbRed; // 红色分量 uint8_t rgbReserved; // 保留(通常为0) } RGBQUAD;
4. 像素数据
- 排列方式:
- 每行像素按 从左到右 存储。
- 行数据需 4字节对齐(每行末尾可能填充0以满足对齐)。
- 图像数据从 左下角 开始(高度为正值时)。
- 24位BMP示例:
每个像素占3字节(按 BGR 顺序存储,无调色板)。
三、关键特性
- 无压缩(通常):
- 多数BMP文件使用
BI_RGB
(压缩方式=0),像素数据未压缩。
- 多数BMP文件使用
- 颜色深度:
biBitCount=24
:真彩色(BGR各8位)。biBitCount=8
:256色(需调色板)。
- 行对齐:
- 每行字节数计算公式:
int rowSize = ((biWidth * biBitCount + 31) / 32) * 4; // 对齐到4字节
- 每行字节数计算公式:
四、代码示例:读取24位BMP文件
#include <stdio.h>
#include <stdint.h>
// 定义结构体(确保内存布局紧凑)
#pragma pack(push, 1)
typedef struct { /* 文件头和信息头定义同上 */ } BITMAPFILEHEADER, BITMAPINFOHEADER;
#pragma pack(pop)
int main() {
FILE* file = fopen("image.bmp", "rb");
if (!file) return 1;
// 读取文件头和信息头
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER infoHeader;
fread(&fileHeader, sizeof(fileHeader), 1, file);
fread(&infoHeader, sizeof(infoHeader), 1, file);
// 检查是否为24位无压缩BMP
if (fileHeader.bfType != 0x4D42 || infoHeader.biBitCount != 24 || infoHeader.biCompression != 0) {
fclose(file);
return 1;
}
// 分配像素内存(注意行对齐)
uint8_t* pixels = malloc(infoHeader.biSizeImage);
fseek(file, fileHeader.bfOffBits, SEEK_SET);
fread(pixels, 1, infoHeader.biSizeImage, file);
// 处理像素数据(BGR顺序)
// ...
free(pixels);
fclose(file);
return 0;
}
五、常见问题
- 为什么BMP文件较大?
- 未压缩的像素数据导致体积远大于JPEG/PNG。
- 如何支持透明通道?
- 使用32位BMP(Alpha通道存储在调色板或像素的第四个字节)。
- 高度为负值?
- 表示像素数据 从上到下 存储(罕见,多数BMP从下到上)。
如果需要解析其他格式(如16位或压缩BMP),可以进一步探讨!