Обзор кистей
В этом обзоре описывается создание и использование ID2D1SolidColorBrush, ID2D1LinearGradientBrush, ID2D1RadialGradientBrushи объектов ID2D1BitmapBrush для рисования областей сплошными цветами, градиентами и растровыми изображениями. Он содержит следующие разделы.
Необходимые условия
В этом обзоре предполагается, что вы знакомы со структурой базового приложения Direct2D, как описано в разделе Создание простого приложения Direct2D.
Типы кистей
Кисть "закрашивает" область своим эффектом. Разные кисти имеют разные типы результатов. Direct2D предоставляет четыре типа кистей: ID2D1SolidColorBrush красит область с сплошным цветом, ID2D1LinearGradientBrush с линейным градиентом, ID2D1RadialGradientBrush с радиальным градиентом и ID2D1BitmapBrush с растровым изображением.
Заметка
Начиная с Windows 8, вы также можете использовать ID2D1ImageBrush, которая похожа на кисть растрового изображения, но также можно использовать примитивы.
Все кисти наследуются от ID2D1Brush и совместно используют набор общих функций (настройка и получение непрозрачности и преобразование кистей); они создаются ID2D1RenderTarget и являются ресурсами, зависящими от устройств: приложение должно создавать кисти после инициализации целевого объекта отрисовки, с которым будут использоваться кисти, и повторно создавать кисти при каждом повторном создании целевого объекта отрисовки. (Дополнительные сведения о ресурсах см. в обзоре ресурсов.)
На следующем рисунке показаны примеры каждого из различных типов кистей.
Основы цвета
Прежде чем рисовать с помощью ID2D1SolidColorBrush или градиентной кисти, необходимо выбрать цвета. В Direct2D цвета представлены структурой D2D1_COLOR_F (это просто новое имя структуры, используемой Direct3D, D3DCOLORVALUE).
До Windows 8 D2D1_COLOR_F использует кодировку sRGB. Кодировка sRGB делит цвета на четыре компонента: красный, зеленый, синий и альфа-код. Каждый компонент представлен значением с плавающей запятой с нормальным диапазоном от 0,0 до 1,0. Значение 0.0 указывает на полное отсутствие этого цвета, а значение 1.0 указывает, что цвет полностью присутствует. Для альфа-компонента 0.0 представляет полностью прозрачный цвет, а 1.0 — полностью непрозрачный цвет.
Начиная с Windows 8, D2D1_COLOR_F также принимает кодировку scRGB. scRGB — это супермножество, которое позволяет использовать значения цвета выше 1.0 и ниже 0,0.
Чтобы определить цвет, можно использовать структуру D2D1_COLOR_F и инициализировать свои поля самостоятельно или использовать класс D2D1::ColorF для создания цвета. Класс ColorF предоставляет несколько конструкторов для определения цветов. Если альфа-значение не указано в конструкторах, значение по умолчанию равно 1.0.
Используйте конструктор ColorF(Enum, FLOAT), чтобы указать предопределенный цвет и значение альфа-канала. Значение альфа-канала варьируется от 0.0 до 1.0, где 0.0 представляет полностью прозрачный цвет, а 1.0 представляет полностью непрозрачный цвет. На следующем рисунке показаны несколько предопределенных цветов и их шестнадцатеричных эквивалентов. Полный список предопределенных цветов см. в разделе "Константы цвета" класса ColorF.
В следующем примере создается предопределенный цвет и он используется для указания цвета ID2D1SolidColorBrush.
hr = m_pRenderTarget->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF::Black, 1.0f),
&m_pBlackBrush
);
Используйте конструктор ColorF (FLOAT, FLOAT, FLOAT, FLOAT), чтобы указать цвет в последовательности красного, зеленого, синего и альфа-цвета, где каждый элемент имеет значение от 0,0 до 1.0.
В следующем примере указываются красные, зеленые, синие и альфа-значения цвета.
ID2D1SolidColorBrush *pGridBrush = NULL;
hr = pCompatibleRenderTarget->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF(0.93f, 0.94f, 0.96f, 1.0f)),
&pGridBrush
);
- Используйте конструктор ColorF(UINT32, FLOAT), чтобы указать шестнадцатеричное значение цвета и альфа-значения, как показано в следующем примере.
hr = m_pRenderTarget->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF(0x9ACD32, 1.0f)),
&m_pYellowGreenBrush
);
Альфа-режимы
Независимо от альфа-режима целевого объекта отрисовки, которому применяется кисть, значения D2D1_COLOR_F всегда трактуются как прямая альфа.
Использование кистей для однотонной окраски
Чтобы создать кисть однотонного цвета, вызовите метод ID2D1RenderTarget::CreateSolidColorBrush, который возвращает HRESULT и объект ID2D1SolidColorBrush. На следующем рисунке показан квадрат, нарисованный черной кистью цвета и окрашенный сплошной кистью цвета, которая имеет значение цвета 0x9ACD32.
В следующем коде показано, как создать и использовать черную кисть и кисть с цветом 0x9ACD32 для заливки и рисования этого квадрата.
ID2D1SolidColorBrush *m_pBlackBrush;
ID2D1SolidColorBrush *m_pYellowGreenBrush;
if (SUCCEEDED(hr))
{
hr = m_pRenderTarget->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF::Black, 1.0f),
&m_pBlackBrush
);
}
// Create a solid color brush with its rgb value 0x9ACD32.
if (SUCCEEDED(hr))
{
hr = m_pRenderTarget->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF(0x9ACD32, 1.0f)),
&m_pYellowGreenBrush
);
}
m_pRenderTarget->FillRectangle(&rcBrushRect, m_pYellowGreenBrush);
m_pRenderTarget->DrawRectangle(&rcBrushRect, m_pBlackBrush, 1, NULL);
В отличие от других кистей, создание ID2D1SolidColorBrush является относительно недорогой операцией. Вы можете создавать объекты ID2D1SolidColorBrush при каждом отображении без влияния на производительность. Этот подход не рекомендуется для градиентных или растровых кистей.
Использование кистей линейного градиента
ID2D1LinearGradientBrush красит область с линейным градиентом, определенным вдоль линии, градиентной осью. Вы указываете цвета градиента и их расположение вдоль оси градиента с помощью объектов ID2D1GradientStop. Можно также изменить градиентную ось, которая позволяет создавать горизонтальные и вертикальные градиенты и изменять направление градиента. Чтобы создать кисть с линейным градиентом, вызовите метод ID2D1RenderTarget::CreateLinearGradientBrush.
На следующем рисунке показан квадрат, нарисованный с помощью ID2D1LinearGradientBrush с двумя предопределенными цветами: "Желтый" и "ForestGreen".
Чтобы создать градиент, показанный на предыдущем рисунке, выполните следующие действия:
Объявите два объекта D2D1_GRADIENT_STOP. Каждая остановка градиента указывает цвет и позицию. Позиция 0.0 указывает начало градиента, а позиция 1.0 указывает конец градиента.
Следующий код создает массив двух объектов D2D1_GRADIENT_STOP. Первая остановка указывает цвет "Желтый" в позиции 0, а вторая остановка указывает цвет "Лесной зеленый" в позиции 1.
// Create an array of gradient stops to put in the gradient stop
// collection that will be used in the gradient brush.
ID2D1GradientStopCollection *pGradientStops = NULL;
D2D1_GRADIENT_STOP gradientStops[2];
gradientStops[0].color = D2D1::ColorF(D2D1::ColorF::Yellow, 1);
gradientStops[0].position = 0.0f;
gradientStops[1].color = D2D1::ColorF(D2D1::ColorF::ForestGreen, 1);
gradientStops[1].position = 1.0f;
- Создайте ID2D1GradientStopCollection. В следующем примере вызывается CreateGradientStopCollection, передав массив объектов D2D1_GRADIENT_STOP, количество градиентных остановок (2), D2D1_GAMMA_2_2 для интерполяции и D2D1_EXTEND_MODE_CLAMP для режима расширения.
// Create the ID2D1GradientStopCollection from a previously
// declared array of D2D1_GRADIENT_STOP structs.
hr = m_pRenderTarget->CreateGradientStopCollection(
gradientStops,
2,
D2D1_GAMMA_2_2,
D2D1_EXTEND_MODE_CLAMP,
&pGradientStops
);
- Создайте ID2D1LinearGradientBrush. Следующий пример вызывает метод CreateLinearGradientBrush и передает ему свойства линейной градиентной кисти, которые содержат начальную точку (0, 0), конечную точку (150, 150) и остановы градиента, созданные на предыдущем шаге.
// The line that determines the direction of the gradient starts at
// the upper-left corner of the square and ends at the lower-right corner.
if (SUCCEEDED(hr))
{
hr = m_pRenderTarget->CreateLinearGradientBrush(
D2D1::LinearGradientBrushProperties(
D2D1::Point2F(0, 0),
D2D1::Point2F(150, 150)),
pGradientStops,
&m_pLinearGradientBrush
);
}
- Используйте ID2D1LinearGradientBrush. Следующий пример кода использует кисть для заполнения прямоугольника.
m_pRenderTarget->FillRectangle(&rcBrushRect, m_pLinearGradientBrush);
Дополнительные сведения о точках остановки градиента
D2D1_GRADIENT_STOP является основным элементом градиентной кисти. Градиентная остановка указывает цвет и положение вдоль оси градиента. Значение позиции градиента от 0,0 до 1.0. Чем ближе к 0,0, тем ближе цвет к началу градиента; чем ближе к 1,0, тем ближе цвет к концу градиента.
На следующем рисунке выделены позиции градиента. Круг помечает положение градиентных остановок, а тиреная линия показывает градиентную ось.
Первая градиентная остановка указывает желтый цвет в позиции 0,0. Вторая градиентная остановка указывает красный цвет в позиции 0,25. Слева направо вдоль оси градиента цвета между этими двумя остановками постепенно меняются с желтого на красный. Третья точка градиента задает синий цвет на позиции 0,75. Цвета между второй и третьей точками градиента постепенно меняются от красного до синего. Четвертая градиентная остановка указывает зеленый лайм на позиции 1,0. Цвета между третьей и четвертой точками градиента постепенно меняются из синего в лаймово-зеленый.
Градиентная ось
Как упоминалось ранее, градиентные точки линейной градиентной кисти расположены вдоль линии, ось градиента. При создании линейной градиентной кисти можно указать ориентацию и размер линии с помощью полей начальной точки и конечной точки структуры D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES. После создания кисти можно настроить градиентную ось, вызвав методы УстановитьНачальнуюТочку и УстановитьКонечнуюТочку. Управляя начальной точкой и конечной точкой кисти, можно создавать горизонтальные и вертикальные градиенты, обратное направление градиента и многое другое.
Например, на следующем рисунке начальная точка установлена в (0,0), а конечная — в (150, 50); при этом создается диагональный градиент, начинающийся с левого верхнего угла и расширяющийся до нижнего правого угла окрашиваемой области. При установке начальной точки (0, 25) и конечной точки (150, 25) создается горизонтальный градиент. Аналогичным образом при задании начальной точки (75, 0) и конечной точки (75, 50) создается вертикальный градиент. Задание начальной точки (0, 50) и конечной точки (150, 0) создает диагональный градиент, который начинается в левом нижнем углу и простирается до правого верхнего угла области.
Использование радиальных градиентных заливок
В отличие от ID2D1LinearGradientBrush, которая смешивает два или более цветов вдоль оси градиента, ID2D1RadialGradientBrush закрашивает область радиальным градиентом, который смешивает два или более цветов по эллипсу. Хотя ID2D1LinearGradientBrush определяет свою градиентную ось с начальной точкой и конечной точкой, ID2D1RadialGradientBrush определяет свой градиентный эллипс путем указания центра, горизонтального и вертикального радиусов и смещения градиента.
Как и ID2D1LinearGradientBrush, ID2D1RadialGradientBrush использует ID2D1GradientStopCollection, чтобы указать цвета и позиции в градиенте.
На следующем рисунке показан круг, окрашенный с помощью ID2D1RadialGradientBrush. Круг имеет две градиентные остановки: первый задает предопределенный цвет "Желтый" в позиции 0,0, а второй указывает предопределенный цвет "ForestGreen" в позиции 1.0. Градиент имеет центр (75, 75), смещение источника градиента (0, 0), а также x-и y-радиус 75.
В следующих примерах кода показано, как закрасить этот круг с помощью ID2D1RadialGradientBrush с двумя точками остановки цвета: "Жёлтый" на позиции 0.0 и "Лесной зелёный" на позиции 1.0. Аналогично созданию ID2D1LinearGradientBrush, пример вызывает CreateGradientStopCollection для создания ID2D1GradientStopCollection из массива градиентных стопов.
// Create an array of gradient stops to put in the gradient stop
// collection that will be used in the gradient brush.
ID2D1GradientStopCollection *pGradientStops = NULL;
D2D1_GRADIENT_STOP gradientStops[2];
gradientStops[0].color = D2D1::ColorF(D2D1::ColorF::Yellow, 1);
gradientStops[0].position = 0.0f;
gradientStops[1].color = D2D1::ColorF(D2D1::ColorF::ForestGreen, 1);
gradientStops[1].position = 1.0f;
// Create the ID2D1GradientStopCollection from a previously
// declared array of D2D1_GRADIENT_STOP structs.
hr = m_pRenderTarget->CreateGradientStopCollection(
gradientStops,
2,
D2D1_GAMMA_2_2,
D2D1_EXTEND_MODE_CLAMP,
&pGradientStops
);
Чтобы создать ID2D1RadialGradientBrush, используйте метод ID2D1RenderTarget::CreateRadialGradientBrush. CreateRadialGradientBrush принимает три параметра. Первый параметр, D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES, указывает центр, смещение начальной точки градиента, а также горизонтальный и вертикальный радиусы градиента. Второй параметр — это ID2D1GradientStopCollection, описывающий цвета и их позиции в градиенте, а третий параметр — адрес указателя, который получает новую ссылку ID2D1RadialGradientBrush. Некоторые перегрузки принимают в качестве дополнительного параметра структуру D2D1_BRUSH_PROPERTIES, которая указывает значение непрозрачности и преобразование для применения к новой кисти.
Следующий пример вызывает CreateRadialGradientBrush, передав массив остановок градиента, и свойства радиальной градиентной кисти, имеющие центр значение (75, 75), gradientOriginOffset задано значение (0, 0) и radiusX и radiusY для 75.
// The center of the gradient is in the center of the box.
// The gradient origin offset was set to zero(0, 0) or center in this case.
if (SUCCEEDED(hr))
{
hr = m_pRenderTarget->CreateRadialGradientBrush(
D2D1::RadialGradientBrushProperties(
D2D1::Point2F(75, 75),
D2D1::Point2F(0, 0),
75,
75),
pGradientStops,
&m_pRadialGradientBrush
);
}
В последнем примере используется кисть для заполнения многоточия.
m_pRenderTarget->FillEllipse(ellipse, m_pRadialGradientBrush);
m_pRenderTarget->DrawEllipse(ellipse, m_pBlackBrush, 1, NULL);
Настройка радиального градиента
Различные значения для центра, gradientOriginOffset, radiusX и/или radiusY создают разные градиенты. На следующем рисунке показаны несколько радиальных градиентов с различными смещениями источника градиента, создающие внешний вид света, освещающего круги с разных углов.
Использование растровых кистей
ID2D1BitmapBrush красит область с растровым изображением (представлено объектом ID2D1Bitmap).
На следующем рисунке показан квадратный рисунок с растровым изображением растения.
В следующих примерах показано, как покрасить этот квадрат с помощью ID2D1BitmapBrush.
Первый пример инициализирует ID2D1Bitmap для использования с кистью. ID2D1Bitmap предоставляется вспомогательным методом LoadResourceBitmap, определенным в другом месте примера.
// Create the bitmap to be used by the bitmap brush.
if (SUCCEEDED(hr))
{
hr = LoadResourceBitmap(
m_pRenderTarget,
m_pWICFactory,
L"FERN",
L"Image",
&m_pBitmap
);
}
Чтобы создать кисть растрового изображения, вызовите метод ID2D1RenderTarget::CreateBitmapBrush и укажите ID2D1Bitmap, с помощью которого необходимо нарисовать. Метод возвращает HRESULT и ID2D1BitmapBrush объект. Некоторые перегрузки CreateBitmapBrush позволяют вам указать дополнительные параметры, принимая структуру D2D1_BRUSH_PROPERTIES и структуру D2D1_BITMAP_BRUSH_PROPERTIES.
if (SUCCEEDED(hr))
{
hr = m_pRenderTarget->CreateBitmapBrush(
m_pBitmap,
&m_pBitmapBrush
);
}
В следующем примере используется кисть для заполнения прямоугольника.
m_pRenderTarget->FillRectangle(&rcBrushRect, m_pBitmapBrush);
Настройка режимов расширения
Иногда градиент кисти градиента или растрового рисунка для кисти растрового изображения не полностью заполняет область, нарисованную.
Если это происходит для ID2D1BitmapBrush, Direct2D использует настройки режима расширения по горизонтали (SetExtendModeX) и по вертикали (SetExtendModeY), чтобы определить, как заполнить оставшееся пространство.
Когда это происходит для кисти градиента, Direct2D определяет, как заполнить оставшуюся область с помощью значения параметра D2D1_EXTEND_MODE, указанного при вызове CreateGradientStopCollection для создания градиентной кисти ID2D1GradientStopCollection.
На следующем рисунке показаны результаты всех возможных сочетаний режимов расширения для ID2D1BitmapBrush: D2D1_EXTEND_MODE_CLAMP (CLAMP), D2D1_EXTEND_MODE_WRAP (ЗАВЕРТЫВАНИЕ) и D2D1_EXTEND_MIRROR (ЗЕРКАЛО).
В следующем примере показано, как задать режимы x-и y-расширения кисти растрового рисунка для D2D1_EXTEND_MIRROR. Затем он красит прямоугольник с ID2D1BitmapBrush.
m_pBitmapBrush->SetExtendModeX(D2D1_EXTEND_MODE_MIRROR);
m_pBitmapBrush->SetExtendModeY(D2D1_EXTEND_MODE_MIRROR);
m_pRenderTarget->FillRectangle(exampleRectangle, m_pBitmapBrush);
Он создает выходные данные, как показано на следующем рисунке.
по оси X и по оси Y
Трансформирующиеся кисти
При рисовании кистью, она наносит краску в координатном пространстве рендер-цели. Кисти не позиционируют себя автоматически, чтобы выровняться с объектом; по умолчанию они начинают рисовать в точке начала координат (0, 0) цели отрисовки.
Вы можете переместить градиент, определенный ID2D1LinearGradientBrush в целевую область, задав начальную точку и конечную точку. Аналогичным образом можно переместить градиент, определенный ID2D1RadialGradientBrush, изменив его центр и радии.
Чтобы выровнять содержимое ID2D1BitmapBrush с областью, на которой выполняется рисование, можно использовать метод SetTransform для перемещения растрового изображения в нужное расположение. Это преобразование влияет только на кисть; Он не влияет на любое другое содержимое, рисуемое целевым объектом отрисовки.
На следующих рисунках показан эффект использования ID2D1BitmapBrush для заполнения прямоугольника (100, 100). На рисунке слева показан результат заполнения прямоугольника без преобразования кисти: растровое изображение рисуется в начальной точке целевого объекта отрисовки. В результате в прямоугольнике отображается только часть растрового изображения. На рисунке справа показан результат преобразования ID2D1BitmapBrush, при котором его содержимое смещалось на 50 пикселей вправо и на 50 пикселей вниз. Растровое изображение теперь заполняет прямоугольник.
В следующем коде показано, как это сделать. Сначала примените перевод к ID2D1BitmapBrush, переместив кисть 50 пикселей вправо вдоль оси x и 50 пикселей вниз по оси y. Затем используйте ID2D1BitmapBrush, чтобы заполнить прямоугольник, имеющий левый верхний угол (100, 100) и нижний правый угол (200, 200).
// Create the bitmap to be used by the bitmap brush.
if (SUCCEEDED(hr))
{
hr = LoadResourceBitmap(
m_pRenderTarget,
m_pWICFactory,
L"FERN",
L"Image",
&m_pBitmap
);
}
if (SUCCEEDED(hr))
{
hr = m_pRenderTarget->CreateBitmapBrush(
m_pBitmap,
&m_pBitmapBrush
);
}
D2D1_RECT_F rcTransformedBrushRect = D2D1::RectF(100, 100, 200, 200);
// Demonstrate the effect of transforming a bitmap brush.
m_pBitmapBrush->SetTransform(
D2D1::Matrix3x2F::Translation(D2D1::SizeF(50,50))
);
// To see the content of the rcTransformedBrushRect, comment
// out this statement.
m_pRenderTarget->FillRectangle(
&rcTransformedBrushRect,
m_pBitmapBrush
);
m_pRenderTarget->DrawRectangle(rcTransformedBrushRect, m_pBlackBrush, 1, NULL);