OpenGL织梦之旅【第三章】第3节.实现截图功能

        对于上一章的纹理贴图,不少朋友给我说,有些图片可以成功读取,但是显示不正常。

        例如这只猪:

OpenGL织梦之旅【第三章】第3节.实现截图功能_第1张图片

        解决方案是这样的。造成这个问题的原因是因为像素在bmp图片中保存格式为BGRgluBuild2Dmipmaps函数会要求你提供颜色的组成类型(GL_RGB)而某些OpenGL版本的中并没有GL_BGRGL_BGR_EXT,所以在LoadTexture函数中,我写了个交换器来使其变成RGB,最终使用GL_RGB生成纹理。那么,对于显示不正常的图片,将这个参数改成GL_BGR_EXT即可。

        新的LoadTexture如下:

int LoadBitmap(const char *file)
{
	unsigned int ID;      //纹理的id
	int 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;                                                     //必须设置为0
	FileHeader.bfReserved2=0;                                                     //必须设置为0
	FileHeader.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;														  //必须设置为1
	InfoHeader.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 0x4D42
using std::vector;
int LoadBitmap(const char *file)
{
	unsigned int ID;      //纹理的id
	int 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;          //用来保存生成的纹理id
int 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;                                                     //必须设置为0
	FileHeader.bfReserved2=0;                                                     //必须设置为0
	FileHeader.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;														  //必须设置为1
	InfoHeader.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);
	else
		glColor4f(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节.实现截图功能)