Получить цвет в определенном месте на градиенте - PullRequest
13 голосов
/ 11 марта 2012

У меня есть следующее GradientStopCollection:

GradientStopCollection grsc = new GradientStopCollection(3);
grsc.Add(new GradientStop(Colors.Red, 0));
grsc.Add(new GradientStop(Colors.Yellow, .5));
grsc.Add(new GradientStop(Colors.Green, 1));

Можно ли получить цвет в определенном «месте»?Например:

  • Местоположение 0: красный
  • Местоположение .5: Желтый
  • Местоположение .75: Yellow<~>Green

Естьесть API в WPF / какой-нибудь сторонней библиотеке, которая может это сделать?

Ответы [ 3 ]

18 голосов
/ 11 марта 2012

Чтобы получить цвет в определенной точке, необходимо понимать рассматриваемый градиент, а это не роль класса GradientStopCollection. Концепция этого класса не в том, чтобы понимать градиент, а должна представлять собой простой набор поддержки градиента.

Важно, чтобы вы понимали концепцию каждого класса.

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

но я дам вам более быстрое решение. Вы можете использовать алгоритм градиента для генерации одной точки. Это реализация того, как сделать это с помощью алгоритма линейного градиента:

public static class GradientStopCollectionExtensions
{
    public static Color GetRelativeColor(this GradientStopCollection gsc, double offset)
    {
        var point = gsc.SingleOrDefault(f => f.Offset == offset);
        if (point != null) return point.Color;

        GradientStop before = gsc.Where(w => w.Offset == gsc.Min(m => m.Offset)).First();
        GradientStop after = gsc.Where(w => w.Offset == gsc.Max(m => m.Offset)).First();

        foreach (var gs in gsc)
        {
            if (gs.Offset < offset && gs.Offset > before.Offset)
            {
                before = gs;
            }
            if (gs.Offset > offset && gs.Offset < after.Offset)
            {
                after = gs;
            }
        }

        var color = new Color();

        color.ScA = (float)((offset - before.Offset) * (after.Color.ScA - before.Color.ScA) / (after.Offset - before.Offset) + before.Color.ScA);
        color.ScR = (float)((offset - before.Offset) * (after.Color.ScR - before.Color.ScR) / (after.Offset - before.Offset) + before.Color.ScR);
        color.ScG = (float)((offset - before.Offset) * (after.Color.ScG - before.Color.ScG) / (after.Offset - before.Offset) + before.Color.ScG);
        color.ScB = (float)((offset - before.Offset) * (after.Color.ScB - before.Color.ScB) / (after.Offset - before.Offset) + before.Color.ScB);

        return color;
    }
}

PS: Этот алгоритм предполагает, что нет остановок с одинаковым смещением. Если есть несколько остановок с одним и тем же смещением, выдается InvalidOperationException.

Добавить этот класс в текущий контекст (контекст пространства имен)

Чтобы получить свой цвет в любом месте, вставьте что-то вроде этого:

var color = grsc.GetRelativeColor(.75);
2 голосов
/ 12 сентября 2016

Я попробовал метод, написанный Джонни Пьяцци. Но это не сработало правильно.
Поэтому я пишу свой собственный ниже:

private static Color GetColorByOffset(GradientStopCollection collection, double offset)
{
    GradientStop[] stops = collection.OrderBy(x => x.Offset).ToArray();
    if (offset <= 0) return stops[0].Color;
    if (offset >= 1) return stops[stops.Length - 1].Color;
    GradientStop left = stops[0], right = null;
    foreach (GradientStop stop in stops)
    {
        if (stop.Offset >= offset)
        {
            right = stop;
            break;
        }
        left = stop;
    }
    Debug.Assert(right != null);
    offset = Math.Round((offset - left.Offset)/(right.Offset - left.Offset), 2);
    byte a = (byte) ((right.Color.A - left.Color.A)*offset + left.Color.A);
    byte r = (byte) ((right.Color.R - left.Color.R)*offset + left.Color.R);
    byte g = (byte) ((right.Color.G - left.Color.G)*offset + left.Color.G);
    byte b = (byte) ((right.Color.B - left.Color.B)*offset + left.Color.B);
    return Color.FromArgb(a, r, g, b);
}

Я надеюсь, что это работает для вас!

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

<LinearGradientBrush x:Key="CountBrush" StartPoint="0 0" EndPoint="1 0">
    <GradientStop Offset="0.00" Color="ForestGreen"/>
    <GradientStop Offset="0.50" Color="Yellow"/>
    <GradientStop Offset="1.00" Color="OrangeRed"/>
</LinearGradientBrush>
<local:Int32ToColorConverter x:Key="CountToColorConverter" Min="0" Max="200" LinearBrush="{StaticResource CountBrush}"/>
0 голосов
/ 18 октября 2013
  foreach (var gs in gsc)
            {
                if (gs.Offset == offset) return gs.Color; //new line added
                if (gs.Offset < offset && gs.Offset > before.Offset)
                {
                    before = gs;
                }

                if (gs.Offset > offset && gs.Offset < after.Offset)
                {
                    after = gs;
                }
            }
...