WPF“魔法”否定刷?

发布于 2024-08-21 06:58:08 字数 98 浏览 3 评论 0原文

我有一个可以改变颜色的渐变,我希望其中的文本始终可见。

如果有任何开箱即用的资源,我宁愿动态地进行;我想要一把可以消除颜色的“魔笔”。

有什么实验吗?

I have a gradient that changes its colors, I want the text inside it should always be visible.

I rather doing it dynamically if there is any out-the-box resource; I want a 'magic brush' that negates the color.

Any experiments?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

晒暮凉 2024-08-28 06:58:08

Joel 对于如何对齐渐变画笔给出了很好的答案。我想谈谈自动创建一个新的渐变画笔的复杂性,该画笔保证在旧的画笔上可见。

在 WPF 中,颜色是三维建模的,因为它需要三个数字(例如 R/G/B 或 H/S/B)来定义 WPF 颜色,不包括 alpha 分量。给定的渐变填充可以被视为三维颜色空间中从一个颜色点到另一个颜色点的路径。要创建在每个点都形成对比的反向渐变,需要创建一条附加路径,该路径在任何点都不会“太接近”原始路径。对于这个目的,“太接近”是指人眼难以区分的任何两种颜色。这其实是主观的。大约 4% 的色盲人士对“太接近”的理解与非色盲人士不同。

对于一个非色盲的人来说,“太接近”被合理地定义,总会有许多满足标准的路径。在这种情况下,需要额外的标准来决定哪一个“更好”。例如,文本应该与背景形成尽可能鲜明的对比,还是应该在大部分情况下具有相同的总体色调?

另一方面,考虑到每个人的颜色感知的“太接近”的保守定义,例如“亮度必须相差至少 25%”,将会遇到相反的问题:在每个点满足条件的唯一梯度必须实际上是不连续的,也就是说它必须同时从一种颜色跳到另一种较远的颜色。例如,考虑从黑到白的简单渐变。如果仅涉及亮度,则对比背景必须具有不连续性,否则它将在某个点匹配。

出于这些原因,创建一种通用算法来产生对比背景更像是一门艺术,而不是一门科学。可以使用多种算法,不同的算法适用于不同的情况。

用于此目的的一个简单算法是保持色调和饱和度与梯度相同,并将亮度设置为(luminance + 50%) mod 100%。然而,在大多数情况下,该算法不会产生非常美观的结果,并且亮度变化永远不会超过 50%。该算法的修改是反转或移动色调和饱和度值。

计算对比亮度的更简单的算法是luminance>50%? 0%:100%。这也会带来审美问题。

这里的底线是,反转渐变颜色没有一个正确的答案。但是,如果您有一个算法可以做到这一点,那么使用 Joel 的不透明蒙版技术以及绑定和实现您的算法的 IValueConverter 就可以解决问题。

Joel gave an excellent answer on how to align the gradient brushes. I would like to touch on the complexities of automatically creating a new gradient brush that is guaranteed to be visible against the old one.

In WPF colors are modeled three-dimensionally, since it requires three numbers (such as R/G/B or H/S/B) to define a WPF color, not counting the alpha component. A given gradient fill can be viewed as a path that proceeds from one color point to another in the three dimensional color space. To create a reverse gradient that contrasts at every point requires creation of an additional path that at no point comes "too close" to the original path. "Too close" for this purpose is any two colors that are hard for the human eye to distinguish. This is in fact subjective. The 4% or so of people who are color-blind will have a different interpretation of "too close" than those who aren't.

For a non-colorblind person where "too close" is reasonably defined there will always be a multitude of paths that satisfy the criteria. In this case additional criteria are required to decide which one is "better". For example, should the text contrast as sharply as possible with the background, or it should it have the same general hue most of the way?

On the other hand, a conservative definition of "too close" that takes everyone's color perception into account such as "luminance must differ by at least 25%" will suffer from the opposite problem: The only gradient which satisfies the condition at every point must actually be discontinuous, that is it must jump from one color to a distant color all at once. Consider, for example, the simple gradient from black to white. If only luminance is involved the contrasting background must have a discontinuity otherwise it will match at some point.

For these reasons, creating a general algorithm to produce a contrasting background is more of an art than a science. Multiple algorithms can be used, and different algorithms will be appropriate in different situations.

A simple algorithm that has been used for this purpose is to keep the hue and saturation the same the gradient and set the luminance to (luminance + 50%) mod 100%. This algorithm does not produce very aesthetic results in most cases, however, and it never has a luminance variation of more than 50%. A modification of this algorithm is to invert or shift the hue and saturation values as well.

An even simpler algorithm to compute a contrasting luminance is luminance>50% ? 0% : 100%. This also can have aesthetic problems.

The bottom line here is that there is no one right answer to inverting your gradient colors. But if you have got an algorithm to do it, using Joel's opacity mask technique along with a binding and an IValueConverter that implements your algorithm will do the trick.

醉城メ夜风 2024-08-28 06:58:08

好吧,颜色反转可以作为位图效果来完成,但有一种更简单的方法。

创建一个 Grid 作为 3 个子面板的容器,以便这些子面板彼此完全重叠:

将文本放在具有 Transparent 背景(默认情况下)。将此面板命名为“mask”。

制作另一个名为“mainbackground”的面板,并将主渐变作为背景。将其放在“mask”面板之后,以便它覆盖文本

创建另一个名为“invertedforeground”的面板,并给它相反的渐变。对于主渐变中的每个颜色值,给出相反的值(例如,如果一种颜色是#FF0000,则输入#00FFFF)。您可以为该渐变设置动画,就像为第一个渐变设置动画一样,只是使用相反的值。然后,将此面板的 OpacityMask 设置为 VisualBrush,并将 VisualBrushesVisual 属性设置为 {绑定元素名称=掩码}

<Grid>
    <Grid.Resources>
        <local:MyColorConverter x:Key="colorConverter" />
    </Grid.Resources>
    <Grid
        Name="mask">
        <TextBlock
            Name="mytext"
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            FontSize="32"
            Foreground="White"
            FontWeight="Bold">Blah blah blah</TextBlock>
    </Grid>

    <Grid Name="mainbackground">
        <Grid.Background>
            <LinearGradientBrush
                ColorInterpolationMode="ScRgbLinearInterpolation"
                EndPoint="1,0">
                <GradientStop x:Name="stop1"
                    Color="#FF0000"
                    Offset="0" />
                <GradientStop x:Name="stop2"
                    Color="#00FF00"
                    Offset="0.5" />
                <GradientStop x:Name="stop3"
                    Color="#0000FF"
                    Offset="1" />
            </LinearGradientBrush>
        </Grid.Background>
    </Grid>

    <Grid Name="invertedforeground">
        <Grid.Background>
            <LinearGradientBrush
                ColorInterpolationMode="ScRgbLinearInterpolation"
                EndPoint="1,0">
                <GradientStop
                    Color="{Binding ElementName=stop1, Path=Color, Converter={StaticResource colorConverter}}"
                    Offset="0" />
                <GradientStop
                    Color="{Binding ElementName=stop2, Path=Color, Converter={StaticResource colorConverter}}"
                    Offset="0.5" />
                <GradientStop
                    Color="{Binding ElementName=stop3, Path=Color, Converter={StaticResource colorConverter}}"
                    Offset="1" />
            </LinearGradientBrush>
        </Grid.Background>
        <Grid.OpacityMask>
            <VisualBrush
                Visual="{Binding ElementName=mask}" />
        </Grid.OpacityMask>
    </Grid>
</Grid>

您可能可以使用绑定和值转换器,这样您只需要为一个渐变设置动画,然后另一个渐变就会跟随。


编辑:我尝试为文本设置一个倒置的前景画笔,但它会粘在TextBlock的坐标上,所以我恢复到之前使用文本的解决方案一个OpacityMask


编辑 2: 我添加了自定义 IValueConverter 的示例用法,并将文本渐变的颜色绑定到原始渐变的颜色。您还可以在更高的位置使用绑定和转换器,例如将 invertedforegroundBackground 属性绑定到 mainbackgroundBackground< /code> 属性,转换器接受输入渐变画笔并返回不同的渐变画笔(这允许您创建与原始配置有很大不同的渐变)。

Well, color inversion could possibly be done as a bitmap effect, but there's a simpler way.

Make a Grid that will be the container for 3 child panels so that these child panels will overlap each other completely:

Put the text where you want it in a panel that has a Transparent background (they do by default). Name this panel 'mask'.

Make another panel called 'mainbackground' and give it the main gradient as its background. Put this after the 'mask' panel so that it covers the text

Make another panel called 'invertedforeground' and give it the opposite gradient. For each color value in the main gradient, give this one the opposite (e.g., if one color is #FF0000, put #00FFFF). You could animate this gradient just as you can animate the first, just with opposite values. Then you set the OpacityMask of this panel to a VisualBrush and set the VisualBrushes's Visual property to {Binding ElementName=mask}.

<Grid>
    <Grid.Resources>
        <local:MyColorConverter x:Key="colorConverter" />
    </Grid.Resources>
    <Grid
        Name="mask">
        <TextBlock
            Name="mytext"
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            FontSize="32"
            Foreground="White"
            FontWeight="Bold">Blah blah blah</TextBlock>
    </Grid>

    <Grid Name="mainbackground">
        <Grid.Background>
            <LinearGradientBrush
                ColorInterpolationMode="ScRgbLinearInterpolation"
                EndPoint="1,0">
                <GradientStop x:Name="stop1"
                    Color="#FF0000"
                    Offset="0" />
                <GradientStop x:Name="stop2"
                    Color="#00FF00"
                    Offset="0.5" />
                <GradientStop x:Name="stop3"
                    Color="#0000FF"
                    Offset="1" />
            </LinearGradientBrush>
        </Grid.Background>
    </Grid>

    <Grid Name="invertedforeground">
        <Grid.Background>
            <LinearGradientBrush
                ColorInterpolationMode="ScRgbLinearInterpolation"
                EndPoint="1,0">
                <GradientStop
                    Color="{Binding ElementName=stop1, Path=Color, Converter={StaticResource colorConverter}}"
                    Offset="0" />
                <GradientStop
                    Color="{Binding ElementName=stop2, Path=Color, Converter={StaticResource colorConverter}}"
                    Offset="0.5" />
                <GradientStop
                    Color="{Binding ElementName=stop3, Path=Color, Converter={StaticResource colorConverter}}"
                    Offset="1" />
            </LinearGradientBrush>
        </Grid.Background>
        <Grid.OpacityMask>
            <VisualBrush
                Visual="{Binding ElementName=mask}" />
        </Grid.OpacityMask>
    </Grid>
</Grid>

You could probably use binding and value converters so that you only need to animate one gradient and the other simply follows.


Edit: I tried setting an inverted Foreground brush for the text, but it would stick to the TextBlock's coordinates, so I reverted to the previous solution of using the text as an OpacityMask.


Edit 2: I added example usage of a custom IValueConverter and binding the text gradient's colors to the original gradient's. You could also use binding and a converter somewhere higher up, such as binding invertedforeground's Background property to mainbackground's Background property and the converter takes the input gradient brush and returns a different gradient brush (this allows you to create a gradient with a much different configuration as the original).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文