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

为何BitBlt()复制同一位图自身像素时出现放大?

问题描述

我正在开发一个可复制桌面并对像素进行操作的窗口,通过BitBlt从桌面句柄向窗口句柄复制图像,此步骤可正常生成与桌面完全一致的窗口。但当再次调用BitBlt将窗口内某段像素复制到窗口另一区域时,像素出现放大效果。请问该现象的原因是什么?如何修复?

相关代码
#include <windows.h>

int myWidth, myHeight;

LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_CREATE:
    {
        HDC DhWnd = GetDC(HWND_DESKTOP);
        HDC MhWnd = GetDC(hwnd);
        BitBlt(MhWnd, 0, 0, myWidth, myHeight, DhWnd, 0, 0, SRCCOPY);
        ShowWindow(hwnd, SW_SHOW);
        BitBlt(MhWnd, 300, 0, 100, 500, MhWnd, 300, 0, SRCCOPY);

        /*^^^^ 这段代码明明是把相同坐标的位图复制到自身,却出现了像素放大的效果 ^^^^*/


        ReleaseDC(hwnd, DhWnd);
        ReleaseDC(hwnd, MhWnd);
        return 0;
    }
    case WM_PAINT:
        ValidateRect(hwnd, NULL);
        return 0;

    case WM_CLOSE:
    case WM_DESTROY:
        DestroyWindow(hwnd);
        PostQuitMessage(0);
        return 0;
    default:
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }

}



int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
    RECT Drc;
    MSG msg;
    HWND DhWnd = GetDesktopWindow();
    HWND MyhWnd;
    GetWindowRect(DhWnd, &Drc);
    myWidth = Drc.right - Drc.left;
    myHeight = Drc.bottom - Drc.top;


    WNDCLASS wc;
    wc.style = 0;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = NULL;
    wc.hCursor = LoadCursor(NULL,IDC_ARROW);
    wc.hbrBackground = NULL;
    wc.lpszMenuName = NULL;
    wc.lpszClassName = "MyWindow";


    if (!RegisterClass(&wc))
    {
        MessageBox(NULL, "Window Registration Failed!", "Error!",
            MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    MyhWnd = CreateWindow("MyWindow", NULL, WS_POPUP, 0, 0, myWidth, myHeight, NULL, NULL, hInstance, NULL);
    if (MyhWnd == NULL)
    {
        MessageBox(NULL, "Window Creation Failed!", "Error!",
            MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }


    while (GetMessage(&msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;

}
原因分析
  1. 高DPI缩放不匹配:如果系统开启了高DPI缩放(比如125%、150%),窗口的逻辑坐标和物理像素坐标会错位。你通过GetWindowRect获取的桌面尺寸是物理像素,但创建窗口时使用的是逻辑坐标,导致窗口实际物理尺寸和桌面不一致,BitBlt操作时自然会出现拉伸放大。
  2. 同一DC的重叠复制冲突:当源DC和目标DC为同一个时,直接调用BitBlt若源区域与目标区域有重叠(高DPI下实际坐标已错位,看似相同的坐标其实对应不同物理区域),会导致像素被重复覆盖复制,产生类似放大的异常效果。
修复方法

1. 适配高DPI环境

在WinMain函数开头添加DPI感知声明,让系统按物理像素处理窗口:

SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);

2. 避免同一DC的直接复制

需要在窗口内部复制像素时,先将源区域内容复制到内存DC中,再从内存DC复制到目标区域,绕过同一DC的重叠冲突:

case WM_CREATE:
{
    HDC DhWnd = GetDC(HWND_DESKTOP);
    HDC MhWnd = GetDC(hwnd);
    // 复制桌面到窗口
    BitBlt(MhWnd, 0, 0, myWidth, myHeight, DhWnd, 0, 0, SRCCOPY);
    
    // 创建内存DC和兼容位图
    HDC memDC = CreateCompatibleDC(MhWnd);
    HBITMAP memBmp = CreateCompatibleBitmap(MhWnd, 100, 500);
    HBITMAP oldBmp = (HBITMAP)SelectObject(memDC, memBmp);
    
    // 先把窗口目标区域内容复制到内存DC
    BitBlt(memDC, 0, 0, 100, 500, MhWnd, 300, 0, SRCCOPY);
    // 再从内存DC复制回窗口目标区域
    BitBlt(MhWnd, 300, 0, 100, 500, memDC, 0, 0, SRCCOPY);
    
    // 清理资源
    SelectObject(memDC, oldBmp);
    DeleteObject(memBmp);
    DeleteDC(memDC);
    
    ShowWindow(hwnd, SW_SHOW);
    
    ReleaseDC(hwnd, DhWnd);
    ReleaseDC(hwnd, MhWnd);
    return 0;
}

3. 修正WM_PAINT处理逻辑

当前WM_PAINT直接调用ValidateRect会导致窗口重绘时内容丢失,应该在WM_PAINT中重新执行绘制逻辑:

case WM_PAINT:
{
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hwnd, &ps);
    HDC DhWnd = GetDC(HWND_DESKTOP);
    // 重新复制桌面到窗口
    BitBlt(hdc, 0, 0, myWidth, myHeight, DhWnd, 0, 0, SRCCOPY);
    
    // 再次执行内部像素复制(使用内存DC方式)
    HDC memDC = CreateCompatibleDC(hdc);
    HBITMAP memBmp = CreateCompatibleBitmap(hdc, 100, 500);
    HBITMAP oldBmp = (HBITMAP)SelectObject(memDC, memBmp);
    BitBlt(memDC, 0, 0, 100, 500, hdc, 300, 0, SRCCOPY);
    BitBlt(hdc, 300, 0, 100, 500, memDC, 0, 0, SRCCOPY);
    
    SelectObject(memDC, oldBmp);
    DeleteObject(memBmp);
    DeleteDC(memDC);
    
    ReleaseDC(hwnd, DhWnd);
    EndPaint(hwnd, &ps);
    return 0;
}

内容的提问来源于stack exchange,提问作者bugger

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 08:10:28