如何持续重复使用 HBITMAP 和 HDC?
How can I keep reusing HBITMAP and HDC continually?
出于性能原因,我正在尝试通过重用HBITMAP和HDC来使HBIPMAP工作。
这是一个小型测试项目,我想做一些关于基于 CPU 的光栅化的信息。 对于窗口,我使用 SDL2。
如果我们注释掉,下面的代码有效:
DeleteDC(hdcMem);
hdcMem = CreateCompatibleDC(device);
我在2018+年找不到任何例子。
mBackBuffer 只是一个 Vector(DWORD(
void Device::createDeviceFromHWND(const HWND& hwnd, const int& width, const int& height)
{
// This is hacked code for an example.
auto device = GetDC(hwnd);
DWORD colorSize = 4; // ARGB;
// Create page section
// https://learn.microsoft.com/en-us/windows/desktop/memory/creating-named-shared-memory
HANDLE hMapFile;
LPCTSTR pBuf;
// https://learn.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createfilemappinga
hMapFile = CreateFileMappingA
(
INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
width * height * colorSize,
NULL
);
if (hMapFile == NULL)
{
return;
}
DWORD* buffer = (DWORD*)MapViewOfFile(
hMapFile,
FILE_MAP_ALL_ACCESS,
0,
0,
width * height * colorSize
);
BITMAPINFOHEADER header;
memset(&header, 0, sizeof(BITMAPINFOHEADER));
// https://msdn.microsoft.com/en-us/02f8ed65-8fed-4dda-9b94-7343a0cfa8c1
header.biSize = sizeof(BITMAPINFOHEADER);
header.biWidth = width;
header.biHeight = height;
header.biPlanes = 1;
header.biBitCount = 32;
header.biCompression = BI_RGB;
header.biSizeImage = width * height * sizeof(BYTE);
header.biXPelsPerMeter = 0;
header.biYPelsPerMeter = 0;
header.biClrUsed = 0;
header.biClrImportant = 0;
tagBITMAPINFO bitmap;
memset(&bitmap, 0, sizeof(tagBITMAPINFO));
// https://learn.microsoft.com/en-us/windows/desktop/api/wingdi/ns-wingdi-tagbitmapinfo
tagRGBQUAD RGBQUAD;
memset(&RGBQUAD, 0, sizeof(tagRGBQUAD));
bitmap.bmiHeader = header;
bitmap.bmiColors[0] = RGBQUAD;
LPVOID p;
// https://learn.microsoft.com/en-us/windows/desktop/api/wingdi/nf-wingdi-createdibsection
auto hBitMap = CreateDIBSection
(
device,
&bitmap,
DIB_RGB_COLORS,
&p,
hMapFile,
0
);
for (DWORD i = 0; i < width * height; ++i)
{
buffer[i] = 0xFF0000;
}
HDC hdcMem = CreateCompatibleDC(device);
auto oldHBITMAP = (HBITMAP)SelectObject(hdcMem, hBitMap);
BitBlt(
device,
0,
0,
width,
height,
hdcMem,
0,
0,
SRCCOPY
);
DeleteDC(hdcMem);
for (DWORD i = 0; i < width * height; ++i)
{
buffer[i] = 0;
}
hdcMem = CreateCompatibleDC(device);
BitBlt(
device,
400,
300,
width,
height,
hdcMem,
0,
0,
SRCCOPY
);
}
输出是红色屏幕,但您应该在右上角看到黑色部分。
这里有几个问题,有些与位图无关。
不再需要GetDC
句柄时,应ReleaseDC
清理该句柄。
CreateFileMapping
的手柄应该用CloseHandle
清理,MapViewOfFile
应该用UnmapViewOfFile
清理。
HBITMAP
手柄必须通过DeleteObject
清理
建议在SelectObject
后通过调用SelectOject(hMemDC, oldHBitmap)
进行清理
如果不还原旧位图,并尝试删除hMemDC
,Windows 无法满足请求,因为在设备上下文中选择了另一个位图。Windows 将尝试修复此错误,但如果代码过于复杂,则可能会失败。
请注意,Windows 为您提供了 10,000 个 GDI 句柄的限制。如果您没有正确管理这些句柄,应用程序将很快崩溃。有关这些函数,请参阅 WinAPI 文档。如有疑问,请使用任务管理器监视程序的"GDI 句柄"。
解决这些问题后,代码应按预期工作,请参阅下面的示例。
这当然只是为了演示。在实际应用程序中,您可能希望将HBITMAP
保存在堆中,而不是堆栈中,以及一些其他值。您希望尽量减少重复创建这些句柄。
正如其他答案和评论中所指出的,绘画应该针对WM_PAINT
进行,您从BeginPaint
中获得HDC
(并用EndPaint
进行清理(。因此,您应该避免GetDC
/ReleaseDC
void Device::createDeviceFromHWND(const HWND& hwnd, const int& width, const int& height)
{
auto hdc = GetDC(hwnd);
auto hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
width * height * sizeof(DWORD), NULL);
auto buffer = (DWORD*)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0,
width * height * sizeof(DWORD));
BITMAPINFOHEADER biheader = { sizeof(biheader), width, height, 1, 32, BI_RGB };
LPVOID bits;
auto hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&biheader, DIB_RGB_COLORS,
&bits, hMapFile, 0);
for(int i = 0; i < width * height; ++i)
buffer[i] = 0xFF0000;
auto memdc = CreateCompatibleDC(hdc);
auto oldhbitmap = SelectObject(memdc, hbitmap);
BitBlt(hdc, 0, 0, width, height, memdc, 0, 0, SRCCOPY);
for(int i = 0; i < width * height; ++i)
buffer[i] = 0;
BitBlt(hdc, 0, 0, 100, 100, memdc, 0, 0, SRCCOPY);
SelectObject(memdc, oldhbitmap); //<- ***EDIT***
//oldhbitmap is selected in to memdc, now we can destroy hbitmap and memdc
DeleteObject(hbitmap);
DeleteDC(memdc);
ReleaseDC(hwnd, hdc);
UnmapViewOfFile(buffer);
CloseHandle(hMapFile);
}
旁注,使用引用运算符&
常量值不会获得任何好处。只需更改函数原型,如下所示:
void createDeviceFromHWND(const HWND hwnd, const int width, const int height);
此外,这可以在没有CreateFileMapping
的情况下完成,并使用如下所示buffer
。 只要hbitmap
有效,buffer
就有效。
void test(const HWND hwnd, const int w, const int h)
{
auto hdc = GetDC(hwnd);
//use the negative value of height, so bitmap bits are not upside-down
BITMAPINFOHEADER bi = { sizeof(bi), w, -h, 1, 32, BI_RGB };
DWORD* buffer;
auto hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bi, DIB_RGB_COLORS,
(void**)&buffer, NULL, 0);
auto memdc = CreateCompatibleDC(hdc);
auto oldbmp = SelectObject(memdc, hbitmap);
for(int i = 0; i < w * h; ++i) buffer[i] = 0xFF0000;
BitBlt(hdc, 0, 0, w, h, memdc, 0, 0, SRCCOPY);
//draw black square on top-left
for(int y = 0; y < 100; y++)
for(int x = 0; x < 100; x++)
buffer[y * w + x] = 0;
BitBlt(hdc, 0, 0, 100, 100, memdc, 0, 0, SRCCOPY);
//cleanup:
SelectObject(memdc, oldbmp);
DeleteObject(hbitmap); //<- buffer is not valid after hbitmap is destroyed
DeleteDC(memdc);
ReleaseDC(hwnd, hdc);
}
这种方法是错误的。当目标窗口WM_PAINT时,您的所有工作都将被撤消。
始终使用 WM_PAINT 和 BeginPaint 按照您想要的方式绘制窗口。
您在此处编辑位图时:
for (unsigned int i = 0; i < width * height; ++i)
{
mBackBuffer[i] = 0;
}
只需填充数组,位图就是从中创建的,带有 null。
- 在Windows上用C++裁剪HBITMAP
- 将 win32 hbitmap 转换为 winrt softwarebitmap
- 使用 GDI+ 旋转位图,然后转换为 HDC
- 如何持续重复使用 HBITMAP 和 HDC?
- 将位数组转换为 HBITMAP 后 bmBits 的 NULL 指针
- HBITMAP 能否包含 alpha 通道信息?
- 如何从依赖于设备的 HBITMAP 构造 GDI+ 位图对象
- 无法从 glReadpixel 创建 HBITMAP
- 由PostMessage发送的来自c++的C ++的免费HBITMAP
- HBITMAP 上的 DrawText 没有 "visible" DC?
- MFC如何将带有透明属性的PNG转换为HBITMAP
- HBITMAP HBM = loadImage函数返回null
- 如何将 HDC 位图快速复制到三维阵列?
- 如何从HBITMAP获取RGBQUAD
- 有没有办法在更改与 HDC 关联的位图大小后更新图形对象?
- 从屏幕 HDC 保存像素
- 如何在WinGDI / C++中复制HDC内容
- 调整HBITMAP的大小,同时保持透明背景
- win32程序在GCC中编译,但不是G ++ HDC错误
- winapi: from HDC to an HBITMAP