using System;
using System.Windows.Media;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
namespace Microsoft.Research.DynamicDataDisplay
{
///
/// Represents color in Hue Saturation Brightness color space.
///
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Hsb")]
[DebuggerDisplay("HSBColor A={Alpha} H={Hue} S={Saturation} B={Brightness}")]
public struct HsbColor
{
private double hue;
private double saturation;
private double brightness;
private double alpha;
/// Hue; [0, 360]
public double Hue
{
get { return hue; }
set
{
if (value < 0)
value = 360 - value;
hue = value % 360;
}
}
/// Saturation; [0, 1]
public double Saturation
{
get { return saturation; }
set { saturation = value; }
}
/// Brightness; [0, 1]
public double Brightness
{
get { return brightness; }
set { brightness = value; }
}
/// Alpha; [0, 1]
public double Alpha
{
get { return alpha; }
set { alpha = value; }
}
///
/// Initializes a new instance of the struct.
///
/// The hue; [0; 360]
/// The saturation; [0, 1]
/// The brightness; [0, 1]
public HsbColor(double hue, double saturation, double brightness)
{
this.hue = hue;
this.saturation = saturation;
this.brightness = brightness;
alpha = 1;
}
///
/// Initializes a new instance of the struct.
///
/// The hue; [0, 360]
/// The saturation; [0, 1]
/// The brightness; [0, 1]
/// The alpha; [0, 1]
public HsbColor(double hue, double saturation, double brightness, double alpha)
{
this.hue = hue;
this.saturation = saturation;
this.brightness = brightness;
this.alpha = alpha;
}
///
/// Creates HSBColor from the ARGB color.
///
/// The color.
///
public static HsbColor FromArgbColor(Color color)
{
double limit255 = 255;
double r = color.R / limit255;
double g = color.G / limit255;
double b = color.B / limit255;
double max = Math.Max(Math.Max(r, g), b);
double min = Math.Min(Math.Min(r, g), b);
double len = max - min;
double brightness = max; // 0.5 * (max + min);
double sat;
double hue;
if (max == 0 || len == 0)
{
sat = hue = 0;
}
else
{
sat = len / max;
if (r == max)
{
hue = (g - b) / len;
}
else if (g == max)
{
hue = 2 + (b - r) / len;
}
else
{
hue = 4 + (r - g) / len;
}
}
hue *= 60;
if (hue < 0)
hue += 360;
HsbColor res = new HsbColor();
res.hue = hue;
res.saturation = sat;
res.brightness = brightness;
res.alpha = color.A / limit255;
return res;
}
public static HsbColor FromArgb(int argb)
{
byte a = (byte)(argb >> 24);
byte r = (byte)((argb >> 16) & 0xFF);
byte g = (byte)((argb >> 8) & 0xFF);
byte b = (byte)(argb & 0xFF);
return FromArgbColor(Color.FromArgb(a, r, g, b));
}
///
/// Converts HSBColor to ARGB color space.
///
///
public Color ToArgbColor()
{
double r = 0.0;
double g = 0.0;
double b = 0.0;
double hue = this.hue % 360.0;
if (saturation == 0.0)
{
r = g = b = brightness;
}
else
{
double smallHue = hue / 60.0;
int smallHueInt = (int)Math.Floor(smallHue);
double smallHueFrac = smallHue - smallHueInt;
double val1 = brightness * (1.0 - saturation);
double val2 = brightness * (1.0 - (saturation * smallHueFrac));
double val3 = brightness * (1.0 - (saturation * (1.0 - smallHueFrac)));
switch (smallHueInt)
{
case 0:
r = brightness;
g = val3;
b = val1;
break;
case 1:
r = val2;
g = brightness;
b = val1;
break;
case 2:
r = val1;
g = brightness;
b = val3;
break;
case 3:
r = val1;
g = val2;
b = brightness;
break;
case 4:
r = val3;
g = val1;
b = brightness;
break;
case 5:
r = brightness;
g = val1;
b = val2;
break;
}
}
return Color.FromArgb(
(byte)(Math.Round(alpha * 255)),
(byte)(Math.Round(r * 255)),
(byte)(Math.Round(g * 255)),
(byte)(Math.Round(b * 255)));
}
public int ToArgb()
{
return ToArgbColor().ToArgb();
}
///
/// Determines whether the specified is equal to this instance.
///
/// The to compare with this instance.
///
/// true if the specified is equal to this instance; otherwise, false.
///
public override bool Equals(object obj)
{
if (obj is HsbColor)
{
HsbColor c = (HsbColor)obj;
return (c.alpha == alpha &&
c.brightness == brightness &&
c.hue == hue &&
c.saturation == saturation);
}
else
return false;
}
///
/// Returns a hash code for this instance.
///
///
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
///
public override int GetHashCode()
{
return alpha.GetHashCode() ^
brightness.GetHashCode() ^
hue.GetHashCode() ^
saturation.GetHashCode();
}
///
/// Implements the operator ==.
///
/// The first.
/// The second.
/// The result of the operator.
public static bool operator ==(HsbColor first, HsbColor second)
{
return (first.alpha == second.alpha &&
first.brightness == second.brightness &&
first.hue == second.hue &&
first.saturation == second.saturation);
}
///
/// Implements the operator !=.
///
/// The first.
/// The second.
/// The result of the operator.
public static bool operator !=(HsbColor first, HsbColor second)
{
return (first.alpha != second.alpha ||
first.brightness != second.brightness ||
first.hue != second.hue ||
first.saturation != second.saturation);
}
}
public static class ColorExtensions
{
///
/// Converts the ARGB color to the HSB color.
///
/// The color.
///
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Hsb")]
public static HsbColor ToHsbColor(this Color color)
{
return HsbColor.FromArgbColor(color);
}
}
}