Tutorial: Introdução com DirectWrite

Este documento mostra como usar DirectWrite e Direct2D para criar texto simples que contém um único formato e, em seguida, texto que contém vários formatos.

Este tutorial contém as seguintes partes:

Código-fonte

O código-fonte mostrado nesta visão geral é obtido do exemplo de DirectWrite Olá, Mundo. Cada parte é implementada em uma classe separada (SimpleText e MultiformattedText) e é exibida em uma janela filho separada. Cada classe representa uma janela do Microsoft Win32. Além do método WndProc , cada classe contém os seguintes métodos:

Função Descrição
CreateDeviceIndependentResources Cria recursos que são independentes do dispositivo, para que possam ser reutilizados em qualquer lugar.
DiscardDeviceIndependentResources Libera os recursos independentes do dispositivo depois que eles não são mais necessários.
CreateDeviceResources Cria recursos, como pincéis e destinos de renderização, que estão vinculados a um dispositivo específico.
DiscardDeviceResources Libera os recursos dependentes do dispositivo depois que eles não são mais necessários.
DrawD2DContent Usa Direct2D para renderizar na tela.
Drawtext Desenha a cadeia de caracteres de texto usando Direct2D.
Onresize Redimensiona o destino de renderização do Direct2D quando o tamanho da janela é alterado.

 

Você pode usar o exemplo fornecido ou usar as instruções a seguir para adicionar DirectWrite e Direct2D ao seu próprio aplicativo Win32. Para obter mais informações sobre o exemplo e os arquivos de projeto associados, consulte o DirectWrite HelloWorld.

Desenho de texto simples

Esta seção mostra como usar DirectWrite e Direct2D para renderizar texto simples que tenha um único formato, conforme mostrado na captura de tela a seguir.

captura de tela de

Desenhar texto simples na tela requer quatro componentes:

  • Uma cadeia de caracteres a ser renderizada.
  • Uma instância de IDWriteTextFormat.
  • As dimensões da área a conter o texto.
  • Um objeto que pode renderizar o texto. Neste tutorial, você usa um destino de renderização Direct2D .

A interface IDWriteTextFormat descreve o nome da família de fontes, o tamanho, o peso, o estilo e o alongamento usados para formatar texto e descreve as informações de localidade. IDWriteTextFormat também define métodos para definir e obter as seguintes propriedades:

  • O espaçamento de linha.
  • O alinhamento de texto em relação às bordas esquerda e direita da caixa de layout.
  • O alinhamento do parágrafo em relação à parte superior e inferior da caixa de layout.
  • A direção da leitura.
  • A granularidade de corte de texto para texto que estoura a caixa de layout.
  • A parada de tabulação incremental.
  • A direção do fluxo do parágrafo.

A interface IDWriteTextFormat é necessária para o desenho de texto que usa os dois processos descritos neste documento.

Antes de criar um objeto IDWriteTextFormat ou qualquer outro objeto DirectWrite, você precisa de uma instância IDWriteFactory. Você usa um IDWriteFactory para criar instâncias IDWriteTextFormat e outros objetos DirectWrite. Para obter uma instância de fábrica, use a função DWriteCreateFactory .

Parte 1: declarar recursos DirectWrite e Direct2D.

Nesta parte, você declara os objetos que usará posteriormente para criar e exibir texto como membros de dados privados de sua classe. Todas as interfaces, funções e tipos de dados para DirectWrite são declaradas no arquivo de cabeçalho dwrite.h e aquelas para Direct2D são declaradas no d2d1.h; se você ainda não fez isso, inclua esses cabeçalhos em seu projeto.

  1. No arquivo de cabeçalho de classe (SimpleText.h), declare ponteiros para interfaces IDWriteFactory e IDWriteTextFormat como membros privados.

    IDWriteFactory* pDWriteFactory_;
    IDWriteTextFormat* pTextFormat_;
    
    
  2. Declare membros para manter a cadeia de caracteres de texto a ser renderizada e o comprimento da cadeia de caracteres.

    const wchar_t* wszText_;
    UINT32 cTextLength_;
    
    
  3. Declare ponteiros para interfaces ID2D1Factory, ID2D1HwndRenderTarget e ID2D1SolidColorBrush para renderizar o texto com Direct2D.

    ID2D1Factory* pD2DFactory_;
    ID2D1HwndRenderTarget* pRT_;
    ID2D1SolidColorBrush* pBlackBrush_;
    
    

Parte 2: Criar recursos independentes do dispositivo.

O Direct2D fornece dois tipos de recursos: recursos dependentes do dispositivo e recursos independentes do dispositivo. Os recursos dependentes do dispositivo são associados a um dispositivo de renderização e não funcionam mais se esse dispositivo for removido. Os recursos independentes do dispositivo, por outro lado, podem durar para o escopo do aplicativo.

DirectWrite recursos são independentes do dispositivo.

Nesta seção, você criará os recursos independentes do dispositivo usados pelo aplicativo. Esses recursos devem ser liberados com uma chamada para o método Release da interface.

Alguns dos recursos usados precisam ser criados apenas uma vez e não estão vinculados a um dispositivo. A inicialização desses recursos é colocada no método SimpleText::CreateDeviceIndependentResources , que é chamado ao inicializar a classe .

  1. Dentro do método SimpleText::CreateDeviceIndependentResources no arquivo de implementação de classe (SimpleText.cpp), chame a função D2D1CreateFactory para criar uma interface ID2D1Factory , que é a interface de fábrica raiz para todos os objetos Direct2D . Você usa a mesma fábrica para instanciar outros recursos do Direct2D.

    hr = D2D1CreateFactory(
        D2D1_FACTORY_TYPE_SINGLE_THREADED,
        &pD2DFactory_
        );
    
    
  2. Chame a função DWriteCreateFactory para criar uma interface IDWriteFactory, que é a interface de fábrica raiz para todos os objetos DirectWrite. Você usa a mesma fábrica para instanciar outros recursos DirectWrite.

    if (SUCCEEDED(hr))
    {
        hr = DWriteCreateFactory(
            DWRITE_FACTORY_TYPE_SHARED,
            __uuidof(IDWriteFactory),
            reinterpret_cast<IUnknown**>(&pDWriteFactory_)
            );
    }
    
    
  3. Inicialize a cadeia de caracteres de texto e armazene seu comprimento.

    wszText_ = L"Hello World using  DirectWrite!";
    cTextLength_ = (UINT32) wcslen(wszText_);
    
    
  4. Crie um objeto de interface IDWriteTextFormat usando o método IDWriteFactory::CreateTextFormat . O IDWriteTextFormat especifica a fonte, o peso, o alongamento, o estilo e a localidade que serão usados para renderizar a cadeia de caracteres de texto.

    if (SUCCEEDED(hr))
    {
        hr = pDWriteFactory_->CreateTextFormat(
            L"Gabriola",                // Font family name.
            NULL,                       // Font collection (NULL sets it to use the system font collection).
            DWRITE_FONT_WEIGHT_REGULAR,
            DWRITE_FONT_STYLE_NORMAL,
            DWRITE_FONT_STRETCH_NORMAL,
            72.0f,
            L"en-us",
            &pTextFormat_
            );
    }
    
    
  5. Centralize o texto horizontal e verticalmente chamando os métodos IDWriteTextFormat::SetTextAlignment e IDWriteTextFormat::SetParagraphAlignment .

    // Center align (horizontally) the text.
    if (SUCCEEDED(hr))
    {
        hr = pTextFormat_->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
    }
    
    if (SUCCEEDED(hr))
    {
        hr = pTextFormat_->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
    }
    
    

Nesta parte, você inicializou os recursos independentes do dispositivo usados pelo aplicativo. Na próxima parte, você inicializa os recursos dependentes do dispositivo.

Parte 3: Criar recursos de Device-Dependent.

Nesta parte, você cria um ID2D1HwndRenderTarget e um ID2D1SolidColorBrush para renderizar seu texto.

Um destino de renderização é um objeto Direct2D que cria recursos de desenho e renderiza comandos de desenho em um dispositivo de renderização. Um ID2D1HwndRenderTarget é um destino de renderização que é renderizado para um HWND.

Um dos recursos de desenho que um destino de renderização pode criar é um pincel para estruturas de tópicos de pintura, preenchimentos e texto. Um ID2D1SolidColorBrush pinta com uma cor sólida.

As interfaces ID2D1HwndRenderTarget e ID2D1SolidColorBrush são associadas a um dispositivo de renderização quando são criadas e devem ser liberadas e recriadas se o dispositivo se tornar inválido.

  1. Dentro do método SimpleText::CreateDeviceResources, marcar se o ponteiro de destino de renderização é NULL. Se for, recupere o tamanho da área de renderização e crie um ID2D1HwndRenderTarget desse tamanho. Use o ID2D1HwndRenderTarget para criar um ID2D1SolidColorBrush.

    RECT rc;
    GetClientRect(hwnd_, &rc);
    
    D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
    
    if (!pRT_)
    {
        // Create a Direct2D render target.
        hr = pD2DFactory_->CreateHwndRenderTarget(
                D2D1::RenderTargetProperties(),
                D2D1::HwndRenderTargetProperties(
                    hwnd_,
                    size
                    ),
                &pRT_
                );
    
        // Create a black brush.
        if (SUCCEEDED(hr))
        {
            hr = pRT_->CreateSolidColorBrush(
                D2D1::ColorF(D2D1::ColorF::Black),
                &pBlackBrush_
                );
        }
    }
    
    
  2. No método SimpleText::D iscardDeviceResources, libere o pincel e o destino de renderização.

    SafeRelease(&pRT_);
    SafeRelease(&pBlackBrush_);
    
    

Agora que você criou um destino de renderização e um pincel, você pode usá-los para renderizar o texto.

Parte 4: desenhar texto usando o método DrawText direct2D.

  1. No método SimpleText::D rawText de sua classe, defina a área para o layout de texto recuperando as dimensões da área de renderização e crie um retângulo Direct2D que tenha as mesmas dimensões.

    D2D1_RECT_F layoutRect = D2D1::RectF(
        static_cast<FLOAT>(rc.left) / dpiScaleX_,
        static_cast<FLOAT>(rc.top) / dpiScaleY_,
        static_cast<FLOAT>(rc.right - rc.left) / dpiScaleX_,
        static_cast<FLOAT>(rc.bottom - rc.top) / dpiScaleY_
        );
    
    
  2. Use o método ID2D1RenderTarget::D rawText e o objeto IDWriteTextFormat para renderizar texto na tela. O método ID2D1RenderTarget::D rawText usa os seguintes parâmetros:

    • Uma cadeia de caracteres a ser renderizada.
    • Um ponteiro para uma interface IDWriteTextFormat .
    • Um retângulo de layout Direct2D .
    • Um ponteiro para uma interface que expõe ID2D1Brush.
    pRT_->DrawText(
        wszText_,        // The string to render.
        cTextLength_,    // The string's length.
        pTextFormat_,    // The text format.
        layoutRect,       // The region of the window where the text will be rendered.
        pBlackBrush_     // The brush used to draw the text.
        );
    
    

Parte 5: Renderizar o conteúdo da janela usando o Direct2D

Para renderizar o conteúdo da janela usando Direct2D quando uma mensagem de pintura é recebida, faça o seguinte:

  1. Crie os recursos dependentes do dispositivo chamando o método SimpleText::CreateDeviceResources implementado na Parte 3.
  2. Chame o método ID2D1HwndRenderTarget::BeginDraw do destino de renderização.
  3. Limpe o destino de renderização chamando o método ID2D1HwndRenderTarget::Clear .
  4. Chame o método SimpleText::D rawText, implementado na Parte 4.
  5. Chame o método ID2D1HwndRenderTarget::EndDraw do destino de renderização.
  6. Se for necessário, descarte os recursos dependentes do dispositivo para que eles possam ser recriados quando a janela for redesenhada.
hr = CreateDeviceResources();

if (SUCCEEDED(hr))
{
    pRT_->BeginDraw();

    pRT_->SetTransform(D2D1::IdentityMatrix());

    pRT_->Clear(D2D1::ColorF(D2D1::ColorF::White));

    // Call the DrawText method of this class.
    hr = DrawText();

    if (SUCCEEDED(hr))
    {
        hr = pRT_->EndDraw(
            );
    }
}

if (FAILED(hr))
{
    DiscardDeviceResources();
}

A classe SimpleText é implementada em SimpleText.h e SimpleText.cpp.

Desenho de texto com vários formatos.

Esta seção mostra como usar DirectWrite e Direct2D para renderizar texto com vários formatos, conforme mostrado na captura de tela a seguir.

captura de tela de

O código desta seção é implementado como a classe MultiformattedText no DirectWrite HelloWorld. Ele se baseia nas etapas da seção anterior.

Para criar texto com vários formatos, use a interface IDWriteTextLayout , além da interface IDWriteTextFormat introduzida na seção anterior. A interface IDWriteTextLayout descreve a formatação e o layout de um bloco de texto. Além da formatação padrão especificada por um objeto IDWriteTextFormat , a formatação para intervalos específicos de texto pode ser alterada usando IDWriteTextLayout. Isso inclui nome da família de fontes, tamanho, peso, estilo, alongamento, tachado e sublinhado.

IDWriteTextLayout também fornece métodos de teste de clique. As métricas de teste de clique retornadas por esses métodos são relativas à caixa de layout especificada quando o objeto de interface IDWriteTextLayout é criado usando o método CreateTextLayout da interface IDWriteFactory .

A interface IDWriteTypography é usada para adicionar recursos tipográficos OpenType opcionais a um layout de texto, como swashes e conjuntos de texto estilísticos alternativos. Recursos tipográficos podem ser adicionados a um intervalo específico de texto em um layout de texto chamando o método AddFontFeature da interface IDWriteTypography . Esse método recebe uma estrutura DWRITE_FONT_FEATURE como um parâmetro que contém uma constante de enumeração DWRITE_FONT_FEATURE_TAG e um parâmetro de execução UINT32 . Uma lista de recursos opentype registrados pode ser encontrada no Registro de Marca de Layout OpenType no microsoft.com. Para as constantes de enumeração de DirectWrite equivalentes, consulte DWRITE_FONT_FEATURE_TAG.

Parte 1: criar uma interface IDWriteTextLayout.

  1. Declare um ponteiro para uma interface IDWriteTextLayout como um membro da classe MultiformattedText.

    IDWriteTextLayout* pTextLayout_;
    
    
  2. No final do método MultiformattedText::CreateDeviceIndependentResources, crie um objeto de interface IDWriteTextLayout chamando o método CreateTextLayout . A interface IDWriteTextLayout fornece recursos de formatação adicionais, como a capacidade de aplicar formatos diferentes a partes selecionadas do texto.

    // Create a text layout using the text format.
    if (SUCCEEDED(hr))
    {
        RECT rect;
        GetClientRect(hwnd_, &rect); 
        float width  = rect.right  / dpiScaleX_;
        float height = rect.bottom / dpiScaleY_;
    
        hr = pDWriteFactory_->CreateTextLayout(
            wszText_,      // The string to be laid out and formatted.
            cTextLength_,  // The length of the string.
            pTextFormat_,  // The text format to apply to the string (contains font information, etc).
            width,         // The width of the layout box.
            height,        // The height of the layout box.
            &pTextLayout_  // The IDWriteTextLayout interface pointer.
            );
    }
    
    

Parte 2: Aplicar formatação com IDWriteTextLayout.

A formatação, como tamanho da fonte, peso e sublinhado, pode ser aplicada a subcadeias de caracteres do texto a serem exibidas usando a interface IDWriteTextLayout .

  1. Defina o tamanho da fonte para a subcadeia de caracteres "Di" de "DirectWrite" como 100 declarando um DWRITE_TEXT_RANGE e chamando o método IDWriteTextLayout::SetFontSize.

    // Format the "DirectWrite" substring to be of font size 100.
    if (SUCCEEDED(hr))
    {
        DWRITE_TEXT_RANGE textRange = {20,        // Start index where "DirectWrite" appears.
                                        6 };      // Length of the substring "Direct" in "DirectWrite".
        hr = pTextLayout_->SetFontSize(100.0f, textRange);
    }
    
  2. Sublinhar a subcadeia de caracteres "DirectWrite" chamando o método IDWriteTextLayout::SetUnderline.

    // Format the word "DWrite" to be underlined.
    if (SUCCEEDED(hr))
    {
    
        DWRITE_TEXT_RANGE textRange = {20,      // Start index where "DirectWrite" appears.
                                       11 };    // Length of the substring "DirectWrite".
        hr = pTextLayout_->SetUnderline(TRUE, textRange);
    }
    
  3. Defina o peso da fonte como negrito para a subcadeia de caracteres "DirectWrite" chamando o método IDWriteTextLayout::SetFontWeight.

    if (SUCCEEDED(hr))
    {
        // Format the word "DWrite" to be bold.
        DWRITE_TEXT_RANGE textRange = {20,
                                       11 };
        hr = pTextLayout_->SetFontWeight(DWRITE_FONT_WEIGHT_BOLD, textRange);
    }
    

Parte 3: adicionando recursos tipográficos com IDWriteTypography.

  1. Declare e crie um objeto de interface IDWriteTypography chamando o método IDWriteFactory::CreateTypography .

    // Declare a typography pointer.
    IDWriteTypography* pTypography = NULL;
    
    // Create a typography interface object.
    if (SUCCEEDED(hr))
    {
        hr = pDWriteFactory_->CreateTypography(&pTypography);
    }
    
    
  2. Adicione um recurso de fonte declarando um objeto DWRITE_FONT_FEATURE que tenha o conjunto estilístico 7 especificado e chamando o método IDWriteTypography::AddFontFeature .

    // Set the stylistic set.
    DWRITE_FONT_FEATURE fontFeature = {DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_7,
                                       1};
    if (SUCCEEDED(hr))
    {
        hr = pTypography->AddFontFeature(fontFeature);
    }
    
    
  3. Defina o layout de texto para usar a tipografia sobre toda a cadeia de caracteres declarando uma variável DWRITE_TEXT_RANGE e chamando o método IDWriteTextLayout::SetTypography e passando o intervalo de texto.

    if (SUCCEEDED(hr))
    {
        // Set the typography for the entire string.
        DWRITE_TEXT_RANGE textRange = {0,
                                       cTextLength_};
        hr = pTextLayout_->SetTypography(pTypography, textRange);
    }
    
    
  4. Defina a nova largura e altura do objeto de layout de texto no método MultiformattedText::OnResize.

    if (pTextLayout_)
    {
        pTextLayout_->SetMaxWidth(static_cast<FLOAT>(width / dpiScaleX_));
        pTextLayout_->SetMaxHeight(static_cast<FLOAT>(height / dpiScaleY_));
    }
    

Parte 4: desenhar texto usando o método DrawTextLayout do Direct2D.

Para desenhar o texto com as configurações de layout de texto especificadas pelo objeto IDWriteTextLayout , altere o código no método MultiformattedText::D rawText para usar IDWriteTextLayout::D rawTextLayout.

  1. Delcare uma variável D2D1_POINT_2F e defina-a como o ponto superior esquerdo da janela.

    D2D1_POINT_2F origin = D2D1::Point2F(
        static_cast<FLOAT>(rc.left / dpiScaleX_),
        static_cast<FLOAT>(rc.top / dpiScaleY_)
        );
    
    
  2. Desenhe o texto para a tela chamando o método ID2D1RenderTarget::D rawTextLayout do destino de renderização Direct2D e passando o ponteiro IDWriteTextLayout .

    pRT_->DrawTextLayout(
        origin,
        pTextLayout_,
        pBlackBrush_
        );
    
    

A classe MultiformattedText é implementada em MultiformattedText.h e MultiformattedText.cpp.