tags:

views:

158

answers:

2

I did a search for an HsbToRgb converter in the docs but didn't find anything containing "hsb" or "hsl", so I'm guessing it just doesn't exist. Just to make sure, though, are there any classes that support this conversion?

Update

I ended up going with this, which is slightly different than 0xA3's. I also added an AhsbToArgb so I can convert to RGB and set the alpha channel in one shot.

AhsbToArgb - allows for alpha channel:

public static Color AhsbToArgb(byte a, double h, double s, double b)
{
    var color = HsbToRgb(h, s, b);
    return Color.FromArgb(a, color.R, color.G, color.B);
}

HsbToRgb - converts hue-saturation-brightness to red-green-blue:

public static Color HsbToRgb(double h, double s, double b)
{
    if (s == 0)
        return RawRgbToRgb(b, b, b);
    else
    {
        var sector = h / 60;
        var sectorNumber = (int)Math.Truncate(sector);
        var sectorFraction = sector - sectorNumber;
        var b1 = b * (1 - s);
        var b2 = b * (1 - s * sectorFraction);
        var b3 = b * (1 - s * (1 - sectorFraction));
        switch (sectorNumber)
        {
            case 0:
                return RawRgbToRgb(b, b3, b1);
            case 1:
                return RawRgbToRgb(b2, b, b1);
            case 2:
                return RawRgbToRgb(b1, b, b3);
            case 3:
                return RawRgbToRgb(b1, b2, b);
            case 4:
                return RawRgbToRgb(b3, b1, b);
            case 5:
                return RawRgbToRgb(b, b1, b2);
            default:
                throw new ArgumentException("Hue must be between 0 and 360");
        }
    }
}

RawRgbToRgb - converts doubles to ints and returns a color object:

private static Color RawRgbToRgb(double rawR, double rawG, double rawB)
{
    return Color.FromArgb(
        (int)Math.Round(rawR * 255),
        (int)Math.Round(rawG * 255),
        (int)Math.Round(rawB * 255));
}
+5  A: 

No, in .NET (up to and including 4.0) the Color class only converts automatically from RGB to HSB (via the GetHue, GetSaturation and GetBrightness methods). You have to roll your own method to go from HSB values to RGB values, or use an already-written example, like:

http://www.codeproject.com/KB/GDI-plus/HSBColorClass.aspx

MusiGenesis
+13  A: 

No it does not as far as I know. But the algorithm is not very complicated and you will find working code on the Web such as the one on this CodeProject article on Manipulating colors in .NET by Guillaume Leparmentier:

public static Color HSBtoRGB(double hue, double saturation, double brightness)
{
    double r = 0;
    double g = 0;
    double b = 0;

    if (saturation == 0)
    {
        r = g = b = brightness;
    }
    else
    {
        // The color wheel consists of 6 sectors. 
        // Figure out which sector you're in.
        //
        double sectorPos = hue / 60.0;
        int sectorNumber = (int)(Math.Floor(sectorPos));

        // get the fractional part of the sector
        double fractionalSector = sectorPos - sectorNumber;

        // calculate values for the three axes of the color. 
        double p = brightness * (1.0 - saturation);
        double q = brightness * (1.0 - (saturation * fractionalSector));
        double t = brightness * (1.0 - (saturation * (1 - fractionalSector)));

        // assign the fractional colors to r, g, and b 
        // based on the sector the angle is in.
        switch (sectorNumber)
        {
            case 0:
                r = brightness;
                g = t;
                b = p;
                break;
            case 1:
                r = q;
                g = brightness;
                b = p;
                break;
            case 2:
                r = p;
                g = brightness;
                b = t;
                break;
            case 3:
                r = p;
                g = q;
                b = brightness;
                break;
            case 4:
                r = t;
                g = p;
                b = brightness;
                break;
            case 5:
                r = brightness;
                g = p;
                b = q;
                break;
        }
    }

    return Color.FromArgb(
        (int)(r * 255.0 + 0.5), 
        (int)(g * 255.0 + 0.5), 
        (int)(b * 255.0 + 0.5));
}
0xA3
Well that's handy. Clear and to the point.
Rusty
Nice answer, thanks!
DanM
...although I just noticed, this seems to declare `b` twice.
DanM
Also, the last line can be much simplified to `return new RGB((int)(r * 255.0), (int)(g * 255.0), (int)(b * 255.0));`. I'm not sure I see the value in converting a double to a string which is then parsed back into a double which is then converted to an int. :)
MusiGenesis
@MusiGenesis: Thanks, fixed it up.
0xA3
Since I'm being all nitpicky here anyway, `Math.Round` would look a bit prettier than adding 0.5 to everything. :)
MusiGenesis
@MusiGenesis: Maybe true, but don't forget that it uses bankers rounding as the default. Also have a look at http://www.docjar.com/html/api/java/awt/Color.java.html to see how it is done in Java.
0xA3
OK, you win - you have out-nitpicked me. *Banker's rounding* (shakes head). :)
MusiGenesis
Wait, are you saying you shouldn't use Math.Round because Java doesn't?
MusiGenesis
@MusiGenesis: No.
0xA3
DanM
P.S., speaking of nitpicking, if `Color.FromArgb()` only accepts `int` values between 0 and 255, why doesn't it just require `byte` values instead?
DanM
@DanM: good grief, man. If you could actually tell the difference between a bitmap that resulted from casting to int and a bitmap that resulted from rounding (any flavor at all) and then casting to int, you would possess the world's most phenomenal set of eyeballs. So there is absolutely no chance that you could tell the difference between banker's rounding and rounding away from zero.
MusiGenesis
You're right about FromArbg, though. I like how the Intellisense helpfully tells you that valid values are from 0-255, even though the parameters are ints. I have no clue why that method doesn't take byte params. Equally annoying is the fact that the Color class can convert *to* HSB but not *from* HSB.
MusiGenesis
@MusiGenesis,@DanM: I suppose `FromArgb` uses int simply for convenience. All arithmetics in C# is int-based, e.g. the operation `((byte)10) + 1` yields int. If `FromArgb` used byte parameters you would have to cast the values of most calculations to byte yourself and would possibly get an InvalidCastException. `FromArgb` checks inside if the values are ok and then calls a private method taking byte paramters or throws an ArgumentException. So in general you would save a cast and get an exception anyway; however, in the sample above it wouldn't make a difference as one has to cast anyway.
0xA3