Condividi tramite


Eseguire il rendering usando un renderer di testo personalizzato

Un layout DirectWritetext può essere disegnato da un renderer di testo personalizzato derivato da IDWriteTextRenderer. È necessario un renderer personalizzato per sfruttare alcune funzionalità avanzate di DirectWrite, ad esempio il rendering in una superficie bitmap o GDI, oggetti inline e effetti di disegno client. Questa esercitazione descrive i metodi di IDWriteTextRenderer e fornisce un'implementazione di esempio che usa Direct2D per eseguire il rendering del testo con un riempimento bitmap.

Questa esercitazione contiene le parti seguenti:

Il renderer di testo personalizzato deve implementare i metodi ereditati da IUnknown oltre ai metodi elencati nella pagina di riferimento IDWriteTextRenderer e sotto.

Per il codice sorgente completo per il renderer di testo personalizzato, vedere i file CustomTextRenderer.cpp e CustomTextRenderer.h dell'esempio di DirectWrite Hello World.

Costruttore

Il renderer di testo personalizzato richiederà un costruttore. In questo esempio vengono usati sia pennelli solid che bitmap Direct2D per eseguire il rendering del testo.

A causa di questo, il costruttore accetta i parametri trovati nella tabella seguente con descrizioni.

Parametro Descrizione
pD2DFactory Puntatore a un oggetto ID2D1Factory che verrà usato per creare qualsiasi risorsa Direct2D necessaria.
Prt Puntatore all'oggetto ID2D1HwndRenderTarget a cui verrà eseguito il rendering del testo.
pOutlineBrush Puntatore all'ID2D1SolidColorBrush che verrà usato per disegnare la struttura del testo
pFillBrush Puntatore all'ID2D1BitmapBrush che verrà usato per riempire il testo.

 

Questi verranno archiviati dal costruttore, come illustrato nel codice seguente.

CustomTextRenderer::CustomTextRenderer(
    ID2D1Factory* pD2DFactory, 
    ID2D1HwndRenderTarget* pRT, 
    ID2D1SolidColorBrush* pOutlineBrush, 
    ID2D1BitmapBrush* pFillBrush
    )
:
cRefCount_(0), 
pD2DFactory_(pD2DFactory), 
pRT_(pRT), 
pOutlineBrush_(pOutlineBrush), 
pFillBrush_(pFillBrush)
{
    pD2DFactory_->AddRef();
    pRT_->AddRef();
    pOutlineBrush_->AddRef();
    pFillBrush_->AddRef();
}

DrawGlyphRun()

Il metodo DrawGlyphRun è il metodo di callback principale del renderer di testo. Viene passata un'esecuzione di glifi da rendere oltre alle informazioni, ad esempio l'origine di base e la modalità di misurazione. Passa anche un oggetto effetto disegno client da applicare all'esecuzione del glifo. Per altre informazioni, vedere l'argomento Come aggiungere effetti disegno client a un layout di testo .

Questa implementazione del renderer di testo esegue il rendering del glifo convertendoli in geometrie Direct2D e quindi disegnando e riempiendo le geometrie. Questo è costituito dai passaggi seguenti.

  1. Creare un oggetto ID2D1PathGeometry e quindi recuperare l'oggetto ID2D1GeometrySink usando il metodo ID2D1PathGeometry::Open.

    // Create the path geometry.
    ID2D1PathGeometry* pPathGeometry = NULL;
    hr = pD2DFactory_->CreatePathGeometry(
            &pPathGeometry
            );
    
    // Write to the path geometry using the geometry sink.
    ID2D1GeometrySink* pSink = NULL;
    if (SUCCEEDED(hr))
    {
        hr = pPathGeometry->Open(
            &pSink
            );
    }
    
  2. Il DWRITE_GLYPH_RUN passato a DrawGlyphRun contiene un oggetto IDWriteFontFace , denominato fontFace, che rappresenta il viso del carattere per l'intera esecuzione del glifo. Inserire la struttura del glifo eseguita nel sink geometry usando il metodo IDWriteFontFace:: GetGlyphRunOutline , come illustrato nel codice seguente.

    // Get the glyph run outline geometries back from DirectWrite and place them within the
    // geometry sink.
    if (SUCCEEDED(hr))
    {
        hr = glyphRun->fontFace->GetGlyphRunOutline(
            glyphRun->fontEmSize,
            glyphRun->glyphIndices,
            glyphRun->glyphAdvances,
            glyphRun->glyphOffsets,
            glyphRun->glyphCount,
            glyphRun->isSideways,
            glyphRun->bidiLevel%2,
            pSink
            );
    }
    
  3. Dopo aver riempito il sink geometry, chiuderlo.

    // Close the geometry sink
    if (SUCCEEDED(hr))
    {
        hr = pSink->Close();
    }
    
  4. L'origine dell'esecuzione del glifo deve essere tradotta in modo che venga eseguito il rendering dall'origine della baseline corretta, come illustrato nel codice seguente.

    // Initialize a matrix to translate the origin of the glyph run.
    D2D1::Matrix3x2F const matrix = D2D1::Matrix3x2F(
        1.0f, 0.0f,
        0.0f, 1.0f,
        baselineOriginX, baselineOriginY
        );
    

    La baselineOriginX e baselineOriginY vengono passate come parametri al metodo di callback DrawGlyphRun .

  5. Creare la geometria trasformata usando il metodo ID2D1Factory::CreateTransformedGeometry e passando la geometria del percorso e la matrice di traduzione.

    // Create the transformed geometry
    ID2D1TransformedGeometry* pTransformedGeometry = NULL;
    if (SUCCEEDED(hr))
    {
        hr = pD2DFactory_->CreateTransformedGeometry(
            pPathGeometry,
            &matrix,
            &pTransformedGeometry
            );
    }
    
  6. Infine, disegnare la struttura della geometria trasformata e riempirla usando i metodi ID2D1RenderTarget::D rawGeometry e ID2D1RenderTarget::FillGeometry e i pennelli Direct2D archiviati come variabili membro.

        // Draw the outline of the glyph run
        pRT_->DrawGeometry(
            pTransformedGeometry,
            pOutlineBrush_
            );
    
        // Fill in the glyph run
        pRT_->FillGeometry(
            pTransformedGeometry,
            pFillBrush_
            );
    
  7. Dopo aver completato il disegno, non dimenticare di pulire gli oggetti creati in questo metodo.

    SafeRelease(&pPathGeometry);
    SafeRelease(&pSink);
    SafeRelease(&pTransformedGeometry);
    

DrawUnderline() e DrawStrikethrough()

IDWriteTextRenderer include anche callback per il disegno della sottolineatura e della procedura di attacco. In questo esempio viene disegnato un rettangolo semplice per una sottolineatura o una barratura, ma è possibile disegnare altre forme.

Il disegno di una sottolineatura usando Direct2D è costituito dalla procedura seguente.

  1. Creare prima di tutto una struttura D2D1_RECT_F della dimensione e della forma della sottolineatura. La struttura DWRITE_UNDERLINE passata al metodo callback DrawUnderline fornisce l'offset, la larghezza e lo spessore della sottolineatura.

    D2D1_RECT_F rect = D2D1::RectF(
        0,
        underline->offset,
        underline->width,
        underline->offset + underline->thickness
        );
    
  2. Creare quindi un oggetto ID2D1RectangleGeometry usando il metodo ID2D1Factory::CreateRectangleGeometry e la struttura di D2D1_RECT_F inizializzata.

    ID2D1RectangleGeometry* pRectangleGeometry = NULL;
    hr = pD2DFactory_->CreateRectangleGeometry(
            &rect, 
            &pRectangleGeometry
            );
    
  3. Come per l'esecuzione del glifo, l'origine della geometria di sottolineatura deve essere tradotta in base ai valori di origine di base usando il metodo CreateTransformedGeometry .

    // Initialize a matrix to translate the origin of the underline
    D2D1::Matrix3x2F const matrix = D2D1::Matrix3x2F(
        1.0f, 0.0f,
        0.0f, 1.0f,
        baselineOriginX, baselineOriginY
        );
    
    ID2D1TransformedGeometry* pTransformedGeometry = NULL;
    if (SUCCEEDED(hr))
    {
        hr = pD2DFactory_->CreateTransformedGeometry(
            pRectangleGeometry,
            &matrix,
            &pTransformedGeometry
            );
    }
    
  4. Infine, disegnare la struttura della geometria trasformata e riempirla usando i metodi ID2D1RenderTarget::D rawGeometry e ID2D1RenderTarget::FillGeometry e i pennelli Direct2D archiviati come variabili membro.

        // Draw the outline of the glyph run
        pRT_->DrawGeometry(
            pTransformedGeometry,
            pOutlineBrush_
            );
    
        // Fill in the glyph run
        pRT_->FillGeometry(
            pTransformedGeometry,
            pFillBrush_
            );
    
  5. Dopo aver completato il disegno, non dimenticare di pulire gli oggetti creati in questo metodo.

    SafeRelease(&pRectangleGeometry);
    SafeRelease(&pTransformedGeometry);
    

Il processo di disegno di una procedura di attacco è lo stesso. Tuttavia, il barrato avrà un offset diverso e probabilmente una larghezza e uno spessore diversi.

Snapping pixel, pixel per DIP e trasformazione

IsPixelSnappingDisabled()

Questo metodo viene chiamato per determinare se lo snapping pixel è disabilitato. Il valore predefinito consigliato è FALSE ed è l'output di questo esempio.

*isDisabled = FALSE;

GetCurrentTransform()

In questo esempio viene eseguito il rendering in una destinazione di rendering Direct2D, quindi inoltrare la trasformazione dalla destinazione di rendering usando ID2D1RenderTarget::GetTransform.

//forward the render target's transform
pRT_->GetTransform(reinterpret_cast<D2D1_MATRIX_3X2_F*>(transform));

GetPixelsPerDip()

Questo metodo viene chiamato per ottenere il numero di pixel per Device Independent Pixel (DIP).

float x, yUnused;

pRT_->GetDpi(&x, &yUnused);
*pixelsPerDip = x / 96;

DrawInlineObject()

Un renderer di testo personalizzato include anche un callback per il disegno inline oggetti. In questo esempio DrawInlineObject restituisce E_NOTIMPL. Una spiegazione di come disegnare oggetti inline è oltre l'ambito di questa esercitazione. Per altre informazioni, vedere l'argomento How to Add Inline Objects to a text Layout (Come aggiungere oggetti inline a un layout di testo ).

Distruttore

È importante rilasciare qualsiasi puntatore usato dalla classe di renderer di testo personalizzato.

CustomTextRenderer::~CustomTextRenderer()
{
    SafeRelease(&pD2DFactory_);
    SafeRelease(&pRT_);
    SafeRelease(&pOutlineBrush_);
    SafeRelease(&pFillBrush_);
}

Uso del renderer di testo personalizzato

Viene eseguito il rendering con il renderer personalizzato usando il metodo IDWriteTextLayout::D raw , che accetta un'interfaccia di callback derivata da IDWriteTextRenderer come argomento, come illustrato nel codice seguente.

// Draw the text layout using DirectWrite and the CustomTextRenderer class.
hr = pTextLayout_->Draw(
        NULL,
        pTextRenderer_,  // Custom text renderer.
        origin.x,
        origin.y
        );

Il metodo IDWriteTextLayout::D raw chiama i metodi del callback del renderer personalizzato fornito. I metodi DrawGlyphRun, DrawUnderline, DrawInlineObject e DrawStrike through descritti in precedenza eseguono le funzioni di disegno.