Rendu de texte avec Direct2D et DirectWrite

Contrairement à d'autres API, telles que GDI, GDI+ ou WPF, Direct2D interagit avec une autre API, DirectWrite, pour manipuler et rendre le texte. Cette rubrique décrit les avantages et l'interopérabilité de ces composants distincts.

Cette rubrique contient les sections suivantes.

Direct2D permet une adoption progressive

Le passage d'une application d'une API graphique à une autre peut s'avérer difficile ou ne pas correspondre à ce que vous souhaitez pour diverses raisons. Cela peut être dû au fait que vous devez prendre en charge des plug-ins qui utilisent encore les anciennes interfaces, que l'application elle-même est trop volumineuse pour être portée à une nouvelle API en une seule version ou qu'une partie de la nouvelle API est souhaitable, mais que l'ancienne API fonctionne suffisamment bien pour d'autres parties de l'application.

Direct2D et DirectWrite étant implémentés en tant que composants distincts, vous pouvez mettre à jour l'ensemble de votre système graphique 2D ou uniquement la partie texte de celui-ci. Par exemple, vous pouvez mettre à jour une application pour utiliser DirectWrite pour le texte tout en continuant à utiliser GDI ou GDI+ pour le rendu.

Services de texte et rendu de texte

Au fur et à mesure de l'évolution des applications, les exigences en matière de traitement de texte sont devenues de plus en plus complexes. Au début, le texte était généralement confiné à des interfaces utilisateur statiques et le texte était rendu dans un cadre bien défini, tel qu'un bouton. Lorsque les applications ont commencé à être disponibles dans un nombre croissant de langues, cette approche est devenue plus difficile à maintenir, car la largeur et la hauteur du texte traduit peuvent varier considérablement d'une langue à l'autre. Pour s'adapter, les applications ont commencé à agencer dynamiquement leur interface utilisateur en fonction de la taille réelle du texte, et non l'inverse.

Pour aider les applications à accomplir cette tâche, DirectWrite fournit l'interface IDWriteTextLayout. Cette API permet à une application de spécifier un morceau de texte avec des caractéristiques complexes telles que différentes polices et tailles de police, des soulignements, des biffures, du texte bidirectionnel, des effets, des ellipses et même des caractères non glyphiques intégrés (tels qu'une émoticône bitmap ou une icône). L'application peut ensuite modifier diverses caractéristiques du texte en déterminant de manière itérative la disposition de l'interface utilisateur. L'exemple Hello World de DirectWrite, qui est présenté dans l'illustration suivante et dans le didacticiel : Premiers pas avec DirectWrite, montre plusieurs de ces effets.

capture d'écran de l'exemple « hello world ».

La mise en page peut soit positionner les glyphes idéalement en fonction de leur largeur (comme le fait WPF), soit fixer les glyphes aux positions de pixels les plus proches (comme le fait GDI).

Outre l'obtention des mesures du texte, l'application peut tester diverses parties du texte. Par exemple, elle peut vouloir savoir si un lien hypertexte dans le texte a été cliqué. (Pour plus d'informations sur les tests d'impact, consultez la rubrique Comment effectuer des tests d'impact sur une présentation de texte).

L'interface de mise en page de texte est découplée de l'API de rendu utilisée par l'application, comme le montre le diagramme suivant :

diagramme de l'interface de mise en page de texte et de l'API graphique.

Cette séparation est possible parce que DirectWrite fournit une interface de rendu (IDWriteTextRenderer) que les applications peuvent implémenter pour rendre le texte en utilisant l'API graphique de votre choix. La méthode de rappel IDWriteTextRenderer::DrawGlyphRun, implémentée par l'application, est appelée par DirectWrite lors du rendu d'une disposition de texte. Il incombe à cette méthode d'effectuer les opérations de dessin ou de les transmettre.

Pour dessiner des glyphes, Direct2D fournit ID2D1RenderTarget::DrawGlyphRun pour dessiner sur une surface Direct2D et DirectWrite fournit IDWriteBitmapRenderTarget::DrawGlyphRun pour dessiner sur une surface GDI qui peut ensuite être transférée dans une fenêtre à l'aide de GDI. De manière pratique, DrawGlyphRun dans Direct2D et DirectWrite a des paramètres exactement compatibles avec la méthode DrawGlyphRun que l'application implémente sur IDWriteTextRenderer.

Suivant une séparation similaire, les fonctionnalités spécifiques au texte (telles que l'énumération et la gestion des polices, l'analyse des glyphes, etc.) sont gérées par DirectWrite au lieu de Direct2D. Les objets DirectWrite sont acceptés directement par Direct2D. Pour aider les applications GDI existantes à tirer parti de DirectWrite, l'interface IDWriteGdiInterop est fournie avec des méthodes permettant d'effectuer les opérations suivantes :

Glyphes et texte

Le texte est un ensemble de points de code Unicode (caractères), avec divers modificateurs stylistiques (polices, graisses, soulignés, barrés, etc.) qui sont disposés dans un rectangle. Un glyphe, en revanche, est un index particulier dans un fichier de police particulier. Un glyphe définit un ensemble de courbes qui peuvent être rendues, mais il n'a aucune signification textuelle. Il existe potentiellement un mappage de plusieurs à plusieurs entre les glyphes et les caractères. Une séquence de glyphes provenant de la même police de caractères et disposés séquentiellement sur une ligne de base s'appelle un GlyphRun. DirectWrite et Direct2D appellent tous deux leur API de rendu de glyphes la plus précise, DrawGlyphRun, et leurs signatures sont très similaires. La méthode suivante provient de ID2D1RenderTarget dans 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;

Et cette méthode provient de IDWriteBitmapRenderTarget dans 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 version DirectWrite conserve l'origine de la ligne de base, le mode de mesure et les paramètres d'exécution des glyphes et inclut des paramètres supplémentaires.

DirectWrite vous permet également d'utiliser un moteur de rendu personnalisé pour les glyphes en implémentant l'interface IDWriteTextRenderer. Cette interface possède également une méthode DrawGlyphRun, comme le montre l'exemple de code suivant.

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;

Cette version comprend davantage de paramètres utiles lorsque vous implémentez un moteur de rendu de texte personnalisé. Le dernier paramètre est utilisé pour les effets de dessin personnalisés implémentés par l'application. (Pour plus d'informations sur les effets de dessin client, voir Comment ajouter des effets de dessin client à une présentation de texte.

Chaque cycle de glyphes commence à une origine et est placé sur une ligne partant de cette origine. Les glyphes sont modifiés par la transformation du monde actuel et les paramètres de rendu de texte sélectionnés sur la cible de rendu associée. Cette API n'est généralement appelée directement que par les applications qui effectuent leur propre mise en page (par exemple un traitement de texte) ou par une application qui a implémenté l'interface IDWriteTextRenderer.

DirectWrite et Direct2D

Direct2D fournit des services de rendu au niveau des glyphes par l'intermédiaire de DrawGlyphRun. Cependant, l'application doit mettre en œuvre les détails du rendu, ce qui reproduit fondamentalement la fonctionnalité de l'API DrawText de GDI.

C'est pourquoi Direct2D fournit des API qui acceptent le texte au lieu des glyphes : ID2D1RenderTarget::DrawTextLayout et ID2D1RenderTarget::DrawText. Ces deux méthodes effectuent le rendu sur une surface Direct2D. Pour effectuer le rendu sur une surface GDI, IDWriteBitmapRenderTarget::DrawGlyphRun est fourni. Mais cette méthode nécessite l'implémentation d'un moteur de rendu de texte personnalisé par l'application. (Pour plus d'informations, consultez la rubrique Rendu vers une surface GDI).

L'utilisation du texte par une application commence généralement de manière simple : mettez OK ou Annuler sur un bouton à disposition fixe, par exemple. Cependant, au fil du temps, elle devient plus complexe à mesure que l'internationalisation et d'autres fonctionnalités sont ajoutées. En fin de compte, de nombreuses applications devront utiliser les objets de disposition de texte de DirectWrite et implémenter le moteur de rendu de texte.

C'est pourquoi Direct2D fournit des API en couches qui permettent à une application de commencer simplement et de devenir plus sophistiquée sans avoir à revenir en arrière ou à abandonner son code de travail. Une vue simplifiée est présentée dans le diagramme suivant :

diagramme de l'application directwrite et direct2d.

DrawText

DrawText est l'API la plus simple à utiliser. Elle prend une chaîne Unicode, une brosse d'avant-plan, un objet de format unique et un rectangle de destination. Elle disposera et rendra l'ensemble de la chaîne dans le rectangle de disposition, et l'écrêtera éventuellement. Cette fonction est utile lorsque vous placez un simple morceau de texte dans un élément d'interface utilisateur à disposition fixe.

DrawTextLayout

En créant un objet IDWriteTextLayout, une application peut commencer à mesurer et à disposer le texte et d'autres éléments de l'interface utilisateur, et prendre en charge plusieurs polices, styles, soulignés et barrés. Direct2D fournit l'API DrawTextLayout qui accepte directement cet objet et rend le texte à un point donné. (La largeur et la hauteur sont fournies par l'objet layout). Outre l'implémentation de toutes les fonctionnalités de mise en page de texte attendues, Direct2D interprétera tout objet d'effet comme une brosse et appliquera cette brosse à la plage de glyphes sélectionnée. Il appellera également tous les objets en ligne. Une application peut alors insérer des caractères non glyphiques (icônes) dans le texte si elle le souhaite. Un autre avantage de l'utilisation d'un objet de disposition de texte est que les positions des glyphes sont mises en cache dans cet objet. Par conséquent, un gain de performance important est possible en réutilisant le même objet de disposition pour plusieurs appels de dessin et en évitant de recalculer les positions des glyphes pour chaque appel. Cette possibilité n'existe pas pour DrawText de GDI.

DrawGlyphRun

Enfin, l'application peut mettre en œuvre l'interface IDWriteTextRenderer elle-même et appeler DrawGlyphRun et FillRectangle elle-même, ou toute autre API de rendu. Toutes les interactions existantes avec l'objet Text Layout resteront inchangées.

Pour un exemple de mise en œuvre d'un moteur de rendu de texte personnalisé, consultez la rubrique Rendu à l'aide d'un moteur de rendu de texte personnalisé.

Rendu de glyphes

L'ajout de DirectWrite à une application GDI existante permet à l'application d'utiliser l'API IDWriteBitmapRenderTarget pour effectuer le rendu des glyphes. La méthode IDWriteBitmapRenderTarget::DrawGlyphRun fournie par DirectWrite effectue le rendu en couleur solide sur un DC de mémoire sans nécessiter d'API supplémentaires, telles que Direct2D.

Cela permet à l'application d'obtenir des fonctionnalités avancées de rendu de texte telles que les suivantes :

  • ClearType sous-pixel permet à une application de placer les glyphes sur des positions sous-pixel afin de permettre un rendu et une disposition des glyphes précis.
  • L'anticrénelage dans la direction Y permet un rendu plus lisse des courbes sur les glyphes de grande taille.

Une application passant à Direct2D obtiendra également les fonctionnalités suivantes :

  • Accélération matérielle.
  • La possibilité de remplir le texte avec un pinceau Direct2D arbitraire, tel que des dégradés radiaux, des dégradés linéaires et des bitmaps.
  • Une meilleure prise en charge de la superposition et de l'écrêtage grâce aux API PushAxisAlignedClip, PushLayer et CreateCompatibleRenderTarget.
  • Possibilité de prendre en charge le rendu de texte en niveaux de gris. Cela permet de remplir correctement la chaîne alpha de destination en fonction de l'opacité de la brosse de texte et de l'anticrénelage du texte.

Pour prendre en charge efficacement l'accélération matérielle, Direct2D utilise une approximation légèrement différente de la correction gamma, appelée correction alpha. Cela ne nécessite pas que Direct2D inspecte le pixel de la couleur de la cible de rendu lors du rendu du texte.

Conclusion

Cette rubrique explique les différences et les similitudes entre Direct2D et DirectWrite, ainsi que les raisons architecturales de les fournir en tant qu'API distinctes et coopératives.