Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save SuperKogito/03a64820994bd49a704f98f9317964e5 to your computer and use it in GitHub Desktop.
Save SuperKogito/03a64820994bd49a704f98f9317964e5 to your computer and use it in GitHub Desktop.
A quick comparison between the speed of a screen capture using GDI+ (gdiplus) and another using OpenCV.
/***********************************************************************
* \file CaptureSceenshotUsingGdiplusVSCaptureSceenshotUsingOpenCV.cpp
* \brief capture screenshot using GDI+ and save it to drive or memory.
*
* \author SuperKogito
* \date July 2020
*
* @note:
* references and sources:
*
*
***********************************************************************/
#pragma once
#include <windows.h>
#include <Windows.h>
#include <gdiplus.h>
#include <vector>
#include <tchar.h>
#include <stdio.h>
#include <fstream>
#include <iostream>
#include "atlimage.h"
using namespace Gdiplus;
using namespace std;
#include <opencv2/opencv.hpp>
using namespace cv;
#pragma comment(lib,"gdiplus.lib")
/*
* Timer class to measure the runtimes in seconds of code snippets.
*/
class Timer
{
public:
Timer() : beg_(clock_::now()) {}
void reset() { beg_ = clock_::now(); }
double elapsed() const { return std::chrono::duration_cast<second_> (clock_::now() - beg_).count();}
private:
typedef std::chrono::high_resolution_clock clock_;
typedef std::chrono::duration<double, std::ratio<1> > second_;
std::chrono::time_point<clock_> beg_;
};
/**
* Create a Bitmap file header..
*
* @param hwindowDC : window handle.
* @param widht : image width.
* @param height : image height.
*
* @return Bitmap header.
*/
BITMAPINFOHEADER createBitmapHeader(int width, int height)
{
BITMAPINFOHEADER bi;
// create a bitmap
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = width;
bi.biHeight = -height; //this is the line that makes it draw upside down or not
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;
return bi;
}
/**
* Capture a screen and return the handle to its bitmap.
*
* @param hwnd : window handle.
*/
HBITMAP GdiPlusScreenCapture(HWND hWnd)
{
// get handles to a device context (DC)
HDC hwindowDC = GetDC(hWnd);
HDC hwindowCompatibleDC = CreateCompatibleDC(hwindowDC);
SetStretchBltMode(hwindowCompatibleDC, COLORONCOLOR);
// define scale, height and width
int scale = 1;
int screenx = GetSystemMetrics(SM_XVIRTUALSCREEN);
int screeny = GetSystemMetrics(SM_YVIRTUALSCREEN);
int width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
int height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
// create a bitmap
HBITMAP hbwindow = CreateCompatibleBitmap(hwindowDC, width, height);
BITMAPINFOHEADER bi = createBitmapHeader(width, height);
// use the previously created device context with the bitmap
SelectObject(hwindowCompatibleDC, hbwindow);
// 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.
DWORD dwBmpSize = ((width * bi.biBitCount + 31) / 32) * 4 * height;
HANDLE hDIB = GlobalAlloc(GHND, dwBmpSize);
char* lpbitmap = (char*)GlobalLock(hDIB);
// copy from the window device context to the bitmap device context
StretchBlt(hwindowCompatibleDC, 0, 0, width, height, hwindowDC, screenx, screeny, width, height, SRCCOPY); //change SRCCOPY to NOTSRCCOPY for wacky colors !
GetDIBits(hwindowCompatibleDC, hbwindow, 0, height, lpbitmap, (BITMAPINFO*)&bi, DIB_RGB_COLORS);
// avoid memory leak
DeleteDC(hwindowCompatibleDC);
ReleaseDC(hWnd, hwindowDC);
return hbwindow;
}
/**
* Capture a screen window as a matrix.
*
* @param hwnd : window handle.
*
* @return Mat (Mat of the captured image)
*/
Mat captureScreenMat(HWND hwnd)
{
Mat src = {};
// get handles to a device context (DC)
HDC hwindowDC = GetDC(hwnd);
HDC hwindowCompatibleDC = CreateCompatibleDC(hwindowDC);
SetStretchBltMode(hwindowCompatibleDC, COLORONCOLOR);
// define scale, height and width
int scale = 1;
int screenx = GetSystemMetrics(SM_XVIRTUALSCREEN);
int screeny = GetSystemMetrics(SM_YVIRTUALSCREEN);
int width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
int height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
// create mat object
src.create(height, width, CV_8UC4);
// create a bitmap
HBITMAP hbwindow = CreateCompatibleBitmap(hwindowDC, width, height);
BITMAPINFOHEADER bi = createBitmapHeader(width, height);
// use the previously created device context with the bitmap
SelectObject(hwindowCompatibleDC, hbwindow);
// copy from the window device context to the bitmap device context
StretchBlt(hwindowCompatibleDC, 0, 0, width, height, hwindowDC, screenx, screeny, width, height, SRCCOPY); //change SRCCOPY to NOTSRCCOPY for wacky colors !
GetDIBits(hwindowCompatibleDC, hbwindow, 0, height, src.data, (BITMAPINFO*)&bi, DIB_RGB_COLORS); //copy from hwindowCompatibleDC to hbwindow
// avoid memory leak
DeleteObject(hbwindow);
DeleteDC(hwindowCompatibleDC);
ReleaseDC(hwnd, hwindowDC);
return src;
}
/**
* Save a bitmap to memory using its handle.
*
* @param hbitmap : pointer to a bitmap handle.
* @param data : pointer to a vector of bytes.
* @param dataformat: format of datatype to save data according to it.
*
* @return boolean representing whether the saving successful was or not.
*/
bool saveToMemory(HBITMAP* hbitmap, std::vector<BYTE>& data, std::string dataFormat = "png")
{
Gdiplus::Bitmap bmp(*hbitmap, nullptr);
// write to IStream
IStream* istream = nullptr;
CreateStreamOnHGlobal(NULL, TRUE, &istream);
// define encoding
CLSID clsid;
if (dataFormat.compare("bmp") == 0) { CLSIDFromString(L"{557cf400-1a04-11d3-9a73-0000f81ef32e}", &clsid); }
else if (dataFormat.compare("jpg") == 0) { CLSIDFromString(L"{557cf401-1a04-11d3-9a73-0000f81ef32e}", &clsid); }
else if (dataFormat.compare("gif") == 0) { CLSIDFromString(L"{557cf402-1a04-11d3-9a73-0000f81ef32e}", &clsid); }
else if (dataFormat.compare("tif") == 0) { CLSIDFromString(L"{557cf405-1a04-11d3-9a73-0000f81ef32e}", &clsid); }
else if (dataFormat.compare("png") == 0) { CLSIDFromString(L"{557cf406-1a04-11d3-9a73-0000f81ef32e}", &clsid); }
Gdiplus::Status status = bmp.Save(istream, &clsid, NULL);
if (status != Gdiplus::Status::Ok)
return false;
// get memory handle associated with istream
HGLOBAL hg = NULL;
GetHGlobalFromStream(istream, &hg);
// copy IStream to buffer
int bufsize = GlobalSize(hg);
data.resize(bufsize);
// lock & unlock memory
LPVOID pimage = GlobalLock(hg);
memcpy(&data[0], pimage, bufsize);
GlobalUnlock(hg);
istream->Release();
return true;
}
int main()
{
// initializations
Timer tmr;
int repetitions = 50;
HWND hWnd = GetDesktopWindow();
tmr.reset();
double t = tmr.elapsed();
// Initialize GDI+.
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
std::wcout << "Benchmarks for GDI+ variant of the screenshooter" << std::endl;
std::wcout << "***********************************************************" << std::endl;
// benchmarks for GDI+
tmr.reset();
for (int i = 0; i < repetitions; i++)
{
// capture and encode screenshot
std::vector<BYTE> data;
HBITMAP hBmp = GdiPlusScreenCapture(hWnd);
saveToMemory(&hBmp, data, "png");
data.clear();
}
GdiplusShutdown(gdiplusToken);
t = tmr.elapsed();
std::wcout << " | Number of Runs [#] = " << repetitions << std::endl;
std::wcout << " | Run duration [s] = " << t << std::endl;
std::wcout << " | Average Run duration [ms] = " << t / repetitions << std::endl;
std::wcout << "***********************************************************" << std::endl;
std::wcout << "Benchmarks for OpenCV variant of the screenshooter" << std::endl;
std::wcout << "***********************************************************" << std::endl;
// benchmarks for OpenCV
tmr.reset();
for (int i = 0; i < repetitions; i++)
{
// capture and encode screenshot
std::vector<uchar> buf;
Mat src = captureScreenMat(hWnd);
cv::imencode(".png", src, buf);
// cv::imwrite("test_img_opencv.png", src);
buf.clear();
src.release();
}
t = tmr.elapsed();
std::wcout << " | Number of Runs [#] = " << repetitions << std::endl;
std::wcout << " | Run duration [s] = " << t << std::endl;
std::wcout << " | Average Run duration [ms] = " << t / repetitions << std::endl;
std::wcout << "***********************************************************" << std::endl;
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment