Hinzufügen von Clientzeichnungseffekten zu einem Textlayout

Bietet ein kurzes Lernprogramm zum Hinzufügen von Clientzeichnungseffekten zu einer DirectWrite Anwendung, die Text mit der IDWriteTextLayout-Schnittstelle und einem benutzerdefinierten Textrenderer anzeigt.

Das Endprodukt dieses Lernprogramms ist eine Anwendung, in der Textbereiche mit einem anderen Farbzeichnungseffekt angezeigt werden, wie im folgenden Screenshot gezeigt.

screen shot of

Hinweis

Dieses Lernprogramm ist ein vereinfachtes Beispiel für das Erstellen von benutzerdefinierten Clientzeichnungseffekten, nicht ein Beispiel für eine einfache Methode für Zeichnungsfarbtext. Weitere Informationen finden Sie auf der Seite "IDWriteTextLayout::SetDrawingEffect" .

 

Dieses Lernprogramm enthält die folgenden Teile:

Schritt 1: Erstellen eines Textlayouts

Zunächst benötigen Sie eine Anwendung mit einem IDWriteTextLayout-Objekt . Wenn Sie bereits über eine Anwendung verfügen, die Text mit einem Textlayout anzeigt, oder Sie den Benutzerdefinierten DrawingEffect-Beispielcode verwenden, fahren Sie mit Schritt 2 fort.

Gehen Sie wie folgt vor, um ein Textlayout hinzuzufügen:

  1. Deklarieren Sie einen Zeiger auf eine IDWriteTextLayout-Schnittstelle als Mitglied der Klasse.

    IDWriteTextLayout* pTextLayout_;
    
    
  2. Erstellen Sie am Ende der CreateDeviceIndependentResources-Methode ein IDWriteTextLayout-Schnittstellenobjekt , indem Sie die CreateTextLayout-Methode aufrufen.

    // 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. Denken Sie schließlich daran, das Textlayout im Destruktor freizugeben.

    SafeRelease(&pTextLayout_);
    

Schritt 2: Implementieren einer benutzerdefinierten Zeicheneffektklasse

Außer den von IUnknown geerbten Methoden hat eine benutzerdefinierte Clientzeichnungseffektschnittstelle keine Anforderungen, was sie implementieren muss. In diesem Fall enthält die ColorDrawingEffect-Klasse einfach einen D2D1_COLOR_F Wert und deklariert Methoden zum Abrufen und Festlegen dieses Werts sowie einen Konstruktor, der die Farbe anfänglich festlegen kann.

Nachdem ein Clientzeichnungseffekt auf einen Textbereich in einem IDWriteTextLayout-Objekt angewendet wurde, wird der Zeichnungseffekt an die IDWriteTextRenderer::D rawGlyphRun-Methode einer beliebigen Glyphenausführung übergeben, die gerendert werden soll. Die Methoden des Zeichnungseffekts sind dann für den Textrenderer verfügbar.

Ein Clientzeichnungseffekt kann so komplex sein, wie Sie es erstellen möchten, wobei mehr Informationen enthalten sind als in diesem Beispiel, sowie Methoden zum Ändern von Glyphen, Erstellen von Objekten, die für die Zeichnung verwendet werden sollen, usw.

Schritt 3: Implementieren einer benutzerdefinierten Textrendererklasse

Um einen Clientzeichnungseffekt zu nutzen, müssen Sie einen benutzerdefinierten Textrenderer implementieren. Dieser Textrenderer wendet den an ihn übergebenen Zeichnungseffekt von der IDWriteTextLayout::D raw-Methode auf die Glyphenausführung an, die gezeichnet wird.

Der Konstruktor

Der Konstruktor für den benutzerdefinierten Textrenderer speichert das ID2D1Factory-Objekt , das zum Erstellen von Direct2D-Objekten verwendet wird, und das Direct2D-Renderziel, auf das der Text gezeichnet wird.

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

Die DrawGlyphRun-Methode

Eine Glyphe-Ausführung ist eine Reihe von Glyphen, die dasselbe Format teilen, einschließlich des Clientzeichnungseffekts. Die DrawGlyphRun-Methode übernimmt die Textwiedergabe für eine angegebene Glyphenausführung.

Erstellen Sie zunächst eine ID2D1PathGeometry und eine ID2D1GeometrySink, und rufen Sie dann die Glyphenausführung mithilfe von IDWriteFontFace::GetGlyphRunOutline ab. Transformieren Sie dann den Ursprung der Geometrie mithilfe der Direct2DID2D1Factory::CreateTransformedGeometry-Methode, wie im folgenden Code gezeigt.

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

Deklarieren Sie als Nächstes ein Direct2D-Vollpinselobjekt .

ID2D1SolidColorBrush* pBrush = NULL;

Wenn der ClientDrawingEffect-Parameter nicht NULL ist, abfragen Sie das Objekt für die ColorDrawingEffect-Schnittstelle . Dies funktioniert, da Sie diese Klasse als Clientzeichnungseffekt für Textbereiche des Textlayoutobjekts festlegen.

Sobald Sie einen Zeiger auf die ColorDrawingEffect-Schnittstelle haben, können Sie den D2D1_COLOR_F Wert abrufen, den er mithilfe der GetColor-Methode speichert. Verwenden Sie dann die D2D1_COLOR_F , um eine ID2D1SolidColorBrush in dieser Farbe zu erstellen.

Wenn der Parameter "clientDrawingEffect" NULL ist, erstellen Sie einfach eine schwarze 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 pecified 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);
    }
}

Zeichnen Sie schließlich die Gliederungsgeometrie, und füllen Sie sie mithilfe des soeben erstellten Volltonpinsels aus.

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

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

Der Destruktor

Vergessen Sie nicht, die Direct2D-Factory freizugeben und das Ziel im Destruktor zu rendern.

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

Schritt 4: Erstellen des Textrenderers

Erstellen Sie in den CreateDeviceDependent-Ressourcen das benutzerdefinierte Textrendererobjekt. Es ist geräteabhängig, da er die ID2D1RenderTarget verwendet, die selbst geräteabhängig ist.

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

Schritt 5: Instanziieren der Farbzeichnungseffektobjekte

Instanziieren Sie ColorDrawingEffect-Objekte in Rot, Grün und Blau.

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

Schritt 6: Festlegen des Zeichnungseffekts für bestimmte Textbereiche

Legen Sie den Zeichnungseffekt für bestimmte Textbereiche mithilfe der IDWriteTextLayou::SetDrawingEffect-Methode und einer DWRITE_TEXT_RANGE Struktur fest.

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

Schritt 7: Zeichnen des Textlayouts mithilfe des benutzerdefinierten Renderers

Sie müssen die IDWriteTextLayout::D raw-Methode anstelle der METHODEN ID2D1RenderTarget::D rawText oder ID2D1RenderTarget::D rawTextLayout aufrufen.

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

Schritt 8: Bereinigen

Geben Sie im DemoApp-Destruktor den benutzerdefinierten Textrenderer frei.

SafeRelease(&pTextRenderer_);

Fügen Sie anschließend Code hinzu, um die Clientzeichnungseffektklassen freizugeben.

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