Here’s an an example from the MyPaint digital painting application.
And another one from Corel Painter 12
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.
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 aneducational 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(); } } }
完整的源代码如下
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace DemoDrawColorWheelBitmap { internal class Program { private static void Main(string[] args) { draw2(); } private static void draw2() { string output_filename = "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); // HSV -> RGB const double sat = 1.0; const double val = 1.0; c = HSVToRGB2(hue, sat, val); // HSL -> RGB // const double sat = 1.0; // const double light = 0.5; // c = HSLToRGB2(hue, sat, light); bmp.SetPixel(x, y, c); } } } bmp.Save(output_filename); } } 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(); R = V; G = t; B = p; break; } } } public static System.Drawing.Color HSVToRGB2(double H, double S, double V) { double dr, dg, db; HSVToRGB(H, S, V, out dr, out dg, out db); var c = System.Drawing.Color.FromArgb((int)(dr * 255), (int)(dg * 255), (int)(db * 255)); return c; } } }
转载自:http://viziblr.com/news/2011/12/1/drawing-a-color-hue-wheel-with-c.html