Рисование цветового кольца для выбора оттенка на C#.
Данная статья является переводом Saveen Reddy “ Drawing a Color Hue Wheel with C# ”.
Вы уже видели такой интерфейс раньше. Это – кольцо с плавным переходом между всеми цветами радуги.
Ниже – пример из приложения для рисования MyPaint.
И еще один – из Corel Painter 12.
Обратите внимание на различия в расположении цветов и направлении перехода цвета.
Вчера я планировал написать новую статью в блог о цветах RGB и понял, что мне необходимо создать такой интерфейс. И вместо того, чтобы использовать один из уже существующих, я подумал, что будет хорошей тренировкой создать такое самостоятельно. Итак, ниже – то, что у меня получилось при помощи небольшой собственной программы.
Исходный код доступен на CodePlex странице проекта Vizibl: найдите проект под названием DemoDrawColorWheelBitmap в папке Demos. Ниже я объясню как это работает. Данный код не особо отлажен и не оптимизирован, есть масса других способов реализации такого функционала. Я предлагаю использовать этот код для целей обучения, иллюстрирующих основу концепции. (На самом деле я думаю, что это может быть хорошим вопросом на интервью для соискателя на позицию в Майкрософт).
Основа функционала, представленного ниже, состоит в отрисовке круга. Это работает следующим образом: создается битмап для отображения круга определенной ширины, заданной внутренним и внешним радиусами. Цикл проходит по всем координатам этого битмапа. Если координата находится внутри круга, вычисляется значение цвета на основе координаты и смещения. Если же координата не попадает в круг, оставляется белый пиксель.
Вычисление цвета на основе координаты требует нахождения угла между центром круга и текущей координатой. Данный угол находится в интервале между –Π и Π. Оттенок вычисляется из угла нормализацией его значения на интервал от 0.0 до 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);
Затем значение RGB получается из оттенка, насыщенности и константы, где насыщенность равна 1.0 и константа тоже равна 1.0.
Код, приведенный ниже, конвертирует HSV в RGB. Заметьте, что все значения для ввода и вывода находятся в интервале от 0 до 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();
}
}
}