ColorWheel.cs 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. using System;
  2. using System.Drawing;
  3. using System.Drawing.Drawing2D;
  4. using System.Drawing.Imaging;
  5. using System.Windows.Forms;
  6. namespace PaintDotNet
  7. {
  8. internal class ColorWheel : UserControl
  9. {
  10. /// <summary>
  11. /// Required designer variable.
  12. /// </summary>
  13. private System.ComponentModel.Container components = null;
  14. private Bitmap renderBitmap = null;
  15. private bool tracking = false;
  16. private Point lastMouseXY;
  17. // this number controls what you might call the tesselation of the color wheel. higher #'s = slower, lower #'s = looks worse
  18. private const int colorTesselation = 60;
  19. private PictureBox wheelPictureBox;
  20. private HsvColor hsvColor;
  21. public HsvColor HsvColor
  22. {
  23. get
  24. {
  25. return hsvColor;
  26. }
  27. set
  28. {
  29. if (hsvColor != value)
  30. {
  31. HsvColor oldColor = hsvColor;
  32. hsvColor = value;
  33. this.OnColorChanged();
  34. Refresh();
  35. }
  36. }
  37. }
  38. public ColorWheel()
  39. {
  40. // This call is required by the Windows.Forms Form Designer.
  41. InitializeComponent();
  42. //wheelRegion = new PdnRegion();
  43. hsvColor = new HsvColor(0, 0, 0);
  44. }
  45. private static PointF SphericalToCartesian(float r, float theta)
  46. {
  47. float x;
  48. float y;
  49. x = r * (float)Math.Cos(theta);
  50. y = r * (float)Math.Sin(theta);
  51. return new PointF(x, y);
  52. }
  53. private static PointF[] GetCirclePoints(float r, PointF center)
  54. {
  55. PointF[] points = new PointF[colorTesselation];
  56. for (int i = 0; i < colorTesselation; i++)
  57. {
  58. float theta = ((float)i / (float)colorTesselation) * 2 * (float)Math.PI;
  59. points[i] = SphericalToCartesian(r, theta);
  60. points[i].X += center.X;
  61. points[i].Y += center.Y;
  62. }
  63. return points;
  64. }
  65. private Color[] GetColors()
  66. {
  67. Color[] colors = new Color[colorTesselation];
  68. for (int i = 0; i < colorTesselation; i++)
  69. {
  70. int hue = (i * 360) / colorTesselation;
  71. colors[i] = new HsvColor(hue, 100, 100).ToColor();
  72. }
  73. return colors;
  74. }
  75. protected override void OnLoad(EventArgs e)
  76. {
  77. InitRendering();
  78. base.OnLoad(e);
  79. }
  80. protected override void OnPaint(PaintEventArgs e)
  81. {
  82. InitRendering();
  83. base.OnPaint(e);
  84. }
  85. private void InitRendering()
  86. {
  87. if (this.renderBitmap == null)
  88. {
  89. InitRenderSurface();
  90. this.wheelPictureBox.SizeMode = PictureBoxSizeMode.StretchImage;
  91. int size = (int)Math.Ceiling(ComputeDiameter(this.Size));
  92. this.wheelPictureBox.Size = new Size(size, size);
  93. this.wheelPictureBox.Image = this.renderBitmap;
  94. }
  95. }
  96. private void WheelPictureBox_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
  97. {
  98. float radius = ComputeRadius(Size);
  99. float theta = ((float)HsvColor.Hue / 360.0f) * 2.0f * (float)Math.PI;
  100. float alpha = ((float)HsvColor.Saturation / 100.0f);
  101. float x = (alpha * (radius - 1) * (float)Math.Cos(theta)) + radius;
  102. float y = (alpha * (radius - 1) * (float)Math.Sin(theta)) + radius;
  103. int ix = (int)x;
  104. int iy = (int)y;
  105. // Draw the 'target rectangle'
  106. GraphicsContainer container = e.Graphics.BeginContainer();
  107. e.Graphics.PixelOffsetMode = PixelOffsetMode.None;
  108. e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
  109. e.Graphics.DrawRectangle(Pens.Black, ix - 1, iy - 1, 3, 3);
  110. e.Graphics.DrawRectangle(Pens.White, ix, iy, 1, 1);
  111. e.Graphics.EndContainer(container);
  112. }
  113. private void InitRenderSurface()
  114. {
  115. if (renderBitmap != null)
  116. {
  117. renderBitmap.Dispose();
  118. }
  119. int wheelDiameter = (int)ComputeDiameter(Size);
  120. renderBitmap = new Bitmap(Math.Max(1, (wheelDiameter * 4) / 3),
  121. Math.Max(1, (wheelDiameter * 4) / 3), PixelFormat.Format24bppRgb);
  122. using (Graphics g1 = Graphics.FromImage(renderBitmap))
  123. {
  124. g1.Clear(this.BackColor);
  125. DrawWheel(g1, renderBitmap.Width, renderBitmap.Height);
  126. }
  127. }
  128. private void DrawWheel(Graphics g, int width, int height)
  129. {
  130. float radius = ComputeRadius(new Size(width, height));
  131. PointF[] points = GetCirclePoints(Math.Max(1.0f, (float)radius - 1), new PointF(radius, radius));
  132. using (PathGradientBrush pgb = new PathGradientBrush(points))
  133. {
  134. pgb.CenterColor = new HsvColor(0, 0, 100).ToColor();
  135. pgb.CenterPoint = new PointF(radius, radius);
  136. pgb.SurroundColors = GetColors();
  137. g.FillEllipse(pgb, 0, 0, radius * 2, radius * 2);
  138. }
  139. }
  140. private static float ComputeRadius(Size size)
  141. {
  142. return Math.Min((float)size.Width / 2, (float)size.Height / 2);
  143. }
  144. private static float ComputeDiameter(Size size)
  145. {
  146. return Math.Min((float)size.Width, (float)size.Height);
  147. }
  148. protected override void OnResize(EventArgs e)
  149. {
  150. base.OnResize(e);
  151. if (renderBitmap != null && (ComputeRadius(Size) != ComputeRadius(renderBitmap.Size)))
  152. {
  153. renderBitmap.Dispose();
  154. renderBitmap = null;
  155. }
  156. Invalidate();
  157. }
  158. public event EventHandler ColorChanged;
  159. protected virtual void OnColorChanged()
  160. {
  161. if (ColorChanged != null)
  162. {
  163. ColorChanged(this, EventArgs.Empty);
  164. }
  165. }
  166. private void GrabColor(Point mouseXY)
  167. {
  168. // center our coordinate system so the middle is (0,0), and positive Y is facing up
  169. int cx = mouseXY.X - (Width / 2);
  170. int cy = mouseXY.Y - (Height / 2);
  171. double theta = Math.Atan2(cy, cx);
  172. if (theta < 0)
  173. {
  174. theta += 2 * Math.PI;
  175. }
  176. double alpha = Math.Sqrt((cx * cx) + (cy * cy));
  177. int h = (int)((theta / (Math.PI * 2)) * 360.0);
  178. int s = (int)Math.Min(100.0, (alpha / (double)(Width / 2)) * 100);
  179. int v = 100;
  180. hsvColor = new HsvColor(h, s, v);
  181. OnColorChanged();
  182. Invalidate(true);
  183. }
  184. protected override void OnMouseDown(MouseEventArgs e)
  185. {
  186. base.OnMouseDown(e);
  187. if (e.Button == MouseButtons.Left)
  188. {
  189. tracking = true;
  190. }
  191. }
  192. protected override void OnMouseUp(MouseEventArgs e)
  193. {
  194. base.OnMouseUp(e);
  195. if (tracking)
  196. {
  197. GrabColor(new Point(e.X, e.Y));
  198. }
  199. tracking = false;
  200. }
  201. protected override void OnMouseMove(MouseEventArgs e)
  202. {
  203. base.OnMouseMove(e);
  204. lastMouseXY = new Point(e.X, e.Y);
  205. if (tracking)
  206. {
  207. GrabColor(new Point(e.X, e.Y));
  208. }
  209. }
  210. /// <summary>
  211. /// Clean up any resources being used.
  212. /// </summary>
  213. protected override void Dispose(bool disposing)
  214. {
  215. if (disposing)
  216. {
  217. if (components != null)
  218. {
  219. components.Dispose();
  220. components = null;
  221. }
  222. }
  223. base.Dispose(disposing);
  224. }
  225. #region Component Designer generated code
  226. /// <summary>
  227. /// Required method for Designer support - do not modify
  228. /// the contents of this method with the code editor.
  229. /// </summary>
  230. private void InitializeComponent()
  231. {
  232. this.wheelPictureBox = new System.Windows.Forms.PictureBox();
  233. this.SuspendLayout();
  234. //
  235. // wheelPictureBox
  236. //
  237. this.wheelPictureBox.Location = new System.Drawing.Point(0, 0);
  238. this.wheelPictureBox.Name = "wheelPictureBox";
  239. this.wheelPictureBox.TabIndex = 0;
  240. this.wheelPictureBox.TabStop = false;
  241. this.wheelPictureBox.Click += new System.EventHandler(this.wheelPictureBox_Click);
  242. this.wheelPictureBox.Paint += new System.Windows.Forms.PaintEventHandler(this.WheelPictureBox_Paint);
  243. this.wheelPictureBox.MouseUp += new System.Windows.Forms.MouseEventHandler(this.wheelPictureBox_MouseUp);
  244. this.wheelPictureBox.MouseMove += new System.Windows.Forms.MouseEventHandler(this.wheelPictureBox_MouseMove);
  245. this.wheelPictureBox.MouseDown += new System.Windows.Forms.MouseEventHandler(this.wheelPictureBox_MouseDown);
  246. //
  247. // ColorWheel
  248. //
  249. this.Controls.Add(this.wheelPictureBox);
  250. this.Name = "ColorWheel";
  251. this.ResumeLayout(false);
  252. }
  253. #endregion
  254. private void wheelPictureBox_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
  255. {
  256. OnMouseMove(e);
  257. }
  258. private void wheelPictureBox_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
  259. {
  260. OnMouseUp(e);
  261. }
  262. private void wheelPictureBox_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
  263. {
  264. OnMouseDown(e);
  265. }
  266. private void wheelPictureBox_Click(object sender, System.EventArgs e)
  267. {
  268. OnClick(e);
  269. }
  270. }
  271. }