WPF颜色插值
我正在尝试基于调色板绘制WPF控件的背景,其中每个颜色都指定了值(例如红色=0、深绿色=10、绿色=20、浅绿色=30)和用户选择的值(例如25),这些值将给出结果颜色。我希望得到的颜色是两个最接近的颜色值之间的插值(例如,对于值25,它应该给出介于绿色和浅绿色之间的颜色) 为此,我考虑在WPF中使用现有的LinearGradientBrush;设置渐变停止、偏移,并将颜色设置为指定值。有没有办法做到这一点,或者我应该实现自己的颜色插值功能WPF颜色插值,wpf,colors,Wpf,Colors,我正在尝试基于调色板绘制WPF控件的背景,其中每个颜色都指定了值(例如红色=0、深绿色=10、绿色=20、浅绿色=30)和用户选择的值(例如25),这些值将给出结果颜色。我希望得到的颜色是两个最接近的颜色值之间的插值(例如,对于值25,它应该给出介于绿色和浅绿色之间的颜色) 为此,我考虑在WPF中使用现有的LinearGradientBrush;设置渐变停止、偏移,并将颜色设置为指定值。有没有办法做到这一点,或者我应该实现自己的颜色插值功能 谢谢。你在哪里为你的深绿色/绿色/浅绿色提出了10/2
谢谢。你在哪里为你的深绿色/绿色/浅绿色提出了10/20/30的值 您需要在指定的调色板值和颜色的真实数字表示之间建立某种关联表。。。e、 g
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
从该关联表中,您可以获取任何用户“调色板代码”,从上表中找到闭合匹配的调色板代码对,并在其上进行最佳匹配范围查找。e、 g.如果一些人输入了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
,您需要找到每个值之间的颜色范围的.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
使用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整除,那么查找开始颜色和结束颜色的逻辑将更加复杂。我认为您最好使用值转换器,只需使用其他答案中建议的插值函数之一,然后放入值转换器,使用此转换器将background属性绑定到该值,您就完成了。感谢所有回复。似乎没有办法在指定点获得GradientBrush的“值”。我希望在框架的某个更高版本中纠正这一点。因此,我想现在唯一的选择是按照Mikko的建议实现插值算法。我不确定当时是否是这样,但在.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;
}
资料来源:
正确的方法是处理颜色的色调、饱和度和亮度。可以使用以下方法将颜色和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)));
}
//
///将因子*color1与(1因子)*color2混合。
///
公共静态颜色混合(此颜色颜色1、双因素、颜色2){
if(factor1)抛出新异常($“Factor{Factor}必须小于等于1”);
如果(因子==0)返回颜色2;
如果(因子==1)返回颜色1;
var系数1=1-系数;
返回Color.FromArgb(
(字节)((color1.A*因子+color2.A*因子1)),
(字节)((color1.R*因子+color2.R*因子1)),
(字节)((color1.G*因子+color2.G*因子1)),
(字节)((color1.B*因子+color2.B*因子1));
}
我写了一篇关于CodeProject的长篇文章,正是关于这个主题:作为记录:要么添加255作为Color.FromArgb的第一个参数,要么使用Color.FromRgb。谢谢你的来信。
/// <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)));
}