[英]Why is drawing to a hidden HDC giving different results than when drawing to the HDC returned by GetDC(HWND)
I have two methods, paintDoubleBuffered and paint.我有两种方法,paintDoubleBuffered 和 paint。 They are both supposed to draw this image on the screen:
他们都应该在屏幕上绘制这个图像:
The image is made up of approximately 12 smaller images each sized 256x256 tiled together.该图像由大约 12 个较小的图像组成,每个图像的大小为 256x256,平铺在一起。
My standard painting method works as expected.我的标准绘画方法按预期工作。 Here it is:
这里是:
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(":(");
}
}
}
}
The issue with that paint method is that mdnr_map.get is an io bound call and may take several micro seconds.该绘制方法的问题是 mdnr_map.get 是一个 io 绑定调用,可能需要几微秒。 Because I need to call it about 12 times, it can lead to flickering.
因为我需要调用它大约 12 次,所以它会导致闪烁。
To solve this, I attempted to write a double-buffered paint method, which is as follows:为了解决这个问题,我尝试写了一个双缓冲的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);
}
However, this does not work and instead produces this abombination of an image:然而,这是行不通的,而是产生了这种令人厌恶的图像:
With a little poking and proding, I was able to discover that if I changed my double buffered paint method by multiplying the image size by 1.5, the image was no longer so garbled, but was now zoomed in by a factor of 1.5稍微戳了一下,我发现如果我通过将图像大小乘以 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);
}
My question is why does drawing to the HDC returned by CreateCompatibleBitmap produce a different result than drawing to the HDC returned by GetDC?我的问题是,为什么绘制到 CreateCompatibleBitmap 返回的 HDC 会产生与绘制到 GetDC 返回的 HDC 不同的结果?
I have tried: All raster-operation codes for BltBlt.我已经尝试过: BltBlt 的所有光栅操作代码。 I have checked that the temporary HDC is the same size as the window. I have tried replacing the code snippet
我检查过临时 HDC 的大小与 window 相同。我尝试替换代码片段
const INT win_width{ GetDeviceCaps(hdc, HORZRES) };
const INT win_height{ GetDeviceCaps(hdc, VERTRES) };
with和
RECT rect;
GetWindowRect(hwnd, &rect);
const INT win_width{ rect.right - rect.left };
const INT win_height{ rect.bottom - rect.top };
I have also called SetProcessDPIAware() before drawing.在绘图之前,我还调用了 SetProcessDPIAware()。
Upon feedback from @Paul Sanders, I rewrote my paintDoubleBuffered method as follows, NOTE, I have called BufferedPaintInit in the object constructor:根据@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);
}
Unfortunately, this did not work and the resultant screen looks like this:不幸的是,这不起作用,结果屏幕如下所示:
The issue ended up being not in the approach to double buffering, but in the call to Graphics::DrawImage(Image*, Gdiplus::Point)
in my paint method.问题最终不是双缓冲的方法,而是在我的绘画方法中对
Graphics::DrawImage(Image*, Gdiplus::Point)
的调用。 Changing to DrawImage(Image* image, INT x, INT y, INT width, INT height)
fixed the scaling issue.更改为
DrawImage(Image* image, INT x, INT y, INT width, INT height)
解决了缩放问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.