Representación de texto con Direct2D y DirectWrite

A diferencia de otras API, como GDI, GDI+ o WPF, Direct2D interopera con otra API, DirectWrite, para manipular y representar texto. En este tema se describen las ventajas e interoperación de estos componentes independientes.

En este tema se incluyen las siguientes secciones.

Direct2D habilita la adopción incremental

Mover una aplicación de una API de gráficos a otra puede ser difícil o no lo que desee por diversos motivos. Esto puede deberse a que tiene que admitir complementos que todavía toman las interfaces anteriores, ya que la propia aplicación es demasiado grande para migrar a una nueva API en una versión o porque alguna parte de la API más reciente es deseable, pero la API anterior funciona lo suficientemente bien para otras partes de la aplicación.

Dado que Direct2D y DirectWrite se implementan como componentes independientes, puedes actualizar todo el sistema de gráficos 2D o simplemente la parte de texto. Por ejemplo, podría actualizar una aplicación para usar DirectWrite para texto, pero seguir usando GDI o GDI+ para la representación.

Text Services frente a La representación de texto

A medida que las aplicaciones han evolucionado, sus requisitos de procesamiento de texto han crecido cada vez más complejos. Al principio, el texto se limitaba generalmente a la interfaz de usuario de diseño estático y el texto se representaba en un cuadro bien definido, como un botón. A medida que las aplicaciones empezaron a estar disponibles en un número creciente de idiomas, este enfoque se volvió más difícil de mantener, ya que tanto el ancho como el alto del texto traducido pueden variar significativamente entre idiomas. Para adaptarse, las aplicaciones empezaron a diseñar dinámicamente su interfaz de usuario para depender del tamaño real representado del texto, en lugar del contrario.

Para ayudar a las aplicaciones a completar esta tarea, DirectWrite proporciona la interfaz IDWriteTextLayout. Esta API permite a una aplicación especificar un fragmento de texto con características complejas, como diferentes fuentes y tamaños de fuente, subrayados, tachados, texto bidireccional, efectos, puntos suspensivos e incluso caracteres no glifos incrustados (como un emoticono de mapa de bits o un icono). Después, la aplicación puede cambiar varias características del texto, ya que determina iterativamente su diseño de interfaz de usuario. El ejemplo de DirectWrite Hola mundo, que se muestra en la ilustración siguiente y en el tema Tutorial: Introducción con DirectWrite, muestra muchos de estos efectos.

captura de pantalla del ejemplo

El diseño puede colocar los glifos idealmente en función de sus anchos (como hace WPF), o puede ajustar los glifos a las posiciones de píxeles más cercanas (como hace GDI ).

Además de obtener medidas de texto, la aplicación puede hacer pruebas de posicionamiento de varias partes del texto. Por ejemplo, es posible que quiera saber que se hace clic en un hipervínculo en el texto. (Para obtener más información sobre las pruebas de posicionamiento, vea el tema Cómo realizar pruebas de posicionamiento en un diseño de texto ).

La interfaz de diseño de texto se desacopla de la API de representación que usa la aplicación, como se muestra en el diagrama siguiente:

diseño de texto y diagrama de API de gráficos.

Esta separación es posible porque DirectWrite proporciona una interfaz de representación (IDWriteTextRenderer) que las aplicaciones pueden implementar para representar texto mediante cualquier API de gráficos que desee. El método de devolución de llamada IDWriteTextRenderer::D rawGlyphRun se llama a la aplicación DirectWrite al representar un diseño de texto. Es responsabilidad de este método realizar las operaciones de dibujo o pasarlas.

Para los glifos de dibujo, Direct2D proporciona ID2D1RenderTarget::D rawGlyphRun para dibujar en una superficie direct2D y DirectWrite proporciona IDWriteBitmapRenderTarget::D rawGlyphRun para dibujar en una superficie GDI que luego se puede transferir a una ventana mediante GDI. Convenientemente, DrawGlyphRun en Direct2D y DirectWrite tienen parámetros exactamente compatibles con el método DrawGlyphRun que la aplicación implementa en IDWriteTextRenderer.

Después de una separación similar, las características específicas del texto (como la enumeración de fuentes y la administración, el análisis del glifo, etc.) se controlan mediante DirectWrite en lugar de Direct2D. Direct2D acepta directamente los objetos DirectWrite. Para ayudar a las aplicaciones GDI existentes a aprovechar las ventajas de DirectWrite, proporciona la interfaz del método IDWriteGdiInterop con métodos para hacer lo siguiente:

Glifos frente a texto

Text es un conjunto de puntos de código Unicode (caracteres), con varios modificadores estilísticos (fuentes, pesos, subrayados, tachones, etc.) que se colocan en un rectángulo. Un glifo, en cambio, es un índice determinado en un archivo de fuente determinado. Un glifo define un conjunto de curvas que se pueden representar, pero no tiene ningún significado textual. Puede haber una asignación de varios a varios entre glifos y caracteres. Una secuencia de glifos que proceden de la misma Fuente Face y que se disponen secuencialmente en una línea base se denomina GlyphRun. Tanto DirectWrite como Direct2D llaman a su API de representación de glifo más preciso DrawGlyphRun y tienen firmas muy similares. Lo siguiente es de ID2D1RenderTarget en 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;

Y este método procede de IDWriteBitmapRenderTarget en 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;

La versión de DirectWrite mantiene el origen de línea base, el modo de medición y los parámetros de ejecución del glifo e incluye parámetros adicionales.

DirectWrite también permite usar un representador personalizado para glifos mediante la implementación de la interfaz IDWriteTextRenderer. Esta interfaz también tiene un método DrawGlyphRun , como se muestra en el ejemplo de código siguiente.

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 versión incluye más parámetros que son útiles al implementar un representador de texto personalizado. El parámetro final se usa para efectos de dibujo personalizados implementados por la aplicación. (Para obtener más información sobre los efectos de dibujo de cliente, vea Cómo agregar efectos de dibujo de cliente a un diseño de texto.

Cada ejecución de glifo comienza en un origen y se coloca en una línea a partir de este origen. La transformación del mundo actual cambia los glifos y la configuración de representación de texto seleccionada en el destino de representación asociado. Por lo general, solo las aplicaciones que realizan su propio diseño (por ejemplo, un procesador de Word) o una aplicación que ha implementado la interfaz IDWriteTextRenderer.

DirectWrite y Direct2D

Direct2D proporciona servicios de representación de nivel de glifo a través de DrawGlyphRun. Sin embargo, esto requiere que la aplicación implemente los detalles de la representación, que básicamente reproduce la funcionalidad de drawText API de GDI por sí misma.

Por lo tanto, Direct2D proporciona API que aceptan texto en lugar de glifos: ID2D1RenderTarget::D rawTextLayout e ID2D1RenderTarget::D rawText. Ambos métodos se representan en una superficie direct2D. Para representar en una superficie GDI, se proporciona IDWriteBitmapRenderTarget::D rawGlyphRun . Pero este método requiere que la aplicación implemente un representador de texto personalizado. (Para obtener más información, consulta el tema Render to a GDI Surface ).

El uso de texto de una aplicación suele iniciarse simple: coloque Aceptar o Cancelar en un botón de diseño fijo, por ejemplo. Sin embargo, con el tiempo, se vuelve más complejo a medida que se agregan la internacionalización y otras características. Con el tiempo, muchas aplicaciones tendrán que usar los objetos de diseño de texto de DirectWrite e implementar el representador de texto.

Por lo tanto, Direct2D proporciona API superpuestas que permiten a una aplicación iniciarse simplemente y crecer más sofisticadas sin tener que realizar un seguimiento posterior ni abandonar su código de trabajo. En el diagrama siguiente se muestra una vista simplificada:

Diagrama de aplicación directwrite y direct2d.

Drawtext

DrawText es la más sencilla de las API que se van a usar. Toma una cadena Unicode, un pincel en primer plano, un único objeto de formato y un rectángulo de destino. Diseñará y representará toda la cadena dentro del rectángulo de diseño y, opcionalmente, la recortará. Esto resulta útil cuando se coloca un fragmento de texto simple en una parte de la interfaz de usuario de diseño fijo.

DrawTextLayout

Al crear un objeto IDWriteTextLayout , una aplicación puede empezar a medir y organizar el texto y otros elementos de la interfaz de usuario, y admitir varias fuentes, estilos, subrayados y tachado. Direct2D proporciona la API DrawTextLayout que acepta directamente este objeto y representa el texto en un punto determinado. (El objeto layout proporciona el ancho y el alto). Además de implementar todas las características de diseño de texto esperadas, Direct2D interpretará cualquier objeto de efecto como pincel y aplicará ese pincel al intervalo seleccionado de glifos. También llamará a cualquier objeto insertado. Después, una aplicación puede insertar caracteres no glifos (iconos) en el texto si lo desea. Otra ventaja de usar un objeto de diseño de texto es que las posiciones del glifo se almacenan en caché en él. Por lo tanto, una gran ganancia de rendimiento es posible reutilizando el mismo objeto de diseño para varias llamadas de dibujo y evitando volver a calcular las posiciones del glifo para cada llamada. Esta funcionalidad no está presente para DrawText de GDI.

DrawGlyphRun

Por último, la aplicación puede implementar la propia interfaz IDWriteTextRenderer y llamar a DrawGlyphRun y FillRectangle , o a cualquier otra API de representación. Toda la interacción existente con el objeto Diseño de texto permanecerá sin cambios.

Para obtener un ejemplo de cómo implementar un representador de texto personalizado, consulte el tema Render Using a Custom Text Renderer (Representar mediante un representador de texto personalizado ).

Representación de glifos

Agregar DirectWrite a una aplicación GDI existente permite a la aplicación usar la API IDWriteBitmapRenderTarget para representar glifos. El método IDWriteBitmapRenderTarget::D rawGlyphRun que DirectWrite proporciona se representará en color sólido en un controlador de dominio de memoria sin necesidad de api adicionales, como Direct2D.

Esto permite a la aplicación obtener características avanzadas de representación de texto, como las siguientes:

  • ClearType de sub píxeles permite a una aplicación colocar glifos en posiciones de sub píxeles para permitir la representación de glifos agudos y el diseño del glifo.
  • El suavizado de contorno en dirección Y permite una representación más suave de las curvas en glifos más grandes.

Una aplicación que se mueve a Direct2D también obtendrá las siguientes características:

  • Aceleración de hardware.
  • La capacidad de rellenar texto con un pincel Direct2D arbitrario, como degradados radiales, degradados lineales y mapas de bits.
  • Se admiten más capas y recortes a través de las API PushAxisAlignedClip, PushLayer y CreateCompatibleRenderTarget .
  • La capacidad de admitir la representación de texto de escala de grises. Esto rellena correctamente el canal alfa de destino según la opacidad del pincel de texto y el suavizado del texto.

Para admitir eficazmente la aceleración de hardware, Direct2D usa una aproximación ligeramente diferente a la corrección Gamma denominada corrección alfa. Esto no requiere Direct2D para inspeccionar el píxel de color de destino de representación al representar texto.

Conclusión

En este tema se explican las diferencias y similitudes entre Direct2D y DirectWrite y las motivaciones arquitectónicas para proporcionarlas como API cooperativas independientes.