Condividi tramite


Come aggiungere effetti di disegno client a un layout di testo

Viene fornita una breve esercitazione sull'aggiunta di effetti di disegno client a un'applicazione DirectWrite che visualizza il testo usando l'interfacciaIDWriteTextLayoute un renderer di testo personalizzato.

Il prodotto finale di questa esercitazione è un'applicazione che visualizza testo con intervalli di testo con un effetto di disegno a colori diverso su ognuno, come illustrato nella schermata seguente.

schermata di esempio dell'effetto del disegno cliente in diverse varianti cromatiche

Nota

Questa esercitazione è destinata a essere un esempio semplificato di come creare effetti di disegno client personalizzati, non un esempio di un metodo semplice per il disegno di testo a colori. Per altre informazioni, vedere IDWriteTextLayout::SetDrawingEffect pagina di riferimento.

 

Questa esercitazione contiene le parti seguenti:

Passaggio 1: Creare un layout di testo

Per iniziare, è necessaria un'applicazione con un oggetto IDWriteTextLayout. Se si dispone già di un'applicazione che visualizza il testo con un layout di testo o si usa il codice di esempio disegno personalizzato, passare al passaggio 2.

Per aggiungere un layout di testo, è necessario eseguire le operazioni seguenti:

  1. Dichiarare un puntatore a un'interfaccia IDWriteTextLayout come membro di una classe.

    IDWriteTextLayout* pTextLayout_;
    
    
  2. Alla fine del metodo CreateDeviceIndependentResources, creare un oggetto interfaccia IDWriteTextLayout chiamando il metodo CreateTextLayout.

    // Create a text layout using the text format.
    if (SUCCEEDED(hr))
    {
        RECT rect;
        GetClientRect(hwnd_, &rect); 
        float width  = rect.right  / dpiScaleX_;
        float height = rect.bottom / dpiScaleY_;
    
        hr = pDWriteFactory_->CreateTextLayout(
            wszText_,      // The string to be laid out and formatted.
            cTextLength_,  // The length of the string.
            pTextFormat_,  // The text format to apply to the string (contains font information, etc).
            width,         // The width of the layout box.
            height,        // The height of the layout box.
            &pTextLayout_  // The IDWriteTextLayout interface pointer.
            );
    }
    
    
  3. Infine, ricordarsi di rilasciare il layout di testo nel distruttore.

    SafeRelease(&pTextLayout_);
    

Passaggio 2: Implementare una classe di effetto di disegno personalizzato

Oltre ai metodi ereditati da IUnknown, un'interfaccia personalizzata per l'effetto grafico del client non ha requisiti riguardo a ciò che deve implementare. In questo caso, la classe ColorDrawingEffect contiene semplicemente un valore D2D1_COLOR_F e dichiara i metodi per ottenere e impostare questo valore, nonché un costruttore in grado di impostare il colore inizialmente.

Dopo l'applicazione di un effetto di disegno client a un intervallo di testo in un oggetto IDWriteTextLayout, l'effetto di disegno viene passato al metodo IDWriteTextRenderer::DrawGlyphRun di esecuzione di qualsiasi run glifo che deve essere renderizzato. I metodi dell'effetto di disegno sono quindi disponibili per il renderer di testo.

Un effetto di disegno client può essere complesso quanto si desidera, portando più informazioni rispetto a questo esempio e fornendo metodi per modificare i glifi, creare oggetti da utilizzare per il disegno e così via.

Passaggio 3: Implementare una classe di renderer di testo personalizzata

Per sfruttare i vantaggi di un effetto di disegno client, è necessario implementare un renderer di testo personalizzato. Questo renderer di testo applicherà l'effetto di disegno passato dal metodo IDWriteTextLayout::Draw al frammento di glifo in corso di disegno.

Costruttore

Il costruttore per il renderer di testo personalizzato memorizza l'oggetto ID2D1Factory , che sarà utilizzato per creare oggetti Direct2D , e il target di rendering Direct2D su cui verrà disegnato il testo.

CustomTextRenderer::CustomTextRenderer(
    ID2D1Factory* pD2DFactory, 
    ID2D1HwndRenderTarget* pRT
    )
:
cRefCount_(0), 
pD2DFactory_(pD2DFactory), 
pRT_(pRT)
{
    pD2DFactory_->AddRef();
    pRT_->AddRef();
}

Metodo DrawGlyphRun

Una serie di glifi è un insieme di glifi che condividono il medesimo formato, incluso l'effetto di disegno del client. Il metodo DrawGlyphRun gestisce il rendering del testo per l'esecuzione di un glifo specificato.

Creare prima di tutto un ID2D1PathGeometry e un ID2D1GeometrySinke quindi recuperare la struttura di esecuzione del glifo usando IDWriteFontFace::GetGlyphRunOutline. Trasformare quindi l'origine della geometria usando il metodo Direct2DID2D1Factory::CreateTransformedGeometry, come illustrato nel codice seguente.

HRESULT hr = S_OK;

// 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
        );
}

// 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
        );
}

// Close the geometry sink
if (SUCCEEDED(hr))
{
    hr = pSink->Close();
}

// 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
    );

// Create the transformed geometry
ID2D1TransformedGeometry* pTransformedGeometry = NULL;
if (SUCCEEDED(hr))
{
    hr = pD2DFactory_->CreateTransformedGeometry(
        pPathGeometry,
        &matrix,
        &pTransformedGeometry
        );
}

Successivamente, dichiarare un Direct2D oggetto pennello a tinta unita.

ID2D1SolidColorBrush* pBrush = NULL;

Se il parametro clientDrawingEffect non è NULL, eseguire una query sull'oggetto per l'interfaccia ColorDrawingEffect. Questa operazione funzionerà perché questa classe verrà impostata come effetto di disegno client sugli intervalli di testo dell'oggetto layout di testo.

Una volta che si ha un puntatore all'interfaccia ColorDrawingEffect, è possibile ottenere il valore D2D1_COLOR_F che archivia utilizzando il metodo GetColor. Usare quindi il D2D1_COLOR_F per creare un ID2D1SolidColorBrush in tale colore.

Se il parametro clientDrawingEffect è NULL, è sufficiente creare un nero ID2D1SolidColorBrush.

// If there is a drawing effect create a color brush using it, otherwise create a black brush.
if (clientDrawingEffect != NULL)
{
    // Go from IUnknown to ColorDrawingEffect.
    ColorDrawingEffect *colorDrawingEffect;

    clientDrawingEffect->QueryInterface(__uuidof(ColorDrawingEffect), reinterpret_cast<void**>(&colorDrawingEffect));

    // Get the color from the ColorDrawingEffect object.
    D2D1_COLOR_F color;

    colorDrawingEffect->GetColor(&color);

    // Create the brush using the color specified by our ColorDrawingEffect object.
    if (SUCCEEDED(hr))
    {
        hr = pRT_->CreateSolidColorBrush(
            color,
            &pBrush);
    }

    SafeRelease(&colorDrawingEffect);
}
else
{
    // Create a black brush.
    if (SUCCEEDED(hr))
    {
        hr = pRT_->CreateSolidColorBrush(
            D2D1::ColorF(
            D2D1::ColorF::Black
            ),
            &pBrush);
    }
}

Infine, disegnare la geometria del contorno e riempirla usando il pennello a tinta unita appena creato.

if (SUCCEEDED(hr))
{
    // Draw the outline of the glyph run
    pRT_->DrawGeometry(
        pTransformedGeometry,
        pBrush
        );

    // Fill in the glyph run
    pRT_->FillGeometry(
        pTransformedGeometry,
        pBrush
        );
}

Distruttore

Non dimenticare di rilasciare la factory Direct2D e di eseguire il rendering della destinazione nel distruttore.

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

Passaggio 4: Creare il renderer di testo

Nelle risorse CreateDeviceDependent, creare l'oggetto renderer di testo personalizzato. Dipende dal dispositivo perché usa il ID2D1RenderTarget, che dipende dal dispositivo stesso.

// Create the text renderer
pTextRenderer_ = new CustomTextRenderer(
                        pD2DFactory_,
                        pRT_
                        );

Passaggio 5: Creare un'istanza degli Oggetti Effetto di Disegno a Colori

Istanze di oggetti ColorDrawingEffect in rosso, verde e blu.

// Instantiate some custom color drawing effects.
redDrawingEffect_ = new ColorDrawingEffect(
    D2D1::ColorF(
        D2D1::ColorF::Red
        )
    );

blueDrawingEffect_ = new ColorDrawingEffect(
    D2D1::ColorF(
        D2D1::ColorF::Blue
        )
    );

greenDrawingEffect_ = new ColorDrawingEffect(
    D2D1::ColorF(
        D2D1::ColorF::Green
        )
    );

Passaggio 6: Impostare l'effetto disegno per intervalli di testo specifici

Impostare l'effetto di disegno per intervalli di testo specifici usando il metodo IDWriteTextLayou::SetDrawingEffect e una struttura DWRITE_TEXT_RANGE.

// Set the drawing effects.

// Red.
if (SUCCEEDED(hr))
{
    // Set the drawing effect for the specified range.
    DWRITE_TEXT_RANGE textRange = {0,
                                   14};
    if (SUCCEEDED(hr))
    {
        hr = pTextLayout_->SetDrawingEffect(redDrawingEffect_, textRange);
    }
}

// Blue.
if (SUCCEEDED(hr))
{
    // Set the drawing effect for the specified range.
    DWRITE_TEXT_RANGE textRange = {14,
                                   7};
    if (SUCCEEDED(hr))
    {
        hr = pTextLayout_->SetDrawingEffect(blueDrawingEffect_, textRange);
    }
}

// Green.
if (SUCCEEDED(hr))
{
    // Set the drawing effect for the specified range.
    DWRITE_TEXT_RANGE textRange = {21,
                                   8};
    if (SUCCEEDED(hr))
    {
        hr = pTextLayout_->SetDrawingEffect(greenDrawingEffect_, textRange);
    }
}

Passaggio 7: Disegnare il layout di testo usando il renderer personalizzato

È necessario chiamare il metodo IDWriteTextLayout::Draw anziché il metodo ID2D1RenderTarget::DrawText o ID2D1RenderTarget::DrawTextLayout.

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

Passaggio 8: Pulire

Nel distruttore DemoApp rilasciare il renderer di testo personalizzato.

SafeRelease(&pTextRenderer_);

Successivamente, aggiungere il codice per rilasciare le classi degli effetti grafici del client.

SafeRelease(&redDrawingEffect_);
SafeRelease(&blueDrawingEffect_);
SafeRelease(&greenDrawingEffect_);