I am going to create some kind of "remote desktop" application that streams the content of the screen over a socket to a connected client.
In order to take a screenshot, I've come up with the following piece of code, which is a modified version of examples I've seen here and there.
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
int _tmain( int argc, _TCHAR * argv[] )
{
int ScreenX = 0;
int ScreenY = 0;
BYTE* ScreenData = 0;
HDC hScreen = GetDC(GetDesktopWindow());
ScreenX = GetDeviceCaps(hScreen, HORZRES);
ScreenY = GetDeviceCaps(hScreen, VERTRES);
ScreenData = (BYTE*)calloc(4 * ScreenX * ScreenY, sizeof(BYTE) );
BITMAPINFOHEADER bmi = {0};
bmi.biSize = sizeof(BITMAPINFOHEADER);
bmi.biPlanes = 1;
bmi.biBitCount = 32;
bmi.biWidth = ScreenX;
bmi.biHeight = -ScreenY;
bmi.biCompression = BI_RGB;
bmi.biSizeImage = 0; // 3 * ScreenX * ScreenY;
int iBegTc = ::GetTickCount();
// Take 100 screen captures for a more accurante measurement of the duration.
for( int i = 0; i < 100; ++i )
{
HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, ScreenX, ScreenY);
HDC hdcMem = CreateCompatibleDC (hScreen);
HGDIOBJ hOld = SelectObject(hdcMem, hBitmap);
BitBlt(hdcMem, 0, 0, ScreenX, ScreenY, hScreen, 0, 0, SRCCOPY);
SelectObject(hdcMem, hOld);
GetDIBits(hdcMem, hBitmap, 0, ScreenY, ScreenData, (BITMAPINFO*)&bmi, DIB_RGB_COLORS);
DeleteDC(hdcMem);
DeleteObject(hBitmap);
}
int iEndTc = ::GetTickCount();
printf( "%d ms", (iEndTc - iBegTc) / 100 );
system("PAUSE");
ReleaseDC(GetDesktopWindow(),hScreen);
return 0;
}
My problem is that the code within the loop takes too long too execute. In my case it's about 36 ms per iteration.
I am wondering if there are statements that could be done just once and thus put outside of the loop, likI did for the byte buffer. I don't know however which are the ones that I must do for each new image, and which are the ones I can only do one time.
Keep BitBlt
and GetDIBits
inside the loop, move the rest outside the loop as follows:
HBITMAP hBitmap = CreateCompatibleBitmap(hScreen, ScreenX, ScreenY);
HDC hdcMem = CreateCompatibleDC (hScreen);
HGDIOBJ hOld = SelectObject(hdcMem, hBitmap);
for( int i = 0; i < 100; ++i )
{
BitBlt(hdcMem, 0, 0, ScreenX, ScreenY, hScreen, 0, 0, SRCCOPY);
//hBitmap is updated now
GetDIBits(hdcMem, hBitmap, 0, ScreenY, ScreenData, (BITMAPINFO*)&bmi, DIB_RGB_COLORS);
//wait...
}
SelectObject(hdcMem, hOld);
DeleteDC(hdcMem);
DeleteObject(hBitmap);
In addition bmi.biSizeImage
should be set to data size, in this case 4 * ScreenX * ScreenY
This won't make the code noticeably faster. The bottle neck is at BitBlt
. It's still about 30 frames/sec, this should be okay unless there is a game or movie on the screen.
You might also try saving to a 24 bit bitmap. It won't make any difference in this code but data size would be smaller ((width * bitcount + 31) / 32) * 4 * height)