Comparing GDI and DXGI Screenshot Methods in Qt with Performance Analysis
This article introduces GDI and DXGI screen capture techniques, provides Qt implementations with detailed code examples, compares their performance and visual results, and presents a CPU/GPU usage benchmark, concluding that DXGI offers higher GPU efficiency while both methods have specific trade‑offs.
This article introduces two Windows screenshot methods—GDI and DXGI—and demonstrates how to implement them in Qt, followed by a performance and visual quality comparison.
GDI
GDI captures the screen by obtaining a device context (DC) of the target window and using BitBlt (or StretchBlt) to copy the bitmap. It works across Windows versions and does not require specific OS support.
QImage GrabWindowHelper::grabWindow(WId winId, bool needMouse){
QSize windowSize;
int x = 0;
int y = 0;
HWND hwnd = reinterpret_cast
(winId);
if (!hwnd) {
return QImage();
}
//以下部分在获取这个句柄应用的size
RECT r;
GetClientRect(hwnd, &r);
windowSize = QSize(r.right - r.left, r.bottom - r.top);
int width = windowSize.width();
int height = windowSize.height();
HDC display_dc = GetDC(nullptr);
HDC bitmap_dc = CreateCompatibleDC(display_dc);
HBITMAP bitmap = CreateCompatibleBitmap(display_dc, width, height);
HGDIOBJ null_bitmap = SelectObject(bitmap_dc, bitmap);
BOOL imgFlag = FALSE;HDC window_dc = GetDC(hwnd); //获取句柄DC
imgFlag = BitBlt(bitmap_dc, 0, 0, width, height, window_dc, x, y, SRCCOPY | CAPTUREBLT); //使用Bitblt方法
ReleaseDC(hwnd, window_dc);
if (needMouse) { //需要截取鼠标的话单独处理
CURSORINFO curinfo;
curinfo.cbSize = sizeof(curinfo);
GetCursorInfo(&curinfo);
POINT screenPt = curinfo.ptScreenPos;
ScreenToClient(hwnd, &screenPt);
DrawIcon(bitmap_dc, screenPt.x, screenPt.y, curinfo.hCursor);
}
SelectObject(bitmap_dc, null_bitmap);
DeleteDC(bitmap_dc);
QImage image;
if (imgFlag) {
image = QtWin::imageFromHBITMAP(bitmap);
}
DeleteObject(bitmap);
ReleaseDC(nullptr, display_dc);
return image;
}The code above uses BitBlt; an alternative StretchBlt can perform scaling. Qt also provides a built‑in function:
QPixmap QScreen::grabWindow(WId window, int x = 0, int y = 0, int width = -1, int height = -1)For broader compatibility the article shows how to load PrintWindow dynamically via QLibrary and replace the BitBlt call with PrintWindow when available.
typedef BOOL (__stdcall *PtrPrintWindow )(HWND ,HDC ,UINT );
PtrPrintWindow GrabWindowHelper::printWindow() {
static bool hasTestPrintWindowFunction = false;
static PtrPrintWindow printWindowFunnction = nullptr;
if (hasTestPrintWindowFunction) {
return printWindowFunnction;
}
hasTestPrintWindowFunction = true;
printWindowFunnction = reinterpret_cast
(QLibrary::resolve("user32.dll", "PrintWindow"));
return printWindowFunnction;
}Using PrintWindow simply replaces the BitBlt line with:
imgFlag = printWindow()(hwnd, bitmap_dc, PW_CLIENTONLY | PW_RENDERFULLCONTENT);DXGI
DXGI (DirectX Graphics Infrastructure) is a newer Windows graphics interface that interacts directly with the GPU, offering higher efficiency and lower CPU usage, but requires Windows 8 or later and cannot capture desktop windows directly.
BOOL Init(){
int adaptIndex = 0, outputIndex = 0;
QList
list;
bool flag = getScreens(list); //获取屏幕列表
if (!flag || list.size() == 0) {
return false;
}
adaptIndex = list.at(0).adaptorIndex;
outputIndex = list.at(0).outputIndex;
HRESULT hr = S_OK;
if (m_bInit) {
return FALSE;
}
D3D_DRIVER_TYPE DriverTypes[] = { D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_WARP, D3D_DRIVER_TYPE_REFERENCE };
UINT NumDriverTypes = ARRAYSIZE(DriverTypes);
D3D_FEATURE_LEVEL FeatureLevels[] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_1 };
UINT NumFeatureLevels = ARRAYSIZE(FeatureLevels);
D3D_FEATURE_LEVEL FeatureLevel;
for (UINT DriverTypeIndex = 0; DriverTypeIndex < NumDriverTypes; ++DriverTypeIndex) {
hr = D3D11CreateDevice(NULL, DriverTypes[DriverTypeIndex], NULL, 0, FeatureLevels, NumFeatureLevels, D3D11_SDK_VERSION, &m_hDevice, &FeatureLevel, &m_hContext);
if (SUCCEEDED(hr)) {
break;
}
}
if (FAILED(hr)) {
return FALSE;
}
IDXGIDevice *hDxgiDevice = NULL;
hr = m_hDevice->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast
(&hDxgiDevice));
if (FAILED(hr)){
return FALSE;
}
IDXGIFactory* pFactory;
hr = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)(&pFactory));
IDXGIAdapter* hDxgiAdapter = nullptr;
hr = pFactory->EnumAdapters(0, &hDxgiAdapter);
RESET_OBJECT(hDxgiDevice);
if (FAILED(hr)) {
return FALSE;
}
int nOutput = outputIndex;
IDXGIOutput *hDxgiOutput = NULL;
hr = hDxgiAdapter->EnumOutputs(nOutput, &hDxgiOutput);
RESET_OBJECT(hDxgiAdapter);
if (FAILED(hr)) {
return FALSE;
}
hDxgiOutput->GetDesc(&m_dxgiOutDesc);
IDXGIOutput1 *hDxgiOutput1 = NULL;
hr = hDxgiOutput->QueryInterface(__uuidof(hDxgiOutput1), reinterpret_cast
(&hDxgiOutput1));
RESET_OBJECT(hDxgiOutput);
if (FAILED(hr)) {
return FALSE;
}
hr = hDxgiOutput1->DuplicateOutput(m_hDevice, &m_hDeskDupl);
RESET_OBJECT(hDxgiOutput1);
if (FAILED(hr)) {
return FALSE;
}
m_bInit = TRUE;
return TRUE;
}Note: the initialization thread must not have processed UI before calling this function.
After initialization, frames are captured with:
BOOL QueryFrame(QRect ▭, void *pImgData, INT &nImgSize){
if (!m_bInit || !AttatchToThread()) {
return FALSE;
}
DXGI_OUTDUPL_FRAME_INFO FrameInfo;
IDXGIResource *hDesktopResource = NULL;
HRESULT hr = m_hDeskDupl->AcquireNextFrame(20, &FrameInfo, &hDesktopResource);
if (FAILED(hr)) {
hDesktopResource = nullptr;
return TRUE;
}
ID3D11Texture2D *hAcquiredDesktopImage = NULL;
hr = hDesktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast
(&hAcquiredDesktopImage));
RESET_OBJECT(hDesktopResource);
if (FAILED(hr)) {
return FALSE;
}
D3D11_TEXTURE2D_DESC frameDescriptor;
hAcquiredDesktopImage->GetDesc(&frameDescriptor);
ID3D11Texture2D *hNewDesktopImage = NULL;
frameDescriptor.Usage = D3D11_USAGE_STAGING;
frameDescriptor.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
frameDescriptor.BindFlags = 0;
frameDescriptor.MiscFlags = 0;
frameDescriptor.MipLevels = 1;
frameDescriptor.ArraySize = 1;
frameDescriptor.SampleDesc.Count = 1;
hr = m_hDevice->CreateTexture2D(&frameDescriptor, NULL, &hNewDesktopImage);
if (FAILED(hr)) {
RESET_OBJECT(hAcquiredDesktopImage);
m_hDeskDupl->ReleaseFrame();
return FALSE;
}
m_hContext->CopyResource(hNewDesktopImage, hAcquiredDesktopImage);
RESET_OBJECT(hAcquiredDesktopImage);
m_hDeskDupl->ReleaseFrame();
static IDXGISurface *hStagingSurf = NULL;
if (hStagingSurf == NULL) {
hr = hNewDesktopImage->QueryInterface(__uuidof(IDXGISurface), (void **)(&hStagingSurf));
RESET_OBJECT(hNewDesktopImage);
if (FAILED(hr)) {
return FALSE;
}
}
DXGI_MAPPED_RECT mappedRect;
hr = hStagingSurf->Map(&mappedRect, DXGI_MAP_READ);
if (SUCCEEDED(hr)) {
QRect desttopRect(m_dxgiOutDesc.DesktopCoordinates.left, m_dxgiOutDesc.DesktopCoordinates.top,
m_dxgiOutDesc.DesktopCoordinates.right - m_dxgiOutDesc.DesktopCoordinates.left,
m_dxgiOutDesc.DesktopCoordinates.bottom - m_dxgiOutDesc.DesktopCoordinates.top);
copyImageByRect((char*)mappedRect.pBits, desttopRect.size(), (char*)pImgData, nImgSize, rect);
hStagingSurf->Unmap();
}
RESET_OBJECT(hStagingSurf);
return SUCCEEDED(hr);
}The article then presents a table comparing CPU%, GPU% and memory usage for GDI vs. DXGI at various frame rates, showing that DXGI consistently uses less GPU time and similar memory, confirming its higher efficiency.
Frame
GDI/DXGI (CPU%)
GDI/DXGI (GPU%)
GDI/DXGI (Memory MB)
12
3.9/0
0/0.3
39.6/39.8
20
4.0/0
0/0.3
39.5/39.5
30
4.2/0
0/0.3
39.7/39.8
100
18.1/0.6
0/0.3
48.9/39.9
In conclusion, while both methods can capture the screen, DXGI provides higher GPU efficiency and is preferable for high‑performance scenarios, whereas GDI remains compatible with older Windows versions.
The article ends with a brief note on emerging screenshot technologies such as Magnification and Window Graphics Capturer, encouraging continuous learning.
TAL Education Technology
TAL Education is a technology-driven education company committed to the mission of 'making education better through love and technology'. The TAL technology team has always been dedicated to educational technology research and innovation. This is the external platform of the TAL technology team, sharing weekly curated technical articles and recruitment information.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.