1 | using System;
|
---|
2 | using System.Windows.Media;
|
---|
3 | using System.Diagnostics;
|
---|
4 | using System.Diagnostics.CodeAnalysis;
|
---|
5 |
|
---|
6 | namespace Microsoft.Research.DynamicDataDisplay
|
---|
7 | {
|
---|
8 | /// <summary>
|
---|
9 | /// Represents color in Hue Saturation Brightness color space.
|
---|
10 | /// </summary>
|
---|
11 | [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Hsb")]
|
---|
12 | [DebuggerDisplay("HSBColor A={Alpha} H={Hue} S={Saturation} B={Brightness}")]
|
---|
13 | public struct HsbColor
|
---|
14 | {
|
---|
15 | private double hue;
|
---|
16 | private double saturation;
|
---|
17 | private double brightness;
|
---|
18 | private double alpha;
|
---|
19 |
|
---|
20 | /// <summary>Hue; [0, 360]</summary>
|
---|
21 | public double Hue
|
---|
22 | {
|
---|
23 | get { return hue; }
|
---|
24 | set
|
---|
25 | {
|
---|
26 | if (value < 0)
|
---|
27 | value = 360 - value;
|
---|
28 |
|
---|
29 | hue = value % 360;
|
---|
30 | }
|
---|
31 | }
|
---|
32 |
|
---|
33 | /// <summary>Saturation; [0, 1]</summary>
|
---|
34 | public double Saturation
|
---|
35 | {
|
---|
36 | get { return saturation; }
|
---|
37 | set { saturation = value; }
|
---|
38 | }
|
---|
39 |
|
---|
40 | /// <summary>Brightness; [0, 1]</summary>
|
---|
41 | public double Brightness
|
---|
42 | {
|
---|
43 | get { return brightness; }
|
---|
44 | set { brightness = value; }
|
---|
45 | }
|
---|
46 |
|
---|
47 | /// <summary>Alpha; [0, 1]</summary>
|
---|
48 | public double Alpha
|
---|
49 | {
|
---|
50 | get { return alpha; }
|
---|
51 | set { alpha = value; }
|
---|
52 | }
|
---|
53 |
|
---|
54 | /// <summary>
|
---|
55 | /// Initializes a new instance of the <see cref="HSBColor"/> struct.
|
---|
56 | /// </summary>
|
---|
57 | /// <param name="hue">The hue; [0; 360]</param>
|
---|
58 | /// <param name="saturation">The saturation; [0, 1]</param>
|
---|
59 | /// <param name="brightness">The brightness; [0, 1]</param>
|
---|
60 | public HsbColor(double hue, double saturation, double brightness)
|
---|
61 | {
|
---|
62 | this.hue = hue;
|
---|
63 | this.saturation = saturation;
|
---|
64 | this.brightness = brightness;
|
---|
65 | alpha = 1;
|
---|
66 | }
|
---|
67 |
|
---|
68 | /// <summary>
|
---|
69 | /// Initializes a new instance of the <see cref="HSBColor"/> struct.
|
---|
70 | /// </summary>
|
---|
71 | /// <param name="hue">The hue; [0, 360]</param>
|
---|
72 | /// <param name="saturation">The saturation; [0, 1]</param>
|
---|
73 | /// <param name="brightness">The brightness; [0, 1]</param>
|
---|
74 | /// <param name="alpha">The alpha; [0, 1]</param>
|
---|
75 | public HsbColor(double hue, double saturation, double brightness, double alpha)
|
---|
76 | {
|
---|
77 | this.hue = hue;
|
---|
78 | this.saturation = saturation;
|
---|
79 | this.brightness = brightness;
|
---|
80 | this.alpha = alpha;
|
---|
81 | }
|
---|
82 |
|
---|
83 | /// <summary>
|
---|
84 | /// Creates HSBColor from the ARGB color.
|
---|
85 | /// </summary>
|
---|
86 | /// <param name="color">The color.</param>
|
---|
87 | /// <returns></returns>
|
---|
88 | public static HsbColor FromArgbColor(Color color)
|
---|
89 | {
|
---|
90 | double limit255 = 255;
|
---|
91 |
|
---|
92 | double r = color.R / limit255;
|
---|
93 | double g = color.G / limit255;
|
---|
94 | double b = color.B / limit255;
|
---|
95 |
|
---|
96 | double max = Math.Max(Math.Max(r, g), b);
|
---|
97 | double min = Math.Min(Math.Min(r, g), b);
|
---|
98 |
|
---|
99 | double len = max - min;
|
---|
100 |
|
---|
101 | double brightness = max; // 0.5 * (max + min);
|
---|
102 | double sat;
|
---|
103 | double hue;
|
---|
104 |
|
---|
105 |
|
---|
106 | if (max == 0 || len == 0)
|
---|
107 | {
|
---|
108 | sat = hue = 0;
|
---|
109 | }
|
---|
110 | else
|
---|
111 | {
|
---|
112 | sat = len / max;
|
---|
113 | if (r == max)
|
---|
114 | {
|
---|
115 | hue = (g - b) / len;
|
---|
116 | }
|
---|
117 | else if (g == max)
|
---|
118 | {
|
---|
119 | hue = 2 + (b - r) / len;
|
---|
120 | }
|
---|
121 | else
|
---|
122 | {
|
---|
123 | hue = 4 + (r - g) / len;
|
---|
124 | }
|
---|
125 | }
|
---|
126 |
|
---|
127 | hue *= 60;
|
---|
128 | if (hue < 0)
|
---|
129 | hue += 360;
|
---|
130 |
|
---|
131 |
|
---|
132 | HsbColor res = new HsbColor();
|
---|
133 | res.hue = hue;
|
---|
134 | res.saturation = sat;
|
---|
135 | res.brightness = brightness;
|
---|
136 | res.alpha = color.A / limit255;
|
---|
137 | return res;
|
---|
138 | }
|
---|
139 |
|
---|
140 | public static HsbColor FromArgb(int argb)
|
---|
141 | {
|
---|
142 | byte a = (byte)(argb >> 24);
|
---|
143 | byte r = (byte)((argb >> 16) & 0xFF);
|
---|
144 | byte g = (byte)((argb >> 8) & 0xFF);
|
---|
145 | byte b = (byte)(argb & 0xFF);
|
---|
146 | return FromArgbColor(Color.FromArgb(a, r, g, b));
|
---|
147 | }
|
---|
148 |
|
---|
149 | /// <summary>
|
---|
150 | /// Converts HSBColor to ARGB color space.
|
---|
151 | /// </summary>
|
---|
152 | /// <returns></returns>
|
---|
153 | public Color ToArgbColor()
|
---|
154 | {
|
---|
155 | double r = 0.0;
|
---|
156 | double g = 0.0;
|
---|
157 | double b = 0.0;
|
---|
158 | double hue = this.hue % 360.0;
|
---|
159 | if (saturation == 0.0)
|
---|
160 | {
|
---|
161 | r = g = b = brightness;
|
---|
162 | }
|
---|
163 | else
|
---|
164 | {
|
---|
165 | double smallHue = hue / 60.0;
|
---|
166 | int smallHueInt = (int)Math.Floor(smallHue);
|
---|
167 | double smallHueFrac = smallHue - smallHueInt;
|
---|
168 | double val1 = brightness * (1.0 - saturation);
|
---|
169 | double val2 = brightness * (1.0 - (saturation * smallHueFrac));
|
---|
170 | double val3 = brightness * (1.0 - (saturation * (1.0 - smallHueFrac)));
|
---|
171 | switch (smallHueInt)
|
---|
172 | {
|
---|
173 | case 0:
|
---|
174 | r = brightness;
|
---|
175 | g = val3;
|
---|
176 | b = val1;
|
---|
177 | break;
|
---|
178 |
|
---|
179 | case 1:
|
---|
180 | r = val2;
|
---|
181 | g = brightness;
|
---|
182 | b = val1;
|
---|
183 | break;
|
---|
184 |
|
---|
185 | case 2:
|
---|
186 | r = val1;
|
---|
187 | g = brightness;
|
---|
188 | b = val3;
|
---|
189 | break;
|
---|
190 |
|
---|
191 | case 3:
|
---|
192 | r = val1;
|
---|
193 | g = val2;
|
---|
194 | b = brightness;
|
---|
195 | break;
|
---|
196 |
|
---|
197 | case 4:
|
---|
198 | r = val3;
|
---|
199 | g = val1;
|
---|
200 | b = brightness;
|
---|
201 | break;
|
---|
202 |
|
---|
203 | case 5:
|
---|
204 | r = brightness;
|
---|
205 | g = val1;
|
---|
206 | b = val2;
|
---|
207 | break;
|
---|
208 | }
|
---|
209 | }
|
---|
210 |
|
---|
211 |
|
---|
212 | return Color.FromArgb(
|
---|
213 | (byte)(Math.Round(alpha * 255)),
|
---|
214 | (byte)(Math.Round(r * 255)),
|
---|
215 | (byte)(Math.Round(g * 255)),
|
---|
216 | (byte)(Math.Round(b * 255)));
|
---|
217 | }
|
---|
218 |
|
---|
219 | public int ToArgb()
|
---|
220 | {
|
---|
221 | return ToArgbColor().ToArgb();
|
---|
222 | }
|
---|
223 |
|
---|
224 | /// <summary>
|
---|
225 | /// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
|
---|
226 | /// </summary>
|
---|
227 | /// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
|
---|
228 | /// <returns>
|
---|
229 | /// <c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
|
---|
230 | /// </returns>
|
---|
231 | public override bool Equals(object obj)
|
---|
232 | {
|
---|
233 | if (obj is HsbColor)
|
---|
234 | {
|
---|
235 | HsbColor c = (HsbColor)obj;
|
---|
236 | return (c.alpha == alpha &&
|
---|
237 | c.brightness == brightness &&
|
---|
238 | c.hue == hue &&
|
---|
239 | c.saturation == saturation);
|
---|
240 | }
|
---|
241 | else
|
---|
242 | return false;
|
---|
243 | }
|
---|
244 |
|
---|
245 | /// <summary>
|
---|
246 | /// Returns a hash code for this instance.
|
---|
247 | /// </summary>
|
---|
248 | /// <returns>
|
---|
249 | /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
|
---|
250 | /// </returns>
|
---|
251 | public override int GetHashCode()
|
---|
252 | {
|
---|
253 | return alpha.GetHashCode() ^
|
---|
254 | brightness.GetHashCode() ^
|
---|
255 | hue.GetHashCode() ^
|
---|
256 | saturation.GetHashCode();
|
---|
257 | }
|
---|
258 |
|
---|
259 | /// <summary>
|
---|
260 | /// Implements the operator ==.
|
---|
261 | /// </summary>
|
---|
262 | /// <param name="first">The first.</param>
|
---|
263 | /// <param name="second">The second.</param>
|
---|
264 | /// <returns>The result of the operator.</returns>
|
---|
265 | public static bool operator ==(HsbColor first, HsbColor second)
|
---|
266 | {
|
---|
267 | return (first.alpha == second.alpha &&
|
---|
268 | first.brightness == second.brightness &&
|
---|
269 | first.hue == second.hue &&
|
---|
270 | first.saturation == second.saturation);
|
---|
271 | }
|
---|
272 |
|
---|
273 | /// <summary>
|
---|
274 | /// Implements the operator !=.
|
---|
275 | /// </summary>
|
---|
276 | /// <param name="first">The first.</param>
|
---|
277 | /// <param name="second">The second.</param>
|
---|
278 | /// <returns>The result of the operator.</returns>
|
---|
279 | public static bool operator !=(HsbColor first, HsbColor second)
|
---|
280 | {
|
---|
281 | return (first.alpha != second.alpha ||
|
---|
282 | first.brightness != second.brightness ||
|
---|
283 | first.hue != second.hue ||
|
---|
284 | first.saturation != second.saturation);
|
---|
285 | }
|
---|
286 | }
|
---|
287 |
|
---|
288 | public static class ColorExtensions
|
---|
289 | {
|
---|
290 | /// <summary>
|
---|
291 | /// Converts the ARGB color to the HSB color.
|
---|
292 | /// </summary>
|
---|
293 | /// <param name="color">The color.</param>
|
---|
294 | /// <returns></returns>
|
---|
295 | [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Hsb")]
|
---|
296 | public static HsbColor ToHsbColor(this Color color)
|
---|
297 | {
|
---|
298 | return HsbColor.FromArgbColor(color);
|
---|
299 | }
|
---|
300 | }
|
---|
301 | }
|
---|