繁体   English   中英

如何在画布上绘制 2 个渐变?

[英]How to paint 2 gradients together on a Canvas?

请看我用 Paint 程序制作的这个示例渐变图像:

在此处输入图片说明

它包含 2 个垂直渐变。

从顶部到中间的第一个渐变是白色和浅橙色的混合。

从底部到中间的第二个渐变也是白色的混合,但稍微深一点的橙色。

关键是使用了 2 种渐变,有 4 种颜色、2 种白色和 2 种橙色变化。

我想在 Canvas 上执行此操作,但不知道如何操作。 颜色可以是任何东西,上面只是一个例子。

我该怎么做?

德尔福 2005 及更高版本:

使用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;

早期的 Delphi 版本:

使用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;

渐变填充.png

我使用普通的 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM