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;
}
在本地程序目录下,保存的截图:
OpenGL(十) 截屏并保存BMP文件的更多相关文章
- 一个类实现Java截屏并保存到指定文件夹
不知小伙伴们有没有遇到过使用java来截屏的需求,截屏后保存到指定的目录,在桌面上没有任何体现,完全不知道已经被截屏了.至于截屏后怎么做,可能有的老铁只是好奇想知道某人在干啥?也有的老铁可能想进行文字 ...
- Qt实现截屏并保存(转载)
原博地址:http://blog.csdn.net/qinchunwuhui/article/details/52869451?_t_t_t=0.28889142944202306 目前对应用实现截屏 ...
- Mac下使用Automator实现截屏编辑保存
以前在Windows下使用百度或者搜狗输入法的截图工具很方便.❶快捷键(Alt+X,我设置的是这个),❷选择区域,❸编辑所选区域,包括添加文字,线条框框,调色,❹点击『✔️』选择保存位置,修改文件名保 ...
- Swift实现截屏并保存相册
func saveToLocal() { //截屏 let screenRect = UIScreen.mainScreen().bounds UIGraphicsBeginImageContext( ...
- Selenium | 基础入门 | 截屏并保存于本地
可先参考 Selenium | 基础入门 | 利用Xpath寻找用户框 核心代码: //截屏操作 File srcFile = ((TakesScreenshot)driver).getScree ...
- Selenium基础知识(十)截屏
自动化测试过程中,经常会用截图的方式,更直观的显示展示错误信息:selenium截图的三种方式: driver.get_screenshot_as_file(r'd:\selenium.png') # ...
- iOS截屏保存至相册
#pragma mark 截屏并保存至相册 -(void)screenShotsComplete:(void(^)(UIImage * img)) complete { CGSize imageSiz ...
- Unity3d 截屏保存到相册,并且刷新相册
要做一个截图的功能,并且玩家可以在相册中看到. 做的时候遇到了三个问题: 1.unity自带的截图API,Application.CaptureScreenshot在Android上不生效 2.图片保 ...
- app内区域截图利用html2Canvals保存到手机 截屏 (html2Canvas使用版本是:0.5.0-beta3。)
app内区域截图利用html2Canvals保存到手机 app内有时候需要区域内的截图保存dom为图像,我们可以使用html2Canvas将dom转换成base64图像字符串,然后再利用5+api保存 ...
随机推荐
- [Angular2 Form] patchValue, setValue and reset() for Form
Learn how to update part of form model, full form model and reset whole form. We have form definetio ...
- windows下perl使用
windows下perl使用 windows下perl开发工具 Perl 的官方网址提供下载的windows perl开发环境 ActiveState Perl Strawberry Perl DWI ...
- ios开发网络学习十:利用文件句柄实现大文件下载
#import "ViewController.h" @interface ViewController ()<NSURLSessionDataDelegate> @p ...
- [Webpack] Access Webpack Dev Server from Mobile Safari on an iPhone
Testing your sites on mobile devices is a critical part of the development process. Webpack dev serv ...
- Android Notification如何显示表情?
遇到这种分析用什么实现的,肯定要祭出大杀器Android Device Monitor(AS在Tools->Android)打开之后,选中连接的设备,然后点击小手机图标,即可导出UI层次图.咱们 ...
- source insight totalcmd 中文目录
@echo off for /F "usebackq delims=" %%a in (`echo %1^^^|iconv -f utf-8 -t gb18030`) do ( s ...
- php实现求一个数的质数因子
php实现求一个数的质数因子 一.总结 一句话总结:这么简单的题目,还是把变量定义的位置和自增的位置写错. 1 <?php 2 $num=trim(fgets(STDIN)); 3 //如果$n ...
- 360随身WIFI作USB无线网卡的做法
作者:朱金灿 来源:http://blog.csdn.net/clever101 1. 到控制面板上把360wifi卸载. 2. 到雷凌的官网下载网卡驱动,注意选择USB(RT2870***),操作系 ...
- 微服务学习笔记(1)——使用MagicOnion实现gRPC
原文:微服务学习笔记(1)--使用MagicOnion实现gRPC 1.什么是gRPC 官方文档:https://grpc.io/docs/guides/index.html 2.什么是MagicOn ...
- 最新版Butterknife plugin支持butterknife7.0.1和兼容butterknife 6.1.0及下面
JakeWharton 的butterknife帮我们有效的攻克了findViewById及各种view的监听事件泛滥的问题,极大的简化了代码,假设使用了android Studio开发的配上avas ...