[英]How to paint 2 gradients together on a Canvas?
请看我用 Paint 程序制作的这个示例渐变图像:
它包含 2 个垂直渐变。
从顶部到中间的第一个渐变是白色和浅橙色的混合。
从底部到中间的第二个渐变也是白色的混合,但稍微深一点的橙色。
关键是使用了 2 种渐变,有 4 种颜色、2 种白色和 2 种橙色变化。
我想在 Canvas 上执行此操作,但不知道如何操作。 颜色可以是任何东西,上面只是一个例子。
我该怎么做?
使用GraphUtil单元中的GradientFillCanvas :
procedure TForm1.FormPaint(Sender: TObject);
var
R: TRect;
begin
SetRect(R, 0, 0, ClientWidth, ClientHeight div 2);
GradientFillCanvas(Canvas, clWhite, $00056AFF, R, gdVertical);
SetRect(R, 0, ClientHeight div 2, ClientWidth, ClientHeight);
GradientFillCanvas(Canvas, $000055FF, clWhite, R, gdVertical);
end;
使用Msimg32.dll 中的GradientFill
。 将以下代码添加到全局实用程序单元:
type
PTriVertex = ^TTriVertex;
TTriVertex = record
X, Y: DWORD;
Red, Green, Blue, Alpha: WORD;
end;
function GradientFill(DC: HDC; Vertex: PTriVertex; NumVertex: ULONG;
Mesh: Pointer; NumMesh, Mode: ULONG): BOOL; stdcall; overload;
external msimg32 name 'GradientFill';
function GradientFill(DC: HDC; const ARect: TRect; StartColor,
EndColor: TColor; Vertical: Boolean): Boolean; overload;
const
Modes: array[Boolean] of ULONG = (GRADIENT_FILL_RECT_H, GRADIENT_FILL_RECT_V);
var
Vertices: array[0..1] of TTriVertex;
GRect: TGradientRect;
begin
Vertices[0].X := ARect.Left;
Vertices[0].Y := ARect.Top;
Vertices[0].Red := GetRValue(ColorToRGB(StartColor)) shl 8;
Vertices[0].Green := GetGValue(ColorToRGB(StartColor)) shl 8;
Vertices[0].Blue := GetBValue(ColorToRGB(StartColor)) shl 8;
Vertices[0].Alpha := 0;
Vertices[1].X := ARect.Right;
Vertices[1].Y := ARect.Bottom;
Vertices[1].Red := GetRValue(ColorToRGB(EndColor)) shl 8;
Vertices[1].Green := GetGValue(ColorToRGB(EndColor)) shl 8;
Vertices[1].Blue := GetBValue(ColorToRGB(EndColor)) shl 8;
Vertices[1].Alpha := 0;
GRect.UpperLeft := 0;
GRect.LowerRight := 1;
Result := GradientFill(DC, @Vertices, 2, @GRect, 1, Modes[Vertical]);
end;
现在,绘画代码变为:
procedure TForm1.FormPaint(Sender: TObject);
var
R: TRect;
begin
SetRect(R, 0, 0, ClientWidth, ClientHeight div 2);
GradientFill(Canvas.Handle, R, clWhite, $00056AFF, True);
SetRect(R, 0, ClientHeight div 2, ClientWidth, ClientHeight);
GradientFill(Canvas.Handle, R, $000055FF, clWhite, True);
end;
procedure TForm1.FormResize(Sender: TObject);
begin
Invalidate;
end;
我使用普通的 TCanvas 对其进行编码。
该代码通过稳步增加颜色在该画布上绘制渐变。 例如,您可以通过向开始或结束颜色添加权重来调整它(例如增加白色部分)。
procedure drawGradient(drawCanvas: TCanvas; canvasHeight, canvasWidth, canvasStartPos: Integer; startColor, endColor: TColor);
type
RGBColor = (Blue, Green, Red);
var
diff, startColorArray, endColorArray: array[RGBColor] of Integer;
delta, currentColorFloat: array[RGBColor] of Double;
gradientSize: Integer;
currentColor: TColor;
rgbC: RGBColor;
i: Integer;
begin
gradientSize := canvasHeight div 2;
// Pre-calculate some required values for every RGB color
for rgbC := Low(RGBColor) to High(RGBColor) do
begin
// Split the start end end colors into the RGB values
// The right shift at the end shifts 16, 8 and 0 bits in the three loops
// (I know that's a little hard to read)
startColorArray[rgbC] := $FF and (startColor shr ((2 - Ord(rgbC)) * 8));
endColorArray[rgbC] := $FF and (endColor shr ((2 - Ord(rgbC)) * 8));
// Calculate the difference between the start and end color. This might be
// a negative value, hence the declaration as Integer instead of Byte
diff[rgbC] := startColorArray[rgbC] - endColorArray[rgbC];
// And calculate a float value for each color. This is the increment on
// every drawn line.
delta[rgbC] := diff[rgbC] / gradientSize;
end;
// Initialize the drawn color with the start value
currentColorFloat[Blue] := startColorArray[Blue];
currentColorFloat[Green] := startColorArray[Green];
currentColorFloat[Red] := startColorArray[Red];
// Now draw the gradient line by line
for i := 0 to gradientSize - 1 do
begin
// The target color as TColor
currentColor := 0;
for rgbC := Low(RGBColor) to High(RGBColor) do
begin
// Substract the decrement delta from the current color
currentColorFloat[rgbC] := currentColorFloat[rgbC] - delta[rgbC];
// Round the float value and left shift it to the correct position (16, 8 and 0 bits).
// Then bitwise or it with the current color.
currentColor := currentColor or (Round(currentColorFloat[rgbC]) shl ((2 - Ord(rgbC)) * 8));
end;
// Now draw a 1 pixel thin line from left to right
drawCanvas.Pen.Color := currentColor;
drawCanvas.MoveTo(0, i + canvasStartPos);
drawCanvas.LineTo(canvasWidth, i + canvasStartPos);
end;
end;
像这样调用它:
procedure TForm18.Button1Click(Sender: TObject);
const
white1: TColor = clWhite;
white2: TColor = $00CFCFCF;
color1: TColor = $000080FF;
color2: TColor = $00007AF4;
begin
// pb is a TPaintbox, but this works with any canvas
drawGradient(pb.Canvas, pb.Height, pb.Width, 0, white1, color1);
drawGradient(pb.Canvas, pb.Height, pb.Width, pb.Height div 2, color2, white2);
end;
结果如下所示:
我喜欢在 TCanvas 上使用类助手,所以我修改了另一个答案中的代码。
type
TTriVertex = record
public
type
P = ^TTriVertex;
public
X, Y: DWORD;
Red, Green, Blue, Alpha: WORD;
constructor Create(APoint: TPoint; AColor: TColor; AAlpha: Word = 0);
end;
TTriVertexRect = record
public
TopLeft: TTriVertex;
LowerRight: TTriVertex;
constructor Create(ARect: TRect; Color1,Color2; AAlpha1: Word = 0; AAlpha2: Word = 0);
end;
{Enumerate the constants so they will show up on alt space in the IDE}
TGradientFillType = (
gftRectH = GRADIENT_FILL_RECT_H
,gftRectV = GRADIENT_FILL_RECT_V
,gftTriangle = GRADIENT_FILL_TRIANGLE);
TCavas_Helper = class helper for TCanvas
public
function GradientFill(const Vertexes: array of TTriVertexRect; FillType: TGradientFillType): Boolean; overload;
end;
{ TTriVertex }
constructor TTriVertex.Create(APoint: TPoint; AColor: TColor; AAlpha: Word = 0);
begin
X := APoint.X;
Y := APoint.Y;
Red := GetRValue(ColorToRGB(AColor)) SHL 8;
Green := GetGValue(ColorToRGB(AColor)) SHL 8;
Blue := GetBValue(ColorToRGB(AColor)) SHL 8;
Alpha := 0;
end;
{ TTriVertexRect }
constructor TTriVertexRect.Create(ARect: TRect; Color1, Color2: TColor; AAlpha1,
AAlpha2: Word);
begin
TopLeft.Create(ARect.TopLeft,Color1,AAlpha1);
LowerRight.Create(ARect.BottomRight,Color2,AAlpha2);
end;
{ TCavas_Helper }
function TCavas_Helper.GradientFill(const Vertexes: array of TTriVertexRect; FillType: TGradientFillType): Boolean;
var
GRect: array of TGradientRect;
Index: Integer;
begin
SetLength(GRect,Length(Vertexes));
for Index := 0 to Length(GRect) do begin
GRect[Index].UpperLeft := Index*2;
GRect[Index].LowerRight := Index*2 + 1;
end;
Result := WIndows.GradientFill(Handle,@Vertexes[0],Length(Vertexes)*2,@GRect[0],Length(GRect),Ord(FillType))
end;
所以实际调用看起来像这样
procedure TForm56.FormPaint(Sender: TObject);
begin
Canvas.GradientFill([
TTriVertexRect.Create(Rect(0,0,ClientWidth,ClientHeight DIV 2),clWhite,$00056AFF)
,TTriVertexRect.Create(Rect(0,ClientHeight DIV 2,ClientWidth,ClientHeight),$00056AFF,clWhite)
],gftRectV);
end;
样本:
iWidth: Integer;
iWidth := Width div 2;
GradientFillCanvas(Canvas, Col1, Col2, Rect(0, 0, iWidth, Height), gdHorizontal);
GradientFillCanvas(Canvas, Col2, Col1, Rect(iWidth, 0, Width, Height),gdHorizontal);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.