You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 03:54:11