using System; using System.Drawing; namespace CodeImp.DoomBuilder.ColorPicker { public static class ColorHandler { // Handle conversions between RGB and HSV // (and Color types, as well). public struct RGB { // All values are between 0 and 255. public int Red; public int Green; public int Blue; public RGB(int R, int G, int B) { Red = R; Green = G; Blue = B; } } public struct HSV { // All values are between 0 and 255. public int Hue; public int Saturation; public int value; public HSV(int H, int S, int V) { Hue = H; Saturation = S; value = V; } public override string ToString() { return String.Format("({0}, {1}, {2})", Hue, Saturation, value); } } public static RGB HSVtoRGB(int H, int S, int V) { // H, S, and V must all be between 0 and 255. return HSVtoRGB(new HSV(H, S, V)); } public static Color HSVtoColor(HSV hsv) { RGB RGB = HSVtoRGB(hsv); return Color.FromArgb(RGB.Red, RGB.Green, RGB.Blue); } public static Color HSVtoColor(int H, int S, int V) { return HSVtoColor(new HSV(H, S, V)); } public static Color RGBtoColor(RGB rgb) { return Color.FromArgb(rgb.Red, rgb.Green, rgb.Blue); } public static RGB HSVtoRGB(HSV HSV) { // HSV contains values scaled as in the color wheel: // that is, all from 0 to 255. // for( this code to work, HSV.Hue needs // to be scaled from 0 to 360 (it//s the angle of the selected // point within the circle). HSV.Saturation and HSV.value must be // scaled to be between 0 and 1. float r = 0; float g = 0; float b = 0; // Scale Hue to be between 0 and 360. Saturation // and value scale to be between 0 and 1. float h = ((float)HSV.Hue / 255 * 360) % 360; float s = (float)HSV.Saturation / 255; float v = (float)HSV.value / 255; if(s == 0) { // If s is 0, all colors are the same. // This is some flavor of gray. r = v; g = v; b = v; } else { // The color wheel consists of 6 sectors. // Figure out which sector you//re in. float sectorPos = h / 60; int sectorNumber = (int)(Math.Floor(sectorPos)); // get the fractional part of the sector. // That is, how many degrees into the sector // are you? float fractionalSector = sectorPos - sectorNumber; // Calculate values for the three axes // of the color. float p = v * (1 - s); float q = v * (1 - (s * fractionalSector)); float t = v * (1 - (s * (1 - fractionalSector))); // Assign the fractional colors to r, g, and b // based on the sector the angle is in. switch(sectorNumber) { 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; } } // return an RGB structure, with values scaled // to be between 0 and 255. return new RGB((int)(r * 255), (int)(g * 255), (int)(b * 255)); } public static HSV RGBtoHSV(RGB RGB) { // In this function, R, G, and B values must be scaled // to be between 0 and 1. // HSV.Hue will be a value between 0 and 360, and // HSV.Saturation and value are between 0 and 1. // The code must scale these to be between 0 and 255 for // the purposes of this application. float r = (float)RGB.Red / 255; float g = (float)RGB.Green / 255; float b = (float)RGB.Blue / 255; float h, s, v; float min = Math.Min(Math.Min(r, g), b); float max = Math.Max(Math.Max(r, g), b); v = max; float delta = max - min; if(max == 0 || delta == 0) { // R, G, and B must be 0, or all the same. In this case, S is 0, and H is undefined. // Using H = 0 is as good as any... s = 0; h = 0; } else { s = delta / max; if(r == max) { // Between Yellow and Magenta h = (g - b) / delta; } else if(g == max) { // Between Cyan and Yellow h = 2 + (b - r) / delta; } else { // Between Magenta and Cyan h = 4 + (r - g) / delta; } } // Scale h to be between 0 and 360. // This may require adding 360, if the value is negative. h *= 60; if(h < 0) h += 360; // Scale to the requirements of this application. All values are between 0 and 255. return new HSV((int)(h / 360 * 255), (int)(s * 255), (int)(v * 255)); } } }