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 <stdio.h>
#include <stdlib.h>

#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;  
}  

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

         


评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值