HsvColor.cs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. using System;
  2. using System.Drawing;
  3. namespace PaintDotNet
  4. {
  5. /// <summary>
  6. /// Adapted from:
  7. /// "A Primer on Building a Color Picker User Control with GDI+ in Visual Basic .NET or C#"
  8. /// http://www.msdnaa.net/Resources/display.aspx?ResID=2460
  9. /// </summary>
  10. public struct HsvColor
  11. {
  12. public int Hue; // 0-360
  13. public int Saturation; // 0-100
  14. public int Value; // 0-100
  15. public static bool operator ==(HsvColor lhs, HsvColor rhs)
  16. {
  17. if ((lhs.Hue == rhs.Hue) &&
  18. (lhs.Saturation == rhs.Saturation) &&
  19. (lhs.Value == rhs.Value))
  20. {
  21. return true;
  22. }
  23. else
  24. {
  25. return false;
  26. }
  27. }
  28. public static bool operator !=(HsvColor lhs, HsvColor rhs)
  29. {
  30. return !(lhs == rhs);
  31. }
  32. public override bool Equals(object obj)
  33. {
  34. return this == (HsvColor)obj;
  35. }
  36. public override int GetHashCode()
  37. {
  38. return (Hue + (Saturation << 8) + (Value << 16)).GetHashCode(); ;
  39. }
  40. public HsvColor(int hue, int saturation, int value)
  41. {
  42. if (hue < 0 || hue > 360)
  43. {
  44. throw new ArgumentOutOfRangeException("hue", "must be in the range [0, 360]");
  45. }
  46. if (saturation < 0 || saturation > 100)
  47. {
  48. throw new ArgumentOutOfRangeException("saturation", "must be in the range [0, 100]");
  49. }
  50. if (value < 0 || value > 100)
  51. {
  52. throw new ArgumentOutOfRangeException("value", "must be in the range [0, 100]");
  53. }
  54. Hue = hue;
  55. Saturation = saturation;
  56. Value = value;
  57. }
  58. public static HsvColor FromColor(Color color)
  59. {
  60. RgbColor rgb = new RgbColor(color.R, color.G, color.B);
  61. return rgb.ToHsv();
  62. }
  63. public Color ToColor()
  64. {
  65. RgbColor rgb = ToRgb();
  66. return Color.FromArgb(rgb.Red, rgb.Green, rgb.Blue);
  67. }
  68. public RgbColor ToRgb()
  69. {
  70. // HsvColor contains values scaled as in the color wheel:
  71. double h;
  72. double s;
  73. double v;
  74. double r = 0;
  75. double g = 0;
  76. double b = 0;
  77. // Scale Hue to be between 0 and 360. Saturation
  78. // and value scale to be between 0 and 1.
  79. h = (double)Hue % 360;
  80. s = (double)Saturation / 100;
  81. v = (double)Value / 100;
  82. if (s == 0)
  83. {
  84. // If s is 0, all colors are the same.
  85. // This is some flavor of gray.
  86. r = v;
  87. g = v;
  88. b = v;
  89. }
  90. else
  91. {
  92. double p;
  93. double q;
  94. double t;
  95. double fractionalSector;
  96. int sectorNumber;
  97. double sectorPos;
  98. // The color wheel consists of 6 sectors.
  99. // Figure out which sector you're in.
  100. sectorPos = h / 60;
  101. sectorNumber = (int)(Math.Floor(sectorPos));
  102. // get the fractional part of the sector.
  103. // That is, how many degrees into the sector
  104. // are you?
  105. fractionalSector = sectorPos - sectorNumber;
  106. // Calculate values for the three axes
  107. // of the color.
  108. p = v * (1 - s);
  109. q = v * (1 - (s * fractionalSector));
  110. t = v * (1 - (s * (1 - fractionalSector)));
  111. // Assign the fractional colors to r, g, and b
  112. // based on the sector the angle is in.
  113. switch (sectorNumber)
  114. {
  115. case 0:
  116. r = v;
  117. g = t;
  118. b = p;
  119. break;
  120. case 1:
  121. r = q;
  122. g = v;
  123. b = p;
  124. break;
  125. case 2:
  126. r = p;
  127. g = v;
  128. b = t;
  129. break;
  130. case 3:
  131. r = p;
  132. g = q;
  133. b = v;
  134. break;
  135. case 4:
  136. r = t;
  137. g = p;
  138. b = v;
  139. break;
  140. case 5:
  141. r = v;
  142. g = p;
  143. b = q;
  144. break;
  145. }
  146. }
  147. // return an RgbColor structure, with values scaled
  148. // to be between 0 and 255.
  149. return new RgbColor((int)(r * 255), (int)(g * 255), (int)(b * 255));
  150. }
  151. public override string ToString()
  152. {
  153. return String.Format("({0}, {1}, {2})", Hue, Saturation, Value);
  154. }
  155. }
  156. }