Ты конечно же видел градиентную заливку в заголовках окошек. Логично было бы предположить, что для этого в Винде есть специальная API-функция. И она действительно есть – GradientFill. В MS SDK, которая вместе с Delphi (у меня Delphi 7), ее описания нет. Там градиенты предлагается делать примерно так же, как Dem@nXP в своей статье. Описание ее нашлось в MSDN, а сама она скрывается в msimg32.dll.
Итак, GradientFill предназначена для того чтобы заполнять градиентами прямоугольники и треугольники (!). Т.е. с ее помощью можно залить градиентом не только простую форму, но и какой-то сложный регион. Объявляется так (это объявление отличается от того, что в Windows.pas, почему? – имей терпение) :
function GradientFill(
DC: HDC; // Контекст устройства, на котором будем рисовать
Vertex: Pointer; // Указатель на массив структур TTriVertex
NumVertex: Cardinal; // Количество структур TTriVertex в массиве
Mesh: Pointer; // Указатель на массив структур TGradientRect или TGradientTriangle
NumMesh: DWORD, // Количество структур в этом массиве
Mode: DWORD) // Режим рисования.
: BOOL; stdcall; // Возвращает True если завершилась успешно.
external 'msimg32.dll' name 'GradientFill';
Разберем ее. Начнем с конца:
Mode: DWORD – Может иметь значения:
Vertex: Pointer – Указатель на массив структур TTriVertex, которые описывают точки, из которых мы будем делать наши фигуры.
TTriVertex = packed record
x, y: DWORD; // Координаты вершины (по умолчанию в пикселях)
Red, Green, Blue, Alpha: COLOR16; //Каналы цветов
end;
Тип COLOR16 в Windows.pas определен неправильно: COLOR16 = Shortint; Надо так: COLOR16 = $0000..$FF00; Получается, что значение цвета смещено влево на 1 байт. Зачем это надо сказать трудно.
Ты в своем проекте должен переопределить тип COLOR16, а также структуру TTriVertex и объявление GradientFill, которые этим типом пользуются. Вот зачем я переобъявлял вызов функции (кроме того он удобнее).
////////////////////////////////////////////////////////////////////////////
// Переопределяем типы и функцию GradientFill //
////////////////////////////////////////////////////////////////////////////
type
COLOR16 = $0000..$FF00; //Этот тип в Windows.pas определен неправильно
// Переопределяем TTriVertex с правильным типом. Эта структура описывает
// точку-вершину (Vertex)
TTriVertex = packed record
x, y: DWORD; // Координаты вершины
Red, Green, Blue, Alpha: COLOR16; //Каналы цветов
end;
// GradientFill определена в Windows.pas, но мы ее переобъявляем для
// правильного типа TTriVertex. Не поддерживается Win95 и WinNT
function GradientFill(DC: HDC; Vertex: Pointer; NumVertex: Cardinal;
Mesh: Pointer; NumMesh, Mode: DWORD): BOOL; stdcall;
external 'msimg32.dll' name 'GradientFill';
/////////////////////////////////////////////////////////////////////////////
Mesh: Pointer – Указатель на массив структур TGradientRect или TGradientTriangle. Дело в том, что за один раз ты можешь залить градиентом не одну, а несколько фигур. На выбор: треугольников или прямоугольников. Вершины (или углы) этих фигур берутся из массива Vertex. А в массиве Mesh индексы точек массива Vertex назначаются вершинам этих фигур.
К статье прилагается исходник примера. А результат его работы вот:
Для рабочего примера сделаем процедуру градиентной заливки формы. В качестве параметров передаем в нее начальный и конечный цвета градиента в формате TColor и направление заливки. Вот код этой функции:
/////////////////////////////////////////////////////////////////
// Градиентная заливка прямоугольника //
/////////////////////////////////////////////////////////////////
procedure TForm1.PaintGradient(StartColor, EndColor: TColor;
Orientation: TOrientation);
var
//Массив вершин (нам нужны две - верхнелевая и нижнеправая)
vert: array[0..1] of TTriVertex;
gRect: TGradientRect; //Индексы вершин в массиве vert (из Windows.pas)
begin
// Определяем вершины
vert[0].x := 0; //Верхняя левая точка
vert[0].y := 0;
vert[0].Red := GetRValue(StartColor) shl 8; // Значение цвета смещаем
vert[0].Green := GetGValue(StartColor) shl 8; // влево на 1 байт,
vert[0].Blue := GetBValue(StartColor) shl 8; // чтобы получилось 2 байта.
vert[0].Alpha := $0000; // Видать прозрачность? Не пробовал.
vert[1].x := ClientWidth; // Нижняя правая точка
vert[1].y := ClientHeight;
vert[1].Red := GetRValue(EndColor) shl 8;
vert[1].Green := GetGValue(EndColor) shl 8;
vert[1].Blue := GetBValue(EndColor) shl 8;
vert[1].Alpha := $0000;
gRect.UpperLeft := 0; // Назначаем вершины верхнелевому
gRect.LowerRight := 1; // и нижнеправому углам.
// Заливаем в зависимости от ориентации
if Orientation = orHorizontal then
GradientFill(Canvas.Handle, @vert, 2, @gRect, 1, GRADIENT_FILL_RECT_H)
else
GradientFill(Canvas.Handle, @vert, 2, @gRect, 1, GRADIENT_FILL_RECT_V);
end; /////////////////////////////////////////////////////////////
Чтобы точно определить прямоугольник нужны всего две точки: верхняя левая и нижняя правая. Их координаты и цвет мы и заносим в массив vert: array[0..1] of TTriVertex; Дальше в структуре gRect мы назначаем точки массива vert углам прямоугольника: 0 – верхняя левая, 1 – нижняя правая. При рисовании прямоугольника в Mesh передается указатель на массив из TGradientRect:
TGradientRect = packed record
UpperLeft: ULONG;
LowerRight: ULONG;
end;
В зависимости от параметра Orientation определяем горизонтальную и вертикальную заливку. Рисуем на канве формы (Canvas.Handle). Вызываем GradientFill, и она делает всю грязную работу. Все!
При нажатии на кнопу Button1 («Рисуем градиенты») на форме рисуется немного повернутый квадрат, у которого все углы разного цвета и между ними градиентные переходы. Для этого заливаем 2 треугольника с общей стороной. Смотри код:
////////////////////////////////////////////////////////////////////////
// Градиентная заливка треугольников //
////////////////////////////////////////////////////////////////////////
procedure TForm1.Button1Click(Sender: TObject);
var
vert: array[0..3] of TTriVertex; // 4 точки для 2 треугольников
gTri: array[0..1] of TGradientTriangle; // массив из 2 треугольников
begin
// Определяем вершины треугольников
vert[0].x := 80; vert[0].y := 50; vert[0].Alpha := $0000; //Красная
vert[0].Red := $FF00; vert[0].Green := $0000; vert[0].Blue := $0000;
vert[1].x := 50; vert[1].y := 120; vert[1].Alpha := $0000; //Желтая
vert[1].Red := $FF00; vert[1].Green := $FF00; vert[1].Blue := $0000;
vert[2].x := 150; vert[2].y := 80; vert[2].Alpha := $0000; //Синяя
vert[2].Red := $0000; vert[2].Green := $0000; vert[2].Blue := $FF00;
vert[3].x := 120; vert[3].y := 150; vert[3].Alpha := $0000; //Зеленая
vert[3].Red := $0000; vert[3].Green := $FF00; vert[3].Blue := $0000;
// Назначаем индексы вершин углам треугольников
gTri[0].Vertex1 := 0; gTri[0].Vertex2 := 1; gTri[0].Vertex3 := 2;
gTri[1].Vertex1 := 3; gTri[1].Vertex2 := 1; gTri[1].Vertex3 := 2;
// Рисуем красивый градиент
GradientFill(Canvas.Handle, @vert, 4, @gTri, 2, GRADIENT_FILL_TRIANGLE);
end;
//////////////////////////////////////////////////////////////////////////
Сначала заполняем массив vert четырьмя точками – это углы. Потом заполняем массив gTri: array[0..1] of TGradientTriangle; :
TGradientTriangle = packed record
Vertex1: ULONG;
Vertex2: ULONG;
Vertex3: ULONG;
end;
В нем храним индексы вершин двух треугольников: первый – 0,1,2; второй – 3,1,2. Заполнение треугольника градиентом начинается от Vertex1 и идет параллельно стороне, образованной Vertex2 и Vertex3. Вот собственно и все. Передаем в GradientFill массив точек vert, массив индексов вершин треугольников gTri и в Mode - GRADIENT_FILL_TRIANGLE. Получается красивый разноцветный квадрат.
Исходник примера прилагается.
Удачи! Всегда твой, tripsin.
tripsin@yandex.ru
Опубликована на VR-online в 2006 г.