使用自定义文本呈现器进行呈现

可以通过派生自 IDWriteTextRenderer 的自定义文本呈现器绘制DirectWrite文本布局。 自定义呈现器需要利用DirectWrite的某些高级功能,例如呈现到位图或 GDI 图面、内联对象和客户端绘图效果。 本教程介绍 IDWriteTextRenderer 的方法,并提供一个示例实现,该实现使用 Direct2D 来呈现带有位图填充的文本。

本教程包含以下部分:

除了 IDWriteTextRenderer 引用页和下方列出的方法外,自定义文本呈现器还必须实现从 IUnknown 继承的方法。

有关自定义文本呈现器的完整源代码,请参阅DirectWrite Hello World示例的 CustomTextRenderer.cpp 和 CustomTextRenderer.h 文件。

构造函数

自定义文本呈现器需要构造函数。 此示例使用纯色画笔和位图 Direct2D 画笔呈现文本。

因此,构造函数采用下表中找到的参数,其中包含说明。

参数 说明
pD2DFactory 指向 ID2D1Factory 对象的指针,该对象将用于创建所需的任何 Direct2D 资源。
Prt 指向要呈现文本的 ID2D1HwndRenderTarget 对象的指针。
pOutlineBrush 指向 ID2D1SolidColorBrush 的指针,该指针将用于绘制文本的轮廓
pFillBrush 指向 ID2D1BitmapBrush 的指针,用于填充文本。

 

这些内容将由构造函数存储,如以下代码所示。

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

DrawGlyphRun 方法是文本呈现器的主要回调方法。 除了基线原点和测量模式等信息外,还传递了要呈现的字形运行。 它还传递要应用于字形运行的客户端绘图效果对象。 有关详细信息,请参阅 如何将客户端绘图效果添加到文本布局 主题。

此文本呈现器实现通过将它们转换为 Direct2D 几何图形,然后绘制和填充几何图形来呈现字形运行。 这包括以下步骤。

  1. 创建 ID2D1PathGeometry 对象,然后使用 ID2D1PathGeometry::Open 方法检索 ID2D1GeometrySink 对象。

    // 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. 传递给 DrawGlyphRunDWRITE_GLYPH_RUN包含一个名为 fontFaceIDWriteFontFace 对象,该对象代表整个字形运行的字体面。 使用 IDWriteFontFace:: GetGlyphRunOutline 方法将字形的轮廓放入几何图形接收器中,如以下代码所示。

    // 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. 填充几何图形接收器后,将其关闭。

    // Close the geometry sink
    if (SUCCEEDED(hr))
    {
        hr = pSink->Close();
    }
    
  4. 必须转换字形运行的来源,使其从正确的基线源呈现,如以下代码所示。

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

    baselineOriginXbaselineOriginY 作为参数传递给 DrawGlyphRun 回调方法。

  5. 使用 ID2D1Factory::CreateTransformedGeometry 方法以及传递路径几何图形和转换矩阵来创建转换的几何图形。

    // Create the transformed geometry
    ID2D1TransformedGeometry* pTransformedGeometry = NULL;
    if (SUCCEEDED(hr))
    {
        hr = pD2DFactory_->CreateTransformedGeometry(
            pPathGeometry,
            &matrix,
            &pTransformedGeometry
            );
    }
    
  6. 最后,绘制已转换几何图形的轮廓,并使用 ID2D1RenderTarget::D rawGeometryID2D1RenderTarget::FillGeometry 方法和存储为成员变量的 Direct2D 画笔填充它。

        // Draw the outline of the glyph run
        pRT_->DrawGeometry(
            pTransformedGeometry,
            pOutlineBrush_
            );
    
        // Fill in the glyph run
        pRT_->FillGeometry(
            pTransformedGeometry,
            pFillBrush_
            );
    
  7. 完成绘图后,不要忘记清理在此方法中创建的对象。

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

DrawUnderline () 和 DrawStrikethrough ()

IDWriteTextRenderer 还具有用于绘制下划线和删除线的回调。 本示例为下划线或前线绘制一个简单的矩形,但可以绘制其他形状。

使用 Direct2D 绘制下划线包括以下步骤。

  1. 首先,创建下划线的大小和形状的 D2D1_RECT_F 结构。 传递给 DrawUnderline 回调方法的DWRITE_UNDERLINE结构提供下划线的偏移量、宽度和粗细。

    D2D1_RECT_F rect = D2D1::RectF(
        0,
        underline->offset,
        underline->width,
        underline->offset + underline->thickness
        );
    
  2. 接下来,使用 ID2D1Factory::CreateRectangleGeometry 方法和初始化D2D1_RECT_F结构创建 ID2D1RectangleGeometry 对象。

    ID2D1RectangleGeometry* pRectangleGeometry = NULL;
    hr = pD2DFactory_->CreateRectangleGeometry(
            &rect, 
            &pRectangleGeometry
            );
    
  3. 与字形运行一样,必须使用 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. 最后,绘制已转换几何图形的轮廓,并使用 ID2D1RenderTarget::D rawGeometryID2D1RenderTarget::FillGeometry 方法和存储为成员变量的 Direct2D 画笔填充它。

        // Draw the outline of the glyph run
        pRT_->DrawGeometry(
            pTransformedGeometry,
            pOutlineBrush_
            );
    
        // Fill in the glyph run
        pRT_->FillGeometry(
            pTransformedGeometry,
            pFillBrush_
            );
    
  5. 完成绘图后,不要忘记清理在此方法中创建的对象。

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

绘制删除线的过程相同。 但是,删除线将有不同的偏移量,并且可能有不同的宽度和粗细。

像素对齐、每 DIP 像素和转换像素

IsPixelSnappingDisabled ()

调用此方法以确定是否禁用像素对齐。 建议的默认值为 FALSE,即此示例的输出。

*isDisabled = FALSE;

GetCurrentTransform ()

此示例呈现为 Direct2D 呈现目标,因此使用 ID2D1RenderTarget::GetTransform 从呈现目标转发转换。

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

GetPixelsPerDip ()

调用此方法以获取每个设备独立像素数 (DIP) 。

float x, yUnused;

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

DrawInlineObject ()

自定义文本呈现器还具有用于绘制内联对象的回调。 在此示例中, DrawInlineObject 返回E_NOTIMPL。 有关如何绘制内联对象的说明超出了本教程的范围。 有关详细信息,请参阅 如何将内联对象添加到文本布局 主题。

析构函数

必须释放自定义文本呈现器类使用的任何指针。

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

使用自定义文本呈现器

通过使用 IDWriteTextLayout::D raw 方法来呈现自定义呈现器,该方法采用从 IDWriteTextRenderer 派生的回调接口作为参数,如以下代码所示。

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

IDWriteTextLayout::D raw 方法调用你提供的自定义呈现器回调的方法。 DrawGlyphRunDrawUnderlineDrawInlineObjectDrawStrikethrough 方法执行绘图函数。