ColorPicker.cs 17 KB

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