织梦CMS - 轻松建站从此开始!

罗索

OpenGL中位图的操作(glReadPixels,glDrawPixels和glCopyPixels

落鹤生 发布于 2012-03-14 10:53 点击:次 
OpenGL中位图的操作(glReadPixels,glDrawPixels和glCopyPixels应用举例)2:glReadPixels的用法和举例
TAG:

3、glReadPixels的用法和举例
3.1 函数的参数说明
该函数总共有七个参数。前四个参数可以得到一个矩形,该矩形所包括的像素都会被读取出来。(第一、二个参数表示了矩形的左下角横、纵坐标,坐标以窗口最左下角为零,最右上角为最大值;第三、四个参数表示了矩形的宽度和高度)
第五个参数表示读取的内容,例如:GL_RGB就会依次读取像素的红、绿、蓝三种数据,GL_RGBA则会依次读取像素的红、绿、蓝、alpha四种数据,GL_RED则只读取像素的红色数据(类似的还有GL_GREEN,GL_BLUE,以及GL_ALPHA)。如果采用的不是RGBA颜色模式,而是采用颜色索引模式,则也可以使用GL_COLOR_INDEX来读取像素的颜色索引。目前仅需要知道这些,但实际上还可以读取其它内容,例如深度缓冲区的深度数据等。
第六个参数表示读取的内容保存到内存时所使用的格式,例如:GL_UNSIGNED_BYTE会把各种数据保存为GLubyte,GL_FLOAT会把各种数据保存为GLfloat等。
第七个参数表示一个指针,像素数据被读取后,将被保存到这个指针所表示的地址。注意,需要保证该地址有足够的可以使用的空间,以容纳读取的像素数据。例如一幅大小为256*256的图象,如果读取其RGB数据,且每一数据被保存为GLubyte,总大小就是:256*256*3 = 196608字节,即192千字节。如果是读取RGBA数据,则总大小就是256*256*4 = 262144字节,即256千字节。

注意:glReadPixels实际上是从缓冲区中读取数据,如果使用了双缓冲区,则默认是从正在显示的缓冲(即前缓冲)中读取,而绘制工作是默认绘制到后缓冲区的。因此,如果需要读取已经绘制好的像素,往往需要先交换前后缓冲。

再看前面提到的BMP文件中两个需要注意的地方:
3.2 解决OpenGL常用的RGB像素数据与BMP文件的BGR像素数据顺序不一致问题
可以使用一些代码交换每个像素的第一字节和第三字节,使得RGB的数据变成BGR的数据。当然也可以使用另外的方式解决问题:新版本的OpenGL除了可以使用GL_RGB读取像素的红、绿、蓝数据外,也可以使用GL_BGR按照相反的顺序依次读取像素的蓝、绿、红数据,这样就与BMP文件格式相吻合了。即使你的gl/gl.h头文件中没有定义这个GL_BGR,也没有关系,可以尝试使用GL_BGR_EXT。虽然有的OpenGL实现(尤其是旧版本的实现)并不能使用GL_BGR_EXT,但我所知道的Windows环境下各种OpenGL实现都对GL_BGR提供了支持,毕竟Windows中各种表示颜色的数据几乎都是使用BGR的顺序,而非RGB的顺序。这可能与IBM-PC的硬件设计有关。

3.3 消除BMP文件中“对齐”带来的影响
实际上OpenGL也支持使用了这种“对齐”方式的像素数据。只要通过glPixelStore修改“像素保存时对齐的方式”就可以了。像这样:
int alignment = 4;
glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
第一个参数表示“设置像素的对齐值”,第二个参数表示实际设置为多少。这里像素可以单字节对齐(实际上就是不使用对齐)、双字节对齐(如果长度为奇数,则再补一个字节)、四字节对齐(如果长度不是四的倍数,则补为四的倍数)、八字节对齐。分别对应alignment的值为1, 2, 4, 8。实际上,默认的值是4,正好与BMP文件的对齐方式相吻合。
glPixelStorei也可以用于设置其它各种参数。但我们这里并不需要深入讨论了。

现在,我们已经可以把屏幕上的像素读取到内存了,如果需要的话,我们还可以将内存中的数据保存到文件。正确的对照BMP文件格式,我们的程序就可以把屏幕中的图象保存为BMP文件,达到屏幕截图的效果。
我们并没有详细介绍BMP文件开头的54个字节的所有内容,不过这无伤大雅。从一个正确的BMP文件中读取前54个字节,修改其中的宽度和高度信息,就可以得到新的文件头了。假设我们先建立一个1*1大小的24位色BMP,文件名为dummy.bmp,又假设新的BMP文件名称为grab.bmp。则可以编写如下代码:

  1. FILE* pOriginFile = fopen("dummy.bmp", "rb); 
  2. FILE* pGrabFile = fopen("grab.bmp""wb"); 
  3. char  BMP_Header[54]; 
  4. GLint width, height; 
  5.  
  6. /* 先在这里设置好图象的宽度和高度,即width和height的值,并计算像素的总长度 */ 
  7.  
  8. // 读取dummy.bmp中的头54个字节到数组 
  9. fread(BMP_Header, sizeof(BMP_Header), 1, pOriginFile); 
  10. // 把数组内容写入到新的BMP文件 
  11. fwrite(BMP_Header, sizeof(BMP_Header), 1, pGrabFile); 
  12.  
  13. // 修改其中的大小信息 
  14. fseek(pGrabFile, 0x0012, SEEK_SET); 
  15. fwrite(&width, sizeof(width), 1, pGrabFile); 
  16. fwrite(&height, sizeof(height), 1, pGrabFile); 
  17.  
  18. // 移动到文件末尾,开始写入像素数据 
  19. fseek(pGrabFile, 0, SEEK_END); 
  20.  
  21. /* 在这里写入像素数据到文件 */ 
  22.  
  23. fclose(pOriginFile); 
  24. fclose(pGrabFile); 

我们给出完整的代码,演示如何把整个窗口的图象抓取出来并保存为BMP文件。

  1. #define WindowWidth  400 
  2. #define WindowHeight 400 
  3.  
  4. #include <stdio.h> 
  5. #include <stdlib.h> 
  6.  
  7. /* 函数grab 
  8. * 抓取窗口中的像素 
  9.  * 假设窗口宽度为WindowWidth,高度为WindowHeight 
  10. */ 
  11. #define BMP_Header_Length 54 
  12. void grab(void
  13. FILE*    pDummyFile; 
  14. FILE*    pWritingFile; 
  15. GLubyte* pPixelData; 
  16. GLubyte  BMP_Header[BMP_Header_Length]; 
  17. GLint    i, j; 
  18. GLint    PixelDataLength; 
  19.  
  20. // 计算像素数据的实际长度 
  21.     i = WindowWidth * 3;   // 得到每一行的像素数据长度 
  22.     while( i%4 != 0 )      // 补充数据,直到i是的倍数 
  23.         ++i;               // 本来还有更快的算法, 
  24.                            // 但这里仅追求直观,对速度没有太高要求 
  25.     PixelDataLength = i * WindowHeight; 
  26.  
  27. // 分配内存和打开文件 
  28.     pPixelData = (GLubyte*)malloc(PixelDataLength); 
  29. if( pPixelData == 0 ) 
  30. exit(0); 
  31.  
  32. pDummyFile = fopen("dummy.bmp""rb"); 
  33. if( pDummyFile == 0 ) 
  34. exit(0); 
  35.  
  36. pWritingFile = fopen("grab.bmp""wb"); 
  37. if( pWritingFile == 0 ) 
  38. exit(0); 
  39.  
  40. // 读取像素 
  41.     glPixelStorei(GL_UNPACK_ALIGNMENT, 4); 
  42. glReadPixels(0, 0, WindowWidth, WindowHeight, 
  43. GL_BGR_EXT, GL_UNSIGNED_BYTE, pPixelData); 
  44.  
  45. // 把dummy.bmp的文件头复制为新文件的文件头 
  46.     fread(BMP_Header, sizeof(BMP_Header), 1, pDummyFile); 
  47. fwrite(BMP_Header, sizeof(BMP_Header), 1, pWritingFile); 
  48. fseek(pWritingFile, 0x0012, SEEK_SET); 
  49. i = WindowWidth; 
  50. j = WindowHeight; 
  51. fwrite(&i, sizeof(i), 1, pWritingFile); 
  52. fwrite(&j, sizeof(j), 1, pWritingFile); 
  53.  
  54. // 写入像素数据 
  55.     fseek(pWritingFile, 0, SEEK_END); 
  56. fwrite(pPixelData, PixelDataLength, 1, pWritingFile); 
  57.  
  58. // 释放内存和关闭文件 
  59.     fclose(pDummyFile); 
  60. fclose(pWritingFile); 
  61. free(pPixelData); 

把这段代码复制到以前任何课程的样例程序中,在绘制函数的最后调用grab函数,即可把图象内容保存为BMP文件了。(在我写这个教程的时候,不少地方都用这样的代码进行截图工作,这段代码一旦写好,运行起来是很方便的。)

(zhengym02)
本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www.rosoo.net/a/201203/15820.html]
本文出处:百度博客 作者:zhengym02 原文
顶一下
(2)
66.7%
踩一下
(1)
33.3%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片
栏目列表
将本文分享到微信
织梦二维码生成器
推荐内容