Windows API provides the GradientFill() function which fills rectangular and triangular areas. The problem with that function, as usually, is its documentation. Another problem is that Delphi's 7 import of the function is not correct. So here is a correct header.
function GradientFill(DC: THandle; Vertex: PTriVertex; NumVertex: ULONG; Mesh: Pointer; NumMesh, Mode: ULONG): BOOL;After playing with the function, searching the internet and reading the documentation about the function and the structures it utilizes I concluded the following regarding its usage and parameters.
stdcall; external 'msimg32.dll' name 'GradientFill';
In order to use that GradientFill() function the programmer has to decide what is the best way to split the whole area is going to fill. Two choices here. Rectangles or triangles. This is the role of the Mode parameter.
Rectangles and triangles are defined by using the x and y fields of a TTriVertex structure. It takes two TTriVertex structures to define a rectangle (TopLeft and BottomRight) and it takes three TTriVertex stuctures to define a triangle, the three edges of it. Those TTriVertex structures are passed in the Vertex parameter which is a pointer to an array of TTriVertex structures.
Now because each of those TTriVertex structures defines just a point, the programmer has to defined which points form a single rectangle or triangle. This is done by using another pointer to an array parameter, the Mesh parameter. If rectangles are used then the element of the Mesh array is a TGradientRect stucture. If triangles are used then the element of the Mesh array is a TGradientTriangle structure. That's why the Mesh is an untyped pointer. And guess what. The integer fields both of those TGradientRect and TGradientTriangle structures are just indexes in the Vertex array of points, sorry, TTriVertex structures. A TTriVertex structure defines not only a point but a color as well. That is all.
Many thanks to Joseph M. Newcomer, the author of the Gradient Filler application found at http://www.flounder.com/gradientfiller.htm and the valuable information.
Parameteres in a depth
Mode, the last parameter, defines the gradient mode. The Mode controls the interpretation of Vertex and Mesh array parameteres. Valid values are
GRADIENT_FILL_RECT_H
GRADIENT_FILL_RECT_V
GRADIENT_FILL_TRIANGLE
When Mode is one of the two RECT modes (GRADIENT_FILL_RECT_H or GRADIENT_FILL_RECT_V) it means that the function fills rectangular areas. The TTriVertex structures, the Vertex array parameter contains, are used in pairs in order to define the end points (TopLeft, BottomRight) of a single rectangle.
In that case the Mesh untyped pointer parameter is an array of TGradientRect structures, a single TGradientRect structure for each pair of TTriVertex of the Vertex array param. TGradientRect is defined as
TGradientRect = packed record
UpperLeft: ULONG;
LowerRight: ULONG;
end;
where its two fields (UpperLeft and LowerRight) are indices of a TTriVertex in the Vertex array.
When Mode is GRADIENT_FILL_TRIANGLE it means that the function fills triangular areas. The TTriVertex structures, the Vertex array parameter contains, are used in groups of three in order to define the end points of a single triangle.
In that case the Mesh untyped pointer parameter is an array of TGradientTriangle structures. TGradientTriangle is defined as
TGradientTriangle = packed recordwhere its three fields are indices of a TTriVertex in the Vertex array.
Vertex1: ULONG;
Vertex2: ULONG;
Vertex3: ULONG;
end;
So, the Mesh untyped pointer parameter is an array of TGradientRect or TGradientTriangle structures. NumMesh is the number of the elements in that array.
NumVertex is the number of the elements in the Vertex array parameter. The element type of the Vertex parameter is TTriVertex, which is defined as
TTriVertex = packed recordwhere X and Y define the starting point in the DC, the first parameter, and Red, Green and Blue define the color. Alpha is not used, according to documentation.
x : Longint;
y : Longint;
Red : COLOR16;
Green : COLOR16;
Blue : COLOR16;
Alpha : COLOR16;
end;
And some code
The DrawGradient() function, below, considers the whole area as a single rectangle. That's the easiest way to use the GradientFill() API function. Here is the easiest way to use it.
procedure TForm1.Button1Click(Sender: TObject);The DrawGradientFocal() splits the area into eight triangles. It takes nine points to split the passed rectangle into eight triangles. So using the DrawGradientFocal() the programmer may define which of those nine points is the focal point. The point where to start using the StartColor.
begin
DrawGradient(Canvas, ClientRect, clSilver, clNavy);
end;
Here is the code.
unit Unit2;
interface
uses
Windows
,SysUtils
,Classes
,Graphics
,Controls
;
type
TGradientDirection = (gdLeftRight, gdTopBottom);
procedure DrawGradient(Canvas: TCanvas; R: TRect; StartColor, EndColor: TColor; Direction: TGradientDirection = gdLeftRight);
procedure DrawGradientFocal(Canvas: TCanvas; R: TRect; StartColor, EndColor: TColor; FocalPoint: Integer);
implementation
type
COLOR16 = Word; { is declared as Shortint in Delphi 7 }
PTriVertex = ^TTriVertex;
_TRIVERTEX = packed record
x : Longint;
y : Longint;
Red : COLOR16;
Green : COLOR16;
Blue : COLOR16;
Alpha : COLOR16;
end;
TTriVertex = _TRIVERTEX;
TRIVERTEX = _TRIVERTEX;
function WindowsGradientFill(DC: THandle; Vertex: PTriVertex; NumVertex: ULONG; Mesh: Pointer; NumMesh, Mode: ULONG): BOOL;
stdcall; external 'msimg32.dll' name 'GradientFill';
procedure SetVertex(var Vertex: TTriVertex; X, Y: Integer; Color: TColor);
begin
Vertex.x := X;
Vertex.y := Y;
Vertex.Red := (Color and $000000FF) shl 8;
Vertex.Green := (Color and $0000FF00);
Vertex.Blue := (Color and $00FF0000) shr 8;
Vertex.Alpha := 0;
end;
procedure SetTriangle(var Triangle: TGradientTriangle; Vertex1, Vertex2, Vertex3: Integer);
begin
Triangle.Vertex1 := Vertex1;
Triangle.Vertex2 := Vertex2;
Triangle.Vertex3 := Vertex3;
end;
procedure DrawGradient(Canvas: TCanvas; R: TRect; StartColor, EndColor: TColor; Direction: TGradientDirection);
var
Vertices : array[0..1] of TTriVertex;
Rects : array[0..0] of TGradientRect;
Mode : ULONG;
begin
SetVertex(Vertices[0], R.Left, R.Top, StartColor);
SetVertex(Vertices[1], R.Right, R.Bottom, EndColor);
Rects[0].UpperLeft := 0;
Rects[0].LowerRight := 1;
if Direction = gdLeftRight then
Mode := GRADIENT_FILL_RECT_H
else
Mode := GRADIENT_FILL_RECT_V;
WindowsGradientFill(Canvas.Handle, @Vertices, 2, @Rects, 1, Mode);
end;
procedure DrawGradientFocal(Canvas: TCanvas; R: TRect; StartColor, EndColor: TColor; FocalPoint: Integer);
var
Vertices : array[0..8] of TTriVertex;
Triangles : array[0..7] of TGradientTriangle;
MiddleX : Integer;
MiddleY : Integer;
begin
MiddleX := R.Left + (WidthOf(R) div 2);
MiddleY := R.Top + (HeightOf(R) div 2);
SetVertex(Vertices[0], R.Left ,R.Top ,EndColor);
SetVertex(Vertices[1], MiddleX ,R.Top ,EndColor);
SetVertex(Vertices[2], R.Right ,R.Top ,EndColor);
SetVertex(Vertices[3], R.Left ,MiddleY ,EndColor);
SetVertex(Vertices[4], MiddleX ,MiddleY ,EndColor);
SetVertex(Vertices[5], R.Right ,MiddleY ,EndColor);
SetVertex(Vertices[6], R.Left ,R.Bottom ,EndColor);
SetVertex(Vertices[7], MiddleX ,R.Bottom ,EndColor);
SetVertex(Vertices[8], R.Right ,R.Bottom ,EndColor);
if not (FocalPoint in [0..8]) then
FocalPoint := 4;
SetVertex(Vertices[FocalPoint], Vertices[FocalPoint].X, Vertices[FocalPoint].Y, StartColor);
SetTriangle(Triangles[0], 0, 1, 4);
SetTriangle(Triangles[1], 0, 4, 3);
SetTriangle(Triangles[2], 1, 2, 4);
SetTriangle(Triangles[3], 2, 5, 4);
SetTriangle(Triangles[4], 4, 5, 8);
SetTriangle(Triangles[5], 4, 7, 8);
SetTriangle(Triangles[6], 4, 7, 6);
SetTriangle(Triangles[7], 3, 4, 6);
WindowsGradientFill(Canvas.Handle, @Vertices, 9, @Triangles, 8, GRADIENT_FILL_TRIANGLE);
end;
end.
Subscribe to Posts [Atom]