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 y la interoperación de estos componentes independientes.

En este tema se incluyen las siguientes secciones.

Direct2D habilita la adopción incremental

Trasladar una aplicación de una API gráfica a otra puede resultar difícil o no ser lo que se desea por diversas razones. Esto puede deberse a que hay que dar soporte a complementos que aún utilizan las interfaces antiguas, a que la aplicación en sí es demasiado grande para adaptarla a una nueva API en una sola versión o a que alguna parte de la nueva API es deseable pero la antigua funciona lo suficientemente bien para otras partes de la aplicación.

Dado que Direct2D y DirectWrite se implementan como componentes independientes, puede actualizar todo el sistema de gráficos 2D o simplemente la parte de texto de ella. Por ejemplo, puede actualizar una aplicación para que use DirectWrite para el texto pero que siga usandoGDI o GDI+ para la representación.

Servicios de texto frente a representación de texto

A medida que las aplicaciones han ido evolucionando, sus requisitos de procesamiento de texto se han hecho cada vez más complejos. Al principio, el texto se limitaba a la interfaz de usuario estática y se representaba en un recuadro bien definido, como un botón. Cuando las aplicaciones empezaron a estar disponibles en un número cada vez mayor de idiomas, este planteamiento se hizo más difícil de mantener porque tanto la anchura como la altura del texto traducido pueden variar significativamente de un idioma a otro. Para adaptarse, las aplicaciones empezaron a diseñar dinámicamente su interfaz de usuario en función del tamaño real del texto, y no al revés.

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 distintos tipos y tamaños de letra, subrayados, tachados, texto bidireccional, efectos, puntos suspensivos e incluso caracteres no glifos integrados (como un emoticono de mapa de bits o un icono). A continuación, la aplicación puede cambiar varias características del texto, ya que determina iterativamente su diseño de interfaz de usuario. El ejemplo de Hola mundo de DirectWrite, que se muestra en la siguiente ilustración y en el Tutorial: Introducción a 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 bien puede ajustar los glifos a las posiciones de píxeles más cercanas (como lo hace GDI).

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

La interfaz de diseño de texto está desacoplada de la API de representación que usa la aplicación, como muestra el siguiente diagrama:

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

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 llamada de retorno implementado por la aplicación IDWriteTextRenderer::DrawGlyphRun es llamado por DirectWrite cuando representa 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::DrawGlyphRun para dibujar en una superficie direct2D y DirectWrite proporciona IDWriteBitmapRenderTarget::DrawGlyphRun 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. Los objetos DirectWrite son aceptados directamente por Direct2D. Para ayudar a las aplicaciones GDI existentes a aprovechar DirectWrite, proporciona la interfaz del método IDWriteGdiInterop con métodos para hacer lo siguiente:

Glifos frente a texto

El texto es un conjunto de puntos de código Unicode (caracteres), con varios modificadores estilísticos (fuentes, pesos, subrayados, tachados, etc.) que se disponen 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 y que se colocan secuencialmente en una línea base se denomina GlyphRun. Tanto DirectWrite como Direct2D llaman a la API de representación de glifos más precisa DrawGlyphRun y tienen firmas muy similares. El 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, consulte 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 actual cambia los glifos y la configuración de representación de texto seleccionada en el destino de representación de representación asociado. Por lo general, esta API solo es llamada directamente por aplicaciones que realizan su propio diseño (por ejemplo, un procesador de textos) o por una aplicación que haya 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 la API DrawText de GDI por sí sola.

Por lo tanto, Direct2D proporciona API que aceptan texto en lugar de glifos: ID2D1RenderTarget::DrawTextLayout y ID2D1RenderTarget::DrawText. Ambos métodos se representan en una superficie direct2D. Para representar en una superficie GDI, se proporciona IDWriteBitmapRenderTarget::DrawGlyphRun. Sin embargo, este método requiere que la aplicación implemente un representador de texto personalizado. (Para obtener más información, consulte Representar en un tema de GDI Surface ).

El uso de texto en una aplicación suele ser sencillo: poner 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. Finalmente, 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 empezar simplemente y crecer más sofisticada 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 pueden usar. Toma una cadena Unicode, un pincel de primer plano, un objeto de formato único y un rectángulo de destino. Se distribuirá y representará toda la cadena dentro del rectángulo de distribución y, opcionalmente, se recortará. Esto es útil cuando pone un simple trozo de texto en una pieza de UI 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 tachados. Direct2D proporciona la API DrawTextLayout que acepta directamente este objeto y representa el texto en un punto determinado. (La anchura y la altura las proporciona el objeto de diseño). Además de implementar todas las características de diseño de texto esperadas, Direct2D interpretará cualquier objeto de efecto como un pincel y aplicará ese pincel al rango seleccionado de glifos. También llamará a cualquier objeto en línea. Después, una aplicación puede insertar caracteres no glifos (iconos) en el texto si lo desea. Otra ventaja de utilizar un objeto de diseño de texto es que en él se almacenan en caché las posiciones de los glifos. Por lo tanto, es posible obtener una gran ganancia de rendimiento reutilizando el mismo objeto de diseño para múltiples llamadas de dibujo y evitando recalcular las posiciones de los glifos 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 por sí mismo o cualquier otra API de representación. Toda la interacción existente con el objeto Text Layout permanecerá sin cambios.

Para obtener un ejemplo de cómo implementar un representador de texto personalizado, consulte el tema Representación 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 proporciona DirectWrite 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:

  • El subpíxel ClearType permite que una aplicación coloque los glifos en posiciones subpíxel para permitir tanto una representación nítida de los glifos como su disposición.
  • El suavizado en la dirección Y permite una representación más suave de las curvas en los glifos de mayor tamaño.

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.
  • Más compatibilidad con la capa y el recorte a través de las API PushAxisAlignedClip, PushLayer y CreateCompatibleRenderTarget.
  • La capacidad de admitir la representación de texto en 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.