Windows平台C语言截图如何限制最大分辨率为1080p
限制Windows截图最大分辨率为1080p的最优实现方案
我使用以下微软官方文档的Windows平台C语言截图代码,传入GetDesktopWindow()可捕获对应屏幕分辨率的截图(如4K屏幕则生成4K截图),现需将截图最大分辨率限制为1080p,寻求最优实现方案。
原代码:
int captureImage(HWND hWnd, char * filename){ HDC hdcScreen; HDC hdcWindow; HDC hdcMemDC = NULL; HBITMAP hbmScreen = NULL; BITMAP bmpScreen; DWORD dwBytesWritten = 0; DWORD dwSizeofDIB = 0; HANDLE hFile = NULL; char* lpbitmap = NULL; HANDLE hDIB = NULL; DWORD dwBmpSize = 0; // retrieve handle to display device content hdcScreen = GetDC(NULL); hdcWindow = GetDC(hWnd); // Create a compatible DC (domain controller) hdcMemDC = CreateCompatibleDC(hdcWindow); if (!hdcMemDC){ printf("Error creating hdcMemDC\n"); return 1; } // Make process DPI aware to avoid wrongly sized screenshots when screen resolution changes SetProcessDPIAware(); // get area for size calculations RECT rcClient; GetClientRect(hWnd, &rcClient); // use this stretch mode SetStretchBltMode(hdcWindow, HALFTONE); // the source DC is the entire screen, and the destination in the current window (for now) StretchBlt(hdcWindow, 0, 0, rcClient.right, rcClient.bottom, hdcScreen, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), SRCCOPY); // create a compatible bitmap from the Window DC hbmScreen = CreateCompatibleBitmap(hdcWindow, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top); if (!hbmScreen){ printf("Error creating hmbScreen\n"); return 1; } // Select the compatible bitmap into the memory DC SelectObject(hdcMemDC, hbmScreen); // transfer bits into the memory DC BitBlt(hdcMemDC, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, hdcWindow, 0 , 0, SRCCOPY); // get the bitmap from the hbitmap GetObject(hbmScreen, sizeof(BITMAP), &bmpScreen); // bitmap file header and info header BITMAPFILEHEADER bmfHeader; BITMAPINFOHEADER bi; // set all values for info header 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; dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount+31) / 32) * 4 * bmpScreen.bmHeight; hDIB = GlobalAlloc(GHND, dwBmpSize); lpbitmap = (char*)GlobalLock(hDIB); // get the bits from the bitmap and copy them into the buffer GetDIBits(hdcWindow, hbmScreen, 0, (UINT)bmpScreen.bmHeight, lpbitmap, (BITMAPINFO*)&bi, DIB_RGB_COLORS); // create a file to save the capture into hFile = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); // get the total size of the file dwSizeofDIB = dwBmpSize + (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER); // offset to where our bitmap actually starts bmfHeader.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER); // size of the file bmfHeader.bfSize = dwSizeofDIB; // always set bfType to BM for bitmaps bmfHeader.bfType = 0x4D42; // that's BM // write data to the file 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 stuff GlobalUnlock(hDIB); GlobalFree(hDIB); // close file handle CloseHandle(hFile); // Clean everything up DeleteObject(hbmScreen); DeleteObject(hdcMemDC); ReleaseDC(NULL, hdcScreen); ReleaseDC(hWnd, hdcWindow); return 0; // for normal exit }
最优实现方案(修改后的代码)
#include <windows.h> #include <stdio.h> #define MAX_WIDTH 1920 // 1080p最大宽度 #define MAX_HEIGHT 1080 // 1080p最大高度 int captureImage(HWND hWnd, char * filename){ HDC hdcScreen = NULL; HDC hdcMemDC = NULL; HBITMAP hbmScreen = NULL; BITMAP bmpScreen; DWORD dwBytesWritten = 0; DWORD dwSizeofDIB = 0; HANDLE hFile = NULL; char* lpbitmap = NULL; HANDLE hDIB = NULL; DWORD dwBmpSize = 0; // 先设置DPI感知,确保获取的屏幕尺寸准确 SetProcessDPIAware(); // 获取屏幕DC hdcScreen = GetDC(NULL); if (!hdcScreen) { printf("Error getting screen DC\n"); return 1; } // 获取原始屏幕尺寸 int srcWidth = GetSystemMetrics(SM_CXSCREEN); int srcHeight = GetSystemMetrics(SM_CYSCREEN); // 计算缩放比例,保持宽高比且不超过1080p float scaleX = (float)MAX_WIDTH / srcWidth; float scaleY = (float)MAX_HEIGHT / srcHeight; float scale = (scaleX < scaleY) ? scaleX : scaleY; int destWidth = (int)(srcWidth * scale); int destHeight = (int)(srcHeight * scale); // 如果原始尺寸小于等于1080p,直接用原始尺寸 if (srcWidth <= MAX_WIDTH && srcHeight <= MAX_HEIGHT) { destWidth = srcWidth; destHeight = srcHeight; } // 创建内存DC hdcMemDC = CreateCompatibleDC(hdcScreen); if (!hdcMemDC) { printf("Error creating memory DC\n"); ReleaseDC(NULL, hdcScreen); return 1; } // 创建对应缩放尺寸的兼容位图 hbmScreen = CreateCompatibleBitmap(hdcScreen, destWidth, destHeight); if (!hbmScreen) { printf("Error creating compatible bitmap\n"); DeleteDC(hdcMemDC); ReleaseDC(NULL, hdcScreen); return 1; } // 将位图选入内存DC HBITMAP hbmOld = (HBITMAP)SelectObject(hdcMemDC, hbmScreen); // 设置高质量缩放模式 SetStretchBltMode(hdcMemDC, HALFTONE); SetBrushOrgEx(hdcMemDC, 0, 0, NULL); // HALFTONE模式需要设置画刷原点 // 直接将屏幕DC的内容缩放到内存DC StretchBlt(hdcMemDC, 0, 0, destWidth, destHeight, hdcScreen, 0, 0, srcWidth, srcHeight, SRCCOPY); // 获取位图信息 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; // 计算位图数据大小 dwBmpSize = ((bmpScreen.bmWidth * bi.biBitCount + 31) / 32) * 4 * bmpScreen.bmHeight; // 分配内存存储位图数据 hDIB = GlobalAlloc(GHND, dwBmpSize); if (!hDIB) { printf("Error allocating memory for DIB\n"); SelectObject(hdcMemDC, hbmOld); DeleteObject(hbmScreen); DeleteDC(hdcMemDC); ReleaseDC(NULL, hdcScreen); return 1; } lpbitmap = (char*)GlobalLock(hDIB); // 获取位图数据 if (!GetDIBits(hdcScreen, hbmScreen, 0, (UINT)bmpScreen.bmHeight, lpbitmap, (BITMAPINFO*)&bi, DIB_RGB_COLORS)) { printf("Error getting DIB bits\n"); GlobalUnlock(hDIB); GlobalFree(hDIB); SelectObject(hdcMemDC, hbmOld); DeleteObject(hbmScreen); DeleteDC(hdcMemDC); ReleaseDC(NULL, hdcScreen); return 1; } // 创建输出文件 hFile = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { printf("Error creating output file\n"); GlobalUnlock(hDIB); GlobalFree(hDIB); SelectObject(hdcMemDC, hbmOld); DeleteObject(hbmScreen); DeleteDC(hdcMemDC); ReleaseDC(NULL, hdcScreen); return 1; } // 写入文件头 dwSizeofDIB = dwBmpSize + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); bmfHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); bmfHeader.bfSize = dwSizeofDIB; bmfHeader.bfType = 0x4D42; // BM标识 WriteFile(hFile, &bmfHeader, sizeof(BITMAPFILEHEADER), &dwBytesWritten, NULL); WriteFile(hFile, &bi, sizeof(BITMAPINFOHEADER), &dwBytesWritten, NULL); WriteFile(hFile, lpbitmap, dwBmpSize, &dwBytesWritten, NULL); // 资源清理 CloseHandle(hFile); GlobalUnlock(hDIB); GlobalFree(hDIB); SelectObject(hdcMemDC, hbmOld); DeleteObject(hbmScreen); DeleteDC(hdcMemDC); ReleaseDC(NULL, hdcScreen); return 0; }
关键修改说明
- 调整DPI感知时机:将
SetProcessDPIAware()移到最前面,确保获取的屏幕分辨率是真实物理分辨率,避免DPI缩放导致的尺寸偏差。 - 新增尺寸缩放逻辑:计算原始屏幕尺寸与1080p的缩放比例,保持宽高比的同时将最大尺寸限制在1920x1080以内,若原始尺寸更小则直接使用原始尺寸。
- 简化DC操作流程:移除原代码中多余的窗口DC操作,直接从屏幕DC缩放到内存DC,减少中间步骤,提升效率。
- 优化缩放质量:使用
HALFTONE拉伸模式并设置画刷原点,保证缩放后的图像质量更高,避免出现锯齿或色块。 - 完善错误处理:在每个资源分配步骤后添加错误检查,确保资源能及时释放,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Ribs30
相关产品推荐
相关产品推荐

