Generate color gradient in C#
Asked Answered
L

7

30

My question here is similar to the question here, except that I am working with C#.

I have two colors, and I have a predefine steps. How to retrieve a list of Colors that are the gradients between the two?

This is an approach that I tried, which didn't work:

int argbMax = Color.Chocolate.ToArgb();
int argbMin = Color.Blue.ToArgb();
var colorList = new List<Color>();

for(int i=0; i<size; i++)
{
    var colorAverage= argbMin + (int)((argbMax - argbMin) *i/size);
    colorList.Add(Color.FromArgb(colorAverage));
}

If you try the above code, you will find that a gradual increase in argb doesn't correspond to a visual gradual increase in the color.

Any idea on this?

Lohse answered 6/1, 2010 at 9:2 Comment(0)
S
36

You will have to extract the R, G, B components and perform the same linear interpolation on each of them individually, then recombine.

int rMax = Color.Chocolate.R;
int rMin = Color.Blue.R;
// ... and for B, G
var colorList = new List<Color>();
for(int i=0; i<size; i++)
{
    var rAverage = rMin + (int)((rMax - rMin) * i / size);
    var gAverage = gMin + (int)((gMax - gMin) * i / size);
    var bAverage = bMin + (int)((bMax - bMin) * i / size);
    colorList.Add(Color.FromArgb(rAverage, gAverage, bAverage));
}
Simoneaux answered 6/1, 2010 at 9:3 Comment(2)
I know this is old, but it's marked as the answer. In fact, this will throw an exception if "size" causes a value to go our of the 0 to 255 range of a color's R, G, or B values.Renata
I don't think it will. It is linearly interpolating each component between two starting colors. It's only an invalid input color that will cause the effect you note. the division (i / size) (if taken as non-integer) cannot produce a value less than 0 or greater than 1. Even the division by zero possibility is protected against by the condition on the for loop. So I don't think any value of size can cause problems, it's just the input RGB values that might.Simoneaux
B
22

Oliver's answer was very close... but in my case some of my stepper numbers needed to be negative. When converting the stepper values into a Color struct my values were going from negative to the higher values e.g. -1 becomes something like 254. I setup my step values individually to fix this.

public static IEnumerable<Color> GetGradients(Color start, Color end, int steps)
{
    int stepA = ((end.A - start.A) / (steps - 1));
    int stepR = ((end.R - start.R) / (steps - 1));
    int stepG = ((end.G - start.G) / (steps - 1));
    int stepB = ((end.B - start.B) / (steps - 1));

    for (int i = 0; i < steps; i++)
    {
        yield return Color.FromArgb(start.A + (stepA * i),
                                    start.R + (stepR * i),
                                    start.G + (stepG * i),
                                    start.B + (stepB * i));
    }
}
Brooksbrookshire answered 9/1, 2013 at 20:57 Comment(3)
This is super simple, looks awesome and works. Thanks :-)Jenelljenelle
Hello do the resulting color list also work/compatible with CSSAlrick
@Alrick If you format the Color objects out in an appropriate format (string) it should work just fine. See one of the appropriate formats at developer.mozilla.org/en-US/docs/web/css/colorBrooksbrookshire
S
11

Maybe this function can help:

public IEnumerable<Color> GetGradients(Color start, Color end, int steps)
{
    Color stepper = Color.FromArgb((byte)((end.A - start.A) / (steps - 1)),
                                   (byte)((end.R - start.R) / (steps - 1)),
                                   (byte)((end.G - start.G) / (steps - 1)),
                                   (byte)((end.B - start.B) / (steps - 1)));

    for (int i = 0; i < steps; i++)
    {
        yield return Color.FromArgb(start.A + (stepper.A * i),
                                    start.R + (stepper.R * i),
                                    start.G + (stepper.G * i),
                                    start.B + (stepper.B * i));
    }
}
Selvage answered 6/1, 2010 at 9:43 Comment(2)
This was very close, but I had some negative step values that were breaking it. I posted my altered solution as well.Brooksbrookshire
Hello, do the resulting colors/list also work/*compatible with web CSS*?Alrick
D
8
    public static List<Color> GetGradientColors(Color start, Color end, int steps)
    {
        return GetGradientColors(start, end, steps, 0, steps - 1);
    }

    public static List<Color> GetGradientColors(Color start, Color end, int steps, int firstStep, int lastStep)
    {
        var colorList = new List<Color>();
        if (steps <= 0 || firstStep < 0 || lastStep > steps - 1)
            return colorList;

        double aStep = (end.A - start.A) / steps;
        double rStep = (end.R - start.R) / steps;
        double gStep = (end.G - start.G) / steps;
        double bStep = (end.B - start.B) / steps;

        for (int i = firstStep; i < lastStep; i++)
        {
            var a = start.A + (int)(aStep * i);
            var r = start.R + (int)(rStep * i);
            var g = start.G + (int)(gStep * i);
            var b = start.B + (int)(bStep * i);
            colorList.Add(Color.FromArgb(a, r, g, b));
        }

        return colorList;
    }
Devisee answered 11/10, 2012 at 16:16 Comment(0)
E
5

Use double instead of int:

double stepA = ((end.A - start.A) / (double)(steps - 1));
double stepR = ((end.R - start.R) / (double)(steps - 1));
double stepG = ((end.G - start.G) / (double)(steps - 1));
double stepB = ((end.B - start.B) / (double)(steps - 1));

and:

yield return Color.FromArgb((int)start.A + (int)(stepA * step),
                                            (int)start.R + (int)(stepR * step),
                                            (int)start.G + (int)(stepG * step),
                                            (int)start.B + (int)(stepB * step));
Eh answered 25/8, 2014 at 11:51 Comment(0)
F
2

Combining this answer with the idea from several other answers to use floating-point steps, here's a complete method snippet for stepping with floating point. (With integer stepping, I had been getting asymmetrical gradient colors in a 16-color gradient from blue to red.)

Important difference in this version: you pass the total number of colors you want in the returned gradient sequence, not the number of steps to take within the method implementation.

public static IEnumerable<Color> GetColorGradient(Color from, Color to, int totalNumberOfColors)
{
    if (totalNumberOfColors < 2)
    {
        throw new ArgumentException("Gradient cannot have less than two colors.", nameof(totalNumberOfColors));
    }

    double diffA = to.A - from.A;
    double diffR = to.R - from.R;
    double diffG = to.G - from.G;
    double diffB = to.B - from.B;

    var steps = totalNumberOfColors - 1;

    var stepA = diffA / steps;
    var stepR = diffR / steps;
    var stepG = diffG / steps;
    var stepB = diffB / steps;

    yield return from;

    for (var i = 1; i < steps; ++i)
    {
        yield return Color.FromArgb(
            c(from.A, stepA),
            c(from.R, stepR),
            c(from.G, stepG),
            c(from.B, stepB));

        int c(int fromC, double stepC)
        {
            return (int)Math.Round(fromC + stepC * i);
        }
    }

    yield return to;
}
Florie answered 6/8, 2019 at 22:59 Comment(4)
Hello, do the resulting colors/list also work/*compatible with web CSS*?Alrick
@Alrick The Color struct has R, G, B, and A properties that can be used to specify the corresponding color in CSS with rgba().Florie
Thanks William , will the struct values correspond to hex output that the CSS expects or water put should I format the RGB &A properties so that CSS understands the output on the front end. Also how do I output hsl values for the browser from the back endAlrick
@Alrick (1) rgba() can use the integer byte values from R, G, and B directly, but the A byte value would need to be converted to the [0,1] interval (e.g. dividing A / 255d). (2) Color also has the methods GetHue(), GetSaturation(), and GetBrightness() for HSL coordinates. All the info you need is here and here.Florie
M
0

I have tried multiple answers in this page, but their output has issues, some has result that is missing one color gradient at the end, some has issues with the first input color being included in the output result.

Finally, this is what worked for me in the end:

    public static List<Color> GetGradientColors(Color start, Color end, int steps)
    {
        return GetGradientColors(start, end, steps, 0, steps - 1);
    }

    public static List<Color> GetGradientColors(Color start, Color end, int steps, int firstStep, int lastStep)
    {
        var colorList = new List<Color>();
        if (steps <= 0 || firstStep < 0 || lastStep > steps - 1)
            return colorList;

        double aStep = (end.A - start.A) / (double)steps;
        double rStep = (end.R - start.R) / (double)steps;
        double gStep = (end.G - start.G) / (double)steps;
        double bStep = (end.B - start.B) / (double)steps;

        for (int i = firstStep+1; i <= lastStep; i++)
        {
            var a = start.A + (aStep * i);
            var r = start.R + (rStep * i);
            var g = start.G + (gStep * i);
            var b = start.B + (bStep * i);
            colorList.Add(Color.FromArgb((int)a, (int)r, (int)g, (int)b));
        }

        return colorList;
    }
Martin answered 18/7 at 14:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.