WPF如何为阴影设置期望颜色?

3

这是绘制椭圆的示例代码,启用了阴影。我将填充和阴影颜色都设置为相同的颜色。但在视图中,阴影颜色与实际不同。这可能是WPF的特性,但在我的情况下,我希望为对象设置所需的阴影颜色。

<Window x:Class="Test.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300">   

    <Grid>
      <Canvas>
            <Ellipse  Width="200" Height="300" Fill="#7D00FE">
                <Ellipse.Effect>
                    <DropShadowEffect   
                      ShadowDepth="5" 
                      Color="#7D00FE"/>                    
                </Ellipse.Effect>                
            </Ellipse>
        </Canvas>
    </Grid>
</Window>

1
看起来是DropShadowEffect的问题:https://dev59.com/43XYa4cB1Zd3GeqP6XT2 - undefined
4个回答

4
似乎 DropShadowEffect 在渲染时会影响颜色。对于基本颜色(如红色、蓝色、青色等 Colors,但您也可以通过 #AARRGGBB 格式指定它们),这个问题似乎不存在。
我无法确定它所做的确切修改,也不能提供解决方法(除了使用命名颜色……),但我想也许在答案中提一下这个问题可能是值得的。
请参阅其他问题,这些问题可能指向 DropShadowEffect 的同一“错误”或未记录的特性:
- DropShadowEffect with DynamicResource as color has weak visibility - WPF DropShadowEffect - Unexpected Color Difference 更新: 因此,这是作弊,但对于您的具体问题,它可能会解决问题:
<Grid>
  <Canvas>
        <Ellipse  Width="200" Height="300" Fill="#7D00FE">
            <Ellipse.Effect>
                <DropShadowEffect
                  ShadowDepth="5" 
                  Color="#BA00FE"/>                    
            </Ellipse.Effect>                
        </Ellipse>
    </Canvas>
</Grid>

通过一些投入的工作,可能会有一个转换器,可以将一种颜色转换为另一种颜色,这将是所需的给定颜色的DropShadowEffect颜色。如果我有一点时间,我会回来处理这个问题。
我的直觉表明,该特定效果的着色器代码可能存在问题,并且输出可能因不同的硬件(和/或驱动程序版本)而异,但目前我无法证明这一点。
更新:
我错了关于命名颜色,它并不适用于所有这些,例如:绿色有缺陷,但问题并不仅仅取决于颜色的绿色成分。有趣。
更新2:
所以这里是我之前谈到的转换器:
using System;
using System.Windows.Data;
using System.Windows.Media;

namespace MyCustomConverters
{
    public class ColorToShadowColorConverter: IValueConverter
    {

        #region IValueConverter Members

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            // Only touch the shadow color if it's a solid color, do not mess up other fancy effects
            if (value is SolidColorBrush)
            {
                Color color = ((SolidColorBrush)value).Color;
                var r = Transform(color.R);
                var g = Transform(color.G);
                var b = Transform(color.B);

                // return with Color and not SolidColorBrush, otherwise it will not work
                // This means that most likely the Color -> SolidBrushColor conversion does the RBG -> sRBG conversion somewhere...
                return Color.FromArgb(color.A, r, g, b); 
            }

            return value;
        }

        private byte Transform(byte source)
        {
            // see http://en.wikipedia.org/wiki/SRGB
            return (byte)(Math.Pow(source / 255d, 1 / 2.2d) * 255);
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotSupportedException("ColorToShadowColorConverter is a OneWay converter.");
        }

        #endregion
    }
}

以下是使用方法:

资源部分:

<namespaceDefinedByXmlnsProperty:ColorToShadowColorConverter x:Key="ColorConverter" />

真实用法:

<Ellipse Width="50" Height="100" Fill="#7D00FE">
    <Ellipse.Effect>
        <DropShadowEffect ShadowDepth="50" 
                          Color="{Binding Fill, RelativeSource={RelativeSource 
                                  Mode=FindAncestor, AncestorType={x:Type Ellipse}}, 
                                  Converter={StaticResource ColorConverter}}"/>
    </Ellipse.Effect>
</Ellipse>

感谢Michal Ciechan的答案,它指引了我正确的方向。

2

正如Ciechan在其回答(感谢Ciechan先生)中所解释的那样,微软将DropShadowEffect转换为特定的Sc值。

那么如何解决呢?

只需通过将RGB值输入sRGB让Microsoft进行计算即可。

//Where the variable color is the expected color.
Color newColor = new Color();
newColor.ScR = color.R;
newColor.ScG = color.G;
newColor.ScB = color.B;
//the converted color value is in newColor.R, newColor.G, newColor.B

请参考@qqbenq的答案更新2,了解有关绑定转换器的技术细节(感谢@qqbenq)。

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    // Only touch the shadow color if it's a solid color, do not mess up other fancy effects
    if (value is SolidColorBrush)
    {
        Color color = ((SolidColorBrush)value).Color;
        //Where the variable color is the expected color.
        Color newColor = new Color();
        newColor.ScR = (float)color.R / 255;
        newColor.ScG = (float)color.G / 255;
        newColor.ScB = (float)color.B / 255;

        return newColor;
    }

    return value;
}

2
在某个地方,它将DropShadowEffect转换为特定的Sc值。
越接近1,差异就越小(因此FF/255/1完全可以使用),因为1的n次根是1。
从研究ScRGB并了解其相关知识来看,ScRGB的伽马值大约为2.2。因此,在从RGB转换为ScRGB时,您可能需要除以255,然后取值的n次(2.2)根来得到最终值。
例如:
value 5E is 94

94 / 255 = 0.36862745098039215686274509803922

2.2root of 94/255 = 0.635322735100355

0.635322735100355 * 255 = ~162 = A2

因此,当您将前景的绿色设置为5E时,需要将DropShadowEffect设置为A2。
这只是我的观察和从研究中得出的结论。
为什么微软会这样实现呢?我不知道。
来源: 因此,在您的示例中,要使用相同的颜色,您需要使用#B800FE。

1

这里是对@qqbenq的回答改进后的公式。

改动在Transform函数中。它更加精确,与原来相差约1个值。 因此,在提问者的例子中,要得到相同的颜色,您需要使用#BA00FF,而不是提问者请求的#7D00FE。

公式的参考来源可在https://www.nayuki.io/page/srgb-transform-library找到。

        private byte Transform(byte source)
        {
            // see http://en.wikipedia.org/wiki/SRGB
            return (byte)(Math.Pow(source / 255d, 1 / 2.2d) * 255);
            double x = (double)source / 255;
            if (x <= 0)
                return 0;
            else if (x >= 1)
                return 1;
            else if (x < 0.0031308f)
                return (byte)(x * 12.92f * 255);
            else
                return (byte)((Math.Pow(x, 1 / 2.4) * 1.055 - 0.055) * 255);
        }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接