2

我正在开发一个程序,该程序将具有许多 DIB 位图(由 创建CreateDIBSection),并且必须使用 Win API 在它们上绘制大量文本。

为了在位图上绘图,Windows 需要设备上下文,由CreateCompatibleDC.

现在这里有两种方法:

  1. 我可以为每个位图创建一次 DC,将其用于绘图并在释放位图时将其删除。

  2. 或者我只能在需要绘制位图、调用绘制函数并删除 DC 时创建 DC。

更好的方法是什么?我更喜欢第一个,因为调用更少 - 这将使我的代码更小,也更快一点。

但是为每个位图保存一个长寿命的 DC 是不是太昂贵了?

编辑1:该应用程序实际上是一个 GUI 工具包库,将来可以以不同且不可预测的方式使用,因此我需要一个平衡的决策,以尽可能提高性能和最少的系统资源使用。

4

2 回答 2

4

GDI 对象是有限的,无论是每个进程还是每个会话。您正在与在同一会话中运行的所有其他进程竞争资源。考虑到这一点,您应该仅在需要时使用 GDI 资源(您问题中的选项 2)。

Mark Russinovich 的博客文章推动 Windows 的极限:用户和 GDI 对象 - 第 2 部分详细介绍了很多内容。总结要点,这里列出了窗口管理器对 GDI 资源的限制:

  • 每个进程 10.000 个 GDI 对象(默认值,可通过注册表项HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\GDIProcessHandleQuota 进行配置)。
  • 每个用户会话 65.535 个 GDI 对象。
  • GDI 对象内存限制是分页池限制(请参阅推动 Windows 的限制:分页和非分页池)。
于 2015-11-02T08:56:27.960 回答
3

这是一个测试调用需要多长时间的测试CreateCompatibleDC。我发现平均而言,拨打每个电话大约需要 10 到 15 微秒。与 相比,这相对较快BitBlt,特别是对于较大的图像。因此,持有内存DC并没有太大的优势。

case WM_PAINT:
{
    static HBITMAP hbitmap = (HBITMAP)LoadImage(0, L"path.bmp", 
        IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
    std::wostringstream oss;

    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hwnd, &ps);

    auto start = std::chrono::system_clock::now();
    auto memdc = CreateCompatibleDC(hdc);
    oss << L"CreateCompatibleDC: " 
        << (std::chrono::system_clock::now() - start).count() / 10 << "\n";

    auto oldbitmap = SelectObject(memdc, hbitmap);

    start = std::chrono::system_clock::now();
    BitBlt(hdc, 0, 0, ps.rcPaint.right, ps.rcPaint.bottom, memdc, 0, 0, SRCCOPY);
    oss << L"BitBlt: "
        << (std::chrono::system_clock::now() - start).count() / 10 << "\n";

    SelectObject(memdc, oldbitmap);
    DeleteDC(memdc);

    EndPaint(hwnd, &ps);

    OutputDebugString(oss.str().c_str());
    break;
}

在 Windows 10 上的结果:

24bit 5MB 位图的结果:

CreateCompatibleDC: 17 微秒
BitBlt: 2500 微秒

8bit 275kb 的结果:

CreateCompatibleDC: 12 微秒
BitBlt: 500 微秒

于 2016-10-24T06:50:33.520 回答