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

Direct 2D: Brushes Overview #1

Опубликовано 20.05.2010 г. 20:43

Представляю вам очередную адаптацию статьи Direct 2D Programming Guide: Brushes Overview применительно к использованию в среде Delphi. Данная статья описывает как создавать различные типы кистей и использовать их для заполнения поверхностей.

Обзор содержит следующие разделы:
  1. Типы кистей
  2. Основы определения цвета
  3. Использование сплошных кистей
  4. Использование кистей с линейным градиентом
  5. Об использовании GradientStops
  6. Ось градиента
  7. Использование кистей с круговым градиентом

Типы кистей

С помощью кисти вы можете закрасить требуемую часть поверхности, и каждый тип кисти формирует различный результат. Direct2D предоставляет 4 вида кистей:
  1. ID2D1SolidColorBrush заливает область сплошным цветом
  2. ID2D1LinearGradientBrush заливает область линейным градиентом
  3. ID2D1RadialGradientBrush заливает область с помощью кругового градиента
  4. ID2D1BitmapBrush заполняет область растровым изображением кисти
Все кисти наследуют интерфейс ID2D2Brush и представляют набор общих свойств (прозрачность и преобразования). Кисти создаются с помощью поверхности и зависят от нее. Таким образом если в вашем приложении вам приходится пересоздавать поверхность то вам прийдется заново создавать и кисти. В модуле direct2d.pas описан класс TDirect2DBrush для работы с кистями. Виды кистей представлены на следующем рисунке: Типы кистей

Основы определения цвета

Перед тем как вы будете использовать кисти (сплошную/гридиентную) для рисования вам потребуется установить ее цвет(а). Цвета в Direct2D представимы с помощью структуры D2D1_COLOR_F (TD2D1ColorF), аналогом структуры описывающей цвет в Direct3D (D3DCOLORVALUE - TD3DColorValue) Цвет представляется в формате sRGB с помощью четырех компонент: красный, зеленый, синий, и значение прозрачности (альфа-канал). Каждая компонента представлена значением с плавающей точкой из диапазона [0.0 - 1.0]. Значению 0.0 соответствует полное отсутствие данной цветовой составляющей, 1.0 иначе. Значение альфа составляющей равное 0.0 представляет полностью прозрачный цвет, и 1.0 - полностью непрозраный. Чтобы определить цвета вы можете использовать структуру TD2D1ColorF и самостоятельно определить значения ее полей, либо можете воспользоваться вспомогательной функцией D2D1ColorF чтобы получить ее. В Delphi данная функция имеет две реализации: с 1 параметром - TColor, и с 2мя параметрами TColor + Alpha. При использовании первого варианта значение альфа устанавливается в 1, если в качестве значения цвета выступает что либо отличное от clNone. Можете поглядеть на красивый рисунок стандартных цветов, которые определены в Direct2D, но не перенесены в заголовочные файлы Delphi. Реализация функции с 4мя параметрами для всех составляющих каналов так же отсутствует, но вы без труда сможете реализовать ее самостоятельно если понадобится.

Использование сплошных кистей

Сплошная кисть представляется с помощью объекта типа ID2D1SolidColorBrush, для создания которой используется метод CreateSolidColorBrush поверхности (renderTarget). Ниже приведенный рисунок иллюстрирует прямоугольник закрашенный с помощью сплошной кисти цвета 0x9ACD32.
 
Следуя вышесказанному и имея поверхность renderTarget : ID2D1RenderTarget можно использовать следующий код для создания сплошной кисти:
var  scBrush : ID2D1SolidColorBrush;

  renderTarget.CreateSolidColorBrush(D2D1ColorF($32CD9A), nil, scBrush);
  renderTarget.FillRectangle(getClientRect(), scBrush);
В оригинале метод CreateSolidColorBrush может иметь либо два либо три параметра, первый определяющий цвет, последний выходной получает кисть. В середине может быть указана структура TD2D1BrushProperties, в которой могут быть указаны прозрачность и трансформация кисти. Таким образом прозрачность может быть установлена тремя способами: 1. При указании цвета при создании кисти можно указать значение альфа канала 2. Передать структуру TD2D1BrushProperties с заданным значением opacity 3. воспользоваться методом SetOpacity кисти. При этом прозрачности кисти (методы 2 и 3) комбинируется с прозрачностью цвета (метод 1). Т.е прозрачность кисти и значения альфа канала цвета кисти это не есть одно и то же. Если вы имеете поверхность TDirect2DCanvas (например вместо стандартной канвы формы), то по умолчанию ее свойство Brush является сплошной кистью. Тогда в событии OnPaint формы вы можете применить следующий код для заливки формы:
    with canvas do begin
        Brush.Color := $32CD9A;
        brush.Handle.SetOpacity(0.5);

        BeginDraw();
        FillRect(getClientRect);
        EndDraw();
    end;
Для создания TDirect2DBrush кисти можно воспользоваться методом TDirect2DCanvas.CreateBrush(), который имеет 4 перегруженных варианта. Одной из вариаций является CreateBrush(color:TColor), который и создает сплошную кисть. Как далее говорит MSDN, создание сплошной кисти это весьма не затратная операция, поэтому вы можете создавать кисти каждый раз когда это требуется. Однако данный подход не рекомендуется для градиентных кистей и т.д.

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

Класс кистей ID2D1LinearGradientBrush заливает область с помощью линейного градиента вдоль линии называемой осью, или направлением градиента. Вы можете устанавливать цвета градиента их местоположение на оси с помощью объектов ID2D1GradientStopCollection (тут в MSDN опечатка ;) ) Вы также можете изменять ось градиента, что позволяет задавать горизонтальные или вертикальные градиенты, или менять направление. Для создания кисти с линейным градиентом используется метод CreateLinearGradientBrush() поверхности. На следующем рисунке представлен линейный градиент с переходом от желтого к зеленому цвету:

Чтобы создать такой градиент, нам понадобится:
  1. Две структуры D2D1_GRADIENT_STOP (TD2D1GradientStop) которые описывают цвет и его позицию в градиенте (не знаю какое слово подобрать для перевода stop в данном случае :) пусть будет срез, в смысле некоторое значение чего то в какой то момент). Позиция 0 обозначает начало градиента, и 1 обозначает конец градиента.
  2. Создать объект ID2D1GradientStopCollection передав в качестве параметров набор срезов градиента, их количество, гамму используемую при расчете градиента (подробнее), и метод "растягивания/расширения" - определяет как будет закрашена область за пределами кисти.
  3. Создать кисть с линейным градиентом, передав коллекцию срезов, начальную и конечные точки градиента
Для чего можно применить следующий код:
var gStops : array[0..1] of TD2D1GradientStop;
    gsCollection : ID2D1GradientStopCollection;
    lgBrush : ID2D1LinearGradientBrush;
....
    gStops[0].position := 0;
    gStops[0].color    := D2D1ColorF(clYellow);
    gStops[1].position := 1;
    gStops[1].color    := D2D1ColorF($32CD9A);

    with Canvas do begin
        renderTarget.CreateGradientStopCollection(@gStops[0],2,
                            D2D1_GAMMA_2_2,
                            D2D1_EXTEND_MODE_CLAMP,
                            gsCollection);
        renderTarget.CreateLinearGradientBrush(
                    D2D1LinearGradientBrushProperties(Point(0,0),Point(150,150)),
                    nil,gsCollection,lgBrush);

        BeginDraw();
        renderTarget.FillRectangle(GetClientRect, lgBrush);
        EndDraw();
    end;
С точки зрения объема кода куда проще выглядит код с использованием TDirect2DCanvas и TDirect2DBrush. Один из переопределенных методов создания кисти у канвы создает как раз таки кисть с линейным градиентом, этим мы можем и воспользоваться:
    with canvas do begin
        brush.handle := CreateBrush( [clYellow,$32CD9A],point(0,0),point(150,150));

        BeginDraw();
        FillRect(getClientRect());
        EndDraw();
    end;
при этом получим тот же самый результат. Но следует учитывать, что при таком создании кисти прозрачности цвета всегда устанавливаются равными 1, методы интерполяции и расширения - по умолчанию, а весь интервал градиента делится между цветами равномерно. При этом следует учитывать, что после создания кисти подобным образом, из перечисленных выше свойств кисти можно изменить только начальную и конечные точки.

Об использовании GradientStops

Структура TD2D1GradientStop является базовой для построения градиентной кисти, и определяет цвет и его позицию на оси градиента. Значение позиции изменяется в пределах [0..1]. Чем ближе позиция к нулю тем ближе заданный цвет к началу градиента, и наоборот. На следующем рисунке кружками отмечены позиции цвета в градиенте, и пунктирной линией обозначена ось градиента.

Ось градиента

Как отмечалось ранее, изменение цвета при использовании линейного градиента происходит вдоль линии, называемой осью градиента. Вы можете задавать ориентацию и длину линии с помощью начальной и конечной точки, используя поля startPoint & endPoint структуры TD2D1LinearGradientBrushProperties когда создаете кисть. После того как вы создали кисть, вы можете изменить изменить эти значения используя методы SetStartPoint(), setEndPoint(). Изменяя значения начальной и конечной точки вы можете создавать горизонтальные или вертикальные градиенты, изменить их направление, и т.д. Можете посмотреть рисунок, иллюстрирующий различные вариации начальной и конечной точки для создания разных типов градиента.

Использование кистей с круговым градиентом

В отличии кисти с линейным градиентом, которая заполняет область одним или несколькими цветами вдоль оси градиента, кисть с круговым градиентом заполняет область перпендикулярно эллиптической кривой. Данная кисть представлена объектом ID2D1RadialGradientBrush. Если линейный градиент определяется начальной и конечной точкой, то круговой градиент задается с помощью эллипса, определяя центр, два радиуса, и смещение центра градиента (т.е начало градиента не обязательно совпадет с центром эллипса). Цвета и их позиции также задаются с помощью ID2D1GradientStopCollection. Ниже приведена иллюстрация кисти с круговым градиентом, в котором определены два цвета: желтый в позиции 0, и зеленый в позиции 1. Центр градиента в точке (75,75) и смещение центра градиента (0,0), т.е центры совпадают. Оба радиуса кисти равны 75.

Чтобы создать кисть с круговым градиентом, необходимо использовать метод CreateRadialGradientBrush() поверхности. Данный метод имеет четыре параметра (в оригинале 3 или 4 параметра). Первый параметр - структура TD2D1RadialGradientBrushProperties описывающая центр, смещение и радиусы. Данную структуру можно создать с помощью вспомогательной функции D2D1RadialGradientBrushProperties(). Второй параметр - структура TD2D1BrushProperties, которую описывали ранее (может быть nil). Далее следует объект ID2D1GradientStopCollection, и выходной параметр - наша кисть. Ниже приведен код для создания кисти, предполагается что создание и заполнение ID2D1GradientStopCollection такое же как и при реализации линейной кисти.

var rgBrush : ID2D1RadialGradientBrush;
.....
        renderTarget.CreateRadialGradientBrush(
                D2D1RadialGradientBrushProperties(Point(250,150),Point(20,30),80,80),
                nil,
                gsCollection,
                rgBrush);
        BeginDraw();
        renderTarget.FillRectangle(GetClientRect(), rgBrush);
        EndDraw();
Аналогично, как и в случае использования кисти с линейным градиентом на канве, код создания кисти с круговым градиентом более краток, но обладает теми же недостатками.
    with canvas do begin
        Brush.Handle  := CreateBrush([clYellow,$32CD9A], point(250,150), point(20,30), 80, 80);
        BeginDraw();
        FillRect(getClientRect());
        EndDraw();
    end;
Здесь можно наглядно посмотреть как выглядит кисть с различными смещениями начала градиента относительно центра кисти. Центр кисти задается, кстати, в координатах поверхности, а смещение в координатах кисти. Интересный эффект можно получить если величину смещения установить большей чем радиус самой кисти. На самом деле после того как я написал данную строку то пошел пробовать, что получится. И если честно результат был не таков, как я предполагал. Думал что получится что то наподобие эффекта света в фотошопе (Filters -> Render -> Lighting Effect) т.е эллипс залитый цветом, при этом пусть точка света находится за эллипсом, но конечный вывод всегда остается в пределах этого эллипса. Так сказать взять эллипс, и посветить на него из вне. Но результат, который приведен ниже, мои ожидания опроверг.

На мой взгляд, если я все таки использую круговую кисть, то и результат не должен выходить за ее пределы. Может быть разработчики D2D чего то перемудрили? Чтобы закончить рассмотрение кистей, нам осталось попробовать применить растровые кисти, но об этом в следующий раз.
Метки:  Direct2D  |  brushes 

Комментарии

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