Mike Perrenoud
Mike Perrenoud

Reputation: 67898

What's the correct math to fade a color?

I'm trying to fade a color, let's say Yellow to White over a period of time. I've got the timer worked out, and I'm applying the new color just fine as well, but the fade isn't smooth (e.g. it fades into some weird colors before it gets to White, some of which are darker than their predecessor on the "fade chain" let's call it). I'm confident that's because the math is wrong, but I just can't seem to find a good example of the math for me to modify what I'm doing.

I even pulled the basics of the ColorCeiling code from this question: Fade a color to white (increasing brightness)

Right now I take a color, and call an extension method Increase:

dataGridViewResults.Rows[0].DefaultCellStyle.BackColor.Increase(50);

public static Color Increase(this Color color, byte offset)
{
    return Color.FromArgb(
        color.A.ColorCeiling(offset),
        color.R.ColorCeiling(offset),
        color.G.ColorCeiling(offset),
        color.B.ColorCeiling(offset));
}

and as you can see, each color is then modified by an offset with a ceiling in mind to keep exceptions from being thrown. That extension method, ColorCeiling looks like this:

public static int ColorCeiling(this byte val, byte modifier, byte ceiling = 255)
{
    return (val + modifier > ceiling) ? ceiling : val + modifier;
}

Now, I'm confident that ColorCeiling is the problem, but I honestly just can't find the math. I honestly feel like just incrementing the ARGB is almost certainly the wrong approach, it seems like you'd say I want you to be 50% lighter, but I just don't know what that means as far as code.

Upvotes: 5

Views: 6132

Answers (4)

AnotherUser
AnotherUser

Reputation: 1353

Here is some code I use to fade from one color to another in the RGB color space. It works quite well for my purposes. You might have to adapt it to return the colors in steps. Currently for each i you will have a curClr that is i/discreteUnits percent between baseClr and fadeTo.

int discreteUnits = 10;
Color fadeTo = Color.Green;
Color baseClr = Color.Red;
float correctionFactor = 0.0f;
float corFactorStep = 1.0f / discreteUnits;

for(int i = 0; i < discreteUnits; i++)
{
    correctionFactor += corFactorStep;
    float red = (fadeTo.R - baseClr.R) * correctionFactor + baseClr.R;
    float green = (fadeTo.G - baseClr.G) * correctionFactor + baseClr.G;
    float blue = (fadeTo.B - baseClr.B) * correctionFactor + baseClr.B;
    Color curClr = Color.FromArgb(baseClr.A, (int)red, (int)green, (int)blue);
}

Credit to this site for giving me a starting place: http://www.pvladov.com/2012/09/make-color-lighter-or-darker.html

Upvotes: 0

svidgen
svidgen

Reputation: 14282

Percentage implies multiplication, not addition. In my really simple JavaScript Color class, I do this sort of thing by defining two colors and blending the RGB dimensions.

From my Color class:

this.getBlendedColor = function(color, percent) {
    // limit percent between 0 and 1.
    // this percent is the amount of 'color' rgb components to use
    var p = percent > 0 ? percent : 0;
    p = p < 1 ? p : 1;

    // amount of 'this' rgb components to use
    var tp = 1 - p;

    // blend the colors
    var r = Math.round(tp * this.r + p * color.r);
    var g = Math.round(tp * this.g + p * color.g);
    var b = Math.round(tp * this.b + p * color.b);

    // return new color object
    return new Color(r, g, b);
} // getBlendedColor ()

So, if you had some Color c, and you wanted to get it 50% whiter, you'd do this:

var newColor = c.getBlendedColor(new Color('#ffffff'), 0.50);

If you're using the alpha channel, it can be explicitly included without causing any trouble -- both colors in you example are probably at 100% opacity.

An example gradient using simple RGB Color blending math: http://jsfiddle.net/2MymY/1/

Upvotes: 1

Rachel Gallen
Rachel Gallen

Reputation: 28563

A gradient is what you're looking for. YOu'll have to seperate them and recombine them like so..

int rMax = Color.Yellow.R;
int rMin = Color.White.R;
int bMax = Color.Yellow.B;
int bMin = Color.White.B;
int gMax = Color.Yellow.G;
int gMin = Color.White.G;

var colorList = new List<Color>();
for(int i=0; i<size; i++)
{
    var rAverage = rMin + (int)((rMax - rMin) * i / size);
    var bAverage = bMin + (int)((bMax - bMin) * i / size);
    var gAverage = gMin + (int)((gMax - gMin) * i / size);

    colorList.Add(Color.FromArgb(rAverage, gAverage, bAverage));
}

Upvotes: 2

Eric Lippert
Eric Lippert

Reputation: 660024

Here's an idea that might work.

Don't do the math in Red-Green-Blue space. Instead treat your colour as having three components:

  • Hue (is it red, orange, yellow, green, blue, violet, etc),
  • Saturation (how intense is the color?)
  • Value (how much blackness is in the color?)

There are algorithms for converting from RGB to HSV; look them up. This is a good place to start:

http://en.wikipedia.org/wiki/HSL_and_HSV

Now when you are fading from one color to the other, take steps along the HSV axes, not along the RGB axes.

Upvotes: 8

Related Questions