After playing around with it more, Iāve separated the code into a startup and update function. Also, the mouse was flickering or would disappear with the above code, so I fixed that by removing the CAPTUREBLT
flag from the StretchBlt(...)
function. Iām not an expert on these Windows screen capture functions, so I left some of those in, commented out, in the updateScreenGrab()
function (compare this code to previously posted code).
My application was capturing the output from a window/emulator titled āMario Kart 64 (U) - Project64 2.2.0.3ā. Screen capture for the entire desktop is in the code comments. The emulator window was set to 320x240 pixels, and I was resizing that to 140x104 pixels for my application.
The screen capture coordinates, afaik, have 0,0 in the lower-left corner of the screen, as opposed to the upper-left corner of the screen with OF images/drawing functions. OF Shaders also have 0,0 in the lower-left corner. This is why the capture is flipped upside down (with the option available to leave it upside down). Also, I save the capture first to a char/byte array so that I can have captureImg.setFromPixels(...)
swap the red and blue components as discussed earlier.
This code updates at ~60 fps, the latency is barely noticeable, uses a total of 4% CPU on my computer, and causes no flickering! 
Also, I found another weird bug, where if I try to call captureImg.setFromPixels(...)
after calling GetDIBits(...)
, when the destination image is 138x104 pixels, OF0.9.3/VS2015 triggers a breakpoint at varying locations in the libraries. As discussed in the code comments, I believe this is because 138 contains the prime number ā23ā. I changed my destination resolution to 140x104 and it works now, so I havenāt investigated this bug further.
Iām not calling this code perfect or done, nor guaranteeing it will work for others, but Iām pretty happy with it (feel free to make improvements and repost).
ofApp.h declarations:
ofImage captureImg;
int capture_src_width;
int capture_src_height;
int capture_dst_width;
int capture_dst_height;
HDC hSourceDC;
HDC hCaptureDC;
HBITMAP hCaptureBitmap;
HWND window_handle;
BITMAPINFO bmi = { 0 };
unsigned char* capturedPixelData;
ofApp.cpp functions:
//--------------------------------------------------------------
void ofApp::initScreenGrab()
{
capture_src_width = 320;
capture_src_height = 240;
capture_dst_width = 140; // capture_src_width; //
capture_dst_height = 104; // capture_src_height; //
// tried scaling image down to 104 pixels tall using same aspect ratio:
//capture_dst_width = ( capture_src_width * 104 ) / capture_src_height; // = 138
//capture_dst_height = 104
// in catpureImg.setFromPixels(...), OF breaks for some reason; I'm betting it's because 138 contains a 23 prime number factor
// I changed the capture_dst_width to 140 and it works with the height remaining 104
captureImg.allocate( capture_dst_width, capture_dst_height, OF_IMAGE_COLOR );
window_handle = FindWindow( NULL, TEXT( "Mario Kart 64 (U) - Project64 2.2.0.3" ) );
hSourceDC = GetDC( window_handle );//GetDC( NULL ); for entire desktop
hCaptureDC = CreateCompatibleDC( hSourceDC );
hCaptureBitmap = CreateCompatibleBitmap( hSourceDC, capture_dst_width, capture_dst_height );
bmi.bmiHeader.biSize = sizeof( bmi.bmiHeader );
bmi.bmiHeader.biWidth = capture_dst_width;
bmi.bmiHeader.biHeight = capture_dst_height;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 24;
bmi.bmiHeader.biCompression = BI_RGB;
capturedPixelData = new unsigned char[capture_dst_width * capture_dst_height * 3];
}
//--------------------------------------------------------------
void ofApp::updateScreenGrab()
{
//hSourceDC = GetDC( window_handle );
//hCaptureDC = CreateCompatibleDC( hSourceDC );
//hCaptureBitmap = CreateCompatibleBitmap( hSourceDC, capture_dst_width, capture_dst_height );
SelectObject( hCaptureDC, hCaptureBitmap );
// flip right-side up:
StretchBlt( hCaptureDC, 0, capture_dst_height, capture_dst_width, -capture_dst_height, hSourceDC, 0, 0, capture_src_width, capture_src_height, SRCCOPY );
// keep upside-down:
///StretchBlt( hCaptureDC, 0, 0, capture_dst_width, capture_dst_height, hSourceDC, 0, 0, capture_src_width, capture_src_height, SRCCOPY );
//ReleaseDC( window_handle, hSourceDC );
if( GetDIBits( hCaptureDC, hCaptureBitmap, 0, capture_dst_height, (void*) capturedPixelData, &bmi, DIB_RGB_COLORS ) == 0 )
{
cout << "Error: Failed to copy the pixels" << flush;
}
//DeleteDC( hCaptureDC );
//DeleteObject( hCaptureBitmap );
captureImg.setFromPixels( capturedPixelData, capture_dst_width, capture_dst_height, OF_IMAGE_COLOR, false );
captureImg.update();
}