Compartilhar via


Renderização de texto com Direct2D e DirectWrite

Ao contrário de outras APIs, como GDI, GDI+ ou WPF, Direct2D interopera com outra API, DirectWrite, para manipular e renderizar texto. Este tópico descreve os benefícios e a interoperação desses componentes separados.

Este tópico inclui as seções a seguir.

Direct2D habilita a adoção incremental

Mover um aplicativo de uma API gráfica para outra pode ser difícil ou não o que você deseja por vários motivos. Isso pode ser porque você precisa dar suporte a plug-ins que ainda levam as interfaces mais antigas, porque o próprio aplicativo é muito grande para fazer a portabilidade para uma nova API em uma versão ou porque alguma parte da API mais recente é desejável, mas a API mais antiga está funcionando bem o suficiente para outras partes do aplicativo.

Como Direct2D e DirectWrite são implementados como componentes separados, você pode atualizar todo o sistema gráfico 2D ou apenas a parte de texto dele. Por exemplo, você pode atualizar um aplicativo para usar DirectWrite para texto, mas ainda usar GDI ou GDI+ para renderização.

Serviços de Texto versus Renderização de Texto

À medida que os aplicativos evoluíram, seus requisitos de processamento de texto tornaram-se cada vez mais complexos. No início, o texto era geralmente confinado à interface do usuário estaticamente disposta e o texto era renderizado em uma caixa bem definida, como um botão. À medida que os aplicativos começaram a estar disponíveis em um número crescente de idiomas, essa abordagem tornou-se mais difícil de sustentar porque a largura e a altura do texto traduzido podem variar significativamente entre os idiomas. Para se adaptar, os aplicativos começaram a dispor dinamicamente sua interface do usuário para depender do tamanho real renderizado do texto, em vez do contrário.

Para ajudar os aplicativos a concluir essa tarefa, DirectWrite fornece a interface IDWriteTextLayout. Essa API permite que um aplicativo especifique um trecho de texto com características complexas, como diferentes fontes e tamanhos de fonte, sublinhados, tachados, texto bidirecional, efeitos, reticências e até mesmo caracteres não glifos inseridos (como um emoticon bitmap ou um ícone). Em seguida, o aplicativo pode alterar várias características do texto, pois ele determina iterativamente seu layout de interface do usuário. O exemplo de DirectWrite Olá, Mundo, mostrado na ilustração a seguir e no tópico Tutorial: Introdução com DirectWrite, mostra muitos desses efeitos.

captura de tela do exemplo

O layout pode posicionar os glifos idealmente com base em suas larguras (como o WPF faz) ou pode ajustar os glifos às posições de pixel mais próximas (como o GDI faz).

Além de obter medidas de texto, o aplicativo pode testar várias partes do texto. Por exemplo, talvez ele queira saber que um hiperlink no texto é clicado. (Para obter mais informações sobre testes de clique, consulte o tópico Como executar testes de clique em um layout de texto .)

A interface de layout de texto é dissociada da API de renderização que o aplicativo usa, como mostra o diagrama a seguir:

layout de texto e diagrama de API de gráficos.

Essa separação é possível porque DirectWrite fornece uma interface de renderização (IDWriteTextRenderer) que os aplicativos podem implementar para renderizar texto usando qualquer API gráfica desejada. O método de retorno de chamada IDWriteTextRenderer::D rawGlyphRun implementado é chamado por DirectWrite ao renderizar um layout de texto. É responsabilidade desse método executar as operações de desenho ou passá-las.

Para desenhar glifos, Direct2D fornece ID2D1RenderTarget::D rawGlyphRun para desenhar em uma superfície Direct2D e DirectWrite fornece IDWriteBitmapRenderTarget::D rawGlyphRun para desenhar em uma superfície GDI que pode ser transferida para uma janela usando GDI. Convenientemente, DrawGlyphRun em Direct2D e DirectWrite têm parâmetros exatamente compatíveis com o método DrawGlyphRun que o aplicativo implementa em IDWriteTextRenderer.

Após uma separação semelhante, os recursos específicos de texto (como enumeração e gerenciamento de fontes, análise de glifo e assim por diante) são tratados por DirectWrite em vez de Direct2D. Os objetos DirectWrite são aceitos diretamente pelo Direct2D. Para ajudar os aplicativos GDI existentes a aproveitar DirectWrite, ele fornece a interface do método IDWriteGdiInterop com métodos para fazer o seguinte:

Glifos versus Texto

O texto é um conjunto de pontos de código Unicode (caracteres), com vários modificadores estilísticos (fontes, pesos, sublinhados, tachados e assim por diante) dispostos em um retângulo. Um glifo, por outro lado, é um índice específico em um arquivo de fonte específico. Um glifo define um conjunto de curvas que podem ser renderizadas, mas não tem nenhum significado textual. Há potencialmente um mapeamento muitos para muitos entre glifos e caracteres. Uma sequência de glifos provenientes da mesma Face de Fonte e que são dispostos sequencialmente em uma linha de base é chamada de GlyphRun. Tanto DirectWrite quanto Direct2D chamam sua API de renderização de glifo mais precisa DrawGlyphRun e têm assinaturas muito semelhantes. O seguinte é de ID2D1RenderTarget no Direct2D:

STDMETHOD_(void, DrawGlyphRun)(
        D2D1_POINT_2F baselineOrigin,
        __in CONST DWRITE_GLYPH_RUN *glyphRun,
        __in ID2D1Brush *foregroundBrush,
        DWRITE_MEASURING_MODE measuringMode = DWRITE_MEASURING_MODE_NATURAL 
        ) PURE;

E esse método é de IDWriteBitmapRenderTarget no DirectWrite:

STDMETHOD(DrawGlyphRun)(
        FLOAT baselineOriginX,
        FLOAT baselineOriginY,
        DWRITE_MEASURING_MODE measuringMode,
        __in DWRITE_GLYPH_RUN const* glyphRun,
        IDWriteRenderingParams* renderingParams,
        COLORREF textColor,
        __out_opt RECT* blackBoxRect = NULL
        ) PURE;

A versão DirectWrite mantém a origem da linha de base, o modo de medição e os parâmetros de execução do glifo e inclui parâmetros adicionais.

DirectWrite também permite que você use um renderizador personalizado para glifos implementando a interface IDWriteTextRenderer. Essa interface também tem um método DrawGlyphRun , como mostra o exemplo de código a seguir.

STDMETHOD(DrawGlyphRun)(
        __maybenull void* clientDrawingContext,
        FLOAT baselineOriginX,
        FLOAT baselineOriginY,
        DWRITE_MEASURING_MODE measuringMode,
        __in DWRITE_GLYPH_RUN const* glyphRun,
        __in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
        __maybenull IUnknown* clientDrawingEffect
        ) PURE;

Esta versão inclui mais parâmetros que são úteis quando você implementa um renderizador de texto personalizado. O parâmetro final é usado para efeitos de desenho personalizados implementados pelo aplicativo. (Para obter mais informações sobre efeitos de desenho do cliente, consulte Como adicionar efeitos de desenho do cliente a um layout de texto.

Cada execução de glifo começa em uma origem e é colocada em uma linha a partir dessa origem. Os glifos são alterados pela transformação do mundo atual e pelas configurações de renderização de texto selecionadas no destino de renderização associado. Essa API geralmente é chamada diretamente apenas por aplicativos que fazem seu próprio layout (por exemplo, um processador de Word) ou por um aplicativo que implementou a interface IDWriteTextRenderer.

DirectWrite e Direct2D

Direct2D fornece serviços de renderização no nível do glifo por meio de DrawGlyphRun. No entanto, isso requer que o aplicativo implemente os detalhes da renderização, que basicamente reproduz a funcionalidade da API DrawText da GDI por conta própria.

Portanto, Direct2D fornece APIs que aceitam texto em vez de glifos: ID2D1RenderTarget::D rawTextLayout e ID2D1RenderTarget::D rawText. Ambos os métodos são renderizados em uma superfície Direct2D. Para renderizar em uma superfície GDI, IDWriteBitmapRenderTarget::D rawGlyphRun é fornecido. Mas esse método requer que um renderizador de texto personalizado seja implementado pelo aplicativo. (Para obter mais informações, consulte o tópico Renderizar para um Surface GDI .)

O uso de texto de um aplicativo normalmente começa simples: colocar OK ou Cancelar em um botão de layout fixo, por exemplo. No entanto, com o tempo, ela se torna mais complexa à medida que a internacionalização e outros recursos são adicionados. Eventualmente, muitos aplicativos terão que usar os objetos de layout de texto do DirectWrite e implementar o renderizador de texto.

Portanto, Direct2D fornece APIs em camadas que permitem que um aplicativo comece de forma simples e se tornar mais sofisticado sem precisar voltar atrás ou abandonar seu código de trabalho. Uma exibição simplificada é mostrada no diagrama a seguir:

diagrama de aplicativo directwrite e direct2d.

Drawtext

DrawText é a mais simples das APIs a serem usadas. Ele usa uma cadeia de caracteres Unicode, um pincel de primeiro plano, um único objeto de formato e um retângulo de destino. Ele disporá e renderizará toda a cadeia de caracteres dentro do retângulo de layout e, opcionalmente, a recortará. Isso é útil quando você coloca um texto simples em uma parte da interface do usuário de layout fixo.

DrawTextLayout

Ao criar um objeto IDWriteTextLayout , um aplicativo pode começar a medir e organizar o texto e outros elementos da interface do usuário e dar suporte a várias fontes, estilos, sublinhados e tachados. Direct2D fornece a API DrawTextLayout que aceita diretamente esse objeto e renderiza o texto em um determinado ponto. (A largura e a altura são fornecidas pelo objeto de layout). Além de implementar todos os recursos de layout de texto esperados, Direct2D interpretará qualquer objeto de efeito como um pincel e aplicará esse pincel ao intervalo selecionado de glifos. Ele também chamará quaisquer objetos embutidos. Um aplicativo pode inserir caracteres não glifos (ícones) no texto, se desejar. Outra vantagem de usar um objeto de layout de texto é que as posições de glifo são armazenadas em cache nele. Portanto, um grande ganho de desempenho é possível reutilizando o mesmo objeto de layout para várias chamadas de desenho e evitando recalcular as posições de glifo para cada chamada. Essa funcionalidade não está presente para DrawText da GDI.

DrawGlyphRun

Por fim, o aplicativo pode implementar a própria interface IDWriteTextRenderer e chamar DrawGlyphRun e FillRectangle em si ou qualquer outra API de renderização. Toda a interação existente com o objeto Layout de Texto permanecerá inalterada.

Para obter um exemplo de como implementar um renderizador de texto personalizado, consulte o tópico Renderizar usando um renderizador de texto personalizado .

Renderização de glifo

Adicionar DirectWrite a um aplicativo GDI existente permite que o aplicativo use a API IDWriteBitmapRenderTarget para renderizar glifos. O método IDWriteBitmapRenderTarget::D rawGlyphRun que DirectWrite fornece renderizará em cor sólida para um DC de memória sem a necessidade de APIs adicionais, como Direct2D.

Isso permite que o aplicativo obtenha recursos avançados de renderização de texto, como o seguinte:

  • O ClearType de sub pixel permite que um aplicativo coloque glifos em posições de sub-pixel para permitir a renderização de glifo afiado e o layout de glifo.
  • A suavização de direção Y permite renderização mais suave de curvas em glifos maiores.

Um aplicativo que muda para Direct2D também obterá os seguintes recursos:

  • Aceleração de hardware.
  • A capacidade de preencher texto com um pincel arbitrário Direct2D, como gradientes radiais, gradientes lineares e bitmaps.
  • Mais suporte para camadas e recorte por meio das APIs PushAxisAlignedClip, PushLayer e CreateCompatibleRenderTarget .
  • A capacidade de dar suporte à renderização de texto em escala de cinza. Isso preenche corretamente o canal alfa de destino de acordo com a opacidade do pincel de texto e a suavização do texto.

Para dar suporte eficiente à aceleração de hardware, Direct2D usa uma aproximação ligeiramente diferente para a correção gama chamada correção alfa. Isso não exige Direct2D inspecionar o pixel de cor de destino de renderização ao renderizar o texto.

Conclusão

Este tópico explica as diferenças e semelhanças entre Direct2D e DirectWrite e as motivações arquitetônicas para fornecê-las como APIs cooperativas separadas.