Este artigo foi traduzido por máquina.

Windows com C++

Janelas sobrepostas com o Direct2D

Kenny Kerr

Minha terceira edição em Direct2D, vou mostrar desativar alguns sua energia não coincidente quando se trata de interoperabilidade. Em vez de detalhando exaustivamente todas as opções de interoperabilidade vários que fornece Direct2D, vou orientá-lo uma aplicação prática: janelas em camadas. Janelas em camadas são um desses recursos do Windows que tenham sido ao redor por um longo tempo, mas ainda não evoluíram muito e, assim, exigem cuidado especial para usar efetivamente com tecnologias de gráficos modernos.

Este artigo vou supor que você tenha uma Familiaridade básica com programação Direct2D. Se não, recomendo a leitura Meus artigos anteriores de junho (msdn.microsoft.com/magazine/dd861344 ) e problemas de setembro (msdn.microsoft.com/magazine/ee413543 ) que apresentou os conceitos básicos da programação e de desenho com Direct2D.

Originalmente, janelas em camadas servidas algumas finalidades diferentes. Em particular, eles poderiam ser usados com facilidade e eficiência produzir efeitos visuais e processamento cintilações. Em dias quando GDI foi o método predominante para produzir elementos gráficos, esse era um bônus real. No mundo acelerada por hardware hoje, no entanto, não é mais atraente pois janelas em camadas ainda pertencem ao mundo User32/GDI e não foram atualizadas de forma alguma significativa para oferecer suporte a DirectX, a plataforma da Microsoft para gráficos de alta qualidade e alto desempenho.

Janelas em camadas fornecem a capacidade de exclusiva para redigir uma janela na área de trabalho usando alfa por pixel misturando, que não pode ser obtida de qualquer forma com o SDK do Windows.

Devo mencionar que realmente existem dois tipos de janela em camadas. A distinção vem para baixo para se você precisa de controle de opacidade por pixel ou apenas precisar controlar a opacidade da janela como um todo. Este artigo é sobre o anterior, mas se você precisa realmente apenas controlar a opacidade da janela, que você pode fazer isso simplesmente chamando a função de SetLayeredWindowAttributes após criar a janela para definir o valor de alfa.

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

Isso pressupõe que você criou a janela com WS_EX_LAYERED estilo estendido ou aplicadas após o fato usando a função SetWindowLong. Figura 1 fornece um exemplo de uma janela. O benefício deve ser óbvio: Você não precisa alterar algo sobre a maneira como o seu aplicativo pinta a janela conforme o DWM (Desktop Window Manager) irá misturar automaticamente a janela apropriadamente. No outro lado, você precisa desenhar absolutamente tudo. Obviamente, se você estiver usando uma tecnologia totalmente nova renderização como Direct2D, que não é um problema!

Window with Alpha Value

Figura 1 Window com Alpha valor

Portanto, o que está envolvido? Bem, em um nível fundamental é simples. Primeiro, você precisa preencher uma estrutura UPDATELAYEREDWINDOWINFO. Essa estrutura fornece a posição e tamanho de uma janela em camadas, bem como um contexto de dispositivo GDI (DC) que define a superfície de janela — e nele se encontra o problema. DCs pertencem ao mundo antigo da GDI e estão longe de mundo da aceleração de hardware e DirectX. Mais sobre isso em instantes.

Além de estar cheia de ponteiros para estruturas que você precisa alocar você mesmo, a estrutura UPDATELAYEREDWINDOWINFO não está totalmente documentada no SDK do Windows, tornando menos óbvio para usar. Em todos, você precisa alocar cinco estruturas. Há a posição de origem identifica o local do bitmap para copiar do DC. Há a posição da janela Identificando onde a janela será posicionada na área de trabalho atualizada uma vez. Há tamanho do bitmap para copiar, que também define o tamanho da janela:

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

Em seguida, é a estrutura BLENDFUNCTION que define como a janela em camadas irá ser combinada com a área de trabalho. Essa é uma estrutura surpreendentemente versátil que normalmente ignorada, mas pode ser bastante útil. Normalmente você pode preenchê-la como segue:

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

A constante AC_SRC_ALPHA apenas indica que o bitmap de origem tem um canal alfa, que é o cenário mais comum.

SourceConstantAlpha, no entanto, é interessante que você pode usá-la praticamente da mesma maneira que você pode utilizar a função SetLayeredWindowAttributes para controlar a opacidade da janela como um todo. Quando é definido como 255, janela em camadas usará apenas os valores alfa por pixel, mas você pode ajustar a ele para zero, ou totalmente transparente para produzir efeitos como dégradé janela in ou out sem o custo de redesenho. Deve agora ser óbvio por que a estrutura BLENDFUNCTION é nomeada como é: a janela resultante de alfa combinada é uma função de valor da estrutura.

Por último, é a estrutura UPDATELAYEREDWINDOWINFO une todos juntos:

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

Isso deve ser bastante auto-explicativos neste ponto, com o único membro não documentado sendo variável dwFlags. Um valor ULW_ALPHA, deve parecer familiar Se você usou a função UpdateLayeredWindow antiga antes, simplesmente indica que a função de combinação deve ser usada.

Finalmente, você precisa fornecer o identificador para o controlador de domínio de origem e chamar a função UpdateLayeredWindowIndirect para atualizar a janela:

info.hdcSrc = sourceDC;

Verify(UpdateLayeredWindowIndirect(
  windowHandle, &info));

E isso é tudo. A janela não recebe quaisquer mensagens WM_PAINT. Qualquer tempo que necessário para mostrar ou atualizar a janela, basta chame a função UpdateLayeredWindowIndirect. Para manter todo esse código clichê fora do caminho, vou usar a classe de wrapper LayeredWindowInfo mostrada Figura 2 no restante deste artigo.

Figura 2 LayeredWindowInfo Wrapper Class

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 fornece um esqueleto básico para uma janela em camadas usando ATL/WTL e a classe de wrapper LayeredWindowInfo from do Figura 2. Esta primeira coisa a observar é que há necessidade de chamar UpdateWindow desde que esse código não usa WM_PAINT. Em vez disso, ele imediatamente chama o método Render, que por sua vez é necessário para executar alguns desenho e fornecer um DC para método de atualização do LayeredWindowInfo. Como esse desenho ocorre e onde o DC vem de é onde obtém interessante.

Figura 3 em camadas Window esqueleto

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

A GDI / GDI + Way

Primeiro mostrarei como foi feito no GDI / GDI +. Primeiro você precisa criar um bitmap pre-multiplied 32-bits-por pixel (bpp) usando uma ordem de byte azul-verde vermelho alfa (BGRA) cor canal. Pre-multiplied significa apenas que os valores de canal de cor já tem sido multiplicados pelo valor alfa. Isso tende a fornecer melhor desempenho para imagens alpha blending, mas significa que você precisa reverter o processo dividindo os valores de cor por valor 
alpha para obter seus valores true color. Na terminologia GDI, isso é chamado um 32 bpp independente de dispositivo DIB (bitmap) e é criado por preencher uma estrutura BITMAPINFO e passando-o para o CreateDIBSection funcionar (consulte do Figura 4).

Figura 4 Criando um 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

Há muitos detalhes aqui, mas não são relevantes para a discussão. Esta função API volta um longo caminho. O que você deve tomar nota do é que especifiquei uma altura negativa para o bitmap. A estrutura BITMAPINFOHEADER define inferior-up ou um bitmap de cima para baixo. Se a altura é positiva que acabará com um bitmap de baixo para cima e se for negativo, você obterá um bitmap de cima para baixo. Bitmaps de cima para baixo tem sua origem no canto superior esquerdo, enquanto inferior down bitmaps têm sua origem no canto inferior esquerdo.

Embora não seja estritamente necessário nesse caso, eu tendem a usar bitmaps de cima para baixo conforme o que é o formato usado pela maioria dos componentes modernos imagens no Windows e, portanto, melhora a interoperabilidade. Isso também leva a um stride positivo, que pode ser calculado da seguinte maneira:

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

Neste ponto, você tem informações suficientes para iniciar o desenho no bitmap através de ponteiro bits. Obviamente, a menos que você esteja completamente insane você desejará usar algumas funções de desenho, mas infelizmente a maioria daqueles fornecidos por GDI não suporta o canal alfa. Que é onde GDI + entra.

Embora você poderia passar os dados de bitmap diretamente para GDI +, let’s em vez disso criar um DC para ele desde que você precisará-lo assim mesmo passar para a função UpdateLayeredWindowIndirect. Para criar o DC, chame a função CreateCompatibleDC adequadamente nomeada, que cria uma memória DC é compatível com a área de trabalho. Em seguida, você pode chamar a função SelecionarObjeto para selecionar o bitmap para o DC. Classe de wrapper GdiBitmap no do Figura 5 conclui tudo isso e fornece alguns faxina extra.

Figura 5 do DIB wrapper class

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

A classe de gráficos GDI +, que fornece métodos para desenho para algum dispositivo, pode ser construída com DC do bitmap. Figura 6 mostra como a classe LayeredWindow from do Figura 3 pode ser atualizada para oferecer suporte a processamento com GDI +. Depois de ter todos clichê código GDI do caminho, é bem simples. O tamanho da janela é passado para o construtor GdiBitmap e DC do bitmap é passado para o construtor de gráficos e o método Update. Embora simples, GDI nem GDI + são acelerada por hardware (na maior parte) nem fazer eles fornecem funcionalidade de processamento particularmente poderosas.

Figura 6 GDI em camadas 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());
  }
...

Problema de arquitetura

Por outro lado, isso é tudo criar uma janela em camadas com o Windows Presentation Foundation (WPF):

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

    // Do some drawing here
  }
}

Embora incrivelmente simples belies complexidade envolvida e limitações arquitetônicas do uso de janelas em camadas. Não importa como você sugarcoat, janelas em camadas devem seguir os princípios de arquitetura até aqui descritos neste artigo. Embora o WPF pode ser capaz de usar aceleração de hardware para seu processamento, os resultados ainda precisam ser copiados para um bitmap BGRA pre-multiplied selecionado em um DC compatível antes que a exibição é atualizada por meio de uma chamada para a função UpdateLayeredWindowIndirect. Desde que o WPF é expor nada mais que uma variável bool, ele tem fazer certas escolhas em seu nome não tem nenhum controle sobre. Por que essa questão? Ele vem para baixo ao hardware.

Uma unidade de processamento de elementos gráficos (GPU) prefere memória dedicada para obter melhor desempenho. Isso significa que, se você precisar manipular um bitmap existente, ele precisa ser copiados do memória de sistema (RAM) para a memória GPU, o que tende a ser muito mais lento que copiando entre dois locais na memória do sistema. O inverso também é verdadeiro: Se você criar e processar um bitmap usando a GPU, decida copiar a memória do sistema, que é uma operação de cópia caro.

Normalmente isso não deve ocorrer como bitmaps processados pela GPU normalmente são enviados diretamente para o dispositivo de vídeo. No caso de janelas em camadas, o bitmap deve viagem de volta para a memória do sistema desde recursos User32/GDI envolvem recursos de modo kernel e modo de usuário que requerem acesso para o bitmap. Por exemplo, considere o fato de que precisa User32 para teste de visitas em camadas janelas. Teste de visitas de uma janela em camadas é baseado em valores alfa de bitmap, permitindo que mensagens de mouse através de se pixel em um determinado ponto é transparente. Como resultado, uma cópia do bitmap é necessária na memória do sistema para permitir que isso aconteça. Depois que o bitmap foi copiado pelo UpdateLayeredWindowIndirect, ele é enviado back reta à GPU, portanto o DWM pode compor a área de trabalho.

Além das despesas de copiar a memória e para trás, forçando a GPU para sincronizar com a CPU é caro bem. Ao contrário de operações vinculadas à CPU típicas, operações de GPU tendem a todas ser realizadas de forma assíncrona, que fornece excelente desempenho quando um fluxo de comandos de processamento em lotes. Sempre que precisamos cruze caminhos com a CPU, força comandos em lote para ser liberado e a CPU Aguarde até que tenha concluído a GPU, levando à menor que ótimo desempenho.

Tudo isso significa que você precisa ter cuidado sobre esses percursos circulares e a freqüência e os custos envolvidos. Se cenas sendo processadas são suficientemente complexas, o desempenho de aceleração de hardware pode facilmente superam o custo de copiar os bitmaps. Por outro lado, se o processamento não é muito caro e pode ser executado pela CPU, localizar optar sem aceleração de hardware basicamente irá fornecer melhor desempenho. Essas opções não estão fácil fazer. Alguns GPUs não até ter memória dedicada e em vez disso use uma parte da memória do sistema, que reduz o custo da cópia.

O problema é que nem GDI WPF lhe uma escolha. No caso de GDI, você fica preso a CPU. No caso do WPF, você está forçados a integrar usando usos WPF abordagem qualquer processamento, que normalmente é aceleração de hardware por meio do Direct3D.

Em seguida, Direct2D veio.

Direct2D para GDI/DC

Direct2D foi projetada para processar qualquer destino que você escolher. Se for uma janela ou textura do Direct3D, Direct2D faz isso diretamente na GPU sem envolver qualquer copiando. Se for um bitmap do Windows Imaging Component (WIC), Direct2D da mesma forma processa diretamente usando a CPU em vez disso. Enquanto o WPF se esforça para colocar muito seu processamento na GPU e usa um rasterizador de software como um fallback, Direct2D fornece o melhor dos dois mundos com processamento de modo imediato inigualáveis na GPU para aceleração de hardware e altamente otimizado processamento na CPU quando uma GPU é não disponível ou não desejado.

Como você pode imaginar, há algumas maneiras para processar uma janela em camadas com Direct2D. Let’s observar algumas e vai aponto out abordagens recomendadas dependendo se você deseja usar a aceleração de hardware.

Primeiro, você poderia simplesmente copiar out classe Graphics GDI + da Figura 3 de e substitua-lo com um DC Direct2D processar destino. Isso pode fazer sentido se você tiver um aplicativo herdado com muita investida GDI, mas definitivamente não é a solução mais eficiente. Em vez de processamento diretamente para o CD, Direct2D processa primeiro para um bitmap WIC interno e, em seguida, copia o resultado para o CD. Embora mais rápido que o GDI +, isso não obstante envolve copiando extra que poderia ser evitada se você não precisa usar um DC para processamento.

Para usar essa abordagem, inicie inicializar uma estrutura D2D1_RENDER_TARGET_PROPERTIES. Isso informa Direct2D o formato de bitmap para usar seu alvo processado. Lembre-se de que ele precisa ser um formato de pixel BGRA pre-multiplied. Isso é expresso com uma estrutura D2D1_PIXEL_FORMAT e pode ser definido como segue:

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

Agora você pode criar o destino de render DC usando o objeto de fábrica Direct2D:

CComPtr<ID2D1DCRenderTarget> target;

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

Finalmente, você precisa dizer ao alvo processado para qual DC para enviar seus comandos de desenho:

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

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

Neste ponto você pode desenhar com Direct2D normalmente entre chamadas de método BeginDraw e EndDraw e chamar o método Update como antes com DC do bitmap. O método EndDraw garante todas desenho foi liberado para o CD acoplado.

Direct2D para WIC

Agora se você pode evitar inteiramente GDI DC e usar apenas um bitmap WIC diretamente, você pode obter o melhor desempenho possível sem aceleração de hardware. Para usar esta abordagem início criando um bitmap BGRA pre-multiplied diretamente com o 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));

Em seguida você precisa inicializar novamente uma estrutura D2D1_RENDER_TARGET_PROPERTIES muito da mesma maneira que antes, exceto que você também deve dizer Direct2D alvo processado precisa ser compatível com o 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);

Agora você pode criar o destino de render WIC usando o objeto de fábrica Direct2D:

CComPtr<ID2D1RenderTarget> target;

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

Mas o que exatamente D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE faz? É uma dica para Direct2D que irá consultar alvo processado para a interface ID2D1GdiInteropRenderTarget:

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

Para simplicidade e eficiência da implementação, a consulta para esta interface sempre terá êxito. Ele é somente quando você tentar usá-lo, no entanto, que falhará se você não especificar seu desejo antecipadamente.

Interface ID2D1GdiInteropRenderTarget tem apenas dois métodos: GetDC e ReleaseDC. Para otimizar casos onde a aceleração de hardware é usada, esses métodos são restritos usado entre chamadas para métodos de BeginDraw e EndDraw do alvo processado. GetDC irá liberar alvo processado antes de retornar o DC. Como métodos de interoperabilidade da interface precisam ser combinados, faz sentido dispor-los em uma classe C++, como mostrado no do Figura 7.

Figura 7 Render Target DC wrapper class

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 de processamento da janela agora pode ser atualizado para usar RenderTargetDC, como mostrado no do Figura 8. A coisa interessante sobre essa abordagem é que todo o código é específico para criar um destino de render WIC é enfiados ausente no método CreateDeviceResources. Em seguida, mostrarei como criar um destino de processamento Direct3D para obter a aceleração de hardware, mas em qualquer caso, o método Render mostrado do Figura 8 permanece o mesmo. Isso torna possível para o seu aplicativo alternar render destino implementações bastante facilmente sem alterar seu código desenho.

Figura 8 GDI-Compatible Render método

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 obter processamento acelerado por hardware, você precisa usar o Direct3D. Porque você não estiver processando diretamente para um HWND via ID2D1HwndRenderTarget, ganhará aceleração de hardware automaticamente, você precisa criar o dispositivo Direct3D sozinho e conectar pontos no subjacente DirectX Graphics Infrastructure (DXGI) para que você pode obter resultados GDI compatível.

DXGI é um subsistema relativamente novo que reside em uma camada abaixo Direct3D abstrair Direct3D do hardware subjacente e fornecer um gateway de alto desempenho para cenários de interoperabilidade. Direct2D também aproveita essa nova API para simplificar a mudança para versões futuras do Direct3D. Para usar essa abordagem, comece criando um dispositivo de hardware Direct3D. Este é o dispositivo que representa a GPU que irá executar o processamento. Aqui Estou usando a API de 10.1 Direct3D como isso é necessário por Direct2D no 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));

O sinalizador D3D10_CREATE_DEVICE_BGRA_SUPPORT é crucial para Direct2D interoperabilidade e o formato de pixel BGRA deve agora parecer familiar. Em um aplicativo Direct3D tradicional, você pode criar uma cadeia de permuta e recuperar seu buffer de fundo como textura para processar em antes de apresentar a janela processada. Desde que você estiver usando o Direct3D para processamento somente e não para apresentação, você pode simplesmente criar um recurso de textura diretamente. Uma textura é um recurso do Direct3D para armazenar texels, são o equivalente do Direct3D de pixels. Embora o Direct3D fornece 1-, 2 e 3-dimensional texturas, tudo o que você precisa é uma textura 2D, o que mais parecido mapeia para uma 2D superfície (consulte do Figura 9).

Figura 9 de uma 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));

A estrutura D3D10_TEXTURE2D_DESC descreve a textura de criar. A constante D3D10_BIND_RENDER_TARGET indica a textura está acoplada como o buffer de saída ou o destino de processamento do pipeline Direct3D. A constante DXGI_FORMAT_B8G8R8A8_UNORM garante que o Direct3D produzirá o formato de pixel correto para GDI. Finalmente, a constante D3D10_RESOURCE_MISC_GDI_COMPATIBLE instrui a superfície DXGI subjacente para oferecer um DC GDI através do qual os resultados de processamento podem ser obtidos. Este Direct2D expõe através da interface ID2D1GdiInteropRenderTarget discutido na seção anterior.

Como mencionei, Direct2D é capaz de processamento para uma superfície Direct3D via API DXGI para evitar amarrando a API para qualquer versão específica do Direct3D. Isso significa que você precisa obter subjacente DXGI superfície interface a textura Direct3D para passar para Direct2D:

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

Neste ponto você pode usar o objeto de fábrica Direct2D para criar um destino de superfície render DXGI:

CComPtr<ID2D1RenderTarget> target;

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

As propriedades de destino render são a mesma descrita na seção anterior. Apenas lembre-se usar o formato de pixel correto e solicitar compatibilidade GDI. Você pode consultar a interface ID2D1GdiInteropRenderTarget e usar o mesmo método de processamento de do Figura 8.

E isso é tudo o que há para ele. Se você deseja processar sua janela em camadas com aceleração de hardware, use uma textura Direct3D. Caso contrário, use um bitmap WIC. Essas duas abordagens fornecerá o melhor desempenho possível com o mínimo de copiar.

Certifique-se de verificar o blog do DirectX e, em particular, artigo de agosto de 2009 do Ben Condestável em componentization e interoperabilidade em blogs.msdn.com/directx de .

Kenny Kerr um profissional de software apaixonado pelo Windows. Ele também é o criador da janela recortes (windowclippings.com). Entrar em weblogs.asp.net/kennykerr de.

Envie suas dúvidas e comentários para Kerr de mmwincpp@microsoft.com .

Graças aos seguintes especialistas técnicos para revisão deste artigo: Mark Lawrence e Ben Condestável