Este artículo proviene de un motor de traducción automática.

Windows con C++

Ventanas en capas con Direct2D

Kenny Kerr

En Mi tercera entrega en Direct2D, voy a mostrar algunas de su energía no coincidente cuando se trata de interoperabilidad. En lugar de manera exhaustiva que detalla todas las distintas interoperabilidad opciones que proporciona Direct2D, voy a guiarle a través de una aplicación práctica: ventanas por capas. Ventanas por capas son una de estas características de Windows que se han alrededor durante mucho tiempo pero no han evolucionado mucho y, por tanto, requieren un cuidado especial para utilizar eficazmente con tecnologías modernas gráficos.

En este artículo, voy a supone que tiene una familiaridad básica con la programación de Direct2D. Si no es así, le recomiendo leer mis artículos anteriores desde el junio (msdn.microsoft.com/magazine/dd861344 de) y problemas de septiembre (msdn.microsoft.com/magazine/ee413543 de) que introdujo los fundamentos de programación y de dibujo con Direct2D.

Originalmente, ventanas por capas habían servido unos con otros fines. En concreto, podrían utilizarse fácilmente y eficaz producir efectos visuales y representación sin parpadeo. En los días cuando GDI era el método predominante para producir gráficos, esto era una bonificación real. En el mundo acelerado por hardware de hoy, sin embargo, ya no es atractiva porque aún pertenecen al mundo de GDI/User32 ventanas por capas y no se han actualizado de ningún modo significativo para admitir DirectX, la plataforma de Microsoft para gráficos de alto rendimiento y alta calidad.

Ventanas por capas proporcione la capacidad de única para redactar una ventana en el escritorio utilizando alfa por píxel mezcla, que no se puede lograr de ninguna otra forma con el SDK de Windows.

Debo mencionar que realmente existen dos tipos de ventana por capas. La distinción trata si necesita control de opacidad por píxel de o simplemente necesita controlar la opacidad de la ventana como un todo. Este artículo trata sobre el anterior, pero si en realidad, sólo deberá controlar la opacidad de una ventana, que puede hacerlo llamando simplemente a la función SetLayeredWindowAttributes después de crear la ventana para establecer el valor alfa.

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

Se supone que ha creado la ventana con el estilo extendido de WS_EX_LAYERED o aplica después el hecho utilizando la función SetWindowLong. figura 1 proporciona un ejemplo de tal una ventana. La ventaja debería ser evidente: no es necesario cambiar nada acerca de la forma en que la aplicación pinta la ventana, como el Administrador de ventanas de escritorio (DWM) automáticamente fundirá la ventana adecuadamente. En el lado de volteo, necesita dibujar absolutamente todo. Por supuesto, si utiliza una tecnología de representación totalmente nuevo como Direct2D, no es un problema!

Window with Alpha Value

Figura 1 de valor de la ventana con Alpha

Por tanto, ¿qué se necesita? Bueno, en un nivel fundamental es sencillo. En primer lugar, deberá rellenar una estructura de UPDATELAYEREDWINDOWINFO. Esta estructura proporciona la posición y el tamaño de una ventana por capas, así como un contexto de dispositivo GDI (DC) que define la superficie de la ventana, y en él se encuentra el problema. DC pertenecen al mundo antiguo de GDI y alejado del mundo de aceleración de DirectX y hardware. Más en un momento.

Además de estar lleno de punteros a estructuras que tiene que asignar personalmente, la estructura UPDATELAYEREDWINDOWINFO no está totalmente documentada en el SDK de Windows, lo que menos evidente para utilizar. En todos, deberá asignar estructuras de cinco. Hay la posición de origen que identifica la ubicación del mapa de bits para copiar desde el controlador de dominio. Hay que identifica dónde se ubicará la ventana en el escritorio una vez actualizado la posición de la ventana. Hay el tamaño del mapa de bits para copiar, que también define el tamaño de la ventana:

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

Es la estructura BLENDFUNCTION que define cómo se mezclará la ventana por capas con el escritorio. Esto es una estructura sorprendentemente versátil que se suele pasar por alto, pero puede ser bastante útil. Normalmente puede llenarla como sigue:

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

La constante AC_SRC_ALPHA simplemente indica que el mapa de bits de origen tiene un canal alfa, que es el escenario más común.

El SourceConstantAlpha, sin embargo, es interesante el hecho de que lo puede utilizar en gran parte del mismo modo, podría utilizar la función SetLayeredWindowAttributes para controlar la opacidad de la ventana como un todo. Cuando se establece en 255, la ventana por capas sólo utilizará los valores alfa por píxel, pero puede ajustarla a cero, o totalmente transparente, para producir efectos como la atenuación de la ventana o de salida sin el costo de volver a dibujar. Ahora debería ser evidente por qué se denomina la estructura BLENDFUNCTION como es: la ventana resultante con mezcla alfa es una función de valor la estructura de este.

Por último, hay la estructura UPDATELAYEREDWINDOWINFO que lo enlaza todas:

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

Esto debe ser bastante autoexplicativos en este momento, con el único miembro no documentado que se va a la variable dwFlags. Un valor de ULW_ALPHA, que debe resultarle familiar si ha utilizado la función UpdateLayeredWindow antigua antes, sólo indica que debe utilizarse la función de mezcla.

Por último, deberá proporcionar el identificador para el DC de origen y llame a la función UpdateLayeredWindowIndirect para actualizar la ventana:

info.hdcSrc = sourceDC;

Verify(UpdateLayeredWindowIndirect(
  windowHandle, &info));

Y eso es todo. La ventana no recibe ningún mensaje WM_PAINT. Cada vez que necesite para mostrar o actualizar la ventana, simplemente llamar a la función UpdateLayeredWindowIndirect. Para mantener todo este código repetitivo de forma, voy a utilizar la clase contenedora de LayeredWindowInfo que se muestra en de la figura 2 en el resto de este artículo.

Figura 2 de clase contenedora de 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; }
};

figura 3 proporciona una estructura básica para una ventana por capas utilizando ATL/WTL y la clase contenedora de LayeredWindowInfo de de figura 2. Esta primera cosa que debe tener en cuenta es que no es necesario llamar a UpdateWindow dado que este código no usa WM_PAINT. En su lugar inmediatamente llama al método Render, que a su vez es necesario para realizar algún dibujo y para proporcionar un controlador de dominio para el método de actualización del LayeredWindowInfo. Cómo se produce ese dibujo y donde procede el DC es donde obtiene interesante.

Figura 3 de Layered esqueleto de ventana

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);
  }
};

La GDI y GDI + Way

Primero mostraré cómo se realiza en GDI o GDI +. Primero necesita crear un mapa de bits premultiplicada 32-bits por píxel (bpp) utilizando un orden de bytes de canal de color de azul verde-rojo alfa (BGRA). Premultiplicada simplemente significa que los valores de canal de color ya han sido multiplicados por el valor alfa. Esto tiende a proporcionar un mejor rendimiento para las imágenes de mezcla alfa, pero significa que necesita invertir el proceso dividiendo los valores de color por el valor de 
alpha para obtener sus valores de color verdadero. En la terminología GDI, esto se denomina mapa de bits independientes del dispositivo de 32-bpp (DIB) y se crea por rellenar una estructura BITMAPINFO y pasar a la CreateDIBSection funciona (consulte de figura 4).

Figura 4 de crear un 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

Hay muchos detalles aquí, pero no son relevantes para la discusión. Esta función de API vuelve un largo camino. ¿Qué debe tomar nota de es que he especificado un alto negativo para el mapa de bits. La estructura BITMAPINFOHEADER define una parte inferior de seguridad o un mapa de bits de arriba a abajo. Si el alto es positivo que terminará con un mapa de bits de abajo a arriba y si es negativo, obtendrá un mapa de bits de arriba a abajo. Los mapas de bits de arriba a abajo tienen su origen en la esquina superior izquierda, mientras que los mapas de bits inferior desplegable tienen su origen en la esquina inferior izquierda.

Aunque no es estrictamente necesario en este caso, he tienden a utilizar mapas de bits de arriba a abajo como es el formato utilizado por la mayoría de los componentes de creación de imágenes modernos en Windows y, por lo tanto, mejora la interoperabilidad. Esto también lleva a un intervalo positivo, que se puede calcular como sigue:

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

En este punto tiene suficiente información para empezar a dibujar en el mapa de bits a través del puntero de bits. Por supuesto, a menos que esté completamente insane desea utilizar algunas funciones de dibujo, pero lamentablemente la mayoría de los proporcionados por GDI no admite el canal alfa. Que es donde GDI + entra en juego.

Aunque los datos del mapa de bits podría pasar directamente a GDI +, let’s en su lugar crear un controlador de dominio para él ya que será necesario de todos modos para pasar a la función UpdateLayeredWindowIndirect. Para crear el controlador de dominio, llame a la función CreateCompatibleDC acertadamente con nombre, que crea un controlador de dominio que es compatible con el escritorio de memoria. A continuación, se puede llamar a la función SelectObject para seleccionar el mapa de bits en el DC. La clase contenedora de GdiBitmap en de figura 5 todo esto completa y proporciona algunos mantenimiento adicional.

Figura 5 de clase contenedora 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;
  }
};

La clase Graphics GDI +, que proporciona métodos para dibujar en algún dispositivo, se puede construir con DC del mapa de bits. figura 6 muestra cómo la clase LayeredWindow desde de figura 3 puede actualizarse para admitir la representación con GDI +. Una vez que todo el código GDI texto repetitivo de forma, es bastante sencillo. El tamaño de la ventana se pasa al constructor GdiBitmap y DC del mapa de bits se pasa al constructor gráficos y el método Update. Aunque directo, GDI ni GDI + son acelerado por hardware (la mayor parte), ni tampoco ofrecen funcionalidad de procesamiento muy eficaces.

Figura 6 de GDI Layered Window

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());
  }
...

El problema de arquitectura

Por el contrario, esto es todo lo necesario crear una ventana por capas con Windows Presentation Foundation (WPF):

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

    // Do some drawing here
  }
}

Aunque increíblemente sencilla, lo belies la complejidad implicada y ventanas en capas de las limitaciones de arquitectura de uso. Independientemente de cómo se sugarcoat, ventanas por capas deben seguir los principios de arquitectura descritos hasta ahora en este artículo. Aunque WPF es posible que pueda utilizar aceleración de hardware para su procesamiento, los resultados aún deben copiarse a un mapa de bits BGRA premultiplicada seleccionado en un DC compatible antes de la presentación se actualiza mediante una llamada a la función UpdateLayeredWindowIndirect. Dado que WPF no es exponer nada más que una variable de tipo bool, tiene que tomar ciertas decisiones en su nombre que no tiene ningún control sobre. ¿Por qué que importa? Se trata hardware.

Una unidad de procesamiento de gráficos (GPU) prefiere memoria dedicada a conseguir el mejor rendimiento. Esto significa que si necesita manipular un mapa de bits existente, necesita que se va a copiar desde la memoria del sistema (RAM) a la memoria GPU, lo cual tiende a ser mucho más lento que copiar entre dos ubicaciones en la memoria del sistema. Lo contrario también es cierto: a continuación, decidir si crear y representar un mapa de bits mediante la GPU copiarlo en la memoria del sistema, que es una operación de copia caro.

Normalmente esto no se debe producir como mapas de bits representados por la GPU suelen enviarse directamente en el dispositivo de pantalla. En el caso de ventanas por capas, el mapa de bits debe transmitirse de nuevo a la memoria del sistema ya recursos GDI/User32 implican recursos de modo de núcleo y modo de usuario que requieren acceso al mapa de bits. Considere, por ejemplo, el hecho de que necesita User32 para comprobación de clics en capas de ventanas. Comprobación de visitas de una ventana por capas se basa en los valores alfa del mapa de bits, lo que permite que los mensajes del mouse (ratón) a través de si el píxel en un punto determinado es transparente. Como resultado, es necesaria una copia del mapa de bits en la memoria del sistema para permitir que esto ocurra. Una vez se ha copiado el mapa de bits por UpdateLayeredWindowIndirect, enviarlo back recta a la GPU para que DWM puede redactar el escritorio.

Además de los gastos de copia de memoria y atrás, forzando la GPU para sincronizar con la CPU es costoso también. A diferencia de las operaciones típicas de límite de CPU, tienden a operaciones de GPU todas realizarse de forma asincrónica, que proporciona gran rendimiento cuando una secuencia de comandos de procesamiento de procesamiento por lotes. Cada vez que necesitamos cruzar las rutas de acceso con la CPU, obliga a comandos por lotes que hay que vaciar y la CPU para esperar hasta que finalice la GPU conducen a menos de un rendimiento óptimo.

Todo esto significa que deberá tener cuidado con estos viajes de ida y vuelta y la frecuencia y costos implicados. Si el plano que se procesa es suficientemente compleja, el rendimiento de la aceleración de hardware puede superar fácilmente el costo de copiar los mapas de bits. Por otro lado, si la representación no es muy costosa y puede realizarse por la CPU, es posible que optando sin aceleración de hardware en última instancia a proporcionar un mejor rendimiento. Estas opciones no son fáciles de realizar. Algunos GPU no incluso tener memoria dedicada y utilice en su lugar una parte de la memoria del sistema, lo que reduce el costo de la copia.

El truco es que no GDI ni WPF otorgue una elección. En el caso de GDI, está atascado con la CPU. En el caso de WPF, está obligado a utilizando cualquier usos WPF del enfoque de representación, que normalmente es la aceleración de hardware a través de Direct3D.

A continuación, Direct2D venía.

Direct2D a GDI/DC

Direct2D se diseñó para representar a cualquier destino elija. Si es una ventana o la textura de Direct3D, Direct2D hace directamente en la GPU sin involucrar cualquier copiar. Si es un mapa de bits de Windows Imaging Component (WIC), Direct2D del mismo modo procesa directamente mediante la CPU en su lugar. Mientras que WPF se esfuerza en gran parte de su procesamiento colocar en la GPU y utiliza una impresora de trama de software como reserva, Direct2D proporciona el mejor de ambos mundos con representación de organizar el modo inmediato en la GPU aceleración de hardware y representación altamente optimizado en la CPU cuando una GPU es no está disponible o no deseado.

Como puede imaginar, hay bastantes formas de representar una ventana por capas con Direct2D. Let’s eche un vistazo a algunos y pondré señale los enfoques dependiendo de si desea utilizar aceleración de hardware recomendados.

En primer lugar, sólo se puede copiar desde fuera de la clase gráficos GDI + a partir de figura 3 y reemplazar con un DC Direct2D representar destino. Esto podría tener sentido si tiene una aplicación heredada con mucho invertido en GDI, pero no lo es la solución más eficaz. En lugar de representación directamente al controlador de dominio, Direct2D procesa primero a un mapa de bits WIC interno y, a continuación, copia el resultado en el DC. Aunque es más rápido que GDI +, no obstante implica copiar adicional que se podría evitar si no necesita utilizar un controlador de dominio para la representación.

Para utilizar este enfoque, empiece por inicializar una estructura D2D1_RENDER_TARGET_PROPERTIES. Esto indica el formato del mapa de bits para su destino de representación de Direct2D. Recuerde que debe ser un formato de píxel premultiplicado BGRA. Esto se expresa con una estructura D2D1_PIXEL_FORMAT y puede definirse como sigue:

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);

Ahora puede crear el destino de representación de DC mediante el objeto de fábrica Direct2D:

CComPtr<ID2D1DCRenderTarget> target;

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

Por último, necesita saber el destino de representación a qué controlador de dominio para enviar sus dibujo comandos:

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

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

En este momento puede dibujar con Direct2D de forma habitual entre llamadas BeginDraw y EndDraw y, a continuación, llame al método Update como antes con DC del mapa de bits. El método EndDraw garantiza que todo el dibujo se ha vaciado al DC dependiente.

Direct2D para WIC

Ahora si se pueden evitar por completo el DC de GDI y utilizar sólo un mapa de bits WIC directamente, puede lograr el mejor rendimiento posible sin aceleración de hardware. Para utilizar este enfoque para iniciar creando un mapa de bits BGRA premultiplicada directamente con WIC:

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

CComPtr<IWICBitmap> bitmap;

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

A continuación deberá volver a inicializar una estructura D2D1_RENDER_TARGET_PROPERTIES en casi la misma forma que antes, excepto que también debe indicar a Direct2D que debe ser compatible con GDI el destino de representación:

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);

Ahora puede crear el destino de representación WIC utilizando el objeto de fábrica Direct2D:

CComPtr<ID2D1RenderTarget> target;

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

Pero ¿qué exactamente D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE hace? Es una sugerencia para Direct2D consultará el destino de representación para la interfaz ID2D1GdiInteropRenderTarget:

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

Por simplicidad y eficacia de implementación, la consulta de esta interfaz se realizará siempre correctamente. Es sólo cuando intenta utilizarla, sin embargo, que se producirá un error si no ha especificado sus deseos de antemano.

La interfaz de ID2D1GdiInteropRenderTarget tiene sólo dos métodos: GetDC y ReleaseDC. Para optimizar los casos donde se utiliza la aceleración de hardware, estos métodos están restringidos a la que se utiliza entre las llamadas a métodos de BeginDraw y EndDraw del destino de representación. GetDC vaciará el destino de representación antes de devolver el controlador de dominio. Puesto que los métodos de la interfaz interoperabilidad necesitan estar emparejados, tiene sentido ajustar en una clase de C++ como se muestra en de figura 7.

Figura 7 de Render clase contenedora de DC de destino

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;
  }
};

Método Render de la ventana ahora se puede actualizar para usar el RenderTargetDC, como se muestra en de figura 8. Lo bueno sobre este enfoque es que todo el código específico de creación de un destino de representación WIC es apartada ausente en el método CreateDeviceResources. A continuación mostraré cómo crear un destino de representación de Direct3D para tener la aceleración de hardware, pero en cualquier caso, el método Render que se muestra en de figura 8 permanece invariable. Esto hace posible para la aplicación cambiar las implementaciones de destino de representación bastante fácilmente sin cambiar el código de dibujo.

Figura 8 de método Render de GDI-compatible

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);
  }
}

Direct2D para Direct3D/DXGI

Para obtener la representación acelerado por hardware, deberá utilizar Direct3D. Porque no está procesa directamente a un HWND a través de ID2D1HwndRenderTarget, que se obtendría aceleración de hardware automáticamente, deberá crear el dispositivo Direct3D usted mismo y conectar los puntos en la infraestructura de gráficos de DirectX (DXGI) subyacente de modo que puede obtener resultados compatibles con GDI.

DXGI es un subsistema relativamente nuevo que reside en una capa de Direct3D para abstraer Direct3D del hardware subyacente y proporcionar una puerta de enlace de alto rendimiento para escenarios de interoperabilidad. Direct2D también aprovecha las ventajas de esta nueva API para simplificar la migración a versiones futuras de Direct3D. Para utilizar este enfoque, empiece por crear un dispositivo de hardware Direct3D. Éste es el dispositivo que representa la GPU que realizará el procesamiento. Aquí Estoy utilizando la API de 10.1 Direct3D como esto es necesario por Direct2D en este momento:

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));

El indicador de D3D10_CREATE_DEVICE_BGRA_SUPPORT es crucial para la interoperabilidad de Direct2D y el formato de píxel BGRA debe ahora resultarle familiar. En una aplicación tradicional de Direct3D, podría crear una cadena de intercambio y recuperar su búfer de reserva como textura para procesar en antes de presentar la ventana representada. Puesto que está utilizando Direct3D representar sólo y no para la presentación, simplemente puede crear un recurso de textura directamente. Una textura es un recurso de Direct3D para almacenar téxels, que son el equivalente de Direct3D de píxeles. Aunque Direct3D proporciona 1-, 2 y 3-dimensional texturas, todo lo que necesita es una textura 2D que más se asigna a un 2D de superficie (consulte de figura 9).

Figura 9 de una textura 2D

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));

Para crear la textura describe la estructura de D3D10_TEXTURE2D_DESC. La constante D3D10_BIND_RENDER_TARGET indica que está enlazada la textura como el búfer de salida, o destino de representación, de la canalización de Direct3D. La constante DXGI_FORMAT_B8G8R8A8_UNORM garantiza que Direct3D producirá el formato de píxel correcta para GDI. Por último, la constante D3D10_RESOURCE_MISC_GDI_COMPATIBLE indica a la superficie DXGI subyacente para ofrecer un DC de GDI a través del cual se pueden obtener los resultados de representación. Este Direct2D expone a través de la interfaz de ID2D1GdiInteropRenderTarget que hablé en la sección anterior.

Como mencioné, es capaz de representación para una superficie de Direct3D a través de la API de DXGI para evitar uniendo la API a cualquier versión concreta de Direct3D Direct2D. Esto significa que necesita para obtener DXGI superficie interfaz subyacente de la textura Direct3D para pasar a Direct2D:

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

En este momento puede utilizar el objeto de fábrica Direct2D para crear un destino de representación superficie DXGI:

CComPtr<ID2D1RenderTarget> target;

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

Las propiedades de destino de representación son los mismos que describí en la sección anterior. Recuerde que utilizar el formato de píxel correcta y solicitar la compatibilidad GDI. Puede consultar la interfaz ID2D1GdiInteropRenderTarget y utilizar el mismo método Render de de figura 8.

Y eso es todo lo que hay en él. Si desea representar su ventana por capas con aceleración de hardware, utilizar una textura de Direct3D. De lo contrario, utilice un mapa de bits WIC. Estos dos enfoques proporcionará el mejor rendimiento posible con la menor cantidad de copiar.

Asegúrese de comprobar el blog de DirectX y, en concreto, artículo de agosto de 2009 del Ben Constable en la creación de componentes y la interoperabilidad en blogs.msdn.com/directx de.

Kenny Kerr es un artesano del software apasionado de Windows. También es el creador de recortes de Window (windowclippings.com). Ponerse en weblogs.asp.net/kennykerr de.

Envíe sus preguntas y comentarios para Kerr a mmwincpp@microsoft.com de.

Gracias a los siguientes expertos técnicos para revisar este artículo: Mark Lawrence y Ben Constable