страничка tripsin'а


Главная | Статьи | Заметки | Файлы| Ссылки | я

Снова о градиентных заливках

Ты конечно же видел градиентную заливку в заголовках окошек. Логично было бы предположить, что для этого в Винде есть специальная 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 г.

Hosted by uCoz