Delphi programming blog
Источник: http://teran.karelia.pro/articles/item_5432.html
 

Getting Started with DirectWrite

Опубликовано 28.04.2012 г. 12:11

При первой попытке написать эту статью, меня постигла неудача, текст не сохранился. Так что пишу по второму разу. Хотел было посмотреть на новые возможности Direct2D в Windows 8, но на глаза попался DirectWrite, который я еще не пробовал использовать. Поэтому решил сначала разобраться с ним, а уже затем к этом применить новые эффекты Direct2D в Win8.

Если не вдаваться в подробности, что такое DirectWrite, то по-простому эта технология позволяет выводить более качественный текст, используя аппаратное ускорение, нежели какие то другие. Статья эта основана на одноменной статье Getting Started with DirectWrite в MSDN, и адаптирована для использования в Delphi. В ней мы рассмотрим два варианта вывода текста  на канву: простой текст, и текст множественным форматированием.
В Delphi основной объект для работы с DirectWrite - TDirect2DFont.Сначала я попробовал использовать данный объект для работы, но оказалось, что он не полностью реализует функционал заложенный в DirectWrite и использовать его было не  очень удобно.
Для при работе с текстом в DirectWrite первым делом нам потребуется фабрика IDWriteFactory, экземпляр которой можно получить, используя функцию DWriteFactory() модуля VCL.Direct2D. Далее фабрика может предоставить нам объекты для настройки формата шрифтов. Поскольку это делает именно фабрика, то объекты эти являются "независимыми", т.е их можно использовать на разных поверхностях рисования, в отличии от, например, кистей, которые привязаны к своей канве.

Первый пример - вывод простого текста с заданным форматированием. Что бы нарисовать какой либо текст, нам необходимо иметь 4 составляющих: строка текста, границы, в которых  он будет размещен, поверхность, на которой он будет нарисован, и экземпляр IDWriteTextFormat, описывающий настройки его форматирования. Объект этот создается с помощью, как я уже говорил, фабрики, и при создании требует задания следующих обязательных параметров: название шрифта, размер, толщина, стиль, растяжение, локаль. Эти параметры в дальнейшем не могут быть изменены. К изменяемым (с помощью соответствующих методов) относятся следующие настройки: междустрочный инетрвал, выравнивание по горизонтали и вертикали, направление чтения, обрезка текста (если видимо не влазит в границы), позиции табуляции, направление абзаца (не знаю уж для чего это надо, но значение тут может быть только одно - сверху вниз (: ).
В форму я добавил пару полей - экземпляр фабрики, фоновую градиентную кисть, и объект формата текста. При создании формы мы можем их инициализировать:

    FDWFactory := DWriteFactory();
    //font
    FDWFactory.CreateTextFormat(
                'Gabriola', nil,
                DWRITE_FONT_WEIGHT_REGULAR,
                DWRITE_FONT_STYLE_NORMAL,
                DWRITE_FONT_STRETCH_NORMAL,
                90,
                'en-us',
                FTextFormat
        );
    FTextFormat.SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
    FTextFormat.SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);

 

Здесь мы получили экземпляр фабрики (он фактически синглтон), создали объект формата текста: обычнй (не жирный, без курсива, не растянутый) шрифт Gabriola, размера 90 с локалью en-us. После чего мы задали вырвание по высоте и ширине - по центру. Тут как раз и был мой камень предкновения с TDirect2DFont, он не позволяет задавать выравнивание. А при обращении к его свойству Handle каждый раз создается новый объект IDWriteTextFormat, следовательно надо для этих целей сохранять ссылку на искомый интерфейс.

Следующим шагом явялется рисование. В моем случае клиентская область формы заливается градиентной заливкой, а потом выводится текст. Общий код рисования на форме следующий:

procedure TMainForm.FormPaint(Sender: TObject);
const text = 'Hello Direct Write!';
var rt : ID2D1HwndRenderTarget;
    rc : TD2DRectF;
begin
    rc := GetClientRect();

    FCanvas.BeginDraw();
    rt := FCanvas.RenderTarget as ID2D1HwndRenderTarget;
    try
        FBgBrush.SetStartPoint(D2D1PointF(0, 0));
        FBgBrush.SetEndPoint(D2D1PointF(0, height));
        rt.FillRectangle(rc, FBgBrush);

        FCanvas.Brush.color := $b45f14;
        DrawSimpleText();
        //DrawMultiFormatText();

    finally
        FCanvas.EndDraw();
    end;
end;

 Вложенная процедура DrawSimpleText выводит заданный текст на канву. Код ее весьма прост:

    procedure DrawSimpleText();
    begin
        rt.DrawText(text, length(text),
                    FTextFormat, rc, FCanvas.Brush.Handle);
    end;

 

При выводе указаывается строка текста, ее длина, формат текста, прямоугольная область для вывода, ну и кисть (кисть то кстати может быть любой, не только сплошной но и, например, растровой, так что легко получитьтекст в крапинку какую нить и т.п.). Результатом работы такого кода представлен на рисунке ниже:

Теперь перейдем к тексту с множественным форматированием. Смысл тут в том, что мы можем менять параметры текста для отдельных символов в строке. Для этого необходимо дополнительно использовать интерфейс IDWriteTextLayout. Базовые настройки шрифта все так же задаются с помощью IDwriteTextFormat. Используя IDWriteTextLayout можно изменить даже те параметры, которые были не изменяемы в предыдущем случае (например, название шрифта, размер и т.п.). Этот интерфейс в дополнение позволяет проводить хит-тест, так что можно узнать, ткнул ли пользователь в букву мышью или нет, но об этом в одной из следующих статей. С помощью интерфейса IDWriteTypography доступно так же использование возможностей OpenType шрифтов. Например,  к таким возхможностям относится то, что назвается "нижний" или "верхний" символ.
Создание и рисование текста с мульти-форматирвоанием у меня выполнено в виде следующей процедуры:

    procedure DrawMultiFormatText();
    var tl : IDWriteTextLayout;
        r : TDWriteTextRange;
    begin
        FDWFactory.CreateTextLayout(text, length(text), FTextFormat,
                width, height, tl);

        tl.SetUnderline(true, DWTextRange(1,1));
        tl.SetFontWeight(DWRITE_FONT_WEIGHT_BOLD, DWTextRange(4,1));
        tl.SetFontSize(60,   DWTextRange(6,1));
        tl.SetFontWeight(DWRITE_FONT_WEIGHT_BOLD, DWTextRange(6,1));

        rt.DrawTextLayout(D2D1PointF(0,0), tl, FCanvas.Brush.Handle);
    end;

 

 Здесь мы создали экземпляр IDWriteTextLayout, задав в качестве параметров тект и его длину, исходный формат, ширину и высоту границ текста (ее можно изменять). После чего с помощью набора  Set-Методов мы изменили форматирование некоторых фрагментов текста. При измении форматирования используется структура TDWriteTextRange, определяющая начальную позицию в тексте и длину диапазона. Здесь для заполнения такой структуры используется функция DWTextRange(), код ее приводить не стал. Сначала добавили подчеркивание ко второму символу строки, затем выделили жирным 5й символ (буква о, в hello), уменьшили размер 7го символа до 60 и сделали его жирным (буква D). Вывели текст с помощью метода DrawTextLayout. Первым параметром метода является координаты левого верхнего угла прямоугольника текста. Результат ниже:

На этом знакомоство с DirectWrite завершено, впереди еще пара-тройка статей на эту тему.

Метки:  Direct2D  |  DirectWrite 

Комментарии

Нет комментариев
- Имя
- e-mail*
- Сайт
вы можете использовать теги [i],[b],[code],[quote]
Дополнительно