簡體   English   中英

為什么繪制到隱藏的 HDC 會給出與繪制到 GetDC(HWND) 返回的 HDC 時不同的結果

[英]Why is drawing to a hidden HDC giving different results than when drawing to the HDC returned by GetDC(HWND)

我有兩種方法,paintDoubleBuffered 和 paint。 他們都應該在屏幕上繪制這個圖像: 一張湖的地圖

該圖像由大約 12 個較小的圖像組成,每個圖像的大小為 256x256,平鋪在一起。

我的標准繪畫方法按預期工作。 這里是:

void MainWindow::paint(HWND hwnd) {

    HDC hdc{GetDC(hwnd)};
    paint(hwnd, hdc);
    ReleaseDC(hwnd, hdc);

}

void MainWindow::paint(HWND hwnd, HDC hdc) {
    constexpr INT img_width{ MDNR_Map::pannel_width };
    constexpr INT img_height{ MDNR_Map::pannel_height };

    const INT width{ GetDeviceCaps(hdc, HORZRES) };
    const INT height{ GetDeviceCaps(hdc, VERTRES) };

    const INT num_width_pannels{ (width / img_width) + 1 };
    const INT num_height_pannels{ (height / img_height) + 1 };

    Gdiplus::Graphics g(hdc);

    g.SetCompositingMode(CompositingMode::CompositingModeSourceCopy);

    g.SetInterpolationMode(InterpolationMode::InterpolationModeNearestNeighbor);

    for (INT y = 0; y < num_height_pannels; y++) {
        for (INT x = 0; x < num_width_pannels; x++) {

            Location_t get_loaction(x + map_location.x, y + map_location.y, map_location.layer);
            const IMG_t v{ mdnr_map.get(get_loaction) };

            const Point drawPoint((INT)(img_width * x), (INT)(img_height * y));

            Status stat{ g.DrawImage(v, drawPoint) };

            if (stat != Status::Ok)
            {
                throw std::runtime_error(":(");
            }


        }
    }
}

該繪制方法的問題是 mdnr_map.get 是一個 io 綁定調用,可能需要幾微秒。 因為我需要調用它大約 12 次,所以它會導致閃爍。

為了解決這個問題,我嘗試寫了一個雙緩沖的paint方法,如下:

void MainWindow::paintDoubleBuffered(HWND hwnd) {

    // Get DC for window
    HDC hdc{ GetDC(hwnd) };

    const INT win_width{ GetDeviceCaps(hdc, HORZRES) };
    const INT win_height{ GetDeviceCaps(hdc, VERTRES) };

    // Create an off-screen DC for double-buffering
    HDC hdcMem{ CreateCompatibleDC(hdc) };

    HBITMAP hbmMem{ CreateCompatibleBitmap(hdc, win_width, win_height) };

    HANDLE hOld{ SelectObject(hdcMem, hbmMem) };

    // Draw into hdcMem here
    paint(hwnd, hdcMem);

    // Transfer the off-screen DC to the screen
    BitBlt(hdc, 0, 0, win_width, win_height, hdcMem, 0, 0, SRCCOPY);

    // Free-up the off-screen DC
    SelectObject(hdcMem, hOld);

    DeleteObject(hbmMem);
    DeleteDC(hdcMem);
}

然而,這是行不通的,而是產生了這種令人厭惡的圖像: 一張扭曲的湖泊地圖

稍微戳了一下,我發現如果我通過將圖像大小乘以 1.5 來改變我的雙緩沖繪畫方法,圖像不再那么亂碼,但現在放大了 1.5 倍

void MainWindow::paintDoubleBuffered(HWND hwnd) {

    // Get DC for window
    HDC hdc{ GetDC(hwnd) };

    const INT win_width{ GetDeviceCaps(hdc, HORZRES) };
    const INT win_height{ GetDeviceCaps(hdc, VERTRES) };

    // Create an off-screen DC for double-buffering
    HDC hdcMem{ CreateCompatibleDC(hdc) };

    HBITMAP hbmMem{ CreateCompatibleBitmap(hdc, win_width, win_height) };

    HANDLE hOld{ SelectObject(hdcMem, hbmMem) };

    // Draw into hdcMem here
    
    constexpr INT img_width{ MDNR_Map::pannel_width + 128 }; // MDNR_Map::pannel_width is 256
    constexpr INT img_height{ MDNR_Map::pannel_height + 128}; // MDNR_Map::pannel_height is 256

    const INT num_width_pannels{ (win_width / img_width) + 1 };
    const INT num_height_pannels{ (win_height / img_height) + 1 };

    Gdiplus::Graphics g(hdcMem);

    g.SetCompositingMode(CompositingMode::CompositingModeSourceCopy);

    g.SetInterpolationMode(InterpolationMode::InterpolationModeNearestNeighbor);

    for (INT y = 0; y < num_height_pannels; y++) {
        for (INT x = 0; x < num_width_pannels; x++) {

            Location_t get_loaction(x + map_location.x, y + map_location.y, map_location.layer);
            Gdiplus::Bitmap* pannel{ mdnr_map.get(get_loaction) };

            const Point drawPoint((INT)(img_width * x), (INT)(img_height * y));

            Status stat{ g.DrawImage(pannel, drawPoint) };
            if (stat != Status::Ok)
            {
                throw std::runtime_error(":(");
            }

        }
    }

    // Transfer the off-screen DC to the screen
    BitBlt(hdc, 0, 0, win_width, win_height, hdcMem, 0, 0, SRCCOPY);

    // Free-up the off-screen DC
    SelectObject(hdcMem, hOld);

    DeleteObject(hbmMem);
    DeleteDC(hdcMem);
}

放大的地圖圖像

我的問題是,為什么繪制到 CreateCompatibleBitmap 返回的 HDC 會產生與繪制到 GetDC 返回的 HDC 不同的結果?

我已經嘗試過: BltBlt 的所有光柵操作代碼。 我檢查過臨時 HDC 的大小與 window 相同。我嘗試替換代碼片段

const INT win_width{ GetDeviceCaps(hdc, HORZRES) };
const INT win_height{ GetDeviceCaps(hdc, VERTRES) };

RECT rect;
GetWindowRect(hwnd, &rect);

const INT win_width{ rect.right - rect.left };
const INT win_height{ rect.bottom - rect.top };

在繪圖之前,我還調用了 SetProcessDPIAware()。

根據@Paul Sanders 的反饋,我重寫了我的 paintDoubleBuffered 方法如下,注意,我在 object 構造函數中調用了 BufferedPaintInit :

void MainWindow::paintDoubleBuffered(HWND hwnd) {

    PAINTSTRUCT ps;
    HDC hdc{ BeginPaint(hwnd, &ps)};

    RECT sz;
    GetWindowRect(hwnd, &sz);

    BP_PAINTPARAMS paintParams = { 0 };

    paintParams.cbSize = sizeof(paintParams);
    paintParams.dwFlags = BPPF_ERASE;
    paintParams.pBlendFunction = NULL;
    paintParams.prcExclude = NULL;

    HDC hdcBuffer;

    HPAINTBUFFER hBufferedPaint = BeginBufferedPaint(hdc, &sz, BPBF_COMPATIBLEBITMAP, &paintParams, &hdcBuffer);

    if (hBufferedPaint && this->bufferedInitResult == Ok) {
        // Application specific painting code
        paint(hwnd, hdcBuffer);
        EndBufferedPaint(hBufferedPaint, TRUE);
    }
    else{
        paint(hwnd, hdc);
    }

    ReleaseDC(hwnd, hdc);
}

不幸的是,這不起作用,結果屏幕如下所示: 一張亂七八糟的地圖,頂部和左側有一個白色邊界

問題最終不是雙緩沖的方法,而是在我的繪畫方法中對Graphics::DrawImage(Image*, Gdiplus::Point)的調用。 更改為DrawImage(Image* image, INT x, INT y, INT width, INT height)解決了縮放問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM