zoukankan      html  css  js  c++  java
  • 开始我的GL离屏渲染绑定[转]

    地址: http://wiki.woodpecker.org.cn/moin/lilin/swig-glBmpContext

    呵呵,有了第一次的经验,我们就要开始我们的GL离屏渲染的绑定了。

    关 于OpenGL的离屏渲染,前面已经有一些涉及了。再说一下吧,OpenGL有两种渲染方式:一种是通过操作系统打开窗口进行渲染,然后可以直接在屏幕上 显示,这种渲染方式叫做屏幕渲染。一种通过在内存中一块位图区域内渲染,这种渲染方式在没有通过SwapBuffer方式前不可以在屏幕上显示,所以这种 方法叫离屏渲染。一般来说,OpenGL通过屏幕显示方式展示它的魅力,屏幕渲染方式是大多数情况下的首选。而且很多窗口系统都有实现OpenGL的屏幕 渲染方式。比如glut,wxwidgets,QT,gtk。但是有些时候我们不需要屏幕显示。只是想把它保存成一个图像文件就好。而且我们就是不想打开 一个窗口。这样就需要用离屏渲染的方法。在内存中画,最后保存成图像。

    可 惜的是OpenGL没有统一的离屏渲染操作API。GL把这些事情全部交给系统。这样就导致各个系统的离屏渲染都有各自的 API,Windows,X,Apple,SGI,OS2都有自己的离屏RC上下文构建方法,每套API都不同。在缺少了榜样的力量后,各个系统就纷纷开 始诸侯割据了,就造成天下大乱的局势。这样确实不太好。不过现在乱了就让它乱吧,谁叫我们是“小程序员”?天下大势就这样,你要怎么着吧-_-! 没办法。实在是没办法~~~如今的世界太疯狂…… 如今的世界变化快……

    我还是静下心来看看这么在各个系统上实现离屏渲染吧。OS2大概八辈子用不到了吧,Apple是高高在上的贵族们的东西。咱们老百姓……还是算了吧。老老实实研究一下Windows和X吧。于是先开始研究WGL。

    WGL要建立离屏渲染,可以参看官方解释,不过太多,太乱了,红宝书中的解释比较浅显。这里也有两句解释(不过这里主要是SIG的解释,X的解释也比较详细)。最令人激动的是这里有win32上的完整例子。

    简单得说吧,要进行离屏渲染,win32下需要做下面的几个步骤:

    1. 创建一个内存 DC
    2. 创建一个位图
    3. 把位图选入DC
    4. 设置DC的像元格式
    5. 通过DC创建OpenGL的渲染上下文RC
    6. 开始渲染.

    好了,可用的渲染过程如下:

     

    #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

    创建dc

    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:xf6xd7xe1x87xdbH;yx97x7fxd3x99x14Vxecx06xeaxa2x88'
    >>>

    呵呵,很奇妙吧!

    我也是第一次看到这种做法!

    其他应该都看得懂了。

    好了,现在我们定义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 .uildlib.win32-2.4*.* .in
    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的东西包装成类,销毁的时候自动释放。

  • 相关阅读:
    基于asp.net + easyui框架,一步步学习easyui-datagrid——实现分页和搜索(二)
    Build CRUD Application with jQuery EasyUI
    sql里的ROW_NUMBER() OVER是啥意思?
    EasyUI实战篇之datagrid:如何重新设置datagrid所配置的属性(options)并重新查询列表(relaod)
    UNIX基础知识之程序和进程
    UNIX基础知识之输入和输出
    UNIX基础知识之文件和目录
    输出至标准出错文件的出错处理函数
    apue.h
    目录操作函数opendir、readdir和closedir
  • 原文地址:https://www.cnblogs.com/mazhenyu/p/5276405.html
Copyright © 2011-2022 走看看