5

我需要生成一个渐变位图,显示用户选择的两种颜色之间的彩虹渐变。生成彩虹很容易。下面的代码是我从Wiki获得的,并稍作修改。它的优点是快速和简单。

 function TColor_Dialog.GiveRainbowColor (fraction: double): TAlphaColor;
  var
    m: Double;
    r, g, b, mt: Byte;
  begin
    if fraction <= 0 then m := 0 else
    if fraction >= 1 then m := 6
                     else m := fraction * 6;
    mt := (round (frac (m) * $FF));
    case Trunc (m) of
    0: begin
        R := $FF;
        G := mt;
        B := 0;
      end;
    1: begin
        R := $FF - mt;
        G := $FF;
        B := 0;
      end;
    2: begin
        R := 0;
        G := $FF;
        B := mt;
      end;
    3: begin
        R := 0;
        G := $FF - mt;
        B := $FF;
      end;
    4: begin
        R := mt;
        G := 0;
        B := $FF;
      end;
    5: begin
        R := $FF;
        G := 0;
        B := $FF - mt;
      end;
    end; // case

    Result := ColorToQuad (r, g, b);
  end; // GiveRainbowColor //

这个算法的问题是它不能在两种颜色之间显示部分彩虹。好吧,当然可以,但你必须接近每种颜色的分数,我不喜欢那种解决方案。我尝试将颜色分解为其 r、g、b 通道,但这不起作用。事后看来,原因很明显。假设您需要 FF0000 和 0000FF 之间的渐变。你会有红色从 FF->00 和蓝色从 00->FF。但是,没有明显出现在彩虹渐变中的绿色 (00FF00)。

我需要的是一个渐变函数,我可以给出两种颜色和一个分数,它会生成一种颜色。谁能指出我的文章、算法甚至代码?

更新

NGLN 的答案是这个问题的正确答案。他和 Warren 都想知道当颜色不是亮色(包含 0、$FF 和值的颜色)时该怎么办。我尝试了几个角度:放大/缩小和 HSL 插值。我终于安顿下来了,因为最后一个是最简单的。

基本上你有两种颜色:fromto。使用 RGBtoHSL 从每种颜色中提取 HSL 参数:RGBtoHSL (col_from, hf, sf, lf). 接下来计算两种颜色之间的色调、饱和度和亮度并重建新颜色。这就是 NGLN 在他关于色调的更新中提到的,但如果你概括这个原则,你就会在任何颜色之间找到彩虹。

function TColor_Dialog.interpolate_hsl (col_from, col_to: TAlphaColor; fraction: double): TAlphaColor;
  var af, at, ad: uInt8;
      hf, ht, hd: single;
      sf, st, sd: single;
      lf, lt, ld: single;
  begin
  // Get each rgb color channel
     af := GetAValue (col_from);
     at := GetAValue (col_to);
     RGBtoHSL (col_from, hf, sf, lf);
     RGBtoHSL (col_to,   ht, st, lt);

  // Compute differences
     ad := af + Round (fraction * (at - af));
     hd := hf + fraction * (ht - hf);
     sd := sf + fraction * (st - sf);
     ld := lf + fraction * (lt - lf);

     Result := MakeColor (HSLtoRGB (hd, sd, ld), ad);
  end; // interpolate_hsl //

这为所有可能的颜色提供了彩虹。我对不透明度应用相同的插值,因此使用MakeColor将插值的 Alpha 通道“摸索”到颜色中。

在此处输入图像描述

4

1 回答 1

9

然后你需要计算一个颜色在彩虹中的位置;的倒数GiveRainbowColor

function RainbowIndex(BrightColor: TColor): Double;
var
  R: Byte;
  G: Byte;
  B: Byte;
begin
  R := GetRValue(ColorToRGB(BrightColor));
  G := GetGValue(ColorToRGB(BrightColor));
  B := GetBValue(ColorToRGB(BrightColor));
  if (R * G * B <> 0) or ((R <> 255) and (G <> 255) and (B <> 255)) then
    Result := -1
  else if B = 0 then
    if R = 255 then
      Result := 0 + G / 255
    else
      Result := 1 + (255 - R) / 255
  else if R = 0 then
    if G = 255 then
      Result := 2 + B / 255
    else
      Result := 3 + (255 - G) / 255
  else { G = 0 }
    if B = 255 then
      Result := 4 + R / 255
    else
      Result := 5 + (255 - B) / 255;
  Result := Result / 6;
end;

(但这对于不具有 0 和 255 部分的颜色会显示问题。换句话说:您还需要从阴影、着色或灰色计算明亮的颜色。请参阅下面的更新。)

clRed从to获取彩虹切片的示例用法clBlue

在此处输入图像描述

procedure TForm1.FormPaint(Sender: TObject);
var
  Start: Double;
  Finish: Double;
  X: Integer;
begin
  Start := RainbowIndex(clRed);
  Finish := RainbowIndex(clBlue);
  for X := 0 to ClientWidth - 1 do
  begin
    Canvas.Brush.Color := GiveRainbowColor(0, ClientWidth - 1, X);
    Canvas.FillRect(Bounds(X, 0, 1, 50));
    Canvas.Brush.Color :=
      GiveRainbowColor(0, ClientWidth - 1, Round(Start + (Finish - Start) * X));
    Canvas.FillRect(Bounds(X, 50, 1, 50));
  end;
end;

更新:

上面的RainbowIndex例程实际上只计算颜色的色调属性。该GraphUtil单元为HSL 颜色模型提供了转换例程,这使得这种RainbowIndex例程已经过时,并具有能够提供任何TColor值的优势:

uses
  GraphUtil;

const
  HLSMAX = 240;

function Hue(AColor: TColor): Double;
var
  Hue: Word;
  Luminance: Word;
  Saturation: Word;
begin
  ColorRGBToHLS(ColorToRGB(AColor), Hue, Luminance, Saturation);
  Result := Hue / HLSMAX;
end;

clMoneyGreen从to获取彩虹切片的示例用法clPurple

在此处输入图像描述

function RainbowColor(Hue: Double): TColor; overload;
begin
  Hue := EnsureRange(Hue, 0, 1) * 6;
  case Trunc(Hue) of
    0: Result := RGB(255, Round(Frac(Hue) * 255), 0);
    1: Result := RGB(255 - Round(Frac(Hue) * 255), 255, 0);
    2: Result := RGB(0, 255, Round(Frac(Hue) * 255));
    3: Result := RGB(0, 255 - Round(Frac(Hue) * 255), 255);
    4: Result := RGB(Round(Frac(Hue) * 255), 0, 255);
  else
    Result := RGB(255, 0, 255 - Round(Frac(Hue) * 255));
  end;
end;

function RainbowColor(MinHue, MaxHue, Hue: Integer): TColor; overload;
begin
  Result := RainbowColor((Hue - MinHue) / (MaxHue - MinHue + 1));
end;

procedure TForm1.FormPaint(Sender: TObject);
var
  X: Integer;
  Start: Double;
  Finish: Double;
begin
  Start := Hue(clMoneyGreen);
  Finish := Hue(clPurple);
  for X := 0 to ClientWidth - 1 do
  begin
    Canvas.Brush.Color := RainbowColor(0, ClientWidth - 1, X);
    Canvas.FillRect(Bounds(X, 0, 1, 50));
    Canvas.Brush.Color :=
      RainbowColor(Start + (Finish - Start) * X / ClientWidth);
    Canvas.FillRect(Bounds(X, 50, 1, 50));
  end;
end;

此外,RainbowColor 例程可以缩短为:

function RainbowColor(Hue: Double): TColor; overload;
begin
  Result := ColorHLStoRGB(Round(Hue * HLSMAX), HLSMAX div 2, HLSMAX);
end;
于 2013-11-01T00:51:47.777 回答