Freigeben über


Text-Rendering mit Direct2D und DirectWrite

Im Gegensatz zu anderen APIs, wie GDI, GDI+ oder WPF, arbeitet Direct2D mit einer anderen API, DirectWrite, zusammen, um Text zu bearbeiten und zu rendern. Dieses Thema beschreibt die Vorteile und das Zusammenspiel dieser separaten Komponenten.

Dieses Thema enthält folgende Abschnitte:

Direct2D ermöglicht eine schrittweise Adaption

Die Umstellung einer Anwendung von einer Grafik-API auf eine andere kann sich als schwierig erweisen oder ist aus verschiedenen Gründen nicht wünschenswert. Dies kann daran liegen, dass Sie Plug-Ins unterstützen müssen, die noch die älteren Schnittstellen verwenden, dass die Anwendung selbst zu groß ist, um sie in einer Version auf eine neue API zu portieren oder dass ein Teil der neueren API wünschenswert ist, die ältere API aber für andere Teile der Anwendung gut genug funktioniert.

Da Direct2D und DirectWrite als separate Komponenten implementiert sind, können Sie Ihr gesamtes 2D-Grafiksystem oder nur den Textteil davon aktualisieren. Sie können zum Beispiel eine Anwendung so aktualisieren, dass sie DirectWrite für Text verwendet, aber weiterhin GDI oder GDI+ für das Rendering einsetzt.

Textdienste versus Text-Rendering

Im Laufe der Entwicklung von Anwendungen sind die Anforderungen an die Textverarbeitung immer komplexer geworden. Zunächst war Text allgemein auf statisch gestaltete Benutzeroberflächen beschränkt und wurde in einem genau definierten Rahmen, z. B. einer Schaltfläche, gerendert. Als Anwendungen in immer mehr Sprachen zur Verfügung standen, wurde dieser Ansatz immer schwieriger, da sowohl die Breite als auch die Höhe des übersetzten Textes von Sprache zu Sprache erheblich variieren kann. Um sich daran anzupassen, begannen Anwendungen, ihre Benutzeroberfläche dynamisch zu gestalten, um sich an der tatsächlichen Größe des gerenderten Textes zu orientieren, anstatt umgekehrt.

Um Anwendungen bei dieser Aufgabe zu unterstützen, bietet DirectWrite die IDWriteTextLayout-Schnittstelle. Mit dieser API kann eine Anwendung einen Text mit komplexen Merkmalen wie verschiedenen Schriftarten und -größen, Unterstreichungen, Durchstreichungen, bidirektionalem Text, Effekten, Auslassungspunkten und sogar eingebetteten Nicht-Glyphen-Zeichen (wie einem Bitmap-Emoticon oder einem Symbol) festlegen. Die Anwendung kann dann verschiedene Merkmale des Textes ändern, während sie ihr UI-Layout nach und nach festlegt. Das DirectWrite-Beispiel Hello World, das Sie in der folgenden Abbildung und im Tutorial: Erste Schritte mit DirectWrite finden, zeigt viele dieser Effekte.

Screenshot des Beispiels „Hallo Welt“.

Das Layout kann die Glyphen entweder ideal auf der Grundlage ihrer Breite positionieren (wie bei WPF), oder es kann die Glyphen an den nächstgelegenen Pixelpositionen ausrichten (wie bei GDI).

Neben dem Abrufen der Abmessungen des Textes kann die Anwendung auch verschiedene Teile des Textes testen. Zum Beispiel könnte sie wissen wollen, ob ein Hyperlink im Text angeklickt wurde. (Weitere Informationen über Hit-Tests finden Sie unter Wie man Hit-Tests für ein Textlayout durchführt).

Die Textlayout-Schnittstelle ist von der Rendering-API, die die Anwendung verwendet, entkoppelt, wie das folgende Diagramm zeigt:

Diagramm zu Textlayout und Grafik-API.

Diese Trennung ist möglich, weil DirectWrite eine Rendering-Schnittstelle (IDWriteTextRenderer) bereitstellt, die von Anwendungen implementiert werden kann, um Text unter Verwendung der gewünschten Grafik-API zu rendern. Die von der Anwendung implementierte IDWriteTextRenderer::DrawGlyphRun-Callback-Methode wird von DirectWrite beim Rendern eines Textlayouts aufgerufen. Es liegt in der Verantwortung dieser Methode, die Zeichenvorgänge auszuführen oder sie weiterzureichen.

Für das Zeichnen von Glyphen bietet Direct2D ID2D1RenderTarget::DrawGlyphRun für das Zeichnen auf eine Direct2D-Oberfläche, und DirectWrite bietet IDWriteBitmapRenderTarget::DrawGlyphRun für das Zeichnen auf eine GDI-Oberfläche, die dann per GDI an ein Fenster übertragen werden kann. Praktischerweise hat DrawGlyphRun sowohl in Direct2D als auch in DirectWrite exakt kompatible Parameter zu der DrawGlyphRun-Methode, die die Anwendung bei IDWriteTextRenderer implementiert.

Einer ähnlichen Trennung folgend werden textspezifische Funktionen (wie die Auflistung und Verwaltung von Schriften, die Analyse von Glyphen usw.) von DirectWrite anstelle von Direct2D behandelt. Die DirectWrite-Objekte werden direkt von Direct2D akzeptiert. Um bestehenden GDI-Anwendungen zu helfen, die Vorteile von DirectWrite zu nutzen, gibt es die IDWriteGdiInterop-Methodenschnittstelle mit Methoden für Folgendes:

Glyphen versus Text

Text ist eine Reihe von Unicode-Code-Punkten (Zeichen) mit verschiedenen stilistischen Modifikatoren (Schriftarten, Schriftschnitte, Unterstreichungen, Durchstreichungen usw.), die in einem Rechteck angeordnet sind. Eine Glyphe hingegen ist ein bestimmter Index in einer bestimmten Schriftdatei. Eine Glyphe legt eine Reihe von Kurven fest, die gerendert werden können, aber sie hat keine textliche Bedeutung. Zwischen Glyphen und Zeichen kann es eine Zuordnung von vielen zu vielen geben. Eine Sequenz von Glyphen, die aus derselben Schriftart stammen und nacheinander auf einer Baseline angeordnet sind, wird GlyphRun genannt. Sowohl DirectWrite als auch Direct2D rufen ihre präziseste Glyphen-Rendering-API DrawGlyphRun auf, und sie haben sehr ähnliche Signaturen. Das Folgende stammt aus ID2D1RenderTarget in 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;

Und diese Methode ist aus IDWriteBitmapRenderTarget in 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;

Die DirectWrite-Version behält den Baseline-Ursprung, den Messmodus und die Parameter zum Ausführen der Glyphen bei und enthält zusätzliche Parameter.

DirectWrite ermöglicht es Ihnen außerdem, einen angepassten Renderer für Glyphen zu verwenden, indem Sie die Schnittstelle IDWriteTextRenderer implementieren. Diese Schnittstelle verfügt auch über eine DrawGlyphRun-Methode, wie das folgende Code-Beispiel zeigt.

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;

Diese Version enthält mehr Parameter, die nützlich sind, wenn Sie einen angepassten Text-Renderer implementieren. Der letzte Parameter wird für anwendungsimplementierte, angepasste Zeicheneffekte verwendet. (Weitere Informationen über Client-Zeichnungseffekte finden Sie unter Hinzufügen von Client-Zeichnungseffekten zu einem Textlayout).

Jede Glyph-Ausführung beginnt an einem Ursprung und wird auf eine Zeile gesetzt, die von diesem Ursprung ausgeht. Die Glyphen werden durch die aktuelle Welttransformation und die ausgewählten Text-Rendering-Einstellungen am zugehörigen Renderziel verändert. Diese API wird allgemein nur von Anwendungen direkt aufgerufen, die ihr eigenes Layout erstellen ( z. B. ein Word Processor), oder von einer Anwendung, die die Schnittstelle IDWriteTextRenderer implementiert hat.

DirectWrite und Direct2D

Direct2D bietet über DrawGlyphRun Rendering-Dienste auf Glyphenebene. Dies erfordert jedoch, dass die Anwendung die Details des Renderings implementiert, was im Grunde die Funktionalität der DrawText-API von GDI selbst reproduziert.

Daher bietet Direct2D APIs, die Text anstelle von Glyphen akzeptieren: ID2D1RenderTarget::DrawTextLayout und ID2D1RenderTarget::DrawText. Beide Methoden rendern auf eine Direct2D-Oberfläche. Um auf eine GDI-Oberfläche zu rendern, wird IDWriteBitmapRenderTarget::DrawGlyphRun bereitgestellt. Diese Methode erfordert jedoch einen angepassten Text-Renderer, der von der Anwendung implementiert werden muss. (Weitere Informationen finden Sie im Thema Rendering auf eine GDI-Oberfläche).

Die Verwendung von Text in einer Anwendung beginnt in der Regel ganz einfach: Sie schreiben z. B. OK oder Abbrechen auf eine Schaltfläche mit festem Layout. Im Laufe der Zeit wird sie jedoch komplexer, wenn Internationalisierung und andere Funktionen hinzukommen. Letztendlich müssen viele Anwendungen die Text-Layout-Objekte von DirectWrite verwenden und den Text-Renderer implementieren.

Daher bietet Direct2D mehrschichtige APIs, die es einer Anwendung ermöglichen, einfach zu beginnen und mit der Zeit immer anspruchsvoller zu werden, ohne dass sie ihren funktionierenden Code zurückverfolgen oder aufgeben muss. Eine vereinfachte Ansicht zeigt das folgende Diagramm:

directwrite- und direct2d-Anwendungsdiagramm.

DrawText

DrawText ist die am einfachsten zu verwendende der APIs. Sie nimmt eine Unicode-Zeichenfolge, einen Vordergrund-Brush, ein einzelnes Format-Objekt und ein Zielrechteck entgegen. Die gesamte Zeichenfolge wird innerhalb des Layout-Rechtecks angeordnet und gerendert und optional abgeschnitten. Dies ist nützlich, wenn Sie einen einfachen Text in eine Benutzeroberfläche mit festem Layout einfügen möchten.

DrawTextLayout

Indem Sie ein IDWriteTextLayout-Objekt erstellen, kann eine Anwendung mit der Messung und Anordnung des Textes und anderer UI-Elemente beginnen und mehrere Schriftarten, Stile, Unterstreichungen und Durchstreichungen unterstützen. Direct2D bietet die DrawTextLayout-API, die dieses Objekt direkt akzeptiert und den Text an einem bestimmten Punkt rendert. (Die Breite und Höhe werden vom Layout-Objekt bereitgestellt). Direct2D implementiert nicht nur alle erwarteten Funktionen für das Textlayout, sondern interpretiert auch jedes Effektobjekt als Brush und wendet diesen Brush auf den ausgewählten Bereich von Glyphen an. Es ruft auch alle Inline-Objekte auf. Eine Anwendung kann dann auf Wunsch Nicht-Glyphenzeichen (Symbole) in den Text einfügen. Ein weiterer Vorteil der Verwendung eines Textlayoutobjekts ist, dass die Glyphenpositionen darin zwischenspeichern. Daher ist ein großer Leistungsgewinn möglich, indem dasselbe Layoutobjekt für mehrere Zeichenaufrufe wiederverwendet wird und die Glyphenpositionen nicht für jeden Aufruf neu berechnet werden müssen. Diese Funktionalität ist bei DrawText von GDI nicht vorhanden.

DrawGlyphRun

Schließlich kann die Anwendung die Schnittstelle IDWriteTextRenderer selbst implementieren und DrawGlyphRun und FillRectangle oder jede andere Rendering-API selbst aufrufen. Die gesamte bestehende Interaktion mit dem Textlayout-Objekt bleibt dabei unverändert.

Ein Beispiel für die Implementierung eines angepassten Text-Renderers finden Sie im Thema Rendering mit einem angepassten Text-Renderer.

Glyphen-Rendering

Das Hinzufügen von DirectWrite zu einer bestehenden GDI-Anwendung ermöglicht es der Anwendung, die IDWriteBitmapRenderTarget-API zum Rendern von Glyphen zu verwenden. Die IDWriteBitmapRenderTarget::DrawGlyphRun-Methode, die DirectWrite bereitstellt, rendert in Vollfarbe auf einen Speicher-DC, ohne dass zusätzliche APIs wie Direct2D erforderlich sind.

Dadurch kann die Anwendung fortgeschrittene Funktionen für die Textdarstellung abrufen, wie z. B. die folgenden:

  • Sub-Pixel-ClearType ermöglicht es einer Anwendung, Glyphen an Sub-Pixel-Positionen zu platzieren, um sowohl ein scharfes Glyphen-Rendering als auch ein Glyphen-Layout zu ermöglichen.
  • Antialiasing in Y-Richtung ermöglicht eine glattere Darstellung von Kurven auf größeren Glyphen.

Eine Anwendung, die zu Direct2D wechselt, wird außerdem die folgenden Funktionen nutzen:

  • Hardwarebeschleunigung.
  • Die Möglichkeit, Text mit einem beliebigen Direct2D-Brush zu füllen, sodass Radialverläufe, lineare Verläufe und Bitmaps möglich sind.
  • Mehr Unterstützung für Layering und Clipping durch die APIs PushAxisAlignedClip, PushLayer und CreateCompatibleRenderTarget.
  • Unterstützung für das Rendern von Text in Graustufen. Dadurch wird der Alpha-Kanal des Ziels entsprechend der Deckkraft des Text-Brush und der Antialiasing-Funktion des Textes korrekt gefüllt.

Um die Hardwarebeschleunigung effizient zu unterstützen, verwendet Direct2D eine etwas andere Annäherung an die Gammakorrektur, die Alphakorrektur. Dadurch ist es nicht erforderlich, dass Direct2D beim Rendern von Text das Zielfarbpixel überprüft.

Zusammenfassung

Dieses Thema erklärt die Unterschiede und Gemeinsamkeiten zwischen Direct2D und DirectWrite und die architektonischen Beweggründe, sie als separate, kooperative APIs anzubieten.