Подтвердить что ты не робот

WPF "волшебная" отрицательная кисть?

У меня есть градиент, который меняет его цвета, я хочу, чтобы текст внутри него всегда был видимым.

Я предпочитаю делать это динамически, если есть какой-либо готовый ресурс; Я хочу, чтобы "волшебная кисть" отрицала цвет.

Любые эксперименты?

4b9b3361

Ответ 1

Ну, инверсия цвета может быть выполнена как растровый эффект, но есть более простой способ.

Сделайте Grid, который будет контейнером для 3 дочерних панелей, чтобы эти дочерние панели полностью перекрывали друг друга:

Поместите текст там, где вы хотите, на панели с фоном Transparent (по умолчанию). Назовите эту маску панели.

Сделайте еще одну панель под названием "mainbackground" и придайте ей главный градиент в качестве фона. Поместите это после панели "маска", чтобы она покрывала текст

Сделайте еще одну панель под названием "invertedforeground" и придайте ей противоположный градиент. Для каждого значения цвета в главном градиенте дайте это противоположное (например, если один цвет #FF0000, поместите #00FFFF). Вы можете анимировать этот градиент так же, как вы можете анимировать первый, просто с противоположными значениями. Затем вы установите для OpacityMask этой панели значение VisualBrush и установите для свойства VisualBrushes Visual значение {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>

Вероятно, вы можете использовать конвертеры привязки и значений, чтобы вам нужно было только оживить один градиент, а другой - просто.


Изменить: Я попытался настроить перевернутую кисть переднего плана для текста, но он будет придерживаться координат TextBlock, поэтому я вернулся к предыдущему решению использовать текст как OpacityMask.


Изменить 2: Я добавил пример использования пользовательского IValueConverter и привязки цветов градиента текста к исходному градиенту. Вы также можете использовать привязку и конвертер где-то выше, например привязку свойства invertedforeground Background к свойству mainbackground Background, а конвертер принимает кисть градиента ввода и возвращает другую кисть градиента (это позволяет создавать градиент с совершенно другой конфигурацией в качестве оригинала).

Ответ 2

Джоэл дал отличный ответ о том, как выровнять градиентные кисти. Я хотел бы затронуть сложности автоматического создания новой градиентной кисти, которая гарантированно будет видна против старой.

В WPF цвета моделируются трехмерным образом, поскольку для определения цвета WPF требуется три числа (например, R/G/B или H/S/B), не считая альфа-компонент. Данная градиентная заливка может рассматриваться как путь, который исходит от одной цветовой точки к другой в трехмерном цветовом пространстве. Чтобы создать обратный градиент, который контрастирует в каждой точке, требуется создание дополнительного пути, который ни в коем случае не "слишком близок" к исходному пути. "Слишком близко" для этой цели - любые два цвета, которые трудно отличить для человеческого глаза. Это на самом деле субъективно. 4% или около того людей, которые имеют слепоту, будут иметь другую интерпретацию "слишком близко" , чем те, кто этого не делают.

Для не-цветного слепого человека, где "слишком близко" разумно определено, всегда будет множество путей, которые удовлетворяют критериям. В этом случае необходимы дополнительные критерии, чтобы решить, какой из них "лучше". Например, должен ли текст резко контрастировать с фоном, или он должен иметь тот же общий оттенок большей части пути?

С другой стороны, консервативное определение "слишком близко" , которое учитывает все восприятие цвета, например "яркость должно отличаться не менее чем на 25%", будет иметь противоположную проблему: единственный градиент, который удовлетворяет условию каждая точка должна быть на самом деле прерывистой, то есть она должна прыгать от одного цвета до далекого цвета одновременно. Рассмотрим, например, простой градиент от черного до белого. Если задействована только яркость, контрастный фон должен иметь разрывы, иначе он будет соответствовать в какой-то момент.

По этим причинам создание общего алгоритма для создания контрастного фона - это скорее искусство, чем наука. Можно использовать несколько алгоритмов, и в разных ситуациях будут использоваться различные алгоритмы.

Простым алгоритмом, который использовался для этой цели, является сохранение оттенка и насыщенности одного и того же градиента и установка яркости на (luminance + 50%) mod 100%. Однако этот алгоритм не дает очень эстетических результатов в большинстве случаев, и никогда не имеет изменения яркости более 50%. Модификацией этого алгоритма является инвертирование или изменение значений оттенка и насыщенности.

Еще более простой алгоритм вычисления контрастной яркости luminance>50% ? 0% : 100%. Это также может иметь эстетические проблемы.

Суть в том, что нет ни одного правильного ответа на инвертирование цветов градиента. Но если у вас есть алгоритм для этого, использование метода маскировки непрозрачности Joel наряду с привязкой и IValueConverter, который реализует ваш алгоритм, сделает трюк.