Freigeben über


Rendern unter Verwendung eines benutzerdefinierten Textrenderers

Ein DirectWritetextlayout kann von einem benutzerdefinierten Textrenderer gezeichnet werden, der von IDWriteTextRenderer abgeleitet ist. Ein benutzerdefinierter Renderer ist erforderlich, um einige erweiterte Features von DirectWrite zu nutzen, z. B. Rendern auf eine Bitmap- oder GDI-Oberfläche, Inlineobjekte und Clientzeichnungseffekte. In diesem Tutorial werden die Methoden von IDWriteTextRenderer beschrieben und eine Beispielimplementierung bereitgestellt, die Direct2D verwendet, um Text mit einer Bitmapfüllung zu rendern.

Dieses Tutorial enthält die folgenden Teile:

Ihr benutzerdefinierter Textrenderer muss die von IUnknown geerbten Methoden zusätzlich zu den Methoden implementieren, die auf der IDWriteTextRenderer-Referenzseite und unten aufgeführt sind.

Den vollständigen Quellcode für den benutzerdefinierten Textrenderer finden Sie in den Dateien CustomTextRenderer.cpp und CustomTextRenderer.h des DirectWrite Hallo Welt-Beispiels.

Der Konstruktor

Ihr benutzerdefinierter Textrenderer benötigt einen Konstruktor. In diesem Beispiel werden sowohl Vollton- als auch Bitmap-Direct2D-Pinsel verwendet, um den Text zu rendern.

Aus diesem Gründen übernimmt der Konstruktor die Parameter in der folgenden Tabelle mit Beschreibungen.

Parameter BESCHREIBUNG
pD2DFactory Ein Zeiger auf ein ID2D1Factory-Objekt , das zum Erstellen aller benötigten Direct2D-Ressourcen verwendet wird.
Prt Ein Zeiger auf das ID2D1HwndRenderTarget-Objekt , auf das der Text gerendert wird.
pOutlineBrush Ein Zeiger auf den ID2D1SolidColorBrush , der verwendet wird, um eine Gliederung des Texts zu zeichnen.
pFillBrush Ein Zeiger auf den ID2D1BitmapBrush , der zum Füllen des Texts verwendet wird.

 

Diese werden vom Konstruktor gespeichert, wie im folgenden Code gezeigt.

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()

Die DrawGlyphRun-Methode ist die Standard Rückrufmethode des Textrenderers. Es wird eine Ausführung von Glyphen übergeben, die zusätzlich zu Informationen wie dem Baselineursprung und dem Messmodus gerendert werden sollen. Außerdem wird ein Client-Zeicheneffektobjekt übergeben, das auf die Glyphenausführung angewendet werden soll. Weitere Informationen finden Sie im Thema Hinzufügen von Clientzeichnungseffekten zu einem Textlayout .

Diese Textrendererimplementierung rendert Glyphenausführungen, indem sie in Direct2D-Geometrien konvertiert werden und dann die Geometrien zeichnen und füllen. Dies besteht aus den folgenden Schritten.

  1. Erstellen Sie ein ID2D1PathGeometry-Objekt , und rufen Sie dann das ID2D1GeometrySink-Objekt mithilfe der ID2D1PathGeometry::Open-Methode ab.

    // 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. Die anDrawGlyphRun übergebene DWRITE_GLYPH_RUN enthält ein IDWriteFontFace-Objekt namens fontFace, das die Schriftfläche für die gesamte Glyphenausführung darstellt. Fügen Sie den Umriss der Glyphe in die Geometriesenke ein, indem Sie die IDWriteFontFace:: GetGlyphRunOutline-Methode verwenden, wie im folgenden Code gezeigt.

    // 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. Nachdem Sie die Geometriesenke gefüllt haben, schließen Sie sie.

    // Close the geometry sink
    if (SUCCEEDED(hr))
    {
        hr = pSink->Close();
    }
    
  4. Der Ursprung der Glyphenausführung muss übersetzt werden, damit sie vom richtigen Baselineursprung gerendert wird, wie im folgenden Code gezeigt.

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

    BaselineOriginX und baselineOriginY werden als Parameter an die Rückrufmethode DrawGlyphRun übergeben.

  5. Erstellen Sie die transformierte Geometrie mithilfe der ID2D1Factory::CreateTransformedGeometry-Methode und übergeben Sie die Pfadgeometrie und die Übersetzungsmatrix.

    // Create the transformed geometry
    ID2D1TransformedGeometry* pTransformedGeometry = NULL;
    if (SUCCEEDED(hr))
    {
        hr = pD2DFactory_->CreateTransformedGeometry(
            pPathGeometry,
            &matrix,
            &pTransformedGeometry
            );
    }
    
  6. Zeichnen Sie schließlich die Umrisse der transformierten Geometrie, und füllen Sie sie mit den Methoden ID2D1RenderTarget::D rawGeometry und ID2D1RenderTarget::FillGeometry und den als Membervariablen gespeicherten Direct2D-Pinsel aus.

        // Draw the outline of the glyph run
        pRT_->DrawGeometry(
            pTransformedGeometry,
            pOutlineBrush_
            );
    
        // Fill in the glyph run
        pRT_->FillGeometry(
            pTransformedGeometry,
            pFillBrush_
            );
    
  7. Nachdem Sie nun mit dem Zeichnen fertig sind, vergessen Sie nicht, die Objekte zu sauber, die mit dieser Methode erstellt wurden.

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

DrawUnderline() und DrawStrikethrough()

IDWriteTextRenderer verfügt auch über Rückrufe zum Zeichnen von Unterstrichen und Durchstreichen. In diesem Beispiel wird ein einfaches Rechteck für eine Unterstreichung oder durchgestrichen, aber andere Formen können gezeichnet werden.

Das Zeichnen einer Unterstriche mit Direct2D besteht aus den folgenden Schritten.

  1. Erstellen Sie zunächst eine D2D1_RECT_F Struktur der Größe und Form der Unterstreichung. Die DWRITE_UNDERLINE Struktur, die an die DrawUnderline-Rückrufmethode übergeben wird, stellt den Offset, die Breite und die Stärke der Unterstreichung bereit.

    D2D1_RECT_F rect = D2D1::RectF(
        0,
        underline->offset,
        underline->width,
        underline->offset + underline->thickness
        );
    
  2. Erstellen Sie als Nächstes ein ID2D1RectangleGeometry-Objekt mithilfe der ID2D1Factory::CreateRectangleGeometry-Methode und der initialisierten D2D1_RECT_F-Struktur .

    ID2D1RectangleGeometry* pRectangleGeometry = NULL;
    hr = pD2DFactory_->CreateRectangleGeometry(
            &rect, 
            &pRectangleGeometry
            );
    
  3. Wie beim Glyphenlauf muss der Ursprung der Unterstrichsgeometrie basierend auf den Basisursprungwerten mithilfe der CreateTransformedGeometry-Methode übersetzt werden.

    // 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. Zeichnen Sie schließlich die Umrisse der transformierten Geometrie, und füllen Sie sie mit den Methoden ID2D1RenderTarget::D rawGeometry und ID2D1RenderTarget::FillGeometry und den als Membervariablen gespeicherten Direct2D-Pinsel aus.

        // Draw the outline of the glyph run
        pRT_->DrawGeometry(
            pTransformedGeometry,
            pOutlineBrush_
            );
    
        // Fill in the glyph run
        pRT_->FillGeometry(
            pTransformedGeometry,
            pFillBrush_
            );
    
  5. Nachdem Sie nun mit dem Zeichnen fertig sind, vergessen Sie nicht, die Objekte zu sauber, die mit dieser Methode erstellt wurden.

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

Der Prozess zum Zeichnen eines Durchstrichs ist identisch. Die Durchstreichung weist jedoch einen anderen Offset und wahrscheinlich eine andere Breite und Stärke auf.

Pixel Snapping, Pixel pro DIP und Transformation

IsPixelSnappingDisabled()

Diese Methode wird aufgerufen, um zu bestimmen, ob das Pixel snapping deaktiviert ist. Der empfohlene Standardwert ist FALSE, und das ist die Ausgabe dieses Beispiels.

*isDisabled = FALSE;

GetCurrentTransform()

In diesem Beispiel wird ein Direct2D-Renderziel gerendert. Leiten Sie daher die Transformation vom Renderziel mithilfe von ID2D1RenderTarget::GetTransform weiter.

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

GetPixelsPerDip()

Diese Methode wird aufgerufen, um die Anzahl der Pixel pro device Independent Pixel (DIP) abzurufen.

float x, yUnused;

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

DrawInlineObject()

Ein benutzerdefinierter Textrenderer verfügt auch über einen Rückruf zum Zeichnen von Inlineobjekten. In diesem Beispiel gibt DrawInlineObject E_NOTIMPL zurück. Eine Erklärung zum Zeichnen von Inlineobjekten sprengt den Rahmen dieses Tutorials. Weitere Informationen finden Sie im Thema Hinzufügen von Inlineobjekten zu einem Textlayout .

Der Destruktor

Es ist wichtig, alle Zeiger freizugeben, die von der benutzerdefinierten Textrenderer-Klasse verwendet wurden.

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

Verwenden des benutzerdefinierten Textrenderers

Sie rendern mit dem benutzerdefinierten Renderer mithilfe der IDWriteTextLayout::D raw-Methode , die eine von IDWriteTextRenderer abgeleitete Rückrufschnittstelle als Argument akzeptiert, wie im folgenden Code gezeigt.

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

Die IDWriteTextLayout::D raw-Methode ruft die Methoden des von Ihnen bereitgestellten benutzerdefinierten Renderers-Rückrufs auf. Die oben beschriebenen Methoden DrawGlyphRun, DrawUnderline, DrawInlineObject und DrawStrikethrough führen die Zeichnungsfunktionen aus.