关键字: 位图结构 位图加载
注意:8位位图设置调色板项的时候不起作用。需要在每帧调用的时候,都调用设置调色板项才起作用!!1
2
3
4
5
6
7
8
9// 每帧调用
int Game_Main()
{
...
...
lpddpal->SetEntries(0, 0, MAX_COLORS_PALETTE, bitmap.palette);
...
...
}
1.位图文件结构
磁盘中的.BMP文件结构:
位图文件 .bmp 文件 | |||
---|---|---|---|
位图文件头 | –> | 位图文件信息 | BITMAPFILEHEADER |
位图信息段 | –> | 位图信息以及像素信息。关于数据和BMP尺寸的详细信息 | BITMAPINFO |
位图信息头 | 位图信息 | BITMAPINFOHEADER | |
调色板信息(如果有) | RGB quad[] 结构 | RGBQUAD | |
位图数据RGB像素或索引数据 | –> | 位图像素数据 | Stream Data |
位图文件头 BITMAPFILEHEADER: 存放图像信息的总体信息
1
2
3
4
5
6
7typedef struct tagBITMAPFILEHEADER {
WORD bfType; // 文件type .BMP文件是0x4D42
DWORD bfSize; // bitmap file的size
WORD bfReserved1; // 保留位,一定是0
WORD bfReserved2; // 保留位,一定是0
DWORD bfOffBits; // 从BITMAPFILEHEADER结构到bitmap bits的偏移量
} BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;位图信息段:BITMAPINFO,包括两部分数据结构,BITMAPINFOHEADER部分和调色板信息部分(如果有的话)
BITMAPINFO:1
2
3
4typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader; // 头部
RGBQUAD bmiColors[1]; // 调色板信息
} BITMAPINFO, FAR *LPBITMAPINFO, *PBITMAPINFO;
BITMAPINFOHEADER:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15typedef struct tagBITMAPINFOHEADER{
DWORD biSize; // 此结构体的size
LONG biWidth; // bitmap的宽度,单位是pixels
LONG biHeight; // bitmap的高度,单位是pixels
// 如果是正数,bitmap是bottom-up DIB,初始位置是左下角
// 如果是负数,bitmap是top-down DIB,初始位置是左上角
WORD biPlanes; // 指定色彩平面,一定为1
WORD biBitCount; // 指定单个像素的大小(bits)
DWORD biCompression; // 指定压缩格式。 .BMP的未压缩格式为BI_RGB
DWORD biSizeImage; // size of image in bytes
LONG biXPelsPerMeter; // x轴方向每米的像素数量
LONG biYPelsPerMeter; // y轴方向每米的像素数量
DWORD biClrUsed; // 指定bitmap使用的颜色数量。8位图像为256,其他位图像为0.
DWORD biClrImportant; // 指定导入颜色的数量。8位图像为256,其他位图像为0.
} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;
RGBQUAD: 位图文件中的调色板项。正常PALETTEENTRY的倒序1
2
3
4
5
6
7
8
9
10typedef struct tagRGBQUAD {
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved;
} RGBQUAD;
+ 图像数据区:是一个字节流,描述了1/4/8/16/24位图像像素。
+ 数据是逐行排列的,有时候会颠倒过来(上下颠倒)。通过biHeight的正负号来表示。正值表示颠倒,负值表示正常。
2.加载位图文件
Win32 API中提供加载图像的函数LoadImage(), 但是这个函数太通用了,以至于要真正得到像素数据是很繁琐的.
因此,定义一个新的位图文件类型和加载位图的函数。
2.1 新位图文件结构
1 | // 自定义的位图文件类型。 |
将所有需要的信息都组合在一起。
bitmapfileHeader 为位图文件信息
bitmapInfoHeader 为位图信息
pallette 为调色板项。只有1 4 8位位图有
buffer 为像素数据
2.2 加载位图文件
上下翻转bitmap内存:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// 上下翻转bitmap内存
int Flip_Bitmap(UCHAR* image, int bytes_per_line, int height)
{
UCHAR *buffer;
if (!(buffer = (UCHAR*)malloc(bytes_per_line*height)))
{
return 0;
}
memcpy(buffer, image, bytes_per_line*height);
for (int i = 0; i < height; ++i)
{
memcpy(&image[(height - 1 - i)*bytes_per_line], &buffer[i*bytes_per_line], bytes_per_line);
}
free(buffer);
return 1;
}
加载bitmap文件:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83// 加载bitmap文件
int Load_Bitmap_File(BITMAP_FILE_PTR bitmap, const char* filename)
{
int file_handle, index;
UCHAR *temp_buffer = NULL;
OFSTRUCT file_data;
if ((file_handle = OpenFile(filename, &file_data, OF_READ)) == -1)
{
printf("load_bitmap_file error! openFile failed!!");
return 0;
}
// 读取bitmap文件头部分的信息
_lread(file_handle, &bitmap->bitmapfileHeader, sizeof(BITMAPFILEHEADER));
// 如果读取之后,不是bitmap类型
if (bitmap->bitmapfileHeader.bfType != BITMAP_ID)
{
_lclose(file_handle);
printf("load_bitmap_file error! read headinfo failed!!");
return 0;
}
// 读取bitmap头部分的信息
_lread(file_handle, &bitmap->bitmapInfoHeader, sizeof(BITMAPINFOHEADER));
// 如果是8位模式,读取调色板信息
// 并且调整rgb顺序。读取信息是BGR顺序,Paletteentry是RGB顺序. G的顺序不用改变.
if (bitmap->bitmapInfoHeader.biBitCount == 8)
{
_lread(file_handle, &bitmap->palette, MAX_COLORS_PALETTE * sizeof(PALETTEENTRY));
for (index = 0; index < MAX_COLORS_PALETTE; ++index)
{
int tmp_color = bitmap->palette[index].peRed;
bitmap->palette[index].peRed = bitmap->palette[index].peBlue;
bitmap->palette[index].peBlue = tmp_color;
bitmap->palette[index].peFlags = PC_NOCOLLAPSE;
}
}
// 最后,读取像素信息
// 定位开始位置
_llseek(file_handle, -(int)(bitmap->bitmapInfoHeader.biSizeImage), SEEK_END);
if (bitmap->bitmapInfoHeader.biBitCount == 8 ||
bitmap->bitmapInfoHeader.biBitCount == 16 ||
bitmap->bitmapInfoHeader.biBitCount == 24
)
{
if (bitmap->buffer)
{
free(bitmap->buffer);
}
if (!(bitmap->buffer = (UCHAR*)malloc(bitmap->bitmapInfoHeader.biSizeImage)))
{
_lclose(file_handle);
printf("load_bitmap_file error! malloc failed!!");
return 0;
}
// 读取到buffer中
_lread(file_handle, bitmap->buffer, bitmap->bitmapInfoHeader.biSizeImage);
}
else // 其他位的像素(serious problem)
{
printf("load_bitmap_file error! not 8pixel !!");
return 0;
}
_lclose(file_handle);
Flip_Bitmap(bitmap->buffer,
bitmap->bitmapInfoHeader.biWidth*(bitmap->bitmapInfoHeader.biBitCount / 8),
bitmap->bitmapInfoHeader.biHeight);
return 1;
}
释放bitmap内存:1
2
3
4
5
6
7
8
9
10
11// 释放内存
int Unload_Bitmap_Flie(BITMAP_FILE_PTR bitmap)
{
if (bitmap->buffer)
{
free(bitmap->buffer);
bitmap->buffer = NULL;
}
return 1;
}
2.3其他总结
- 文件操作中SEEK_END表示定位文件位置到最后。文件相关的知识都是互通的,从python的文件操作中获取的知识~
- 读取位图文件的像素信息时,按字节(UCHAR)存储的。在之后的使用过程中,8位位图每个像素一个字节,所以一个UCHAR就代表了;16为位图每个像素2个字节,因此需要使用USHORT获取2个字节;24位位图每个像素3个字节,因此是3个UCHAR表示了一个像素的BGR。
- 关于24位像素深度,PC是不支持的,转换成32位位深加载24位位图,补一个字节的Alpha,转成32位的像素。
- 书中示例代码,设置调色板项都是在初始化的时候设置一次,但是最终显示效果上,没有起作用的。需要在每帧渲染之前都设置一次调色板。
- 使用IrfanView图片查看软件,有调色板的位图,在Image->Palette->Editor Palette中可以查看和修改调色板。(自己用pc自带的图片编辑器做的8位位图和示例中给的8位位图调色板相差非常大)