Hinzufügen von Clientzeichnungseffekten zu einem Textlayout
Enthält ein kurzes Tutorial zum Hinzufügen von Clientzeichnungseffekten zu einer DirectWrite-Anwendung, die Text mithilfe der IDWriteTextLayout-Schnittstelle und einem benutzerdefinierten Textrenderer anzeigt.
Das Endprodukt dieses Tutorials ist eine Anwendung, die Textbereiche mit jeweils unterschiedlichen Farbzeichnungseffekten anzeigt, wie im folgenden Screenshot gezeigt.
Hinweis
Dieses Tutorial soll ein vereinfachtes Beispiel für das Erstellen von benutzerdefinierten Clientzeichnungseffekten sein, kein Beispiel für eine einfache Methode zum Zeichnen von Farbtext. Weitere Informationen finden Sie auf der Referenzseite IDWriteTextLayout::SetDrawingEffect .
Dieses Tutorial enthält die folgenden Teile:
- Schritt 1: Erstellen eines Textlayouts
- Schritt 2: Implementieren einer benutzerdefinierten Zeichnungseffektklasse
- Schritt 3: Implementieren einer benutzerdefinierten Textrenderer-Klasse
- Schritt 4: Erstellen des Textrenderers
- Schritt 5: Instanziieren der Farbzeichnungseffektobjekte
- Schritt 6: Festlegen des Zeichnungseffekts für bestimmte Textbereiche
- Schritt 7: Zeichnen des Textlayouts mit dem benutzerdefinierten Renderer
- Schritt 8: Bereinigen
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 Wenn Sie den benutzerdefinierten Zeichnungsbeispielcode verwenden, fahren Sie mit Schritt 2 fort.
Um ein Textlayout hinzuzufügen, müssen Sie die folgenden Schritte ausführen:
Deklarieren Sie einen Zeiger auf eine IDWriteTextLayout-Schnittstelle als Member der -Klasse.
IDWriteTextLayout* pTextLayout_;
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. ); }
Denken Sie schließlich daran, das Textlayout im Destruktor freizugeben.
SafeRelease(&pTextLayout_);
Schritt 2: Implementieren einer benutzerdefinierten Zeichnungseffektklasse
Abgesehen von den methoden, die von IUnknown geerbt werden, hat eine benutzerdefinierte Clientzeichnungseffektschnittstelle keine Anforderungen hinsichtlich dessen, 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 stehen dann dem Textrenderer zur Verfügung.
Ein Clientzeichnungseffekt kann so komplex sein, wie Sie es möchten, und mehr Informationen als in diesem Beispiel enthält, sowie Methoden zum Ändern von Glyphen, Erstellen von Objekten, die zum Zeichnen verwendet werden sollen usw.
Schritt 3: Implementieren einer benutzerdefinierten Textrenderer-Klasse
Um einen Clientzeichnungseffekt nutzen zu können, müssen Sie einen benutzerdefinierten Textrenderer implementieren. Dieser Textrenderer wendet den zeichnungseffekt an, der von der IDWriteTextLayout::D raw-Methode auf die gezeichnete Glyphenausführung übergeben 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 dem der Text gezeichnet wird.
CustomTextRenderer::CustomTextRenderer(
ID2D1Factory* pD2DFactory,
ID2D1HwndRenderTarget* pRT
)
:
cRefCount_(0),
pD2DFactory_(pD2DFactory),
pRT_(pRT)
{
pD2DFactory_->AddRef();
pRT_->AddRef();
}
Die DrawGlyphRun-Methode
Eine Glyphenausführung ist ein Satz von Glyphen, die das gleiche Format verwenden, einschließlich des Clientzeichnungseffekts. Die DrawGlyphRun-Methode kümmert sich um das Rendern von Text für eine angegebene Glyphenausführung.
Erstellen Sie zunächst eine ID2D1PathGeometry und eine ID2D1GeometrySink, und rufen Sie dann die Glyphenausführungsgliederung mithilfe von IDWriteFontFace::GetGlyphRunOutline ab. Transformieren Sie dann den Ursprung der Geometrie, indem Sie die Direct2DID2D1Factory::CreateTransformedGeometry-Methode verwenden, 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-Volltonpinselobjekt.
ID2D1SolidColorBrush* pBrush = NULL;
Wenn der clientDrawingEffect-Parameter nicht NULL ist, fragen Sie das Objekt für die ColorDrawingEffect-Schnittstelle ab. Dies funktioniert, da Sie diese Klasse als Clientzeichnungseffekt auf Textbereiche des Textlayoutobjekts festlegen.
Sobald Sie über einen Zeiger auf die ColorDrawingEffect-Schnittstelle verfügen, können Sie den D2D1_COLOR_F Wert abrufen, den sie mit der GetColor-Methode speichert. Verwenden Sie dann die D2D1_COLOR_F , um einen ID2D1SolidColorBrush in dieser Farbe zu erstellen.
Wenn der clientDrawingEffect-ParameterNULL 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 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);
}
}
Zeichnen Sie schließlich die Konturgeometrie, und füllen Sie sie mit dem soeben erstellten Volltonpinsel 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 und das Renderziel im Destruktor freizugeben.
CustomTextRenderer::~CustomTextRenderer()
{
SafeRelease(&pD2DFactory_);
SafeRelease(&pRT_);
}
Schritt 4: Erstellen des Textrenderers
Erstellen Sie in den Ressourcen CreateDeviceDependent das benutzerdefinierte Textrendererobjekt. Es ist geräteabhängig, da es den ID2D1RenderTarget verwendet, der selbst geräteabhängig ist.
// Create the text renderer
pTextRenderer_ = new CustomTextRenderer(
pD2DFactory_,
pRT_
);
Schritt 5: Instanziieren der Farbzeichnungseffektobjekte
Instanziieren von ColorDrawingEffect-Objekten 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 fest, indem Sie die IDWriteTextLayou::SetDrawingEffect-Methode und eine DWRITE_TEXT_RANGE Struktur verwenden.
// 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 mit dem benutzerdefinierten Renderer
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 Zeicheneffektklassen des Clients freizugeben.
SafeRelease(&redDrawingEffect_);
SafeRelease(&blueDrawingEffect_);
SafeRelease(&greenDrawingEffect_);