BITMAP文件大体上分成四个部分,如下表所示。

文件部分

长度(字节)

位图文件头 Bitmap File Header

14

位图信息数据头 Bitmap Info Header

40

调色板 Palette

4*n (n≥0)

位图数据 Image Data

不定长

BITMAP文件格式

1、位图文件头 Bitmap File Header

typedef struct tagBITMAPFILEHEADER {
        WORD    bfType;
        DWORD   bfSize;
        WORD    bfReserved1;
        WORD    bfReserved2;
        DWORD   bfOffBits;
} BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;

字段

说明

bfType

文件类型,必须是0x4D42,即字符串“BM”

bfSize

指定文件大小,以字节为单位,包括本结构体长度

bfReserved1

保留字,必须设置为0

bfReserved2

保留字,必须设置为0

bfOffBits

从文件头开始到实际的图象数据的偏移字节数。

2、位图信息数据头 Bitmap Info Header 

typedef struct tagBITMAPINFOHEADER{
        DWORD      biSize;
        LONG       biWidth;
        LONG       biHeight;
        WORD       biPlanes;
        WORD       biBitCount;
        DWORD      biCompression;
        DWORD      biSizeImage;
        LONG       biXPelsPerMeter;
        LONG       biYPelsPerMeter;
        DWORD      biClrUsed;
        DWORD      biClrImportant;
} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;

字段

说明

biSize

该结构体长度。

biWidth

说明图象的宽度,单位为象素。

biHeight

说明图象的高度,单位为象素。

biPlanes

位平面数,必须为1。

biBitCount

指定颜色位数,1为二值,4为16色,8为256色,16、24、32为真彩色。

biCompression

指定是否压缩,有效的值为BI_RGB,BI_RLE8,BI_RLE4、BI_BITFIELDS。

biSizeImage

实际的位图数据占用的字节数。

biXPelsPerMeter

目标设备水平分辨率,单位是每米的像素数。

biYPelsPerMeter

目标设备垂直分辨率,单位是每米的像素数。

biClrUsed

实际使用的颜色数。

biClrImportant

图像中重要的颜色数,若该值为0,则所有的颜色都是重要的。

3、调色板 Palette

第三部分为调色板(Palette),当然,这里是对那些需要调色板的位图文件而言的。有些位图,如真彩色图,前面已经讲过,是不需要调色板的,BITMAPINFOHEADER后直接是位图数据。 
调色板实际上是一个数组,共有biClrUsed个元素(如果该值为零,则有2的biBitCount次方个元素)。数组中每个元素的类型是一个RGBQUAD结构,占4个字节,其定义如下:

typedef struct tagRGBQUAD {
        BYTE    rgbBlue;             //该颜色蓝色分量
        BYTE    rgbGreen;            //该绿色蓝色分量
        BYTE    rgbRed;              //该红色蓝色分量    
        BYTE    rgbReserved;         //保留
} RGBQUAD;

4、位图数据 ImageData

第四部分就是实际的图象数据了。对于用到调色板的位图,图象数据就是该像素颜在调色板中的索引值,对于真彩色图,图象数据就是实际的R,G,B值。下面就2色,16色,256色位图和真彩色位图分别介绍。

  • 2色位图:用1位就可以表示该像素的颜色(一般0表示黑,1表示白),所以一个字节可以表示8个像素。
  • 16色位图:用4位可以表示一个像素的颜色,所以一个字节可以表示2个像素。
  • 256色位图:一个字节刚好可以表示1个像素。 
  • 真彩色图:三个字节才能表示1个像素。

例子

以一幅24位真彩色图像为例,读取并顺时针旋转并保存。该BITMAP图片尺寸为450 * 227。

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

#pragma pack(push)
#pragma pack(1)

//24位真彩色
struct RGB
{
    BYTE			b;
    BYTE			g;
    BYTE			r;
};

#pragma pack(pop)

void imgOpr(RGB &a, RGB &b)
{
    b.b = a.b;
    b.g = a.g;
    b.r = a.r;
}

//二维数组申请img[m][n]
RGB ** RGB_new(int m, int n)
{
	int					i;
	RGB					**img;

	img = new RGB * [m];

	for (i = 0; i < m; i ++)
	{
		img[i] = new RGB [n];
	}

	return img;
}

//释放img[m][n]
void RGB_delete(RGB **img, int m, int n)
{
	int					i;

	for (i = 0; i < m; i ++)
	{
		delete [] img[i];
	}
 
	delete [] img;
}

int main()
{
    BITMAPFILEHEADER	bfh;
    BITMAPINFOHEADER	bih;

	RGB					**img1;
    RGB					**img2;
	BYTE				*tmp;

	int					i, j;
    int					height, width;
	int					m, n;

    FILE				*fp, *fq; 
	errno_t				err;
	
	err = fopen_s(&fp, "D:\\Work\\Debug\\7.bmp", "rb");

	if (err == 0)
	{
		printf("The input file was opened!\n");
	}
	else
	{
		printf("The input file was not opened!\n");
		return 1;
	}

	err = fopen_s(&fq, "D:\\Work\\Debug\\2.bmp", "wb");

	if (err == 0)
	{
		printf("The output file was opened!\n");
	}
	else
	{
		printf("The output file was not opened!\n");
		return 1;
	}

    fread(&bfh, sizeof(BITMAPFILEHEADER), 1, fp);
    fread(&bih, sizeof(BITMAPINFOHEADER), 1, fp);
    
	height = bih.biHeight;
    width = bih.biWidth;
	
	img1 = RGB_new(height, width);
	img2 = RGB_new(width, height);
	
	//计算每行图像字节数,必须是4的整倍数,不够需要填充
	m = (bih.biWidth * bih.biBitCount + 31) / 32 * 4; 
	n = (bih.biHeight * bih.biBitCount + 31) / 32 * 4;
	
	tmp = new BYTE [n];
	memset(tmp, 0x0, n);

    //对图片进行操作
	for (i = 0; i < height; i ++)
	{
		//跳过调色板数据段,并寻址
		fseek(fp, bfh.bfOffBits + i * m, SEEK_SET);
		fread(img1[i], sizeof(RGB), width, fp);

		for (j = 0; j < width; j ++)
		{
			//将数组 img 赋值给 img2
			imgOpr(img1[i][j], img2[j][i]);
		}
	}

	//更新BITMAPINFOHEADER结构体
	bih.biHeight = width;
	bih.biWidth = height;
	bih.biSizeImage = n * width;

	//将修改后的图片保存到文件
	fwrite(&bfh, sizeof(BITMAPFILEHEADER), 1, fq);
	fwrite(&bih, sizeof(BITMAPINFOHEADER), 1, fq);

	for (i = 0; i < width; i ++)
	{
		memcpy(tmp, img2[i], sizeof(RGB) * height);
		fwrite(tmp, 1, n, fq);
	}

    fclose(fp);
    fclose(fq);

	//释放
	RGB_delete(img1, height, width);
	RGB_delete(img2, width, height);
	
	delete [] tmp;
 
	system("pause");

    return 0;
}

原图

BitmapRedisTemplate 位图_格式

顺时针旋转90度