Rendering del testo con Direct2D e DirectWrite

A differenza di altre API, ad esempio GDI, GDI+ o WPF, Direct2D interagisce con un'altra API, DirectWrite, per modificare ed eseguire il rendering del testo. In questo argomento vengono descritti i vantaggi e l'interoperabilità di questi componenti separati.

Questo argomento include le sezioni seguenti.

Direct2D abilita l'adozione incrementale

Lo spostamento di un'applicazione da un'API grafica a un'altra può essere difficile o meno ciò che si vuole per vari motivi. Questo potrebbe essere dovuto al fatto che è necessario supportare i plug-in che accettano ancora le interfacce precedenti, perché l'applicazione stessa è troppo grande per eseguire la conversione in una nuova API in una versione o perché alcune parti dell'API più recente sono auspicabili, ma l'API precedente funziona abbastanza bene per altre parti dell'applicazione.

Poiché Direct2D e DirectWrite vengono implementati come componenti separati, è possibile aggiornare l'intero sistema grafico 2D o solo la parte di testo di esso. Ad esempio, è possibile aggiornare un'applicazione per usare DirectWrite per il testo, ma usare comunque GDI o GDI+ per il rendering.

Servizi di testo e rendering del testo

Man mano che le applicazioni si sono sviluppate, i requisiti di elaborazione del testo sono cresciuti sempre più complessi. In un primo momento, il testo era generalmente limitato all'interfaccia utente disposta in modo statico e il testo è stato sottoposto a rendering in una casella ben definita, ad esempio un pulsante. Quando le applicazioni hanno iniziato a essere disponibili in un numero crescente di lingue, questo approccio è diventato più difficile da sostenere perché sia la larghezza che l'altezza del testo tradotto possono variare in modo significativo tra le lingue. Per adattarsi, le applicazioni hanno iniziato a disporre dinamicamente l'interfaccia utente in modo da dipendere dalle dimensioni effettive del testo di cui è stato eseguito il rendering, anziché dall'altra parte.

Per consentire alle applicazioni di completare questa attività, DirectWrite fornisce l'interfaccia IDWriteTextLayout. Questa API consente a un'applicazione di specificare una parte di testo con caratteristiche complesse, ad esempio tipi di carattere e dimensioni del carattere diversi, sottolineature, barrati, testo bidirezionale, effetti, puntini di sospensione e persino caratteri non glifi incorporati (ad esempio un'emoticon bitmap o un'icona). L'applicazione può quindi modificare varie caratteristiche del testo perché determina in modo iterativo il layout dell'interfaccia utente. L'esempio DirectWrite Hello World, illustrato nella figura seguente e nell'argomento Esercitazione: Introduzione a DirectWrite, mostra molti di questi effetti.

screenshot dell'esempio

Il layout può posizionare i glifi idealmente in base alle loro larghezze (come fa WPF) oppure può agganciare i glifi alle posizioni dei pixel più vicine (come fa GDI ).

Oltre a ottenere misurazioni di testo, l'applicazione può eseguire il hit test di varie parti del testo. Ad esempio, potrebbe essere necessario sapere che viene fatto clic su un collegamento ipertestuale nel testo. Per altre informazioni sull'hit testing, vedereCome eseguire hit testing in un argomento layout di testo.

L'interfaccia del layout di testo è disaccoppiata dall'API di rendering usata dall'applicazione, come illustrato nel diagramma seguente:

diagramma api grafica e layout di testo.

Questa separazione è possibile perché DirectWrite fornisce un'interfaccia di rendering (IDWriteTextRenderer) che le applicazioni possono implementare per eseguire il rendering del testo usando qualsiasi API grafica desiderata. L'applicazione ha implementato il metodo di callback IDWriteTextRenderer::D rawGlyphRun viene chiamato da DirectWrite durante il rendering di un layout di testo. È responsabilità di questo metodo eseguire le operazioni di disegno o passarle.

Per i glifi di disegno, Direct2D fornisce ID2D1RenderTarget::D rawGlyphRun per il disegno in una superficie Direct2D e DirectWrite fornisce IDWriteBitmapRenderTarget::D rawGlyphRun per il disegno in una superficie GDI che può quindi essere trasferita a una finestra usando GDI. In modo pratico, DrawGlyphRun in Direct2D e DirectWrite hanno parametri esattamente compatibili con il metodo DrawGlyphRun implementato dall'applicazione in IDWriteTextRenderer.

In seguito a una separazione simile, le funzionalità specifiche del testo ,ad esempio l'enumerazione e la gestione dei caratteri, l'analisi dei glifi e così via, vengono gestite da DirectWrite invece di Direct2D. Gli oggetti DirectWrite vengono accettati direttamente da Direct2D. Per aiutare le applicazioni GDI esistenti a sfruttare DirectWrite, fornisce l'interfaccia del metodo IDWriteGdiInterop con metodi per eseguire le operazioni seguenti:

Glifi e testo

Il testo è un set di punti di codice Unicode (caratteri), con vari modificatori stilistici (tipi di carattere, pesi, sottolineature, barrati e così via) disposti in un rettangolo. Un glifo, al contrario, è un indice particolare in un file di tipo di carattere specifico. Un glifo definisce un set di curve di cui è possibile eseguire il rendering, ma non ha alcun significato testuale. Esiste potenzialmente un mapping molti-a-molti tra glifi e caratteri. Una sequenza di glifi provenienti dallo stesso tipo di carattere e disposti in sequenza su una linea di base è detta GlyphRun. Sia DirectWrite che Direct2D chiamano l'API di rendering degli glifi più precisa DrawGlyphRun e hanno firme molto simili. Di seguito è riportato 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;

Questo metodo proviene da 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;

La versione directWrite mantiene l'origine di base, la modalità di misurazione e i parametri di esecuzione del glifo e include parametri aggiuntivi.

DirectWrite consente anche di usare un renderer personalizzato per i glifi implementando l'interfaccia IDWriteTextRenderer . Questa interfaccia include anche un metodo DrawGlyphRun , come illustrato nell'esempio di codice seguente.

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;

Questa versione include più parametri utili quando si implementa un renderer di testo personalizzato. Il parametro finale viene usato per gli effetti di disegno personalizzati implementati dall'applicazione. Per altre informazioni sugli effetti di disegno client, vedere Come aggiungere effetti di disegno client a un layout di testo.

Ogni esecuzione del glifo inizia da un'origine e viene inserita su una riga a partire da questa origine. I glifi vengono modificati dalla trasformazione globale corrente e dalle impostazioni di rendering del testo selezionate nella destinazione di rendering associata. Questa API viene in genere chiamata direttamente solo dalle applicazioni che eseguono il proprio layout (ad esempio un elaboratore di word) o da un'applicazione che ha implementato l'interfaccia IDWriteTextRenderer .

DirectWrite e Direct2D

Direct2D offre servizi di rendering a livello di glifo tramite DrawGlyphRun. Tuttavia, è necessario che l'applicazione implementi i dettagli del rendering, che fondamentalmente riproduce autonomamente la funzionalità dell'API DrawText da GDI.

Direct2D fornisce quindi API che accettano testo anziché glifi: ID2D1RenderTarget::D rawTextLayout e ID2D1RenderTarget::D rawText. Entrambi i metodi eseguono il rendering in una superficie Direct2D. Per eseguire il rendering in una superficie GDI, viene fornito IDWriteBitmapRenderTarget::D rawGlyphRun. Tuttavia, questo metodo richiede l'implementazione di un renderer di testo personalizzato dall'applicazione. Per altre informazioni, vedere Eseguire il rendering in un argomento surface GDI.

L'utilizzo del testo di un'applicazione inizia in genere semplice: ad esempio, inserire OK o Annulla su un pulsante di layout fisso. Tuttavia, nel corso del tempo, diventa più complesso man mano che vengono aggiunte l'internazionalizzazione e altre funzionalità. Alla fine molte applicazioni dovranno usare gli oggetti layout di testo di DirectWrite e implementare il renderer di testo.

Di conseguenza, Direct2D fornisce API a più livelli che consentono a un'applicazione di iniziare semplicemente e crescere più sofisticate senza dover monitorare o abbandonare il codice funzionante. Una visualizzazione semplificata è illustrata nel diagramma seguente:

diagramma dell'applicazione directwrite e direct2d.

DrawText

DrawText è la più semplice delle API da usare. Accetta una stringa Unicode, un pennello in primo piano, un singolo oggetto di formato e un rettangolo di destinazione. Verrà eseguito il layout e il rendering dell'intera stringa all'interno del rettangolo di layout e, facoltativamente, ritagliarlo. Ciò è utile quando si inserisce una parte semplice di testo in una parte dell'interfaccia utente con layout fisso.

DrawTextLayout

Creando un oggetto IDWriteTextLayout , un'applicazione può iniziare a misurare e disporre il testo e altri elementi dell'interfaccia utente e supportare più tipi di carattere, stili, sottolineature e barrati. Direct2D fornisce l'API DrawTextLayout che accetta direttamente questo oggetto ed esegue il rendering del testo in un determinato punto. La larghezza e l'altezza vengono fornite dall'oggetto layout. Oltre a implementare tutte le funzionalità di layout del testo previste, Direct2D interpreterà qualsiasi oggetto effetto come pennello e applicherà tale pennello all'intervallo selezionato di glifi. Chiamerà anche qualsiasi oggetto inline. Un'applicazione può quindi inserire caratteri non glifi (icone) nel testo, se lo desidera. Un altro vantaggio dell'uso di un oggetto layout di testo è che le posizioni del glifo vengono memorizzate nella cache. Pertanto, un notevole miglioramento delle prestazioni è possibile riutilizzando lo stesso oggetto di layout per più chiamate di disegno ed evitando di ricalcolare le posizioni del glifo per ogni chiamata. Questa funzionalità non è presente per DrawText di GDI.

DrawGlyphRun

Infine, l'applicazione può implementare l'interfaccia IDWriteTextRenderer stessa e chiamare DrawGlyphRun e FillRectangle stesso o qualsiasi altra API di rendering. Tutte le interazioni esistenti con l'oggetto Layout di testo rimarranno invariate.

Per un esempio di come implementare un renderer di testo personalizzato, vedere l'argomento Rendering con un renderer di testo personalizzato.

Rendering del glifo

L'aggiunta di DirectWrite a un'applicazione GDI esistente consente all'applicazione di usare l'API IDWriteBitmapRenderTarget per il rendering dei glifi. Il metodo IDWriteBitmapRenderTarget::D rawGlyphRun fornito da DirectWrite eseguirà il rendering in un controller di dominio di memoria senza richiedere API aggiuntive, ad esempio Direct2D.

Ciò consente all'applicazione di ottenere funzionalità avanzate di rendering del testo, ad esempio le seguenti:

  • ClearType sub-pixel consente a un'applicazione di inserire glifi in posizioni sub-pixel per consentire sia il rendering nitido del glifo che il layout del glifo.
  • L'antialiasing della direzione Y consente un rendering più uniforme delle curve su glifi più grandi.

Un'applicazione che passa a Direct2D otterrà anche le funzionalità seguenti:

  • Accelerazione hardware.
  • Possibilità di riempire il testo con un pennello Direct2D arbitrario, ad esempio sfumature radiali, sfumature lineari e bitmap.
  • Maggiore supporto per la creazione di livelli e il ritaglio tramite le API PushAxisAlignedClip, PushLayer e CreateCompatibleRenderTarget.
  • Possibilità di supportare il rendering del testo in scala di grigi. Questo popola correttamente il canale alfa di destinazione in base all'opacità del pennello di testo e all'antialiasing del testo.

Per supportare in modo efficiente l'accelerazione hardware, Direct2D usa un'approssimazione leggermente diversa per la correzione gamma denominata correzione alfa. Non è necessario che Direct2D controlli il pixel del colore di destinazione di rendering durante il rendering del testo.

Conclusione

Questo argomento illustra le differenze e le analogie tra Direct2D e DirectWrite e le motivazioni dell'architettura per fornirle come API cooperative separate.