To do this, in my WM_PAINT handler function I have:
hdc = BeginPaint(hWnd, &ps);
displayFunc(hdc);
if (!skipsaveframes) saveframetobmpfile(hWnd, hdc);
EndPaint(hWnd, &ps);
where the function displayFunc() does some basic OpenGL rendering (essentially some GL_LINES with many GL_POLYGONs, no texture, no shading) and the function saveframetobmpfile() saves the window frame as a .bmp image file on disk. Now, in my other applications where I render the window frame using only GDI this saveframetobmpfile() function works perfectly.
But in this actual case where frame rendering is done by OpenGL the saveframetobmpfile() function always saves the same initially rendered frame to disk. So I get a series of identical .bmp images on disk, all showing this same first rendered frame.
I will paste down here both functions, there must be something obvious I am missing. I am not an expert with OpenGL, so this is probably why I have to ask you guys, the experts!
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;
}
For simplicity, I will not paste my OpenGL render function, but if you guys ask, I will gladly do it in here.