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


Отрисовка с использованием пользовательского средства отрисовки текста

Макет DirectWriteтекста может быть нарисован пользовательским отрисовщиком текста, производным от IDWriteTextRenderer. Пользовательский отрисовщик необходим для использования некоторых расширенных функций DirectWrite, таких как отрисовка в растровом рисунке или поверхности GDI, встроенные объекты и клиентские эффекты рисования. В этом руководстве описаны методы IDWriteTextRenderer и приведен пример реализации, в котором Direct2D используется для отрисовки текста с растровой заливкой.

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

Пользовательский отрисовщик текста должен реализовывать методы, унаследованные от IUnknown, в дополнение к методам, перечисленным на странице ссылки IDWriteTextRenderer и ниже.

Полный исходный код пользовательского отрисовщика текста см. в файлах CustomTextRenderer.cpp и CustomTextRenderer.h примера DirectWrite Hello World.

Конструктор

Для пользовательского отрисовщика текста потребуется конструктор. В этом примере для отрисовки текста используются как кисти 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 является main методом обратного вызова отрисовщика текста. В дополнение к таким сведениям, как источник базового плана и режим измерения, передается выполнение глифов, которые будут отображаться. Он также передает объект клиентского эффекта рисования для применения к выполнению глифа. Дополнительные сведения см. в разделе Добавление клиентских эффектов рисования в текстовый макет .

Эта реализация отрисовщика текста отрисовывает глифы, преобразуя их в геометрические объекты Direct2D , а затем рисуя и заполняя геометрические объекты. Это состоит из следующих шагов.

  1. Создайте объект ID2D1PathGeometry , а затем получите объект ID2D1GeometrySink с помощью метода ID2D1PathGeometry::Open .

    // 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. DWRITE_GLYPH_RUN, передаваемый в DrawGlyphRun, содержит объект IDWriteFontFace с именем fontFace, который представляет лицо шрифта для всего выполнения глифа. Поместите контур глифа в приемник geometry с помощью метода 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. После заполнения приемника geometry закройте его.

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

    BaselineOriginX и baselineOriginY передаются в качестве параметров методу обратного вызова DrawGlyphRun.

  5. Создайте преобразованную геометрию с помощью метода ID2D1Factory::CreateTransformedGeometry и передав геометрию пути и матрицу перевода.

    // Create the transformed geometry
    ID2D1TransformedGeometry* pTransformedGeometry = NULL;
    if (SUCCEEDED(hr))
    {
        hr = pD2DFactory_->CreateTransformedGeometry(
            pPathGeometry,
            &matrix,
            &pTransformedGeometry
            );
    }
    
  6. Наконец, нарисуйте контур преобразованной геометрии и заполните ее с помощью методов ID2D1RenderTarget::D rawGeometry и ID2D1RenderTarget::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 структуру размера и формы подчеркивания. Структура DWRITE_UNDERLINE , передаваемая методу обратного вызова DrawUnderline , обеспечивает смещение, ширину и толщину подчеркивания.

    D2D1_RECT_F rect = D2D1::RectF(
        0,
        underline->offset,
        underline->width,
        underline->offset + underline->thickness
        );
    
  2. Затем создайте объект ID2D1RectangleGeometry с помощью метода ID2D1Factory::CreateRectangleGeometry и инициализированнойструктуры D2D1_RECT_F .

    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 rawGeometry и ID2D1RenderTarget::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 вызывает методы пользовательского обратного вызова отрисовщика, который вы предоставляете. Описанные выше методы DrawGlyphRun, DrawUnderline, DrawInlineObject и DrawStrikethrough выполняют функции рисования.