python opengl 截图_OpenGL(十) 截屏并保存BMP文件

BMP文件格式

BMP图像又称为Bitmap(位图),是Windows系统中广泛采用的图像格式。BMP文件的数据按照从文件头开始的先后顺序分为四个部分:

我们一般见到的图像以24位图像为主,即R、G、B三种颜色各用8个bit来表示,这样的图像称为真彩色,这种格式不需要调色表,所以位图信息头后紧跟的、从文件头开始偏移54个字节就是位图数据了。

BMP图像文件四字节对齐规则

为了提高处理效率,BMP文件在每一行采用一种4对齐的机制,就是如果一行的数据长度(以位为单位)不是4的整数倍的话,则填充一些空白数据使它补齐为4的整数倍。例如对于一个23*24的24位BMP文件来说,其每一行的字节数=23*3=69,应该补齐为72,所以23*24跟24824的BMP所占用的空间是一样的,都是24*24*3位。BMP文件占用内存是大于等于图像宽*高*每一像素的字节数的,为BMP文件分配内存空间时一定要考虑到这一点。

读取图像数据

glReadPixels用于从帧缓存里读取一个像素块,该函数总共有7个参数,函数原型:

glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels);

第一到第四个参数组成一个矩形,该矩形所包括的像素都会被读取出来。第一、二个参数表示了矩形的左下角横、纵坐标,坐标以窗口最左下角为零,最右上角为最大值;第三、四个参数表示了矩形的宽度和高度。第五个参数表示读取的内容,例如:GL_RGB就会依次读取像素的红、绿、蓝三种数据,GL_RGBA则会依次读取像素的红、绿、蓝、alpha四种数据,GL_RED则只读取像素的红色数据(类似的还有GL_GREEN,GL_BLUE,以及GL_ALPHA)(除此之外,还可以读取其它内容,例如深度缓冲区的深度数据等)。第六个参数表示读取的内容保存到内存时所使用的格式,例如:GL_UNSIGNED_BYTE会把各种数据保存为GLubyte,GL_FLOAT会把各种数据保存为GLfloat等。第七个参数表示一个指针,像素数据被读取后,将被保存到这个指针所表示的地址。注意,需要保证该地址有足够的可以使用的空间,每一行都要补齐4Byte整数倍,以容纳读取的像素数据。

在使用glReadPixels把像素内容读入内存之前,需要设置一下内存中这些像素的保存方式,保证每一行数据的字节数是4的整数倍。通过调用glPixelStore(GL_UNPACK_ALIGNMENT,4),保证OpenGL会按照4字节对齐的方式保存BMP文件。

生成文件头和信息头

我们不必深入探究BMP文件头和信息头内部数据的组织形式,这部分内容可以通过读取并复制另一幅BMP文件的对应信息实现,只需要修改一下图像的宽高信息就可以了。

对于24位不压缩的BMP文件,图像的宽高数据都是一个4字节的32位整数,在BMP文件中的地址分别是)0X0012和0X0016,用Windows的画图板新建一个图像并保存为24位位图文件,通过fread读取该图像整个的文件头和信息头数据,通过fwrtie修改宽高数据。

以下是glReadPixels的应用实例,在窗口上显示画面之后调用glReadPixels读取当前窗口中的图像数据,并保存为本地BMP文件(主函数借用之前太阳地球模型)。

#include "glut.h"

#include

#include

#define WindowWidth 400

#define WindowHeight 400

#define BMP_Header_Length 54

void grab(void)

{

FILE* pDummyFile; //指向另一bmp文件,用于复制它的文件头和信息头数据

FILE* pWritingFile; //指向要保存截图的bmp文件

GLubyte* pPixelData; //指向新的空的内存,用于保存截图bmp文件数据

GLubyte BMP_Header[BMP_Header_Length];

GLint i, j;

GLint PixelDataLength; //BMP文件数据总长度

// 计算像素数据的实际长度

i = WindowWidth * 3; // 得到每一行的像素数据长度

while( i%4 != 0 ) // 补充数据,直到i是的倍数

++i;

PixelDataLength = i * WindowHeight; //补齐后的总位数

// 分配内存和打开文件

pPixelData = (GLubyte*)malloc(PixelDataLength);

if( pPixelData == 0 )

exit(0);

pDummyFile = fopen("bitmap1.bmp", "rb");//只读形式打开

if( pDummyFile == 0 )

exit(0);

pWritingFile = fopen("grab.bmp", "wb"); //只写形式打开

if( pWritingFile == 0 )

exit(0);

//把读入的bmp文件的文件头和信息头数据复制,并修改宽高数据

fread(BMP_Header, sizeof(BMP_Header), 1, pDummyFile); //读取文件头和信息头,占据54字节

fwrite(BMP_Header, sizeof(BMP_Header), 1, pWritingFile);

fseek(pWritingFile, 0x0012, SEEK_SET); //移动到0X0012处,指向图像宽度所在内存

i = WindowWidth;

j = WindowHeight;

fwrite(&i, sizeof(i), 1, pWritingFile);

fwrite(&j, sizeof(j), 1, pWritingFile);

// 读取当前画板上图像的像素数据

glPixelStorei(GL_UNPACK_ALIGNMENT, 4); //设置4位对齐方式

glReadPixels(0, 0, WindowWidth, WindowHeight,

GL_BGR_EXT, GL_UNSIGNED_BYTE, pPixelData);

// 写入像素数据

fseek(pWritingFile, 0, SEEK_END);

//把完整的BMP文件数据写入pWritingFile

fwrite(pPixelData, PixelDataLength, 1, pWritingFile);

// 释放内存和关闭文件

fclose(pDummyFile);

fclose(pWritingFile);

free(pPixelData);

}

static GLfloat angle = 0.0f;

void myDisplay(void)

{

glClearColor(0.3,0.7,0.5,0);

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清理颜色和深度缓存

// 创建透视效果视图

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

gluPerspective(80.0f, 1.0f, 1.0f, 20.0f);

glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

gluLookAt(0.0, 12.0, 10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

// 定义太阳光源,它是一种白色的光源

{

GLfloat sun_light_position[] = {0.0f, 0.0f, 0.0f, 1.0f}; //光源的位置在世界坐标系圆心,齐次坐标形式

GLfloat sun_light_ambient[] = {0.0f, 0.0f, 0.0f, 1.0f}; //RGBA模式的环境光,为0

GLfloat sun_light_diffuse[] = {1.0f, 1.0f, 1.0f, 1.0f}; //RGBA模式的漫反射光,全白光

GLfloat sun_light_specular[] = {1.0f, 1.0f, 1.0f, 1.0f}; //RGBA模式下的镜面光 ,全白光

glLightfv(GL_LIGHT0, GL_POSITION, sun_light_position);

glLightfv(GL_LIGHT0, GL_AMBIENT, sun_light_ambient);

glLightfv(GL_LIGHT0, GL_DIFFUSE, sun_light_diffuse);

glLightfv(GL_LIGHT0, GL_SPECULAR, sun_light_specular);

//开启灯光

glEnable(GL_LIGHT0);

glEnable(GL_LIGHTING);

glEnable(GL_DEPTH_TEST);

}

// 定义太阳的材质并绘制太阳

{

GLfloat sun_mat_ambient[] = {0.0f, 0.0f, 0.0f, 1.0f}; //定义材质的环境光颜色,为0

GLfloat sun_mat_diffuse[] = {0.0f, 0.0f, 0.0f, 1.0f}; //定义材质的漫反射光颜色,为0

GLfloat sun_mat_specular[] = {0.0f, 0.0f, 0.0f, 1.0f}; //定义材质的镜面反射光颜色,为0

GLfloat sun_mat_emission[] = {0.8f, 0.0f, 0.0f, 1.0f}; //定义材质的辐射广颜色,为偏红色

GLfloat sun_mat_shininess = 0.0f;

glMaterialfv(GL_FRONT, GL_AMBIENT, sun_mat_ambient);

glMaterialfv(GL_FRONT, GL_DIFFUSE, sun_mat_diffuse);

glMaterialfv(GL_FRONT, GL_SPECULAR, sun_mat_specular);

glMaterialfv(GL_FRONT, GL_EMISSION, sun_mat_emission);

glMaterialf (GL_FRONT, GL_SHININESS, sun_mat_shininess);

glutSolidSphere(3.0, 40, 32);

}

// 定义地球的材质并绘制地球

{

GLfloat earth_mat_ambient[] = {0.0f, 0.0f, 1.0f, 1.0f}; //定义材质的环境光颜色,骗蓝色

GLfloat earth_mat_diffuse[] = {0.0f, 0.0f, 0.5f, 1.0f}; //定义材质的漫反射光颜色,偏蓝色

GLfloat earth_mat_specular[] = {1.0f, 0.0f, 0.0f, 1.0f}; //定义材质的镜面反射光颜色,红色

GLfloat earth_mat_emission[] = {0.0f, 0.0f, 0.0f, 1.0f}; //定义材质的辐射光颜色,为0

GLfloat earth_mat_shininess = 30.0f;

glMaterialfv(GL_FRONT, GL_AMBIENT, earth_mat_ambient);

glMaterialfv(GL_FRONT, GL_DIFFUSE, earth_mat_diffuse);

glMaterialfv(GL_FRONT, GL_SPECULAR, earth_mat_specular);

glMaterialfv(GL_FRONT, GL_EMISSION, earth_mat_emission);

glMaterialf (GL_FRONT, GL_SHININESS, earth_mat_shininess);

glRotatef(angle, 0.0f, -1.0f, 0.0f);

glTranslatef(7.0f, 0.0f, 0.0f);

glutSolidSphere(3.0, 40, 32);

}

glutSwapBuffers();

grab();

}

void myIdle(void)

{

angle += 1.0f;

if( angle >= 360.0f )

angle = 0.0f;

myDisplay();

}

int main(int argc, char* argv[])

{

glutInit(&argc, argv);

glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);

glutInitWindowPosition(200, 200);

glutInitWindowSize(400, 400);

glutCreateWindow("OpenGL光照演示");

glutDisplayFunc(&myDisplay);

glutIdleFunc(&myIdle);

glutMainLoop();

return 0;

}

在本地程序目录下,保存的截图:

         

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值