读取并在屏幕上显示24位bmp图像
PS:本文非原创,code来自互联网。
代码如下:
/*2
这里仅仅是一个简单的显示24位BMP图象的程序,如果读者对BMP文件格式比较熟悉,3
也可以写出适用于各种BMP图象的显示程序,在像素处理时,它们所使用的方法是类似的。4
OpenGL在绘制像素之前,可以对像素进行若干处理。最常用的可能就是对整个像素图象进行放大/缩小。5
使用glPixelZoom来设置放大/缩小的系数,该函数有两个参数,分别是水平方向系数和垂直方向系数。6
例如设置glPixelZoom(0.5f, 0.8f);则表示水平方向变为原来的50%大小,而垂直方向变为原来的80%大小。7
我们甚至可以使用负的系数,使得整个图象进行水平方向或垂直方向的翻转8
(默认像素从左绘制到右,但翻转后将从右绘制到左。默认像素从下绘制到上,但翻转后将从上绘制到下。9
因此,glRasterPos*函数设置的“开始位置”不一定就是矩形的左下角)。10
*/11
#include <gl/glut.h>12

13
#define FileName "night.bmp"14
static const char *str = FileName;15

16
static GLint ImageWidth;17
static GLint ImageHeight;18
static GLint PixelLength;19
static GLubyte* PixelData;20

21
#include <stdio.h>22
#include <stdlib.h>23

24
void display(void)25
{26
// 清除屏幕并不必要27
// 每次绘制时,画面都覆盖整个屏幕28
// 因此无论是否清除屏幕,结果都一样29
// glClear(GL_COLOR_BUFFER_BIT);30

31
// 绘制像素32
glDrawPixels(ImageWidth, ImageHeight,33
GL_BGR_EXT, GL_UNSIGNED_BYTE, PixelData);34

35
// 完成绘制36
glutSwapBuffers();37
}38

39
int main(int argc, char* argv[])40
{41
// 打开文件42
FILE* pFile = fopen(str, "rb");43
if( pFile == 0 )44
exit(0);45

46
// 读取图象的大小信息47
fseek(pFile, 0x0012, SEEK_SET);48
fread(&ImageWidth, sizeof(ImageWidth), 1, pFile);49
fread(&ImageHeight, sizeof(ImageHeight), 1, pFile);50

51
// 计算像素数据长度52
PixelLength = ImageWidth * 3;53
while( PixelLength % 4 != 0 )54
++PixelLength;55
PixelLength *= ImageHeight;56

57
// 读取像素数据58
PixelData = (GLubyte*)malloc(PixelLength);59
if( PixelData == 0 )60
exit(0);61

62
fseek(pFile, 54, SEEK_SET);63
fread(PixelData, PixelLength, 1, pFile);64

65
// 关闭文件66
fclose(pFile);67

68
glutInit(&argc, argv);69
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);70
glutInitWindowPosition(100, 100);71
glutInitWindowSize(ImageWidth, ImageHeight);72
glutCreateWindow(FileName);73
glutDisplayFunc(&display);74
glutMainLoop();75
return 0;76
}77

显示结果截图如下:
为方便理解程序,下面把网上找的关于部分bmp图像格式的说明也写在下面了,如果还想了解的更清楚,就自己再google下,很多相关资料的。
BMP文件是一种像素文件,它保存了一幅图象中所有的像素。这种文件格式可以保存单色位图、16色或256色索引模式像素图、24位真彩色图象,每种模式种单一像素的大小分别为1/8字节,1/2字节,1字节和3字节。目前最常见的是256色BMP和24位色BMP。这种文件格式还定义了像素保存的几种方法,包括不压缩、RLE压缩等。常见的BMP文件大多是不压缩的。
这里为了简单起见,我们仅讨论24位色、不使用压缩的BMP。(如果你使用Windows自带的画图程序,很容易绘制出一个符合以上要求的BMP)
Windows所使用的BMP文件,在开始处有一个文件头,大小为54字节。保存了包括文件格式标识、颜色数、图象大小、压缩方式等信息,因为我们仅讨论24位色不压缩的BMP,所以文件头中的信息基本不需要注意,只有“大小”这一项对我们比较有用。图象的宽度和高度都是一个32位整数,在文件中的地址分别为0x0012和0x0016,于是我们可以使用以下代码来读取图象的大小信息:
GLint width, height; // 使用OpenGL的GLint类型,它是32位的。
// 而C语言本身的int则不一定是32位的。
FILE* pFile;
// 在这里进行“打开文件”的操作
fseek(pFile, 0x0012, SEEK_SET); // 移动到0x0012位置
fread(&width, sizeof(width), 1, pFile); // 读取宽度
fseek(pFile, 0x0016, SEEK_SET); // 移动到0x0016位置
// 由于上一句执行后本就应该在0x0016位置
// 所以这一句可省略
fread(&height, sizeof(height), 1, pFile); // 读取高度
54个字节以后,如果是16色或256色BMP,则还有一个颜色表,但24位色BMP没有这个,我们这里不考虑。接下来就是实际的像素数据了。24位色的BMP文件中,每三个字节表示一个像素的颜色。
注意,OpenGL通常使用RGB来表示颜色,但BMP文件则采用BGR,就是说,顺序被反过来了。
另外需要注意的地方是:像素的数据量并不一定完全等于图象的高度乘以宽度乘以每一像素的字节数,而是可能略大于这个值。原因是BMP文件采用了一种“对齐”的机制,每一行像素数据的长度若不是4的倍数,则填充一些数据使它是4的倍数。这样一来,一个17*15的24位BMP大小就应该是834字节(每行17个像素,有51字节,补充为52字节,乘以15得到像素数据总长度780,再加上文件开始的54字节,得到834字节)。分配内存时,一定要小心,不能直接使用“图象的高度乘以宽度乘以每一像素的字节数”来计算分配空间的长度,否则有可能导致分配的内存空间长度不足,造成越界访问,带来各种严重后果。
一个很简单的计算数据长度的方法如下:
int LineLength, TotalLength;
LineLength = ImageWidth * BytesPerPixel; // 每行数据长度大致为图象宽度乘以
// 每像素的字节数
while( LineLength % 4 != 0 ) // 修正LineLength使其为4的倍数
++LineLenth;
TotalLength = LineLength * ImageHeight; // 数据总长 = 每行长度 * 图象高度
这并不是效率最高的方法,但由于这个修正本身运算量并不大,使用频率也不高,我们就不需要再考虑更快的方法了。


浙公网安备 33010602011771号