借助 C++ 进行 Windows 开发

使用 Direct2D 绘制分层窗口

Kenny Kerr

在 Direct2D 上我第三个分期付款,我将展示其不匹配的电源的一些谈到的互操作性。而不是彻底详细描述各种互操作性的所有选项 Direct2D 提供,我将引导您完成实用的应用程序:分层的窗口。分层的窗口是那些已周围很长时间但 haven’t 发展大部分并因此需要特别小心,要有效地使用与现代图形的技术的 Windows 功能之一。

本文中我将假定您有基本的熟悉 Direct2D 编程。如果不是,从六月 ( msdn.microsoft.com/magazine/dd861344 ) 读取我先前的文章和九月 ( msdn.microsoft.com/magazine/ee413543 ) 发出引入的编程和绘图 Direct2D 与基础知识的建议。

最初,分层的窗口提供几个不同的用途。在具体的方式而言它们可用于轻松、 高效地生成视觉效果和无闪烁的呈现。在 GDI 时已产生图形的主要方法将内容天什么内,这是真实的奖金。在今天已经硬件加速世界上,但是,它不再引人注目因为分层的窗口仍属于 User32/GDI 的世界,并且没有更新任何重大的方式支持 DirectX,Microsoft 平台的高性能和高质量的图形。

分层的窗口提供唯一的撰写一个窗口在桌面上使用每像素 alpha 混合,它不能以其他方式与 Windows SDK 才能实现的能力。

我应该更不用说有真正两种类型的分层窗口。区别涉及到您是否需要每像素不透明度控件向下或只是需要控制整个窗口的不透明度。本文是前者但如果实际上只是需要控制一个窗口的不透明度有关可以简单地创建设置 alpha 值窗口后调用 SetLayeredWindowAttributes 函数来执行此操作。

Verify(SetLayeredWindowAttributes(
  windowHandle,
  0, // no color key
  180, // alpha value
  LWA_ALPHA));

本示例假定使用扩展样式 WS_EX_LAYERED 中创建窗口或使用 SetWindowLong 函数事实后应用它。图 1 提供这样的窗口的一个示例。好处应该是显而易见的:don’t 需要更改您的应用程序进行绘制桌面窗口管理器 (DWM) 自动将适当地进行窗口混合窗口方式有关的任何内容。翻转侧需要自己绘制绝对的所有内容。课程如果使用全新的呈现技术 (如 Direct2D 的不 ’s 问题 !

图 1 窗口具有 Alpha 值

因此涉及什么?嗯,在基本级别它十分简单。首先,需要填充 UPDATELAYEREDWINDOWINFO 结构中。此结构提供位置和大小的分层的窗口,以及定义窗口图面一个 GDI 设备上下文 (DC) — 和其中位于该问题。dc 属于旧的世界的 GDI 和是远从世界的 DirectX 和硬件加速。在稍后的上的更多。

除了需要分配您自己的结构的指针的完整,UPDATELAYEREDWINDOWINFO 结构 isn’t 完全记录在 Windows SDK,使其小于明显使用中。在所有,需要分配五种结构。有 ’s 源位置标识将从 DC 复制位图的位置。有 ’s 窗口位置标识窗口中一次更新在桌面上的放置位置。有 ’s 它还定义窗口的大小将复制,位图的大小:

POINT sourcePosition = {};
POINT windowPosition = {};
SIZE size = { 600, 400 };

然后 ’s BLENDFUNCTION 结构定义了如何与桌面混合分层的窗口。 这是一种令人惊讶的是通用结构,它经常忽视但十分有用。 通常您可能会填充它,如下所示:

BLENDFUNCTION blend = {};
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;

AC_SRC_ALPHA 常数只指示源位图已是最常见的方案的 alpha 通道。

在 SourceConstantAlpha 但是,是有趣的您可以使用它在很大程度相同的方式,您可能使用 SetLayeredWindowAttributes 函数来控制整个窗口的不透明度。 当设置为 255 分层的窗口将仅使用每个像素的 alpha 值,但您可以调整它向零,或完全透明生成如淡入淡出的重绘成本的情况下窗口中或签出的效果。 它现在应该很明显 BLENDFUNCTION 结构将被命名为它的原因:得到的 alpha 混合窗口是此结构已经值的函数。

上次,’s 一起将 UPDATELAYEREDWINDOWINFO 结构:

UPDATELAYEREDWINDOWINFO info = {};
info.cbSize = sizeof(UPDATELAYEREDWINDOWINFO);
info.pptSrc = &sourcePosition;
info.pptDst = &windowPosition;
info.psize = &size;
info.pblend = &blend;
info.dwFlags = ULW_ALPHA;

这应该是相当容易理解在这时带有唯一未记录的成员被 dwFlags 变量。 外观应熟悉如果使用较旧的 UpdateLayeredWindow 函数之前的 ULW_ALPHA 的值只是表示应使用混合函数。

最后,您需要提供到源 DC 句柄和调用 UpdateLayeredWindowIndirect 函数以更新窗口:

info.hdcSrc = sourceDC;

Verify(UpdateLayeredWindowIndirect(
  windowHandle, &info));

就这样, 在窗口 won’t 接收 WM_PAINT 消息。 您需要显示或更新窗口的任何时间只是调用 UpdateLayeredWindowIndirect 函数。 若要保留此样板代码的所有开,我将使用 本文的其余部分中的 图 2 所示 LayeredWindowInfo 包装类。

图 2 的 LayeredWindowInfo 包装类

class LayeredWindowInfo {
  const POINT m_sourcePosition;
  POINT m_windowPosition;
  CSize m_size;
  BLENDFUNCTION m_blend;
  UPDATELAYEREDWINDOWINFO m_info;

public:

  LayeredWindowInfo(
    __in UINT width,
    __in UINT height) :
    m_sourcePosition(),
    m_windowPosition(),
    m_size(width, height),
    m_blend(),
    m_info() {

      m_info.cbSize = sizeof(UPDATELAYEREDWINDOWINFO);
      m_info.pptSrc = &m_sourcePosition;
      m_info.pptDst = &m_windowPosition;
      m_info.psize = &m_size;
      m_info.pblend = &m_blend;
      m_info.dwFlags = ULW_ALPHA;

      m_blend.SourceConstantAlpha = 255;
      m_blend.AlphaFormat = AC_SRC_ALPHA;
    }

  void Update(
    __in HWND window,
    __in HDC source) {

    m_info.hdcSrc = source;

    Verify(UpdateLayeredWindowIndirect(window, &m_info));
  }

  UINT GetWidth() const { return m_size.cx; }

  UINT GetHeight() const { return m_size.cy; }
};

图 3 为使用 ATL/WTL 和 LayeredWindowInfo 包装类 的 图 2 从一个分层窗口提供了基本的骨架。 注意到此第一件事是 ’s 不需要调用 UpdateWindow,因为此代码 doesn’t 使用 WM_PAINT。 相反,它立即调用反过来,则需要执行一些绘图,并提供到 LayeredWindowInfo 已经 Update 方法 DC 该呈现方法。 该绘图发生方式,以及在 DC 所来自的地方是有趣获取。

图 3 分层窗口框架

class LayeredWindow :
  public CWindowImpl<LayeredWindow, 
  CWindow, CWinTraits<WS_POPUP, WS_EX_LAYERED>> {

  LayeredWindowInfo m_info;

public:

  BEGIN_MSG_MAP(LayeredWindow)
    MSG_WM_DESTROY(OnDestroy)
  END_MSG_MAP()

  LayeredWindow() :
    m_info(600, 400) {

    Verify(0 != __super::Create(0)); // parent
    ShowWindow(SW_SHOW);
    Render();
  }

  void Render() {
    // Do some drawing here

    m_info.Update(m_hWnd,
      /* source DC goes here */);
  }

  void OnDestroy() {
    PostQuitMessage(1);
  }
};

GDI / GDI + 方式

我首先向您展示如何 GDI 中已完成 / GDI +。 首先,您需要创建 pre-multiplied 的 32-位-每像素 (bpp) 位图使用蓝绿-红 alpha (BGRA) 颜色通道字节顺序。 颜色通道值具有已通过 alpha 值乘以被 pre-multiplied 的只是表示。 这往往 alpha 混合图像为提供更好的性能,但它意味着您需要反向除以颜色值,通过获取其真彩色值 
alpha 值的过程。 在 GDI 术语这被称为 32 bpp 设备无关位图 (DIB) 和通过填写 BITMAPINFO 结构与传递它要在 CreateDIBSection 工作 (请参阅 的 图 4) 创建。

图 4 创建 DIB

BITMAPINFO bitmapInfo = {};
bitmapInfo.bmiHeader.biSize = 
  sizeof(bitmapInfo.bmiHeader);
bitmapInfo.bmiHeader.biWidth = 
  m_info.GetWidth();
bitmapInfo.bmiHeader.biHeight = 
  0 – m_info.GetHeight();
bitmapInfo.bmiHeader.biPlanes = 1;
bitmapInfo.bmiHeader.biBitCount = 32;
bitmapInfo.bmiHeader.biCompression = 
  BI_RGB;

void* bits = 0;

CBitmap bitmap(CreateDIBSection(
  0, // no DC palette
  &bitmapInfo,
  DIB_RGB_COLORS,
  &bits,
  0, // no file mapping object
  0)); // no file offset

有很多此处,详细信息,但它们 aren’t 相关到讨论。 此 API 函数返回一个长的方式。 什么您应注意的是确保指定为位图负高度。 BITMAPINFOHEADER 结构定义底部向上或自上而下的位图。 如果高度为正数则结束与自下而上的位图和 ’s 负数则得到自上而下的位图。 自上而下的位图左上角有其原始位置,而底部鎷位图中左下角有其原始位置。

尽管不是必需的但在本例中我倾向于使用自上而下的位图,则由大多数的现代的图像处理组件在 Windows 中使用该格式,从而提高了互操作性。 这还产生一个正的步幅,计算如下:

UINT stride = (width * 32 + 31) / 32 * 4;

此时,您有足够的信息,可以开始通过位指针位图中的绘图。 课程除非 ’re 完全 insane 希望使用某些绘图的函数,但遗憾的是那些由 GDI 提供的大多数 don’t 支持 alpha 通道。 ’s 其中 GDI + 附带。

尽管您可能位图数据直接传递到 GDI +,let’s 相反 DC 为其创建由于需要它仍要传递给 UpdateLayeredWindowIndirect 函数。 若要创建该 DC,调用创建内存与桌面兼容的 DC 在恰当命名的 CreateCompatibleDC 函数。 然后可以调用,以选择到 DC 的位图 SelectObject 函数。 GdiBitmap 的包装类,在 的 图 5 中环绕所有这样的并提供了一些额外的维护管理。

图 5 的 DIB 包装类

class GdiBitmap {
  const UINT m_width;
  const UINT m_height;
  const UINT m_stride;
  void* m_bits;
  HBITMAP m_oldBitmap;

  CDC m_dc;
  CBitmap m_bitmap;

public:

  GdiBitmap(__in UINT width,
            __in UINT height) :
    m_width(width),
    m_height(height),
    m_stride((width * 32 + 31) / 32 * 4),
    m_bits(0),
    m_oldBitmap(0) {

    BITMAPINFO bitmapInfo = { };
    bitmapInfo.bmiHeader.biSize = 
      sizeof(bitmapInfo.bmiHeader);
    bitmapInfo.bmiHeader.biWidth = 
      width;
    bitmapInfo.bmiHeader.biHeight = 
      0 - height;
    bitmapInfo.bmiHeader.biPlanes = 1;
    bitmapInfo.bmiHeader.biBitCount = 32;
    bitmapInfo.bmiHeader.biCompression = 
      BI_RGB;

    m_bitmap.Attach(CreateDIBSection(
      0, // device context
      &bitmapInfo,
      DIB_RGB_COLORS,
      &m_bits,
      0, // file mapping object
      0)); // file offset
    if (0 == m_bits) {
      throw bad_alloc();
    }

    if (0 == m_dc.CreateCompatibleDC()) {
      throw bad_alloc();
    }

    m_oldBitmap = m_dc.SelectBitmap(m_bitmap);
  }

  ~GdiBitmap() {
    m_dc.SelectBitmap(m_oldBitmap);
  }

  UINT GetWidth() const {
    return m_width;
  }

  UINT GetHeight() const {
    return m_height;
  }

  UINT GetStride() const {
    return m_stride;
  }

  void* GetBits() const {
    return m_bits;
  }

  HDC GetDC() const {
    return m_dc;
  }
};

可以用位图已经 DC 构造提供到一些设备的绘图的方法,在 GDI + 图形类。 图 6 显示的 的 图 3 从 LayeredWindow 类为支持用 GDI + 呈现的更新方式。 一旦您拥有样板 GDI 代码的所有开,’s 非常简单。 窗口已经大小传递给 GdiBitmap 构造函数和位图已经 DC 传递给图形构造函数和 Update 方法。 虽然简单,既不 GDI GDI + (大部分),将硬件加速也执行它们提供特别功能强大的呈现功能。

图 6 GDI 分层窗口

class LayeredWindow :
  public CWindowImpl< ... {

  LayeredWindowInfo m_info;
  GdiBitmap m_bitmap;
  Graphics m_graphics;

public:
  LayeredWindow() :
    m_info(600, 400),
    m_bitmap(m_info.GetWidth(), m_info.GetHeight()),
    m_graphics(m_bitmap.GetDC()) {
    ...
  }

  void Render() {
    // Do some drawing with m_graphics object

    m_info.Update(m_hWnd,
      m_bitmap.GetDC());
  }
...

体系结构问题

这是通过相反所有所需创建分层的窗口与 Windows 演示基础 (WPF):

class LayeredWindow : Window {
  public LayeredWindow() {
    WindowStyle = WindowStyle.None;
    AllowsTransparency = true;

    // Do some drawing here
  }
}

尽管令人难以置信简单,但它 belies 涉及的复杂性和使用的体系结构限制分层窗口。 不管如何 sugarcoat 它,分层的窗口必须按照本文中到目前为止概述该体系结构原则。 尽管 WPF 也许能够为其呈现使用硬件加速,结果仍需要复制到 pre-multiplied BGRA 位图选入兼容的 DC,通过对 UpdateLayeredWindowIndirect 函数调用更新显示之前。 WPF 不公开任何多于 bool 变量内容,因为它具有您没有控件的上方代表您做出某些选择。 为什么重要? 谈到硬件。

图形处理单元 (GPU) 首选以获得最佳性能的专用的内存。 到往往比系统内存中的两个位置之间复制要慢得多的 GPU 内存这意味着如果希望处理现有的位图它需要复制从系统内存 (RAM)。 在相反,也是这样:如果创建呈现位图使用该 GPU 然后决定将其复制到 ’s 昂贵的复制操作的系统内存。

通常这应该不会由该 GPU 呈现位图通常发送到显示设备的直接。 分层窗口的情况下该位图必须旅行返回到系统内存由于 User32/GDI 资源涉及到内核模式和用户模式需要访问该位图的资源。 渚嬪考虑 User32 需要命中测试事实分层窗口。 命中测试的分层窗口基于 alpha 的值,该位图允许通过鼠标消息,如果在某个特定时刻像素是透明。 作为结果的位图副本系统内存中需要允许发生这种情况。 一旦已经通过 UpdateLayeredWindowIndirect 复制位图,它是以该 GPU 发送直后,因此该 DWM 可以撰写在桌面上。

除了复制来回内存的零用金,强制与 CPU 同步 GPU 昂贵也是。 与典型的 CPU 绑定操作不同 GPU 操作倾向于所有被异步,执行批处理的呈现命令流时,它提供了很好的性能。 每次我们需要交叉路径替换为 CPU,它会强制刷新成批的命令并等待直到完成该 GPU,不是最佳的性能从而导致 CPU。

所有意味着您需要小心这些往返次数和频率以及所涉及的成本。 足够复杂呈现的场景是否硬件加速的性能可以很容易地超过复制该位图的成本。 手动,如果呈现不是非常昂贵,并且可以由 CPU 执行,您可能会发现对于没有硬件加速选择将最终会提供更好的性能。 这些选择 aren’t 容易进行。 某些 gpu don’t 甚至有专用的内存,而是使用减少了副本的成本的系统内存的一部分。

该问题是 GDI 和 WPF 都不向您提供一个选择。 在 GDI 的情况下您附着与 CPU。 在 WPF 的情况下您强制进入使用任何呈现方法 WPF 用途,这通常是通过 Direct3D 硬件加速。

然后来源 Direct2D。

GDI/DC 的 Direct2D

Direct2D 旨在呈现为任何面向您选择。 如果它 ’s Direct3D 纹理或窗口,Direct2D 执行此直接在该 GPU 上而不涉及任何复制。 如果它 ’s Windows 图像处理组件 (WIC) 位图,Direct2D 同样呈现直接转而使用 CPU。 而 WPF 力争其呈现的大部分内容置于该 GPU,并使用一个软件光栅化程序作为一个回退,Direct2D 提供最好的两个世界上的硬件加速 GPU unparalleled 即时模式呈现和高度优化的呈现与 CPU 上一个 GPU 时不可用或不需。

可以想象有相当几种方法可以呈现与 Direct2D 分层的窗口。 let’s 看一看一些和我指出推荐的方法,具体取决于您要使用硬件加速。

首先,您可能只需翻录出 GDI + 图形类,从 的 图 3 和替换它 Direct2D DC 与呈现目标。 这可能会使意义上,如果您有 GDI 中, 投入到很多旧版应用程序,但它 ’s 明确不最有效的解决方案。 而不是直接到 DC 呈现,Direct2D 呈现第一个对内部的 WIC 位图,然后将结果复制到 DC。 尽管比 GDI + 快,这不过涉及额外复制如果 didn’t 需要使用一个 DC 的呈现,则可以避免的。

若要用于这种方法启动通过初始化 D2D1_RENDER_TARGET_PROPERTIES 结构。 这会告诉 Direct2D 的位图以使用为其呈现目标格式。 回忆一下,它必须是 pre-multiplied 的 BGRA 像素格式。 这表示具有 D2D1_PIXEL_FORMAT 结构,定义如下:

const D2D1_PIXEL_FORMAT format = 
  D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,
  D2D1_ALPHA_MODE_PREMULTIPLIED);

const D2D1_RENDER_TARGET_PROPERTIES properties = 
  D2D1::RenderTargetProperties(
  D2D1_RENDER_TARGET_TYPE_DEFAULT,
  format);

现在,您可以创建使用 Direct2D 工厂对象 DC 呈现目标:

CComPtr<ID2D1DCRenderTarget> target;

Verify(factory->CreateDCRenderTarget(
  &properties,
  &target));

最后,您需要告诉到要发送其绘图命令哪些 DC 呈现目标:

const RECT rect = {0, 0, bitmap.GetWidth(), bitmap.GetHeight()};

Verify(target->BindDC(bitmap.GetDC(), &rect));

此时可以使用 Direct2D 照常 BeginDraw 和 EndDraw 方法调用之间绘制,然后调用 Update 方法作为前的与位图已经 DC。 EndDraw 方法可确保所有绘图已被都刷新到绑定的 DC。

WIC 的 Direct2D

现在如果您可以完全避免 GDI DC,只需直接使用 WIC 位图,您可以实现硬件加速不可能性能最佳。 若要使用此方法开始通过直接与 WIC 创建 pre-multiplied 的 BGRA 位图:

CComPtr<IWICImagingFactory> factory;
Verify(factory.CoCreateInstance(
  CLSID_WICImagingFactory));

CComPtr<IWICBitmap> bitmap;

Verify(factory->CreateBitmap(
  m_info.GetWidth(),
  m_info.GetHeight(),
  GUID_WICPixelFormat32bppPBGRA,
  WICBitmapCacheOnLoad,
  &bitmap));

接下来您需要再一次初始化 D2D1_RENDER_TARGET_PROPERTIES 结构中那样作为之前,只不过您必须还告诉 Direct2D 呈现目标需要是 GDI 兼容:

const D2D1_PIXEL_FORMAT format = 
  D2D1::PixelFormat(
  DXGI_FORMAT_B8G8R8A8_UNORM,
  D2D1_ALPHA_MODE_PREMULTIPLIED);

const D2D1_RENDER_TARGET_PROPERTIES properties = 
  D2D1::RenderTargetProperties(
  D2D1_RENDER_TARGET_TYPE_DEFAULT,
  format,
  0.0f, // default dpi
  0.0f, // default dpi
  D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE);

现在,您可以创建使用 Direct2D 工厂对象 WIC 呈现目标:

CComPtr<ID2D1RenderTarget> target;

Verify(factory->CreateWicBitmapRenderTarget(
  bitmap,
  properties,
  &target));

但是,完全不会 D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE 做什么? 它 ’s 提示来 Direct2D 您将查询 ID2D1GdiInteropRenderTarget 接口为呈现目标:

CComPtr<ID2D1GdiInteropRenderTarget> interopTarget;
Verify(target.QueryInterface(&interopTarget));

为简单起见,并实现的效率,此接口的查询将总是成功。 当只尝试使用它,但是,它将失败如果 didn’t 指定预先您 desires。

ID2D1GdiInteropRenderTarget 接口具有只是两种方法:GetDC 和 ReleaseDC。 若要优化使用硬件加速的位置的情况下,这些方法被限制为呈现目标已经 BeginDraw 和 EndDraw 方法调用之间使用。 GetDC 将返回该 DC 之前刷新呈现目标。 由于 interop 接口已经方法需要成对,它意义 的 图 7 所示在 c + + 类环绕。

图 7 呈现目标 DC 包装类

class RenderTargetDC {
  ID2D1GdiInteropRenderTarget* m_renderTarget;
  HDC m_dc;

public:
  RenderTargetDC(ID2D1GdiInteropRenderTarget* renderTarget) :
    m_renderTarget(renderTarget),
    m_dc(0) {

    Verify(m_renderTarget->GetDC(
      D2D1_DC_INITIALIZE_MODE_COPY,
      &m_dc));

  }

  ~RenderTargetDC() {
    RECT rect = {};
    m_renderTarget->ReleaseDC(&rect);
  }

  operator HDC() const {
    return m_dc;
  }
};

图 8 所示,现在可以使用该 RenderTargetDC 更新窗口已经呈现方法。 有关这种方法很好的事情是代码的所有特定于创建 WIC 呈现目标代码的 tucked 走 CreateDeviceResources 方法中。 接下来我向您展示如何创建 Direct3D 呈现目标,以获得对硬件加速,但在任一情况中 的 图 8 所示,呈现方法保持不变。 这使得您的应用程序相当方便地切换而不更改所有绘图代码的呈现目标的实现。

图 8 GDI 兼容呈现方法

void Render() {
  CreateDeviceResources();
  m_target->BeginDraw();
  // Do some drawing here
  {
    RenderTargetDC dc(m_interopTarget);
    m_info.Update(m_hWnd, dc);
  }

  const HRESULT hr = m_target->EndDraw();

  if (D2DERR_RECREATE_TARGET == hr) {
    DiscardDeviceResources();
  }
  else {
    Verify(hr);
  }
}

direct3d/DXGI 的 Direct2D

若要获得硬件加速的呈现,您需要使用 Direct3D。 因为不呈现直接与通过将自动,获得硬件加速的 ID2D1HwndRenderTarget HWND 需要自己创建 Direct3D 设备和连接点在基础 DirectX 图形基础结构 (DXGI),以便您可以获得兼容 GDI 的结果。

DXGI 是一个相对较新的子系统,居住从底层硬件抽象 Direct3D 和互操作方案提供了高性能网关的 Direct3D 下面图层上。 Direct2D 还利用简化的 Direct3D 的将来版本在移动此新的 API。 若要用于此方法首先创建 Direct3D 硬件设备。 这是表示 GPU 将执行呈现的设备。 此处为目前这必需的 Direct2D 使用 Direct3D 10.1 API:

CComPtr<ID3D10Device1> device;

Verify(D3D10CreateDevice1(
  0, // adapter
  D3D10_DRIVER_TYPE_HARDWARE,
  0, // reserved
  D3D10_CREATE_DEVICE_BGRA_SUPPORT,
  D3D10_FEATURE_LEVEL_10_0,
  D3D10_1_SDK_VERSION,
  &device));

D3D10_CREATE_DEVICE_BGRA_SUPPORT 标志是至关重要的 Direct2D 互用性和 BGRA 像素格式现在外观应熟悉。 传统的 Direct3D 应用程序中可能会创建交换链,并作为纹理来呈现到提供呈现的窗口之前检索其后台缓冲区。 因为对于仅呈现,不应用于演示文稿在您使用 Direct3D,您只是可以直接创建纹理资源。 纹理是用于存储是像素的 Direct3D 等效的像素 Direct3D 资源。 尽管 Direct3D 提供 1-,2 和 3 维纹理您所需要的全部工作就是一个二维纹理,最紧密映射到一个二维表面 (请参阅 的 图 9)。

图 9 的 一个二维纹理

D3D10_TEXTURE2D_DESC description = {};
description.ArraySize = 1;
description.BindFlags = 
  D3D10_BIND_RENDER_TARGET;
description.Format = 
  DXGI_FORMAT_B8G8R8A8_UNORM;
description.Width = GetWidth();
description.Height = GetHeight();
description.MipLevels = 1;
description.SampleDesc.Count = 1;
description.MiscFlags = 
  D3D10_RESOURCE_MISC_GDI_COMPATIBLE;

CComPtr<ID3D10Texture2D> texture;

Verify(device->CreateTexture2D(
  &description,
  0, // no initial data
  &texture));

D3D10_TEXTURE2D_DESC 结构描述来创建纹理。 D3D10_BIND_RENDER_TARGET 常数指示该纹理作为输出缓冲区或呈现目标 Direct3D 管道的绑定。 DXGI_FORMAT_B8G8R8A8_UNORM 常数可确保 Direct3D 将为 GDI 产生正确的像素格式。 最后,D3D10_RESOURCE_MISC_GDI_COMPATIBLE 常数,指示基础的 DXGI 面,以提供可通过其获得的呈现结果 GDI DC。 通过在上一节中介绍 ID2D1GdiInteropRenderTarget 界面公开此 Direct2D。

如我提到 Direct2D 都能够呈现到 DXGI API 来避免将关联到任何特定版本的 Direct3D API 通过一个 Direct3D 图面。 这意味着您需要获取 Direct3D 纹理已经基础 DXGI 表面接口传递给 Direct2D:

CComPtr<IDXGISurface> surface;
Verify(texture.QueryInterface(&surface));

此时可以使用 Direct2D 工厂对象创建 DXGI 表面呈现目标:

CComPtr<ID2D1RenderTarget> target;

Verify(factory->CreateDxgiSurfaceRenderTarget(
  surface,
  &properties,
  &target));

呈现目标属性是我在上一节中描述的相同。请记住使用正确的像素格式,并请求 GDI 兼容性。您可以然后 ID2D1GdiInteropRenderTarget 接口查询和使用 的 图 8 从相同的呈现方法。

所有 ’s 并没有给它。如果要呈现与硬件加速您分层的窗口使用 Direct3D 纹理。否则使用 WIC 位图。这两种方法将提供与复制的最小可能性能最佳。

请务必检查出 DirectX 博客,并在特定的、 componentization 和互操作性在 blogs.msdn.com/directx 上的 Ben Constable 已经八月 2009年文章。

Kenny Kerr* 是一个软件艺术家热烈有关 Windows。他也是窗口剪辑 (windowclippings.com) 的创建者。在 weblogs.asp.net/kennykerr 到达他。*

mmwincpp@microsoft.com Kerr 的发送您的问题和批注。

感谢到下面的技术专家为审阅此文章:Ben Constable 和 Mark Lawrence