【问题标题】:WPF Color interpolationWPF 颜色插值
【发布时间】:2009-04-26 11:49:05
【问题描述】:

我正在尝试根据调色板绘制 WPF 控件的背景,其中每种颜色都分配有值(例如 Red = 0、DarkGreen = 10、Green = 20、LightGreen =30)和用户选择的值(例如25)这将给出结果颜色。我希望得到的颜色是两个最接近的颜色值之间的插值(例如,对于 25 的值,它应该给出绿色和浅绿色之间的颜色)

为此,我正在考虑在 WPF 中使用现有的 LinearGradientBrush;设置 GradientStops、偏移量并获取指定值的颜色。有没有办法做到这一点,或者我应该实现自己的颜色插值函数?

谢谢。

【问题讨论】:

    标签: wpf colors


    【解决方案1】:

    使用 LinearGradientBrush 听起来会有点开销。虽然没有知识。不过,颜色插值函数并不难写。

    为简单起见,我假设您的调色板具有可被 10 整除的值。

    public static Color GetColor(int value)
    {
        int startIndex = (value/10)*10;
        int endIndex = startIndex + 10;
    
        Color startColor = Palette[startIndex];
        Color endColor = Palette[endIndex];
    
        float weight = (value - startIndex)/(float)(endIndex - startIndex);
    
        return Color.FromArgb(
            (int)Math.Round(startColor.R * (1 - weight) + endColor.R * weight),
            (int)Math.Round(startColor.G * (1 - weight) + endColor.G * weight),
            (int)Math.Round(startColor.B * (1 - weight) + endColor.B * weight));
    
    }
    

    如果定义的颜色不能被 10 整除,则查找开始颜色和结束颜色的逻辑会有点复杂。

    【讨论】:

    • 记录:要么添加 255 作为 Color.FromArgb 的第一个参数,要么使用 Color.FromRgb。谢谢你的帖子。
    【解决方案2】:

    您的深绿色/绿色/浅绿色颜色值 10/20/30 是从哪里得出的。

    您需要在分配的调色板值和颜色的真实数字表示之间建立某种关联表...例如

    Color             Pal-Code     RGB            HSL
    Red               0            255,0,0        0,240,120
    Dark Green        10           0,128,0        80,240,60
    Green             20           0,255,0        80,240,120
    Light Green       30           128,255,128    80,240,180
    

    从该相关表中,您可以获取任何用户“调色板代码”,从上表中找到封闭匹配的调色板代码对,并在其上进行最佳匹配范围查找。例如如果有人输入25(为方便起见,我们使用HSL),那么公式将是......

    Green             20           0,255,0        80,240,120
    Light Green       30           128,255,128    80,240,180
    

    25 在两个代码之间,所以

    Palette Code     Hue        Sat      Luminence
    20               80         240      120
    30               80         240      180
    -------------------------------------------------
    25               80         240      150 
    

    如果他们选择了6,您需要找到每个值之间颜色范围的 0.6。

    Red               0            255,0,0        0,240,120
    Dark Green        10           0,128,0        80,240,60
    
    Palette Code     Hue        Sat      Luminence
    0                0          240      120
    10               80         240      60
    -------------------------------------------------
    6                48         240      84
    
    0->80      = +80 * 60%   = +48    So 0+48   = 48
    240->240   =   0 * 60%   = 0      So 240+0  = 240
    120->60    = -60 * 60%   = -36    So 120-36 = 84
    

    【讨论】:

      【解决方案3】:

      我认为您最好使用值转换器,只需采用其他答案中建议的插值函数之一并放入值转换器,使用此转换器将背景属性绑定到值,然后您重做。

      【讨论】:

        【解决方案4】:

        感谢大家的回复。似乎没有办法在指定点获得 GradientBrush 的“值”。我希望这在框架的某些更高版本中得到纠正。所以我想目前唯一的选择是按照 Mikko 的建议实现插值算法。

        【讨论】:

          【解决方案5】:

          我不确定当时是否是这种情况,但在 .NET 4.0 中可以从 LinearGradientBrush 获取颜色。

          private Color GetColor(double ratio)
          {
              if (ratio < 0) ratio = 0;
              else if (ratio > 1) ratio = 1;
          
              //Find gradient stops that surround the input value
              GradientStop gs0 = ColorScale.GradientStops.Where(n => n.Offset <= ratio).OrderBy(n => n.Offset).Last();
              GradientStop gs1 = ColorScale.GradientStops.Where(n => n.Offset >= ratio).OrderBy(n => n.Offset).First();
          
              float y = 0f;
              if (gs0.Offset != gs1.Offset)
              {
                  y = (float)((ratio - gs0.Offset) / (gs1.Offset - gs0.Offset));
              }
          
              //Interpolate color channels
              Color cx = new Color();
              if (ColorScale.ColorInterpolationMode == ColorInterpolationMode.ScRgbLinearInterpolation)
              {
                  float aVal = (gs1.Color.ScA - gs0.Color.ScA) * y + gs0.Color.ScA;
                  float rVal = (gs1.Color.ScR - gs0.Color.ScR) * y + gs0.Color.ScR;
                  float gVal = (gs1.Color.ScG - gs0.Color.ScG) * y + gs0.Color.ScG;
                  float bVal = (gs1.Color.ScB - gs0.Color.ScB) * y + gs0.Color.ScB;
                  cx = Color.FromScRgb(aVal, rVal, gVal, bVal);
              }
              else
              {
                  byte aVal = (byte)((gs1.Color.A - gs0.Color.A) * y + gs0.Color.A);
                  byte rVal = (byte)((gs1.Color.R - gs0.Color.R) * y + gs0.Color.R);
                  byte gVal = (byte)((gs1.Color.G - gs0.Color.G) * y + gs0.Color.G);
                  byte bVal = (byte)((gs1.Color.B - gs0.Color.B) * y + gs0.Color.B);
                  cx = Color.FromArgb(aVal, rVal, gVal, bVal);
              }
              return cx;
          }
          

          这适用于如下配置的画笔(例如):

          var brush = new LinearGradientBrush();
          brush.StartPoint = new Point(0, 0);
          brush.EndPoint = new Point(1, 0);
          
          //Set brush colors
          brush.GradientStops.Add(new GradientStop() { Color = Color.FromRgb(102, 40, 0), Offset = 0 });
          brush.GradientStops.Add(new GradientStop() { Color = Color.FromRgb(254, 167, 80), Offset = 0.25 });
          brush.GradientStops.Add(new GradientStop() { Color = Color.FromRgb(0, 153, 51), Offset = 0.5 });
          brush.GradientStops.Add(new GradientStop() { Color = Color.FromRgb(232, 165, 255), Offset = 0.75 });
          brush.GradientStops.Add(new GradientStop() { Color = Color.FromRgb(66, 0, 89), Offset = 1 });
          

          来源: http://dotupdate.wordpress.com/2008/01/28/find-the-color-of-a-point-in-a-lineargradientbrush/

          【讨论】:

            【解决方案6】:

            正确的方法是使用颜色的色调、饱和度和亮度。您可以使用以下方法将 RGB 颜色转换为其 HSB 值:

            public static (int Hue, double Saturation, double Brightness)GetHSB(this Color color) {
              int max = Math.Max(color.R, Math.Max(color.G, color.B));
              int min = Math.Min(color.R, Math.Min(color.G, color.B));
              int hue = 0;//for black, gray or white, hue could be actually any number, but usually 0 is 
                          //assign, which means red
              if (max-min!=0) {
                //not black, gray or white
                int maxMinDif = max-min;
                if (max==color.R) {
                  #pragma warning disable IDE0045 // Convert to conditional expression
                  if (color.G>=color.B) {
                  #pragma warning restore IDE0045
                    hue = 60 * (color.G-color.B)/maxMinDif;
                  } else {
                    hue = 60 * (color.G-color.B)/maxMinDif + 360;
                  }
                } else if (max==color.G) {
                  hue = 60 * (color.B-color.R)/maxMinDif + 120;
                } else if(max == color.B) {
                  hue = 60 * (color.R-color.G)/maxMinDif + 240;
                }
              }
            
              double saturation = (max == 0) ? 0.0 : (1.0-((double)min/(double)max));
            
              return (hue, saturation, (double)max/0xFF);
            }
            

            色调定义颜色,例如 0 到 360 度的红色或蓝色。如果您想要蓝色(240 度)和红色(360 度)之间的渐变,请将 120 度除以步数并将其转换回 RGB。但这有点复杂。更简单的是使用不同的权重混合两种颜色:

            /// <summary>
            /// Mixes factor*color1 with (1-factor)*color2.
            /// </summary>
            public static Color Mix(this Color color1, double factor, Color color2) {
              if (factor<0) throw new Exception($"Factor {factor} must be greater equal 0.");
              if (factor>1) throw new Exception($"Factor {factor} must be smaller equal 1.");
            
              if (factor==0) return color2;
              if (factor==1) return color1;
            
              var factor1 = 1 - factor;
              return Color.FromArgb(
                (byte)((color1.A * factor + color2.A * factor1)),
                (byte)((color1.R * factor + color2.R * factor1)),
                (byte)((color1.G * factor + color2.G * factor1)),
                (byte)((color1.B * factor + color2.B * factor1)));
            }
            

            我在 CodeProject 上写了一篇长文,正是关于这个主题:Definitive Guide to WPF Colors, Color Spaces, Color Pickers and Creating Your Own Colors for Mere Mortals

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-08-11
              • 2014-01-21
              相关资源
              最近更新 更多