ColorPicker.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Drawing;
  5. using System.Drawing.Drawing2D;
  6. using System.Windows.Forms;
  7. namespace Optimizer.Controls
  8. {
  9. [DefaultProperty("Color")]
  10. [DefaultEvent("ColorChanged")]
  11. public partial class ColorPicker : Control
  12. {
  13. #region Fields
  14. private Brush _brush;
  15. private PointF _centerPoint;
  16. private Color _color;
  17. private int _colorStep;
  18. private bool _dragStartedWithinWheel;
  19. private HslColor _hslColor;
  20. private int _largeChange;
  21. private float _radius;
  22. private int _selectionSize;
  23. private int _smallChange;
  24. private int _updateCount;
  25. #endregion
  26. #region Constructors
  27. public ColorPicker()
  28. {
  29. this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.Selectable | ControlStyles.StandardClick | ControlStyles.StandardDoubleClick, true);
  30. this.Color = Color.Black;
  31. this.ColorStep = 4;
  32. this.SelectionSize = 10;
  33. this.SmallChange = 1;
  34. this.LargeChange = 5;
  35. this.SelectionGlyph = this.CreateSelectionGlyph();
  36. }
  37. #endregion
  38. #region Events
  39. [Category("Property Changed")]
  40. public event EventHandler ColorChanged;
  41. [Category("Property Changed")]
  42. public event EventHandler ColorStepChanged;
  43. [Category("Property Changed")]
  44. public event EventHandler HslColorChanged;
  45. [Category("Property Changed")]
  46. public event EventHandler LargeChangeChanged;
  47. [Category("Property Changed")]
  48. public event EventHandler SelectionSizeChanged;
  49. [Category("Property Changed")]
  50. public event EventHandler SmallChangeChanged;
  51. #endregion
  52. #region Overridden Methods
  53. protected override void Dispose(bool disposing)
  54. {
  55. if (disposing)
  56. {
  57. if (_brush != null)
  58. {
  59. _brush.Dispose();
  60. }
  61. if (this.SelectionGlyph != null)
  62. {
  63. this.SelectionGlyph.Dispose();
  64. }
  65. }
  66. base.Dispose(disposing);
  67. }
  68. protected override bool IsInputKey(Keys keyData)
  69. {
  70. bool result;
  71. if ((keyData & Keys.Left) == Keys.Left || (keyData & Keys.Up) == Keys.Up || (keyData & Keys.Down) == Keys.Down || (keyData & Keys.Right) == Keys.Right || (keyData & Keys.PageUp) == Keys.PageUp || (keyData & Keys.PageDown) == Keys.PageDown)
  72. {
  73. result = true;
  74. }
  75. else
  76. {
  77. result = base.IsInputKey(keyData);
  78. }
  79. return result;
  80. }
  81. protected override void OnGotFocus(EventArgs e)
  82. {
  83. base.OnGotFocus(e);
  84. this.Invalidate();
  85. }
  86. protected override void OnKeyDown(KeyEventArgs e)
  87. {
  88. HslColor color;
  89. double hue;
  90. int step;
  91. color = this.HslColor;
  92. hue = color.H;
  93. step = e.Shift ? this.LargeChange : this.SmallChange;
  94. switch (e.KeyCode)
  95. {
  96. case Keys.Right:
  97. case Keys.Up:
  98. hue += step;
  99. break;
  100. case Keys.Left:
  101. case Keys.Down:
  102. hue -= step;
  103. break;
  104. case Keys.PageUp:
  105. hue += this.LargeChange;
  106. break;
  107. case Keys.PageDown:
  108. hue -= this.LargeChange;
  109. break;
  110. }
  111. if (hue >= 360)
  112. {
  113. hue = 0;
  114. }
  115. if (hue < 0)
  116. {
  117. hue = 359;
  118. }
  119. if (hue != color.H)
  120. {
  121. color.H = hue;
  122. this.LockUpdates = true;
  123. this.Color = color.ToRgbColor();
  124. this.HslColor = color;
  125. this.LockUpdates = false;
  126. e.Handled = true;
  127. }
  128. base.OnKeyDown(e);
  129. }
  130. protected override void OnLostFocus(EventArgs e)
  131. {
  132. base.OnLostFocus(e);
  133. this.Invalidate();
  134. }
  135. protected override void OnMouseDown(MouseEventArgs e)
  136. {
  137. base.OnMouseDown(e);
  138. if (!this.Focused && this.TabStop)
  139. {
  140. this.Focus();
  141. }
  142. if (e.Button == MouseButtons.Left && this.IsPointInWheel(e.Location))
  143. {
  144. _dragStartedWithinWheel = true;
  145. this.SetColor(e.Location);
  146. }
  147. }
  148. protected override void OnMouseMove(MouseEventArgs e)
  149. {
  150. base.OnMouseMove(e);
  151. if (e.Button == MouseButtons.Left && _dragStartedWithinWheel)
  152. {
  153. this.SetColor(e.Location);
  154. }
  155. }
  156. protected override void OnMouseUp(MouseEventArgs e)
  157. {
  158. base.OnMouseUp(e);
  159. _dragStartedWithinWheel = false;
  160. }
  161. protected override void OnPaddingChanged(EventArgs e)
  162. {
  163. base.OnPaddingChanged(e);
  164. this.RefreshWheel();
  165. }
  166. protected override void OnPaint(PaintEventArgs e)
  167. {
  168. base.OnPaint(e);
  169. if (this.AllowPainting)
  170. {
  171. base.OnPaintBackground(e);
  172. if (this.BackgroundImage == null && this.Parent != null && (this.BackColor == this.Parent.BackColor || this.Parent.BackColor.A != 255))
  173. {
  174. ButtonRenderer.DrawParentBackground(e.Graphics, this.DisplayRectangle, this);
  175. }
  176. if (_brush != null)
  177. {
  178. e.Graphics.FillPie(_brush, this.ClientRectangle, 0, 360);
  179. }
  180. e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
  181. using (Pen pen = new Pen(this.BackColor, 2))
  182. {
  183. e.Graphics.DrawEllipse(pen, new RectangleF(_centerPoint.X - _radius, _centerPoint.Y - _radius, _radius * 2, _radius * 2));
  184. }
  185. if (!this.Color.IsEmpty)
  186. {
  187. this.PaintCurrentColor(e);
  188. }
  189. }
  190. }
  191. protected override void OnResize(EventArgs e)
  192. {
  193. base.OnResize(e);
  194. this.RefreshWheel();
  195. }
  196. #endregion
  197. #region Public Properties
  198. [Category("Appearance")]
  199. [DefaultValue(typeof(Color), "Black")]
  200. public virtual Color Color
  201. {
  202. get { return _color; }
  203. set
  204. {
  205. if (this.Color != value)
  206. {
  207. _color = value;
  208. this.OnColorChanged(EventArgs.Empty);
  209. }
  210. }
  211. }
  212. [Category("Appearance")]
  213. [DefaultValue(4)]
  214. public virtual int ColorStep
  215. {
  216. get { return _colorStep; }
  217. set
  218. {
  219. if (value < 1 || value > 359)
  220. {
  221. throw new ArgumentOutOfRangeException("value", value, "Value must be between 1 and 359");
  222. }
  223. if (this.ColorStep != value)
  224. {
  225. _colorStep = value;
  226. this.OnColorStepChanged(EventArgs.Empty);
  227. }
  228. }
  229. }
  230. [Category("Appearance")]
  231. [DefaultValue(typeof(HslColor), "0, 0, 0")]
  232. [Browsable(false)]
  233. [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  234. public virtual HslColor HslColor
  235. {
  236. get { return _hslColor; }
  237. set
  238. {
  239. if (this.HslColor != value)
  240. {
  241. _hslColor = value;
  242. this.OnHslColorChanged(EventArgs.Empty);
  243. }
  244. }
  245. }
  246. [Category("Behavior")]
  247. [DefaultValue(5)]
  248. public virtual int LargeChange
  249. {
  250. get { return _largeChange; }
  251. set
  252. {
  253. if (this.LargeChange != value)
  254. {
  255. _largeChange = value;
  256. this.OnLargeChangeChanged(EventArgs.Empty);
  257. }
  258. }
  259. }
  260. [Category("Appearance")]
  261. [DefaultValue(10)]
  262. public virtual int SelectionSize
  263. {
  264. get { return _selectionSize; }
  265. set
  266. {
  267. if (this.SelectionSize != value)
  268. {
  269. _selectionSize = value;
  270. this.OnSelectionSizeChanged(EventArgs.Empty);
  271. }
  272. }
  273. }
  274. [Category("Behavior")]
  275. [DefaultValue(1)]
  276. public virtual int SmallChange
  277. {
  278. get { return _smallChange; }
  279. set
  280. {
  281. if (this.SmallChange != value)
  282. {
  283. _smallChange = value;
  284. this.OnSmallChangeChanged(EventArgs.Empty);
  285. }
  286. }
  287. }
  288. #endregion
  289. #region Protected Properties
  290. protected virtual bool AllowPainting
  291. {
  292. get { return _updateCount == 0; }
  293. }
  294. protected Color[] Colors { get; set; }
  295. protected bool LockUpdates { get; set; }
  296. protected PointF[] Points { get; set; }
  297. protected Image SelectionGlyph { get; set; }
  298. #endregion
  299. #region Public Members
  300. public virtual void BeginUpdate()
  301. {
  302. _updateCount++;
  303. }
  304. public virtual void EndUpdate()
  305. {
  306. if (_updateCount > 0)
  307. {
  308. _updateCount--;
  309. }
  310. if (this.AllowPainting)
  311. {
  312. this.Invalidate();
  313. }
  314. }
  315. #endregion
  316. #region Protected Members
  317. protected virtual void CalculateWheel()
  318. {
  319. List<PointF> points;
  320. List<Color> colors;
  321. points = new List<PointF>();
  322. colors = new List<Color>();
  323. if (this.ClientSize.Width > 16 && this.ClientSize.Height > 16)
  324. {
  325. int w;
  326. int h;
  327. w = this.ClientSize.Width;
  328. h = this.ClientSize.Height;
  329. _centerPoint = new PointF(w / 2.0F, h / 2.0F);
  330. _radius = this.GetRadius(_centerPoint);
  331. for (double angle = 0; angle < 360; angle += this.ColorStep)
  332. {
  333. double angleR;
  334. PointF location;
  335. angleR = angle * (Math.PI / 180);
  336. location = this.GetColorLocation(angleR, _radius);
  337. points.Add(location);
  338. colors.Add(new HslColor(angle, 1, 0.5).ToRgbColor());
  339. }
  340. }
  341. this.Points = points.ToArray();
  342. this.Colors = colors.ToArray();
  343. }
  344. protected virtual Brush CreateGradientBrush()
  345. {
  346. Brush result;
  347. if (this.Points.Length != 0 && this.Points.Length == this.Colors.Length)
  348. {
  349. result = new PathGradientBrush(this.Points, WrapMode.Clamp)
  350. {
  351. CenterPoint = _centerPoint,
  352. CenterColor = Color.White,
  353. SurroundColors = this.Colors
  354. };
  355. }
  356. else
  357. {
  358. result = null;
  359. }
  360. return result;
  361. }
  362. protected virtual Color GetContrastColor(Color c)
  363. {
  364. double brightness = c.R * 0.299 + c.G * 0.587 + c.B * 0.114;
  365. return brightness > 149 ? Color.Black : Color.White;
  366. }
  367. protected virtual Image CreateSelectionGlyph()
  368. {
  369. Image image;
  370. int halfSize;
  371. halfSize = this.SelectionSize / 2;
  372. image = new Bitmap(this.SelectionSize + 1, this.SelectionSize + 1);
  373. using (Graphics g = Graphics.FromImage(image))
  374. {
  375. g.SmoothingMode = SmoothingMode.AntiAlias;
  376. g.PixelOffsetMode = PixelOffsetMode.HighQuality;
  377. g.InterpolationMode = InterpolationMode.High;
  378. g.DrawEllipse(new Pen(GetContrastColor(Color)), halfSize - 4.5f, halfSize - 4.5f, 4.5f + 4.5f, 4.5f + 4.5f);
  379. g.FillEllipse(new SolidBrush(this.Color), halfSize - 4, halfSize - 4, 4 + 4, 4 + 4);
  380. }
  381. return image;
  382. }
  383. protected PointF GetColorLocation(Color color)
  384. {
  385. return this.GetColorLocation(new HslColor(color));
  386. }
  387. protected virtual PointF GetColorLocation(HslColor color)
  388. {
  389. double angle;
  390. double radius;
  391. angle = color.H * Math.PI / 180;
  392. radius = _radius * color.S;
  393. return this.GetColorLocation(angle, radius);
  394. }
  395. protected PointF GetColorLocation(double angleR, double radius)
  396. {
  397. double x;
  398. double y;
  399. x = this.Padding.Left + _centerPoint.X + Math.Cos(angleR) * radius;
  400. y = this.Padding.Top + _centerPoint.Y - Math.Sin(angleR) * radius;
  401. return new PointF((float)x, (float)y);
  402. }
  403. protected float GetRadius(PointF centerPoint)
  404. {
  405. return Math.Min(centerPoint.X, centerPoint.Y) - (Math.Max(this.Padding.Horizontal, this.Padding.Vertical) + (this.SelectionSize / 2));
  406. }
  407. protected bool IsPointInWheel(Point point)
  408. {
  409. PointF normalized;
  410. normalized = new PointF(point.X - _centerPoint.X, point.Y - _centerPoint.Y);
  411. return (normalized.X * normalized.X + normalized.Y * normalized.Y) <= (_radius * _radius);
  412. }
  413. protected virtual void OnColorChanged(EventArgs e)
  414. {
  415. EventHandler handler;
  416. if (!this.LockUpdates)
  417. {
  418. this.HslColor = new HslColor(this.Color);
  419. }
  420. this.SelectionGlyph = this.CreateSelectionGlyph();
  421. this.Refresh();
  422. handler = this.ColorChanged;
  423. if (handler != null)
  424. {
  425. handler(this, e);
  426. }
  427. }
  428. protected virtual void OnColorStepChanged(EventArgs e)
  429. {
  430. EventHandler handler;
  431. this.RefreshWheel();
  432. handler = this.ColorStepChanged;
  433. if (handler != null)
  434. {
  435. handler(this, e);
  436. }
  437. }
  438. protected virtual void OnHslColorChanged(EventArgs e)
  439. {
  440. EventHandler handler;
  441. if (!this.LockUpdates)
  442. {
  443. this.Color = this.HslColor.ToRgbColor();
  444. }
  445. this.Invalidate();
  446. handler = this.HslColorChanged;
  447. if (handler != null)
  448. {
  449. handler(this, e);
  450. }
  451. }
  452. protected virtual void OnLargeChangeChanged(EventArgs e)
  453. {
  454. EventHandler handler;
  455. handler = this.LargeChangeChanged;
  456. if (handler != null)
  457. {
  458. handler(this, e);
  459. }
  460. }
  461. protected virtual void OnSelectionSizeChanged(EventArgs e)
  462. {
  463. EventHandler handler;
  464. if (this.SelectionGlyph != null)
  465. {
  466. this.SelectionGlyph.Dispose();
  467. }
  468. this.SelectionGlyph = this.CreateSelectionGlyph();
  469. this.RefreshWheel();
  470. handler = this.SelectionSizeChanged;
  471. if (handler != null)
  472. {
  473. handler(this, e);
  474. }
  475. }
  476. protected virtual void OnSmallChangeChanged(EventArgs e)
  477. {
  478. EventHandler handler;
  479. handler = this.SmallChangeChanged;
  480. if (handler != null)
  481. {
  482. handler(this, e);
  483. }
  484. }
  485. protected void PaintColor(PaintEventArgs e, HslColor color)
  486. {
  487. this.PaintColor(e, color, false);
  488. }
  489. protected virtual void PaintColor(PaintEventArgs e, HslColor color, bool includeFocus)
  490. {
  491. PointF location;
  492. location = this.GetColorLocation(color);
  493. if (!float.IsNaN(location.X) && !float.IsNaN(location.Y))
  494. {
  495. int x;
  496. int y;
  497. x = (int)location.X - (this.SelectionSize / 2);
  498. y = (int)location.Y - (this.SelectionSize / 2);
  499. if (this.SelectionGlyph == null)
  500. {
  501. e.Graphics.DrawRectangle(Pens.Black, x, y, this.SelectionSize, this.SelectionSize);
  502. }
  503. else
  504. {
  505. e.Graphics.DrawImage(this.SelectionGlyph, x, y);
  506. }
  507. }
  508. }
  509. protected virtual void PaintCurrentColor(PaintEventArgs e)
  510. {
  511. this.PaintColor(e, this.HslColor, true);
  512. }
  513. protected virtual void SetColor(Point point)
  514. {
  515. double dx;
  516. double dy;
  517. double angle;
  518. double distance;
  519. double saturation;
  520. dx = Math.Abs(point.X - _centerPoint.X - this.Padding.Left);
  521. dy = Math.Abs(point.Y - _centerPoint.Y - this.Padding.Top);
  522. angle = Math.Atan(dy / dx) / Math.PI * 180;
  523. distance = Math.Pow((Math.Pow(dx, 2) + (Math.Pow(dy, 2))), 0.5);
  524. saturation = distance / _radius;
  525. if (distance < 6)
  526. {
  527. saturation = 0;
  528. }
  529. if (point.X < _centerPoint.X)
  530. {
  531. angle = 180 - angle;
  532. }
  533. if (point.Y > _centerPoint.Y)
  534. {
  535. angle = 360 - angle;
  536. }
  537. this.LockUpdates = true;
  538. this.HslColor = new HslColor(angle, saturation, 0.5);
  539. this.Color = this.HslColor.ToRgbColor();
  540. this.LockUpdates = false;
  541. }
  542. #endregion
  543. #region Private Members
  544. private void RefreshWheel()
  545. {
  546. if (_brush != null)
  547. {
  548. _brush.Dispose();
  549. }
  550. this.CalculateWheel();
  551. _brush = this.CreateGradientBrush();
  552. this.Invalidate();
  553. }
  554. #endregion
  555. }
  556. }