« Test Post (Ignore) | Main | Hands-On with with Freemake Video Downloader 3.0 »
Thursday
Dec012011

Drawing a Color Hue Wheel with C#

You’ve see the interface before. It’s a thick ring with a smooth rainbow of colors.

Here’s an an example from the MyPaint digital painting application.

image

And another one from Corel Painter 12

image

Notice how they differ in terms of where there colors are on the wheel and the direction of the colors.

 

Yesterday I was planning a few blog posts about RGB colors and realized I needed to create one of these. And rather than re-using one from an existing application, I thought I’d would be a good learning exercise to build my own. So, below is the one I created using a little program I wrote.

 

colorwheel

 

GET SOURCE CODE

HOW IT WORKS

The code below is not efficient. It certainly is not optimized. And I admit there are better ways of doing it. I intended the code as an educational sample that illustrates basic concepts. Now that I think about it, this is a good coding question I should ask the next time I interview someone for a position at Microsoft.

It works like this: a bitmap is created to hold a circular of some width – defined by an inner radius and and outer radius. A loop goes through each coordinate in the bitmap; if the coordinate is within the ring then color is computed based on the coordinate and drawn. If the coordinate is outside the ring, a simple white pixel is drawn.

Computing the color from the coordinate requires first finding the angle produced between the center of the ring and the current coordinate. This angle can range from –pi to pi. The hue is computed from the angle by normalizing the angle to a range of 0.0 to 1.0.

Then the RGB value is constructed from a Hue, Saturation, and Value  - where Saturation is 1.0 and Value is 1.0.

-----



string output_filename = "D:\\colorwheel.png";

int padding = 10;
int inner_radius = 200;
int outer_radius = inner_radius + 50;

int bmp_width = (2 * outer_radius) + (2 * padding);
int bmp_height = bmp_width;

var center = new System.Drawing.Point(bmp_width / 2, bmp_height / 2);
var c = System.Drawing.Color.Red;

using (var bmp = new System.Drawing.Bitmap(bmp_width, bmp_height))
{
    using (var g = System.Drawing.Graphics.FromImage(bmp))
    {
        g.FillRectangle(System.Drawing.Brushes.White, 0, 0, bmp.Width, bmp.Height);
    }
    for (int y = 0; y < bmp_width; y++)
    {
        int dy = (center.Y - y);

        for (int x = 0; x < bmp_width; x++)
        {
            int dx = (center.X - x);

            double dist = System.Math.Sqrt(dx * dx + dy * dy);


            if (dist >= inner_radius && dist <= outer_radius)
            {
                double theta = System.Math.Atan2(dy, dx);
                // theta can go from -pi to pi

                double hue = (theta + System.Math.PI) / (2 * System.Math.PI);

                double dr, dg, db;
                const double sat = 1.0;
                const double val = 1.0;
                HSVToRGB(hue, sat, val, out dr, out dg, out db);
                c = System.Drawing.Color.FromArgb((int)(dr * 255), (int)(dg * 255), (int)(db * 255));

                bmp.SetPixel(x, y, c);
            }
        }
    }
    bmp.Save(output_filename);

----

 

The code to convert HSV to RGB is below. Note that all the values for input and output should be in the range of 0 to 1.0.

-----

public static void HSVToRGB(double H, double S, double V, out double R, out double G, out double B)
{
    if (H == 1.0)
    {
        H = 0.0;
    }

    double step = 1.0/6.0;
    double vh = H/step;

    int i = (int) System.Math.Floor(vh);

    double f = vh - i;
    double p = V*(1.0 - S);
    double q = V*(1.0 - (S*f));
    double t = V*(1.0 - (S*(1.0 - f)));

    switch (i)
    {
        case 0:
            {
                R = V;
                G = t;
                B = p;
                break;
            }
        case 1:
            {
                R = q;
                G = V;
                B = p;
                break;
            }
        case 2:
            {
                R = p;
                G = V;
                B = t;
                break;
            }
        case 3:
            {
                R = p;
                G = q;
                B = V;
                break;
            }
        case 4:
            {
                R = t;
                G = p;
                B = V;
                break;
            }
        case 5:
            {
                R = V;
                G = p;
                B = q;
                break;
            }
        default: 
            {
                // not possible - if we get here it is an internal error
                throw new ArgumentException();
            }
    }
}

----

PrintView Printer Friendly Version

EmailEmail Article to Friend

Reader Comments (4)

Hi Saveen

I went looking for the code that you pointed to but couldnt..could you post the full code here or see if it still is on the link?

Thanks

January 28, 2012 | Unregistered CommenterSamarth

@Samarth

Here is a link to the zip file: https://skydrive.live.com/redir.aspx?cid=1ff099edb1c7ebfa&resid=1FF099EDB1C7EBFA!1559&parid=1FF099EDB1C7EBFA!1551

January 28, 2012 | Registered Commentersaveenr

Thank you!

January 28, 2012 | Unregistered CommenterSamarth

Thanks very much, this HSVToRGB function is exactly what I needed :)

October 5, 2012 | Unregistered CommenterTheXenocide

PostPost a New Comment

Enter your information below to add a new comment.
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>