呵呵,有了第一次的经验,咱们就要开始咱们的GL离屏渲染的绑定了。 html
关 于OpenGL的离屏渲染,前面已经有一些涉及了。再说一下吧,OpenGL有两种渲染方式:一种是经过操做系统打开窗口进行渲染,而后能够直接在屏幕上 显示,这种渲染方式叫作屏幕渲染。一种经过在内存中一块位图区域内渲染,这种渲染方式在没有经过SwapBuffer方式前不能够在屏幕上显示,因此这种 方法叫离屏渲染。通常来讲,OpenGL经过屏幕显示方式展现它的魅力,屏幕渲染方式是大多数状况下的首选。并且不少窗口系统都有实现OpenGL的屏幕 渲染方式。好比glut,wxwidgets,QT,gtk。可是有些时候咱们不须要屏幕显示。只是想把它保存成一个图像文件就好。并且咱们就是不想打开 一个窗口。这样就须要用离屏渲染的方法。在内存中画,最后保存成图像。 python
可 惜的是OpenGL没有统一的离屏渲染操做API。GL把这些事情所有交给系统。这样就致使各个系统的离屏渲染都有各自的 API,Windows,X,Apple,SGI,OS2都有本身的离屏RC上下文构建方法,每套API都不一样。在缺乏了榜样的力量后,各个系统就纷纷开 始诸侯割据了,就形成天下大乱的局势。这样确实不太好。不过如今乱了就让它乱吧,谁叫咱们是“小程序员”?天下大势就这样,你要怎么着吧-_-! 没办法。实在是没办法~~~现在的世界太疯狂…… 现在的世界变化快…… ios
我仍是静下心来看看这么在各个系统上实现离屏渲染吧。OS2大概八辈子用不到了吧,Apple是高高在上的贵族们的东西。我们老百姓……仍是算了吧。老老实实研究一下Windows和X吧。因而先开始研究WGL。 c++
WGL要创建离屏渲染,能够参看官方解释,不过太多,太乱了,红宝书中的解释比较浅显。这里也有两句解释(不过这里主要是SIG的解释,X的解释也比较详细)。最使人激动的是这里有win32上的完整例子。 程序员
简单得说吧,要进行离屏渲染,win32下须要作下面的几个步骤: 小程序
好了,可用的渲染过程以下: windows
#include "stdafx.h"
#include <windows.h> #include <iostream> #include <commctrl.h> #include <gl/gl.h> #include <gl/glu.h> #include <string> using namespace std; BOOL SaveBmp(HBITMAP hBitmap, string FileName) { HDC hDC; //当前分辨率下每象素所占字节数 int iBits; //位图中每象素所占字节数 WORD wBitCount; //定义调色板大小, 位图中像素字节大小 ,位图文件大小 , 写入文件字节数 DWORD dwPaletteSize=0, dwBmBitsSize=0, dwDIBSize=0, dwWritten=0; //位图属性结构 BITMAP Bitmap; //位图文件头结构 BITMAPFILEHEADER bmfHdr; //位图信息头结构 BITMAPINFOHEADER bi; //指向位图信息头结构 LPBITMAPINFOHEADER lpbi; //定义文件,分配内存句柄,调色板句柄 HANDLE fh, hDib, hPal,hOldPal=NULL; //计算位图文件每一个像素所占字节数 hDC = CreateDC("DISPLAY", NULL, NULL, NULL); iBits = GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES); DeleteDC(hDC); if (iBits <= 1) wBitCount = 1; else if (iBits <= 4) wBitCount = 4; else if (iBits <= 8) wBitCount = 8; else wBitCount = 24; GetObject(hBitmap, sizeof(Bitmap), (LPSTR)&Bitmap); bi.biSize = sizeof(BITMAPINFOHEADER); bi.biWidth = Bitmap.bmWidth; bi.biHeight = Bitmap.bmHeight; bi.biPlanes = 1; bi.biBitCount = wBitCount; bi.biCompression = BI_RGB; bi.biSizeImage = 0; bi.biXPelsPerMeter = 0; bi.biYPelsPerMeter = 0; bi.biClrImportant = 0; bi.biClrUsed = 0; dwBmBitsSize = ((Bitmap.bmWidth * wBitCount + 31) / 32) * 4 * Bitmap.bmHeight; //为位图内容分配内存 hDib = GlobalAlloc(GHND,dwBmBitsSize + dwPaletteSize + sizeof(BITMAPINFOHEADER)); lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib); *lpbi = bi; // 处理调色板 hPal = GetStockObject(DEFAULT_PALETTE); if (hPal) { hDC = ::GetDC(NULL); hOldPal = ::SelectPalette(hDC, (HPALETTE)hPal, FALSE); RealizePalette(hDC); } // 获取该调色板下新的像素值 GetDIBits(hDC, hBitmap, 0, (UINT) Bitmap.bmHeight, (LPSTR)lpbi + sizeof(BITMAPINFOHEADER) +dwPaletteSize, (BITMAPINFO *)lpbi, DIB_RGB_COLORS); //恢复调色板 if (hOldPal) { ::SelectPalette(hDC, (HPALETTE)hOldPal, TRUE); RealizePalette(hDC); ::ReleaseDC(NULL, hDC); } //建立位图文件 fh = CreateFile(FileName.c_str(), GENERIC_WRITE,0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (fh == INVALID_HANDLE_VALUE) return FALSE; // 设置位图文件头 bmfHdr.bfType = 0x4D42; // "BM" dwDIBSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dwPaletteSize + dwBmBitsSize; bmfHdr.bfSize = dwDIBSize; bmfHdr.bfReserved1 = 0; bmfHdr.bfReserved2 = 0; bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER) + dwPaletteSize; // 写入位图文件头 WriteFile(fh, (LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER), &dwWritten, NULL); // 写入位图文件其他内容 WriteFile(fh, (LPSTR)lpbi, dwDIBSize, &dwWritten, NULL); //清除 GlobalUnlock(hDib); GlobalFree(hDib); CloseHandle(fh); return TRUE; } void mGLRender() { glClearColor(0.9f,0.9f,0.3f,1.0f); glClear(GL_COLOR_BUFFER_BIT); glMatrixMode(GL_PROJECTION); gluPerspective(30.0, 1.0, 1.0, 10.0); glMatrixMode(GL_MODELVIEW); gluLookAt(0, 0, -5, 0, 0, 0, 0, 1, 0); glBegin(GL_TRIANGLES); glColor3d(1, 0, 0); glVertex3d(0, 1, 0); glColor3d(0, 1, 0); glVertex3d(-1, -1, 0); glColor3d(0, 0, 1); glVertex3d(1, -1, 0); glEnd(); glFlush(); // remember to flush GL output! } int _tmain(int argc, _TCHAR* argv[]) { const int WIDTH = 500; const int HEIGHT = 500; // Create a memory DC compatible with the screen HDC hdc = CreateCompatibleDC(0); if (hdc == 0) cout<<"Could not create memory device context"; // Create a bitmap compatible with the DC // must use CreateDIBSection(), and this means all pixel ops must be synchronised // using calls to GdiFlush() (see CreateDIBSection() docs) BITMAPINFO bmi = { { sizeof(BITMAPINFOHEADER), WIDTH, HEIGHT, 1, 32, BI_RGB, 0, 0, 0, 0, 0 }, { 0 } }; DWORD *pbits; // pointer to bitmap bits HBITMAP hbm = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, (void **) &pbits, 0, 0); if (hbm == 0) cout<<"Could not create bitmap"; //HDC hdcScreen = GetDC(0); //HBITMAP hbm = CreateCompatibleBitmap(hdcScreen,WIDTH,HEIGHT); // Select the bitmap into the DC HGDIOBJ r = SelectObject(hdc, hbm); if (r == 0) cout<<"Could not select bitmap into DC"; // Choose the pixel format PIXELFORMATDESCRIPTOR pfd = { sizeof (PIXELFORMATDESCRIPTOR), // struct size 1, // Version number PFD_DRAW_TO_BITMAP | PFD_SUPPORT_OPENGL, // use OpenGL drawing to BM PFD_TYPE_RGBA, // RGBA pixel values 32, // color bits 0, 0, 0, // RGB bits shift sizes... 0, 0, 0, // Don't care about them 0, 0, // No alpha buffer info 0, 0, 0, 0, 0, // No accumulation buffer 32, // depth buffer bits 0, // No stencil buffer 0, // No auxiliary buffers PFD_MAIN_PLANE, // Layer type 0, // Reserved (must be 0) 0, // No layer mask 0, // No visible mask 0 // No damage mask }; int pfid = ChoosePixelFormat(hdc, &pfd); if (pfid == 0) cout<<"Pixel format selection failed"; // Set the pixel format // - must be done *after* the bitmap is selected into DC BOOL b = SetPixelFormat(hdc, pfid, &pfd); if (!b) cout<<"Pixel format set failed"; // Create the OpenGL resource context (RC) and make it current to the thread HGLRC hglrc = wglCreateContext(hdc); if (hglrc == 0) cout<<"OpenGL resource context creation failed"; wglMakeCurrent(hdc, hglrc); // Draw using GL - remember to sync with GdiFlush() GdiFlush(); mGLRender(); SaveBmp(hbm,"output.bmp"); /* Examining the bitmap bits (pbits) at this point with a debugger will reveal that the colored triangle has been drawn. */ // Clean up wglDeleteContext(hglrc); // Delete RC SelectObject(hdc, r); // Remove bitmap from DC DeleteObject(hbm); // Delete bitmap DeleteDC(hdc); // Delete DC return 0; }
好了,编译成功,运行,确实是能够啊!看看步骤是什么样的: 数组
CreateCompatibleDC安全 |
建立dcapp |
CreateDIBSection |
建立图像 |
SelectObject |
图像选入DC |
SetPixelFormat |
设置像元格式 |
wglCreateContext |
建立RC |
wglMakeCurrent |
选择RC |
mGLRender |
开始渲染 |
SaveBmp |
保存图像(这段是我从网上随便摘下来的) |
... |
清理 |
好的,既然C++能够,那么Python……
等等,Python好像不行!
单 单是OpenGL的世界乱了,也就算了,恰恰Python也来凑热闹。PyWin32里我死活找不到CreateDIBSection。好 吧,PyWin32找不到,那么我还有PIL。里面有个ImageWin.Dib,我试过,不行。老是在SetPixelFormat中出现问题。后来我 把 CreateDIBSection的部分整个注释掉改为相似:
HDC hdcScreen = GetDC(0);
HBITMAP hbm = CreateCompatibleBitmap(hdcScreen,WIDTH,HEIGHT);
的 代码。固然这是C++的改动,python改动也相似。由于这两个函数PyWin32里有,如今经过了。而且运行到了wglCreateContext的 步骤。等等,提示空间不够?什么空间不够?我在C++中都运行好好的。对比两个语言的两段代码,彻底同样的步骤,竟然一个能够一个就是不行!发个邮件给 pyopengl的邮件列表吧,几天没回应……真的晕了。
大概多是我不懂怎么玩PyWin32或者PyOpenGL,或者PIL的Dib类我用得不对,可是我在泡了三天的google后,我放弃了。与其在这个问题上拖延时间,不如另辟蹊径。(若是你成功得在Python下离屏渲染了,必定要告诉我哦!)
既然C++能够,为何不用C++来作?而后用Swig来绑定?不就是建立一个环境吗?我在C++中建立好,而后在Python中渲染,而后在C++中关闭环境。反正环境在哪里不是同样建立!
如今个人思路就定下来,用C++写两个函数,用来建立离屏RC环境和关闭环境。名字就叫StartBmpContext和EndBmpContext。
建立一个工程。叫glBmpContext。而后作一些什么取消stdafx,清空等善前工做。而后写入内容。
#include <windows.h>
#include <iostream> #include <commctrl.h> #include <gl/gl.h> #include <gl/glu.h> using namespace std; static HDC hdc; static HBITMAP hbm; static HGDIOBJ r; static HGLRC hglrc; static DWORD *pbits;// pointer to bitmap bits static int WIDTH = 120; static int HEIGHT = 90; __declspec(dllexport) void StartBmpContext(int width,int height) { WIDTH = width; HEIGHT = height; // Create a memory DC compatible with the screen hdc = CreateCompatibleDC(0); if (hdc == 0) cout<<"Could not create memory device context"; // Create a bitmap compatible with the DC // must use CreateDIBSection(), and this means all pixel ops must be synchronised // using calls to GdiFlush() (see CreateDIBSection() docs) BITMAPINFO bmi = { { sizeof(BITMAPINFOHEADER), WIDTH, HEIGHT, 1, 32, BI_RGB, 0, 0, 0, 0, 0 }, { 0 } }; hbm = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, (void **) &pbits, 0, 0); /*HBITMAP hbm = CreateCompatibleBitmap(hdc,WIDTH,HEIGHT);*/ if (hbm == 0) cout<<"Could not create bitmap"; // Select the bitmap into the DC r = SelectObject(hdc, hbm); if (r == 0) cout<<"Could not select bitmap into DC"; // Choose the pixel format PIXELFORMATDESCRIPTOR pfd = { sizeof (PIXELFORMATDESCRIPTOR), // struct size 1, // Version number PFD_DRAW_TO_BITMAP | PFD_SUPPORT_OPENGL, // use OpenGL drawing to BM PFD_TYPE_RGBA, // RGBA pixel values 32, // color bits 0, 0, 0, // RGB bits shift sizes... 0, 0, 0, // Don't care about them 0, 0, // No alpha buffer info 0, 0, 0, 0, 0, // No accumulation buffer 32, // depth buffer bits 0, // No stencil buffer 0, // No auxiliary buffers PFD_MAIN_PLANE, // Layer type 0, // Reserved (must be 0) 0, // No layer mask 0, // No visible mask 0 // No damage mask }; int pfid = ChoosePixelFormat(hdc, &pfd); cout<<pfid<<endl; if (pfid == 0) cout<<"Pixel format selection failed"; // Set the pixel format // - must be done *after* the bitmap is selected into DC BOOL b = SetPixelFormat(hdc, pfid, &pfd); if (!b) cout<<"Pixel format set failed"; // Create the OpenGL resource context (RC) and make it current to the thread hglrc = wglCreateContext(hdc); if (hglrc == 0) cout<<"OpenGL resource context creation failed"; wglMakeCurrent(hdc, hglrc); } int SaveBmp(HBITMAP hBitmap, char* FileName) { HDC hDC; //当前分辨率下每象素所占字节数 int iBits; //位图中每象素所占字节数 WORD wBitCount; //定义调色板大小, 位图中像素字节大小 ,位图文件大小 , 写入文件字节数 DWORD dwPaletteSize=0, dwBmBitsSize=0, dwDIBSize=0, dwWritten=0; //位图属性结构 BITMAP Bitmap; //位图文件头结构 BITMAPFILEHEADER bmfHdr; //位图信息头结构 BITMAPINFOHEADER bi; //指向位图信息头结构 LPBITMAPINFOHEADER lpbi; //定义文件,分配内存句柄,调色板句柄 HANDLE fh, hDib, hPal,hOldPal=NULL; //计算位图文件每一个像素所占字节数 hDC = CreateDC("DISPLAY", NULL, NULL, NULL); iBits = GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES); DeleteDC(hDC); if (iBits <= 1) wBitCount = 1; else if (iBits <= 4) wBitCount = 4; else if (iBits <= 8) wBitCount = 8; else wBitCount = 24; GetObject(hBitmap, sizeof(Bitmap), (LPSTR)&Bitmap); bi.biSize = sizeof(BITMAPINFOHEADER); bi.biWidth = Bitmap.bmWidth; bi.biHeight = Bitmap.bmHeight; bi.biPlanes = 1; bi.biBitCount = wBitCount; bi.biCompression = BI_RGB; bi.biSizeImage = 0; bi.biXPelsPerMeter = 0; bi.biYPelsPerMeter = 0; bi.biClrImportant = 0; bi.biClrUsed = 0; dwBmBitsSize = ((Bitmap.bmWidth * wBitCount + 31) / 32) * 4 * Bitmap.bmHeight; //为位图内容分配内存 hDib = GlobalAlloc(GHND,dwBmBitsSize + dwPaletteSize + sizeof(BITMAPINFOHEADER)); lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib); *lpbi = bi; // 处理调色板 hPal = GetStockObject(DEFAULT_PALETTE); if (hPal) { hDC = ::GetDC(NULL); hOldPal = ::SelectPalette(hDC, (HPALETTE)hPal, FALSE); RealizePalette(hDC); } // 获取该调色板下新的像素值 GetDIBits(hDC, hBitmap, 0, (UINT) Bitmap.bmHeight, (LPSTR)lpbi + sizeof(BITMAPINFOHEADER) +dwPaletteSize, (BITMAPINFO *)lpbi, DIB_RGB_COLORS); //恢复调色板 if (hOldPal) { ::SelectPalette(hDC, (HPALETTE)hOldPal, TRUE); RealizePalette(hDC); ::ReleaseDC(NULL, hDC); } //建立位图文件 fh = CreateFile(FileName, GENERIC_WRITE,0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (fh == INVALID_HANDLE_VALUE) return 1; // 设置位图文件头 bmfHdr.bfType = 0x4D42; // "BM" dwDIBSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dwPaletteSize + dwBmBitsSize; bmfHdr.bfSize = dwDIBSize; bmfHdr.bfReserved1 = 0; bmfHdr.bfReserved2 = 0; bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER) + dwPaletteSize; // 写入位图文件头 WriteFile(fh, (LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER), &dwWritten, NULL); // 写入位图文件其他内容 WriteFile(fh, (LPSTR)lpbi, dwDIBSize, &dwWritten, NULL); //清除 GlobalUnlock(hDib); GlobalFree(hDib); CloseHandle(fh); return 0; } __declspec(dllexport) int SaveBmp(char* FileName) { return SaveBmp(hbm,FileName); } __declspec(dllexport) int GetWidth() { return WIDTH; } __declspec(dllexport) int GetHeight() { return HEIGHT; } __declspec(dllexport) void GetMemBmpData(char **s,int *slen) { *s = (char*)pbits; *slen = WIDTH*HEIGHT*4; } __declspec(dllexport) void EndBmpContext() { // Clean up wglDeleteContext(hglrc); // Delete RC SelectObject(hdc, r); // Remove bitmap from DC DeleteObject(hbm); // Delete bitmap DeleteDC(hdc); // Delete DC }
其实这里作得事情也就是这样,把前面那段C++代码拆开,把开始渲染前和渲染结束后两个部分单独拆出来,放到Start和End两个函数里。为了能在最后作清理工做,把一些句柄作成全程静态变量。提到开头而已。
等一下,多了不少函数。
是 的。这里多了SaveBmp,这个是为了测试数据的正确性。用vc的方法保存bmp图像。可是我并不想在vc中保存图像。太麻烦了。咱们有PIL啊!保存 只要一句的PIL啊~~~~~因此我须要有个函数读取bmp图像的信息。因此我添加了个GetMemBmpData函数。用于获取图像数据的二进制表示。 固然,渲染图像大小不能够定死,因此我暴露了获取图像大小的函数,并在初始化环境的时候用两个参数定义宽高。
好了,编译,连接,成功。(须要说明的是,这里的GetMemBmpData的参数很奇怪,这是由于要返回二进制时Swig的特殊要求决定的)
咱们如今有了C++的库了。
好,开始定义glBmpContext.i,这是重点!
%module glBmpContext
%include "cstring.i" %cstring_output_allocate_size(char **s, int *slen, free(*$1)); %{ #include <windows.h> #include <iostream> #include <commctrl.h> #include <gl/gl.h> #include <gl/glu.h> using namespace std; void StartBmpContext(int w,int h); int SaveBmp( char* FileName ); void GetMemBmpData(char **s,int *slen); void EndBmpContext(); int GetWidth(); int GetHeight(); %} void StartBmpContext(int w,int h); int SaveBmp( char* FileName ); void GetMemBmpData(char **s,int *slen); void EndBmpContext(); int GetWidth(); int GetHeight();
首 先,咱们定义模块名称,而后引入一个叫cstring的swig预约义模块,以及定义一种返回值形式。引入这个模块是由于咱们须要在 GetMemBmpData中返回图像格式的二进制形式给Python,而后经过PIL.Image的fromstring函数转化成图像并能够用 save保存。
Python 中不仅仅是int,double,这样的简单类型。一些如数组,指针,字典,等等就比较麻烦了。Swig定义了不少预约义的模块来处理这些东西。通 过%include 来定义这些数据格式和操做。这才是从C++到Python的恶梦。也是swig最核心的东西。这些东西是不少的,须要咱们慢慢去掌握。
先 掌握两个。一个是字符串。在Python中字符串是一个很强大的东西,但在swig定义中却看起来不是那么强大。由于它被定义成c的字符串形式。一个 char* !不错,是char*。看SaveBmp的参数,是一个char*。这就是Python中的字符串!在Python中调用就像这样:
SaveBmp("f:/image/img.bmp")
好了,再看一个,返回一个二进制数据对象!这个比较复杂,能够看这个,这个解释十分详细。还有好几种类型。咱们用的是最后那个。由于C++/C不比Python,能够返回一个列表,它只能返回一个东西。因此在Python绑定定义中要用参数来代替返回。
还有更多的东西能够看这里。
函数定义像这样:
void foo(char **s, int *sz) {
*s = (char *) malloc(64); *sz = 64; // Write some binary data ... }
在swig定义.i 文件中就要这样写:
%cstring_output_allocate_size(char **s, int *slen, free(*$1));
... void foo(char **s, int *slen);
在Python中就要这样调:
>>> foo()
'\xa9Y:\xf6\xd7\xe1\x87\xdbH;y\x97\x7f\xd3\x99\x14V\xec\x06\xea\xa2\x88' >>>
呵呵,很奇妙吧!
我也是第一次看到这种作法!
其余应该都看得懂了。
好了,如今咱们定义setup.py:
from distutils.core import setup,Extension
include_dirs = [] libraries = ['glBmpContextD','opengl32','glu32'] library_dirs = ['./glBmpContext/'] extra_link_args = [] glBmpContext_module = Extension('_glBmpContext', sources = ['glBmpContext_wrap.cpp'], include_dirs = include_dirs, libraries = libraries, library_dirs = library_dirs, extra_link_args = extra_link_args, ) setup(name='glBmpContext wrapper', version='1.0', py_modules=["glBmpContext"], ext_modules=[glBmpContext_module], )
这个和前一个例子很像。特别注意的是Libraries,这里放了opengl32 和glu32 是为了能连接经过。
好了,写个脚原本运行swig和编译。
@echo off
swig -c++ -python -modern -new_repr -I. -o glBmpContext_wrap.cpp glBmpContext.i python.exe setup.py build copy .\build\lib.win32-2.4\*.* .\bin\ pause
好了,运行编译经过后就能够了。这个脚本还把生成的pyd拷贝到bin目录下。
好了,在bin目录下创建一个测试脚本
import _glBmpContext
from OpenGL.GL import * from OpenGL.GLU import * from Numeric import * import Image w = 500 h = 400 _glBmpContext.StartBmpContext(w,h) glMatrixMode(GL_PROJECTION); gluPerspective(30.0, w*1.0/h, 1.0, 10.0); glMatrixMode(GL_MODELVIEW); gluLookAt(0, 0, -5, 0, 0, 0, 0, 1, 0); glBegin(GL_TRIANGLES); glColor3d(1, 0, 0); glVertex3d(0, 1, 0); glColor3d(0, 1, 0); glVertex3d(-1, -1, 0); glColor3d(0, 0, 1); glVertex3d(1, -1, 0); glEnd(); glFlush(); data = _glBmpContext.GetMemBmpData() #print len(data),type(data) w = _glBmpContext.GetWidth() h = _glBmpContext.GetHeight() arr = fromstring(data,Int8) arr.shape = (-1,4) arr = arr[:,0:3] print arr.shape img = Image.fromstring("RGB",(w,h),arr.tostring()).transpose(Image.FLIP_TOP_BOTTOM) \ .save("../tt.png","png") _glBmpContext.SaveBmp("hehe.bmp") _glBmpContext.EndBmpContext()
运行,嗯,一切尽在掌握中。个人目的实现了!能够在StartBmpContext后尽情渲染,而后GetMemBmpData得到数据,而后用Image操做数据保存成各类图片。最后EndBmpContext关闭环境。
这里须要注意的是内存中的HBITMAP存储的图像是倒着的。要在Image中执行transpose(Image.FLIP_TOP_BOTTOM)把它转回来。
固然这样作很不安全。可能被恶意地重复建立环境,而且一旦出错,环境就无法释放。能够把_glBmpContext的东西包装成类,销毁的时候自动释放。