OpenGL织梦之旅【第三章】第3节.实现截图功能
来源:互联网 发布:seo房地产关键字 编辑:程序博客网 时间:2024/06/09 14:30
对于上一章的纹理贴图,不少朋友给我说,有些图片可以成功读取,但是显示不正常。
例如这只猪:
解决方案是这样的。造成这个问题的原因是因为像素在bmp图片中保存格式为BGR,gluBuild2Dmipmaps函数会要求你提供颜色的组成类型(GL_RGB),而某些OpenGL版本的中并没有GL_BGR或GL_BGR_EXT,所以在LoadTexture函数中,我写了个交换器来使其变成RGB,最终使用GL_RGB生成纹理。那么,对于显示不正常的图片,将这个参数改成GL_BGR_EXT即可。
新的LoadTexture如下:
int LoadBitmap(const char *file){unsigned int ID; //纹理的idint width,height; byte *image; //接受图像数据FILE *fp; //文件指针BITMAPFILEHEADER FileHeader; //接受位图文件头BITMAPINFOHEADER InfoHeader; //接受位图信息头fp=fopen(file,"rb");if (fp == NULL){printf("Exception: Fail to open file!\n");return -1;}fread(&FileHeader, sizeof(BITMAPFILEHEADER), 1, fp);if(FileHeader.bfType != BITMAP_ID) //确保文件是一个位图文件,效验文件类型{ printf("Exception: This file is not a bmp file!\n");fclose(fp);return -1;}fread(&InfoHeader, sizeof(BITMAPINFOHEADER), 1, fp);width=InfoHeader.biWidth; height=InfoHeader.biHeight;InfoHeader.biSizeImage = width*height*3;fseek(fp, FileHeader.bfOffBits, SEEK_SET); //将文件指针移动到实际图像数据处image=(byte *)malloc(sizeof(byte)*InfoHeader.biSizeImage); //申请空间if (image == NULL){free(image);printf("Exception: No enough space!\n");return -1;}fread(image, 1, InfoHeader.biSizeImage, fp);fclose(fp); glGenTextures(1, &ID); glBindTexture(GL_TEXTURE_2D, ID);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP); gluBuild2DMipmaps(GL_TEXTURE_2D, 3, width, height, GL_BGR_EXT, GL_UNSIGNED_BYTE, image);return ID; }
2012/12/24
plusplus7
在上面的小节里,我们掌握了如何把位图文件读入到OpenGL中,那么这一节,我们就来实现把OpenGL显示在屏幕上的内容保存在bmp文件里。
类似于QQ截图一样。
听起来好像挺复杂的,实际上很简单。定义一个函数bool SnapScreen(int width,int height,const char *file)。
我们已经了解了24位位图的基本组成,所以先定义文件头和信息头以及像素数据数组image。
byte *image; //接受图像数据FILE *fp; //文件指针BITMAPFILEHEADER FileHeader; //接受位图文件头BITMAPINFOHEADER InfoHeader; //接受位图信息头
接下来就是对这两个结构体的设置,注释很详细,更多信息还是参见MSDN。
FileHeader.bfType=BITMAP_ID; //ID设置为位图的id号FileHeader.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER); //实际图像数据的位置在文件头和信息头之后FileHeader.bfReserved1=0; //必须设置为0FileHeader.bfReserved2=0; //必须设置为0FileHeader.bfSize=height*width*24+FileHeader.bfOffBits; //BMP图像文件大小InfoHeader.biXPelsPerMeter = 0; //水平分辨率,这里暂时设为0就是InfoHeader.biYPelsPerMeter = 0; //垂直分辨率,这里暂时设为0就是InfoHeader.biClrUsed = 0; //图像使用的颜色,这里暂时设为0就是InfoHeader.biClrImportant = 0; //重要的颜色数,这里暂时设为0就是 //垂直分辨率,这里暂时设为0就是InfoHeader.biPlanes=1; //必须设置为1InfoHeader.biCompression=BI_RGB; //设置为BI_RGB时,表示图像并没有彩色表InfoHeader.biBitCount=24; //图像的位数InfoHeader.biSize=sizeof(BITMAPINFOHEADER); //结构体的大小InfoHeader.biHeight=height; InfoHeader.biWidth=width; InfoHeader.biSizeImage=height*width*3;
接下来就要把显示的内容读取到image数组里了。
OpenGL提供了这样一个函数,glReadPixels()简单地说,它就是已经绘制好的像素读取到内存中。前四个参数是指定要读取像素的位置,后三个就如同gluBuild2Dmipmaps的后三个一样。但是在那之前需要先调用glPixelStorei把像素格式对齐。
//像素格式设置4字节对齐glPixelStorei(GL_UNPACK_ALIGNMENT,4);//接收出像素的数据glReadPixels(0,0,width,height,GL_BGR_EXT,GL_UNSIGNED_BYTE,image);
再把数据写入进文件就完成了。
fwrite(&FileHeader, sizeof(BITMAPFILEHEADER), 1, fp);fwrite(&InfoHeader, sizeof(BITMAPINFOHEADER), 1, fp);fwrite(image, InfoHeader.biSizeImage, 1, fp);
如果我们在Update函数中添加一句
if (cnt == 80){SnapScreen(400,400,"data/screen.bmp");}
那么,就可以看见你的图片在循环显示的同时,在data文件夹下,生成了一张图片,打开就可以看见一个模糊的图片screen.bmp,这就是当cnt等于80时截图后的结果。大家可以尝试把cnt改成0,100,50试试~~
附本节全部代码:
#include <GL/glut.h>#include <stdio.h>#include <math.h>#include <time.h>#include <stdlib.h>#include <windows.h>#include <vector>#include <iostream>#define PI 3.1415926#define BITMAP_ID 0x4D42using std::vector;int LoadBitmap(const char *file){unsigned int ID; //纹理的idint width,height; byte *image; //接受图像数据FILE *fp; //文件指针BITMAPFILEHEADER FileHeader; //接受位图文件头BITMAPINFOHEADER InfoHeader; //接受位图信息头fp=fopen(file,"rb");if (fp == NULL){printf("Exception: Fail to open file!\n");return -1;}fread(&FileHeader, sizeof(BITMAPFILEHEADER), 1, fp);if(FileHeader.bfType != BITMAP_ID) //确保文件是一个位图文件,效验文件类型{ printf("Exception: This file is not a bmp file!\n");fclose(fp);return -1;}fread(&InfoHeader, sizeof(BITMAPINFOHEADER), 1, fp);width=InfoHeader.biWidth; height=InfoHeader.biHeight;InfoHeader.biSizeImage = width*height*3;fseek(fp, FileHeader.bfOffBits, SEEK_SET); //将文件指针移动到实际图像数据处image=(byte *)malloc(sizeof(byte)*InfoHeader.biSizeImage); //申请空间if (image == NULL){free(image);printf("Exception: No enough space!\n");return -1;}fread(image, 1, InfoHeader.biSizeImage, fp);fclose(fp); glGenTextures(1, &ID); glBindTexture(GL_TEXTURE_2D, ID);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP); gluBuild2DMipmaps(GL_TEXTURE_2D, 3, width, height, GL_BGR_EXT, GL_UNSIGNED_BYTE, image);return ID; }vector<unsigned int> V_ID; //用来保存生成的纹理idint now,cnt; //now是当前所使用的纹理在vector中的下标//cnt是用来计时的变量bool SnapScreen(int width,int height,const char *file){byte *image; //接受图像数据FILE *fp; //文件指针BITMAPFILEHEADER FileHeader; //接受位图文件头BITMAPINFOHEADER InfoHeader; //接受位图信息头FileHeader.bfType=BITMAP_ID; //ID设置为位图的id号FileHeader.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER); //实际图像数据的位置在文件头和信息头之后FileHeader.bfReserved1=0; //必须设置为0FileHeader.bfReserved2=0; //必须设置为0FileHeader.bfSize=height*width*24+FileHeader.bfOffBits; //BMP图像文件大小InfoHeader.biXPelsPerMeter = 0; //水平分辨率,这里暂时设为0就是InfoHeader.biYPelsPerMeter = 0; //垂直分辨率,这里暂时设为0就是InfoHeader.biClrUsed = 0; //图像使用的颜色,这里暂时设为0就是InfoHeader.biClrImportant = 0; //重要的颜色数,这里暂时设为0就是 //垂直分辨率,这里暂时设为0就是InfoHeader.biPlanes=1; //必须设置为1InfoHeader.biCompression=BI_RGB; //设置为BI_RGB时,表示图像并没有彩色表InfoHeader.biBitCount=24; //图像的位数InfoHeader.biSize=sizeof(BITMAPINFOHEADER); //结构体的大小InfoHeader.biHeight=height; InfoHeader.biWidth=width; InfoHeader.biSizeImage=height*width*4; image=(byte *)malloc(sizeof(byte)*InfoHeader.biSizeImage);if (image == NULL){free(image);printf("Exception: No enough space!\n");return false;}//像素格式设置4字节对齐glPixelStorei(GL_UNPACK_ALIGNMENT,4);//接收出像素的数据glReadPixels(0,0,width,height,GL_BGR_EXT,GL_UNSIGNED_BYTE,image);fp=fopen(file,"wb");if (fp == NULL){printf("Exception: Fail to open file!\n");return false;}fwrite(&FileHeader, sizeof(BITMAPFILEHEADER), 1, fp);fwrite(&InfoHeader, sizeof(BITMAPINFOHEADER), 1, fp);fwrite(image, InfoHeader.biSizeImage, 1, fp);free(image);fclose(fp);return true;}void Draw(){ glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, V_ID[now]);glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);if (cnt <= 50)glColor4f(1.0,1.0,1.0,(float)cnt/50.0);elseglColor4f(1.0,1.0,1.0,1.0-((float)cnt-50.0)/50.0);glBegin(GL_QUADS);glTexCoord2f(0.0f, 0.0f);glVertex2f(-1,-1);glTexCoord2f(1.0f, 0.0f);glVertex2f(1,-1);glTexCoord2f(1.0f, 1.0f);glVertex2f(1,1);glTexCoord2f(0.0f, 1.0f);glVertex2f(-1,1);glEnd();glDisable(GL_TEXTURE_2D); glutSwapBuffers();}void Update(){cnt++;if (cnt == 80){SnapScreen(400,400,"data/screen.bmp");}if (cnt >= 100) //当cnt加到100的时候,换下一张图片{now++;now%=V_ID.size(); //保证now值的正确性cnt=0; //将cnt值置0,开始下一个图片的显示} glutPostRedisplay();}void Reshape(int w,int h){ w=w>h?h:w; glViewport(0,0,(GLsizei)w,(GLsizei)w);}void init(){int n,i;char str[100],file[100];V_ID.clear(); //清空容器FILE *fp;fp=fopen("data/data.txt","r");fscanf(fp,"%d",&n);while (n--){fscanf(fp,"%s",str);sprintf(file,"data/%s",str); i=LoadBitmap(file);if (i == -1) //读取图片失败continue;else V_ID.push_back(i); //把图片的id放到V_ID的末尾保存起来}fclose(fp);now=0;}int main(int argc, char *argv[]){ glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); glutInitWindowPosition(100, 100); glutInitWindowSize(400, 400); glutCreateWindow("HelloOpenGL"); glutReshapeFunc(&Reshape); glutIdleFunc(&Update); glutDisplayFunc(&Draw);init(); glutMainLoop(); return 0;}
- OpenGL织梦之旅【第三章】第3节.实现截图功能
- OpenGL织梦之旅【第三章】第1节.纹理贴图
- OpenGL织梦之旅【第三章】第2节.循环显示图片
- OpenGL织梦之旅【第三章】第4节.glut键盘鼠标响应
- ios截图功能 opengl实现
- ios截图功能 opengl实现
- OpenGL织梦之旅【第二章】第2节.实现动画
- OpenGL织梦之旅【第二章】第3节.指定颜色以及定义视口
- OpenGL织梦之旅【第四章】第2节.简单的3D动画
- OpenGL织梦之旅【第0章】前言
- OpenGL织梦之旅【第二章】第1节.绘制基本的几何图形
- OpenGL织梦之旅【第四章】第1节.设置视点函数gluLookAt
- OpenGL织梦之旅【第三章】编写一个显示图片的程序
- 截图功能的实现
- winform实现截图功能
- 简单截图功能实现
- Android截图功能实现
- winform实现截图功能
- Struct和Class的区别
- .NET 中的Cache
- [编程之美]求二进制中1的个数
- IEEE802.11 无线联网
- Extjs4 日期+时间选择控件
- OpenGL织梦之旅【第三章】第3节.实现截图功能
- ViewGroup学习之绘制过程
- Android短信监听器
- Xml基础
- 二分法求数组最大最小值
- 进程与线程的区别
- 数据结构基础
- gcc 若干安全相关选项
- 【Linux学习】linux源代码版本控制RCS