BMP文件格式详解
-------------------------------------------------------------------------------------------------------
Lena
摘录百科:BMP是英文Bitmap(位图)的简写,它是Windows操作系统中的标准图像文件格式,能够被多种Windows应用程序所支持。
BMP文件存储的是原始的BGR数据,格式非常简单,研究数字图入门必备。因为数据没有经过任何压缩,所以BMP文件都比较大。
BMP文件格式
- 文件信息 -->BITMAPFILEHEADER
- 图像信息 -->BITMAPINFOHEADER
- 调色板 -->由颜色索引数决定
- 图像数据 -->由图像尺寸决定
1. BITMAPFILEHEADER
typedef struct {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAP_FILE_HEADER;
WORD --> unsigned short
DWORD --> unsigned long (32位占4位,64位占8位)
变量名 |
大小(字节) |
作用 |
bfType |
2 |
文件标头BM |
bfSize |
4 |
整个文件大小 |
bfReserved1 |
2 |
保留 |
bfReserved2 |
2 |
保留 |
bfOffBits |
4 |
偏移,一般偏移长度是BITMAPFILEHEADER+BITMAPINFOHEADER,如果存在调色板则随着调色板长度变化 |
2.BITMAPINFOHEADER
typedef struct BITMAP_INFO_HEADER {
DWORD biSize;
int biWidth;
int biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
int biXPelsPerMeter;
int biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAP_INFO_HEADER;
这里的int原来是long,64位下会出问题改了下。
变量名 |
大小(字节) |
作用 |
biSize |
4 |
sizeof(BITMAP_INFO_HEADER) |
biWidth |
4 |
宽度 |
biHeight |
4 |
高度 BMP文件图像是倒着存储的,所以如果biHeight>0表示图像是倒立的,如果biHeight<0表示图像是正的。 |
biPlanes |
2 |
颜色平面数 |
biBitCount |
2 |
位数(1,4,8,16,24,32) |
biCompression |
4 |
压缩 0 BI_RGB(BMP文件不压缩) 1 BI_RLE8 2 BI_RLE4 3 BI_BITFILELDS 4 BI_JPEG 5 BI_PNG |
biSizeImage |
4 |
图像数据大小 |
biXPelsPerMeter |
4 |
水平分辨率 |
biYPelsPerMeter |
4 |
垂直分辨率 |
biClrUsed |
4 |
实际使用的颜色索引数 |
biClrImportant |
4 |
对图像显示有重要影响的颜色索引的数目,如果是0表示都重要 |
3.调色板
调色板实际上是一种颜色索引表,因为常见的BMP文件都是24和32位的,而24和32位不需要调色板,所以这里不做过多研究。
4.图像数据
24位位图 --> bgr,bgr,bgr
32位位图 --> bgra,bgra,bgra
5.内存对齐
Windows默认是4字节对齐,如果不了解内存对齐可以研究一下,c语言sizeof(结构体)的大小。内存对齐的主要目的是为了加速,BMP文件也需要进行内存对齐处理,简单来说就是每一行的字节数%4==0,如果不足则由0补足。比如:一个24位BMP,宽度为99,那么他的图像部分字节数位 99 * 3 ==> 297 % 4 = 1,需要补3个字节0。
参考文章:
https://www.cnblogs.com/Matrix_Yao/archive/2009/12/02/1615295.html
/***************************************************** BMP文件读写 [email protected] *****************************************************/ #include <stdio.h> #include <stdlib.h> #include <string.h> typedef unsigned short WORD; typedef unsigned int DWORD; #pragma pack(1) typedef struct { WORD bfType; DWORD bfSize; WORD bfReserved1; WORD bfReserved2; DWORD bfOffBits; } BITMAP_FILE_HEADER; typedef struct BITMAP_INFO_HEADER { DWORD biSize; int biWidth; int biHeight; WORD biPlanes; WORD biBitCount; DWORD biCompression; DWORD biSizeImage; int biXPelsPerMeter; int biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant; } BITMAP_INFO_HEADER; #pragma pack() /** * 32->24 */ static void bmp_remove_aplpha (unsigned char *data, unsigned char *buf, size_t *size, size_t w, size_t h) { size_t i = 0, j = 0, dst_fix = 0; dst_fix = 4 - ((w * 24)>>3) & 3; *size = ((((w * 24) + 31) >> 5) << 2) * h; for (j = 0; j < h; j++) { for (i = 0; i < w; i++) { memcpy(buf, data, 3); data += 4; buf += 3; } buf += dst_fix; } } /** * 倒转图像 */ static void bmp_reverse (unsigned char *data, unsigned char *buf, size_t size, size_t w, size_t h) { unsigned char *src = NULL; int i = 0, j = 0, preline = 0; preline = size / h; for (i = 0, j = h - 1; j >= 0; i++, j--) { src = data + j * preline; memcpy(buf + i * preline, src, preline); } } /** * bmp_read * * 读取一个BMP文件 * 只支持24/32位,32位会转为24位,倒转的图像会摆正 */ unsigned char *bmp_read (const char *path, size_t *size, size_t *w, size_t *h) { FILE *fp = NULL; BITMAP_FILE_HEADER file = {0}; BITMAP_INFO_HEADER info = {0}; unsigned char *data = NULL, *buf = NULL; if (!path || *path == 0 || !size || !w || !h) return NULL; if (!(fp = fopen(path, "rb"))) return NULL; if (fread(&file, 1, sizeof(BITMAP_FILE_HEADER), fp) != sizeof(BITMAP_FILE_HEADER)) { fclose(fp); return NULL; } if (fread(&info, 1, sizeof(BITMAP_INFO_HEADER), fp) != sizeof(BITMAP_INFO_HEADER)) { fclose(fp); return NULL; } //非BM开头 if (file.bfType != 0x4d42) { fclose(fp); return NULL; } //暂时只能支持24与32位 if (info.biBitCount != 24 && info.biBitCount != 32) { fclose(fp); return NULL; } if (info.biSizeImage == 0) { info.biSizeImage = info.biWidth * abs(info.biHeight) * (info.biBitCount == 24 ? 3 : 4); } data = (unsigned char *)malloc(sizeof(unsigned char) * info.biSizeImage); buf = (unsigned char *)malloc(sizeof(unsigned char) * info.biSizeImage); if (!data || !buf) { if (data) free(data); if (buf) free(buf); fclose(fp); return NULL; } if (fread(data, 1, info.biSizeImage, fp) != info.biSizeImage) { free(data); free(buf); fclose(fp); return NULL; } fclose(fp); *size = info.biSizeImage; *w = info.biWidth; *h = abs(info.biHeight); //32转24 if (info.biBitCount == 32) { bmp_remove_aplpha(data, buf, size, *w, *h); memcpy(data, buf, *size); } //图像倒转 if (info.biHeight > 0) { bmp_reverse(data, buf, *size, *w, *h); memcpy(data, buf, *size); } free(buf); return data; } /** * bmp_write */ int bmp_write (unsigned char *data, size_t size, size_t w, size_t h, const char *path) { FILE *fp = NULL; BITMAP_FILE_HEADER file = {0}; BITMAP_INFO_HEADER info = {0}; unsigned char *src = NULL; int hsrc = 0, preline = 0; if (!data || size == 0 || w == 0 || h == 0 || !path || *path == 0) return 0; if (!(fp = fopen(path, "wb+"))) return 0; file.bfType = 0x4d42; file.bfSize = sizeof(BITMAP_FILE_HEADER) + sizeof(BITMAP_INFO_HEADER) + size; file.bfReserved1 = 0; file.bfReserved2 = 0; file.bfOffBits = sizeof(BITMAP_FILE_HEADER) + sizeof(BITMAP_INFO_HEADER); info.biSize = sizeof(BITMAP_INFO_HEADER); info.biWidth = w; info.biHeight = h; info.biPlanes = 1; info.biBitCount = 24; info.biCompression = 0L; info.biSizeImage = size; info.biXPelsPerMeter = 0; info.biYPelsPerMeter = 0; info.biClrUsed = 0; info.biClrImportant = 0; fwrite(&file, 1, sizeof(BITMAP_FILE_HEADER), fp); fwrite(&info, 1, sizeof(BITMAP_INFO_HEADER), fp); //倒转写入图像数据 preline = size / h; for (hsrc = h - 1; hsrc >= 0; hsrc--) { src = data + hsrc * preline; fwrite(src, 1, preline, fp); } fclose(fp); return 1; } int main (int argc, char *argv[]) { unsigned char *data = NULL; size_t size = 0, w = 0, h = 0; data = bmp_read("1.bmp", &size, &w, &h); bmp_write(data, size, w, h, "2.bmp"); return 1; }
原文地址:https://www.cnblogs.com/hatsusakana/p/12643850.html