Поделиться через


Добавление клиентских эффектов рисования в текстовый макет

Краткое руководство по добавлению клиентских эффектов рисования в приложение DirectWrite, которое отображает текст с помощью интерфейса IDWriteTextLayout и пользовательского отрисовщика текста.

Конечным продуктом этого учебника является приложение, которое отображает текст с диапазонами текста с разными цветными эффектами рисования, как показано на следующем снимке экрана.

Снимок экрана:

Примечание

Это руководство предназначено для упрощения создания пользовательских клиентских эффектов рисования, а не простого метода рисования цветного текста. Дополнительные сведения см. на странице справочника IDWriteTextLayout::SetDrawingEffect .

 

Это руководство содержит следующие части:

Шаг 1. Создание макета текста

Для начала потребуется приложение с объектом IDWriteTextLayout . Если у вас уже есть приложение, отображающее текст с текстовым макетом, или вы используете пример пользовательского кода DrawingEffect, перейдите к шагу 2.

Чтобы добавить текстовый макет, выполните следующие действия.

  1. Объявите указатель на интерфейс IDWriteTextLayout в качестве члена класса .

    IDWriteTextLayout* pTextLayout_;
    
    
  2. В конце метода CreateDeviceIndependentResources создайте объект интерфейса IDWriteTextLayout , вызвав метод 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. Наконец, не забудьте освободить макет текста в деструкторе.

    SafeRelease(&pTextLayout_);
    

Шаг 2. Реализация класса пользовательского эффекта рисования

Помимо методов, унаследованных от IUnknown, настраиваемый интерфейс клиентского эффекта рисования не имеет требований к тому, что он должен реализовать. В этом случае класс ColorDrawingEffect просто содержит значение D2D1_COLOR_F и объявляет методы для получения и задания этого значения, а также конструктор, который может задать цвет изначально.

После применения эффекта рисования клиента к текстовому диапазону в объекте IDWriteTextLayout эффект рисования передается в метод IDWriteTextRenderer::D rawGlyphRun любого отрисовки глифа. Методы эффекта рисования становятся доступными для отрисовщика текста.

Клиентский эффект рисования может быть настолько сложным, насколько вы хотите сделать его, и содержать больше информации, чем в этом примере, а также предоставлять методы для изменения глифов, создания объектов, которые будут использоваться для рисования, и т. д.

Шаг 3. Реализация класса пользовательского отрисовщика текста

Чтобы воспользоваться преимуществами клиентского эффекта рисования, необходимо реализовать пользовательский отрисовщик текста. Этот отрисовщик текста будет применять эффект рисования, переданный ему методом IDWriteTextLayout::D raw , к отрисовываемого выполнения глифа.

Конструктор

Конструктор пользовательского отрисовщика текста хранит объект ID2D1Factory , который будет использоваться для создания объектов Direct2D , и целевой объект отрисовки Direct2D, на котором будет нарисован текст.

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

Метод DrawGlyphRun

Выполнение глифа — это набор глифов, которые имеют одинаковый формат, включая эффект рисования клиента. Метод DrawGlyphRun отвечает за отрисовку текста для указанного выполнения глифа.

Сначала создайте ID2D1PathGeometry и ID2D1GeometrySink, а затем получите структуру запуска глифа с помощью IDWriteFontFace::GetGlyphRunOutline. Затем преобразуйте источник геометрии с помощью метода Direct2DID2D1Factory::CreateTransformedGeometry , как показано в следующем коде.

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

Затем объявите объект сплошной кисти Direct2D .

ID2D1SolidColorBrush* pBrush = NULL;

Если параметр clientDrawingEffect не имеет значения NULL, запросите к объекту интерфейс ColorDrawingEffect . Это будет работать, так как этот класс будет задан в качестве клиентского эффекта рисования для текстовых диапазонов объекта макета текста.

Получив указатель на интерфейс ColorDrawingEffect , можно получить значение D2D1_COLOR_F , которое он хранит, с помощью метода GetColor . Затем используйте D2D1_COLOR_F , чтобы создать ID2D1SolidColorBrush в этом цвете.

Если параметр clientDrawingEffect имеет значение NULL, просто создайте черный 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);
    }
}

Наконец, нарисуйте геометрию контура и заполните ее с помощью только что созданной кисти сплошного цвета.

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

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

Деструктор

Не забудьте освободить фабрику Direct2D и отобразить целевой объект в деструкторе.

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

Шаг 4. Создание отрисовщика текста

В ресурсах CreateDeviceDependent создайте пользовательский объект отрисовщика текста. Он зависит от устройства, так как использует ID2D1RenderTarget, который сам по себе зависит от устройства.

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

Шаг 5. Создание экземпляра объектов цветного эффекта рисования

Создайте экземпляры объектов ColorDrawingEffect красного, зеленого и синего цветов.

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

Шаг 6. Настройка эффекта рисования для определенных текстовых диапазонов

Задайте эффект рисования для определенных диапазонов текста с помощью метода IDWriteTextLayou::SetDrawingEffect и структуры 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);
    }
}

Шаг 7. Рисование макета текста с помощью пользовательского отрисовщика

Необходимо вызвать метод IDWriteTextLayout::D raw , а не метод ID2D1RenderTarget::D rawText или ID2D1RenderTarget::D rawTextLayout .

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

Шаг 8. Очистка

В деструкторе DemoApp отпустите пользовательский отрисовщик текста.

SafeRelease(&pTextRenderer_);

После этого добавьте код, чтобы освободить классы клиентских эффектов рисования.

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