CreateCompatibleDC() 是不是需要在一个显示器上使用窗口?

Posted

技术标签:

【中文标题】CreateCompatibleDC() 是不是需要在一个显示器上使用窗口?【英文标题】:Is CreateCompatibleDC() necessary working with windows on one display?CreateCompatibleDC() 是否需要在一个显示器上使用窗口? 【发布时间】:2019-10-12 08:53:48 【问题描述】:

此示例代码手动读取位图文件,使用CreateDIBSection() 让 GDI 为其分配内存,并创建一个 hbitmap 句柄。然后它使用 MemoryDC 将位图绘制到窗口 DC:

ftp://ftp.oreilly.com/examples/9781572319950/cd_contents/Chap15/DibSect/DibSect.c

hdc = BeginPaint (hwnd, &ps) ;
...
hdcMem = CreateCompatibleDC (hdc) ;

为什么我们不能将GetDC()NULLhwndDesktop 一起使用?为什么我们不能缓存设备上下文而不是重复创建它?

如果机器只有一个显示设备,而我们只在窗口上绘图,为什么我们需要不断协调位图和设备上下文?一旦像素数据被复制到 GDI 提供的缓冲区,当 HBITMAP 加载到 DC 并绘制时,GDI 是否会更新它?如果用户也想借鉴,是否有必要同步访问? (先打电话给GDIFlush()?)

当大多数对象属性都是不透明和抽象的时,很难弄清楚这一点。我已经阅读了几乎所有相关的 MSDN、很多 Petzold 的书和一些文章:

Display Device Contexts

CreateCompatibleDC()

CreateDIBSection()

Memory Device Contexts

Guide to Win32 Memory DC

Guide to WIN32 Paint for Intermediates

Programming Windows®, Fifth Edition

编辑:

我认为我的问题归结为:

设备上下文是一种显示类型,还是一种能够显示的图形数据的实例。一台计算机通常只有几个显示器,但它可能有数百个东西要显示在上面。

【问题讨论】:

您可能是对的,但使用适用于 X 显示器的代码比使用适用于 1 个显示器的代码要好。 如果您不想使用源 DC,可以使用 SetDIBitsToDevice() 我不确定这是否是您正在寻找的答案,但它是这样的:设备上下文是一种广义的渲染抽象。它充当渲染代码和输出设备之间的代理。输出可以到显示器、打印机或绘图仪、EMF 文件或任何其他支持的设备。它允许您使用相同的渲染代码,而不管目的地是什么;低级细节会根据输出设备为您处理,包括剪辑、缩放和视口转换。就实现细节而言:构建 DC 是一项相当便宜的操作。 请记住,官方文档 (Device Contexts) 主要讨论的是它的实现,而不是它的目的。 【参考方案1】:

GetDC(NULL)是屏幕HDC,屏幕是共享资源,所以你应该只对这个HDC进行读/查询操作。由于DWM,在 Vista 及更高版本上写入此 HDC 不是一个好主意。

由于一个 HDC 只能包含一个位图、一个画笔和一支笔,Windows/应用程序显然需要图形引擎提供的多个 HDC。

你可以指望CreateCompatibleDC 是相对便宜的操作,我相信 Windows 有一个它可以分发的 DC 缓存。如果您正在创建游戏/动画类型的应用程序,您可能希望自己缓存其中一些图形对象,但普通应用程序不应该这样做。

您通常不会调用GDIFlush,除非您在多个线程之间共享 GDI 对象。如果您想混合原始像素字节访问和 GDI,可以使用 SetDIBits

我并没有真正理解一次屏幕的说法,Windows 自 Windows 98 起就支持多台显示器,您无法阻止用户连接另一台显示器。

【讨论】:

所以我认为如果您将一个窗口从一个显示器拖到另一个显示器,则需要一个新的设备上下文。我猜我们必须在 WM_PAINT 中创建一个兼容的 DC,因为这可能随时发生。即使有多个显示器,今天它们不都是 24 位/相同类型的吗? 这些天,Windows 几乎锁定在 32bpp。在过去,您需要处理 16 和 256 色的调色板更改消息等。【参考方案2】:

我认为您的问题是您对 Microsoft 的事物名称、Microsoft 的名称“设备上下文”以及诸如“CreateCompatibleDC”之类的调用名称感到困惑。

“设备上下文”是一个坏名字。 Win32 文档将告诉您设备上下文是一种数据结构,用于存储用于呈现图形命令的特定设备的状态。这只是部分正确。查看存在的不同类型的 DC:(1) 屏幕设备上下文,(2) 打印机设备上下文,(3) 内存中位图使用的设备上下文,以及 (4) 元文件设备上下文。其中只有 (1) 或 (2) 确实在做文档声称他们正在做的事情。在其他情况下,设备上下文用作绘图调用的目标,但不作为某些物理设备状态的容器。 (这在元文件 DC 的情况下非常明显:元文件是一个旧的 Win32 东西,基本上只是缓存进入它们的 GDI 调用,以便稍后重放,这是一种粗略的矢量格式。)

在假设的 Win32 面向对象编程版本中,设备上下文可能是某个类的实例,该类实现了一个公开图形绘制调用的接口。这种类的一个更好的名称应该是“图形”之类的东西,实际上在 GDI+ 中,这就是类似构造的实际名称。当我们“创建”时——通过 CreateDC、CreateCompatibleDC 等——我们创建了这些对象之一。当我们 GetDC 时,我们会抓取这样一个已经存在的对象。

回答您的问题:

设备上下文是一种显示类型,还是一种能够显示的图形数据的实例。 ?

它们是某种意义上的显示器。您可以将它们视为具有私有实现的对象类的实例,这些实现公开了公开绘图命令的公共接口。

为什么我们不能将 GetDC() 与 NULL 或 hwndDesktop 一起使用?

您不能使用 GetDC(NULL) 作为您要选择内存位图的设备上下文,因为在这种情况下您需要创建一个尚不存在的设备上下文; GetDC(NULL) 就像一个已经在使用的单例实例。

因此,您通常使用 CreateCompatibleDC(NULL) 或 CreateCompatibleDC(hdcScreen)。 CreateCompatibleDC(...) 再次是一个令人困惑的名称。想象一下这里发生的假设的面向对象版本。假设有一个由 RasterGraphics、PrinterGraphics 和 MetafileGraphics 实现的 IGraphics 接口。想象一下“RasterGraphics”类同时用于屏幕和内存位图。然后 CreateCompatibleDC(...) 就像工厂调用 Graphics.CreateFrom(IGraphics g) 返回相同具体类型的新实例,可能初始化了一些状态变量。

为什么我们不能缓存设备上下文而不是重复创建它?

你可以。您不需要跨函数调用删除设备上下文。人们经常这样做的唯一原因是它们是一种共享的、有限的资源,而且创造它们的成本很低。我认为实际上它们在旧版本的 Windows 下曾经非常有限,所以旧的 Win32 程序员往往不会将它们从过去的肌肉记忆中缓存出来,从 Windows 95 天开始。

如果机器只有一个显示设备,而我们只是在窗口上绘图,为什么我们需要不断协调位图和设备上下文?

不要将 CreateCompatibleDC(...) 中的“兼容”视为“与屏幕协调”,将其视为“好的 Windows,我想创建一个图形界面对象,我想要类似于这种,它是一种普通的光栅图形,不适用于打印机或元文件。”

【讨论】:

您正在将抽象分解为不存在的组成部分。建议的 DC (3) 不存在。 每个 DC 都有一个选定的内存位图。你可以像在任何其他 DC 中一样渲染它。同样,DC (4) 完全按照它的意思做:提供一种方法来生成可以显示的对象。您不想缓存从BeginPaint 返回的 DC 的真正原因也不是稀缺性。它不应该被缓存,因为剪切矩形在任何时候都可能不同,例如调整窗口大小时。 不应该缓存从开始绘制的那个,这是真的。我们创建的 Dcs 可以被缓存 此外,“设备上下文” 不是 Microsoft 术语。这是 Microsoft 正在实施的模式的既定名称。我第一次遇到它是在 1982 年出版的“计算机图形学:原理与实践”中。在 Windows 中也没有任何关于它面向对象的性质的假设。设备上下文显示 all object-oriented concepts. 我使用“hypothetical”等来解决您的主要问题:我说的是对不存在的部分的抽象。看,答案是试图灌输一种心智模型,通过比喻习惯于一流 OOP 而不是临时 C 风格 OOP 的人,在处理 win32 的这一部分时有所帮助。这种心智模型可能很有用,但并不完全正确。事实上,如果它们完全准确,它们就不会是模型。如果您认为这种出于教学目的的偷工减料没有用,很好,我可以理解。 建立心智模型很重要。简化也很好,只要简化的故事抓住了核心概念。这就是这个提议的答案出错的地方:它发明了 4 种不同类型的 DC(其中一种甚至不存在),并提出它们在某种程度上有所不同。然而,它们都具有相同的界面,并以相同的方式使用。事实上,设备上下文的主要目的是隐藏每个设备的实现细节,并为所有设备的客户端提供一致的接口。这也不是细节;这是一个基本的方面。

以上是关于CreateCompatibleDC() 是不是需要在一个显示器上使用窗口?的主要内容,如果未能解决你的问题,请参考以下文章

CreateCompatibleDC 介绍

Python win32ui.error:CreateCompatibleDC 失败

win32贴图——TransparentBlt

我们可以在 GDI 中为 bitblt 使用 png 而不是位图吗?

[ATL/WTL]_[0基础]_[CBitmap复制图片-截取图片-平铺图片]

VC中怎么用多线程画图,不显示画图过程啊,我是新手