如何在Windows下的c ++中将OpenGL渲染保存到bmp文件?

时间:2018-04-06 10:23:38

标签: c++ windows opengl bitmap

为此,在我的WM_PAINT处理函数中我有:

hdc = BeginPaint(hWnd, &ps);
displayFunc(hdc);
if (!skipsaveframes) saveframetobmpfile(hWnd, hdc);
EndPaint(hWnd, &ps);

其中函数displayFunc()执行一些基本的OpenGL渲染(实际上是一些GL_LINES,有许多GL_POLYGON,没有纹理,没有着色),函数saveframetobmpfile()将窗口框架保存为磁盘上的.bmp图像文件。现在,在我使用GDI渲染窗口框架的其他应用程序中,这个saveframetobmpfile()函数完美地运行。

但是在这种由OpenGL完成帧渲染的实际情况下,saveframetobmpfile()函数总是将最初渲染的帧保存到磁盘。所以我在磁盘上得到了一系列相同的.bmp图像,所有这些都显示了相同的第一个渲染帧。

我将在这里粘贴这两个函数,必须有一些我不知道的明显的东西。我不是OpenGL的专家,所以这可能就是为什么我要问你们这些专家!

bool currentlysavingframe = false; //2018april05
bool skipsaveframes = true;
string global_outputfoldername = "frames";
int global_frameid = 0;
void saveframetobmpfile(HWND hwnd, HDC hdc)
{
    currentlysavingframe = true;
    //HDC hdcScreen;
    HDC hdcWindow;
    HDC hdcMemDC = NULL;
    HBITMAP hbmScreen = NULL;
    BITMAP bmpScreen;

    // Retrieve the handle to a display device context for the client 
    // area of the window. 
    //hdcScreen = GetDC(NULL);
    hdcWindow = hdc; //hdcWindow = GetDC(g_hWnd);

    // Create a compatible DC which is used in a BitBlt from the window DC
    hdcMemDC = CreateCompatibleDC(hdcWindow);
    if (!hdcMemDC)
    {
        currentlysavingframe = false;
        return;
    }
    RECT rcClient;
    GetClientRect(hwnd, &rcClient); //GetClientRect(g_hWnd, &rcClient);
    hbmScreen = CreateCompatibleBitmap(hdcWindow, rcClient.right - rcClient.left,
        rcClient.bottom - rcClient.top);
    if (!hbmScreen)
    {
        DeleteObject(hdcMemDC);
        currentlysavingframe = false;
        return;
    }

    HBITMAP oldhbitmap = (HBITMAP)SelectObject(hdcMemDC, hbmScreen);

    if (!BitBlt(hdcMemDC,
        0, 0,
        rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
        hdcWindow,
        0, 0,
        SRCCOPY))
    {
        SelectObject(hdcMemDC, oldhbitmap);
        DeleteObject(hbmScreen);
        DeleteObject(hdcMemDC);
        currentlysavingframe = false;
        return;
    }
    GetObject(hbmScreen, sizeof(BITMAP), &bmpScreen);

    BITMAPFILEHEADER   bmfHeader;
    BITMAPINFOHEADER   bi;

    bi.biSize = sizeof(BITMAPINFOHEADER);
    bi.biWidth = bmpScreen.bmWidth;
    bi.biHeight = bmpScreen.bmHeight;
    bi.biPlanes = 1;
    bi.biBitCount = 32;
    bi.biCompression = BI_RGB;
    bi.biSizeImage = 0;
    bi.biXPelsPerMeter = 0;
    bi.biYPelsPerMeter = 0;
    bi.biClrUsed = 0;
    bi.biClrImportant = 0;

    DWORD dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 *
        bmpScreen.bmHeight;

    // Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper functions that 
    // call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc and LocalAlloc 
    // have greater overhead than HeapAlloc.
    HANDLE hDIB = GlobalAlloc(GHND, dwBmpSize);
    char *lpbitmap = (char *)GlobalLock(hDIB);

    // Gets the "bits" from the bitmap and copies them into a buffer 
    // which is pointed to by lpbitmap.
    GetDIBits(hdcWindow, hbmScreen, 0,
        (UINT)bmpScreen.bmHeight,
        lpbitmap,
        (BITMAPINFO *)&bi, DIB_RGB_COLORS);

    // A file is created, this is where we will save the screen capture.
    string filename = global_outputfoldername;
    filename += "\\frame_";
    global_frameid++;
    char buf[256];
    sprintf(buf, "%06d", global_frameid);
    filename += buf;
    filename += ".bmp";
    HANDLE hFile = CreateFileA(filename.c_str(),
        GENERIC_WRITE,
        0,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL, NULL);

    // Add the size of the headers to the size of the bitmap to get the total file size
    DWORD dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER)+
        sizeof(BITMAPINFOHEADER);

    //Offset to where the actual bitmap bits start.
    bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER)+
        (DWORD)sizeof(BITMAPINFOHEADER);

    //Size of the file
    bmfHeader.bfSize = dwSizeofDIB;

    //bfType must always be BM for Bitmaps
    bmfHeader.bfType = 0x4D42; //BM   

    DWORD dwBytesWritten = 0;
    WriteFile(hFile, (LPSTR)&bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL);
    WriteFile(hFile, (LPSTR)&bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL);
    WriteFile(hFile, (LPSTR)lpbitmap, dwBmpSize, &dwBytesWritten, NULL);

    //Unlock and Free the DIB from the heap
    GlobalUnlock(hDIB);
    GlobalFree(hDIB);

    //Close the handle for the file that was created
    CloseHandle(hFile);

    //Clean up
    /*
    done:
    DeleteObject(hbmScreen);
    DeleteObject(hdcMemDC);
    //ReleaseDC(NULL,hdcScreen);
    //ReleaseDC(g_hWnd,hdcWindow);
    */
    SelectObject(hdcMemDC, oldhbitmap);
    DeleteObject(hbmScreen);
    DeleteObject(hdcMemDC);
    currentlysavingframe = false;
    return;

}

为简单起见,我不会粘贴我的OpenGL渲染功能,但如果你们问,我很乐意在这里做。

0 个答案:

没有答案