ColorGradientControl.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726
  1. using SmartCoalApplication.Core;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.ComponentModel;
  5. using System.Data;
  6. using System.Drawing;
  7. using System.Drawing.Drawing2D;
  8. using System.Linq;
  9. using System.Text;
  10. using System.Threading.Tasks;
  11. using System.Windows.Forms;
  12. namespace SmartCoalApplication.PluginAssemblys
  13. {
  14. public sealed class ColorGradientControl
  15. : UserControl
  16. {
  17. private Point lastTrackingMouseXY = new Point(-1, -1);
  18. private int tracking = -1;
  19. private int highlight = -1;
  20. private const int triangleSize = 7;
  21. private const int triangleHalfLength = (triangleSize - 1) / 2;
  22. private Orientation orientation = Orientation.Vertical;
  23. private Color[] customGradient = null;
  24. private bool drawNearNub = true;
  25. public bool DrawNearNub
  26. {
  27. get
  28. {
  29. return this.drawNearNub;
  30. }
  31. set
  32. {
  33. this.drawNearNub = value;
  34. Invalidate();
  35. }
  36. }
  37. private bool drawFarNub = true;
  38. public bool DrawFarNub
  39. {
  40. get
  41. {
  42. return this.drawFarNub;
  43. }
  44. set
  45. {
  46. this.drawFarNub = value;
  47. Invalidate();
  48. }
  49. }
  50. private int[] vals;
  51. // value from [0,255] that specifies the hsv "value" component
  52. // where we should draw little triangles that show the value
  53. public int Value
  54. {
  55. get
  56. {
  57. return GetValue(0);
  58. }
  59. set
  60. {
  61. SetValue(0, value);
  62. }
  63. }
  64. public Color[] CustomGradient
  65. {
  66. get
  67. {
  68. if (this.customGradient == null)
  69. {
  70. return null;
  71. }
  72. else
  73. {
  74. return (Color[])this.customGradient.Clone();
  75. }
  76. }
  77. set
  78. {
  79. if (value != this.customGradient)
  80. {
  81. if (value == null)
  82. {
  83. this.customGradient = null;
  84. }
  85. else
  86. {
  87. this.customGradient = (Color[])value.Clone();
  88. }
  89. Invalidate();
  90. }
  91. }
  92. }
  93. public Orientation Orientation
  94. {
  95. get
  96. {
  97. return this.orientation;
  98. }
  99. set
  100. {
  101. if (value != this.orientation)
  102. {
  103. this.orientation = value;
  104. Invalidate();
  105. }
  106. }
  107. }
  108. public int Count
  109. {
  110. get
  111. {
  112. return vals.Length;
  113. }
  114. set
  115. {
  116. if (value < 0 || value > 16)
  117. {
  118. throw new ArgumentOutOfRangeException("value", value, "Count must be between 0 and 16");
  119. }
  120. vals = new int[value];
  121. if (value > 1)
  122. {
  123. for (int i = 0; i < value; i++)
  124. {
  125. vals[i] = i * 255 / (value - 1);
  126. }
  127. }
  128. else if (value == 1)
  129. {
  130. vals[0] = 128;
  131. }
  132. OnValueChanged(0);
  133. Invalidate();
  134. }
  135. }
  136. public int GetValue(int index)
  137. {
  138. if (index < 0 || index >= vals.Length)
  139. {
  140. throw new ArgumentOutOfRangeException("index", index, "Index must be within the bounds of the array");
  141. }
  142. int val = vals[index];
  143. return val;
  144. }
  145. public void SetValue(int index, int val)
  146. {
  147. int min = -1;
  148. int max = 256;
  149. if (index < 0 || index >= vals.Length)
  150. {
  151. throw new ArgumentOutOfRangeException("index", index, "Index must be within the bounds of the array");
  152. }
  153. if (index - 1 >= 0)
  154. {
  155. min = vals[index - 1];
  156. }
  157. if (index + 1 < vals.Length)
  158. {
  159. max = vals[index + 1];
  160. }
  161. if (vals[index] != val)
  162. {
  163. int newVal = Utility.Clamp(val, min + 1, max - 1);
  164. vals[index] = newVal;
  165. OnValueChanged(index);
  166. Invalidate();
  167. }
  168. Update();
  169. }
  170. public event IndexEventHandler ValueChanged;
  171. private void OnValueChanged(int index)
  172. {
  173. if (ValueChanged != null)
  174. {
  175. ValueChanged(this, new IndexEventArgs(index));
  176. }
  177. }
  178. [Obsolete("Use MinColor property instead", true)]
  179. public Color BottomColor
  180. {
  181. get
  182. {
  183. return MinColor;
  184. }
  185. set
  186. {
  187. MinColor = value;
  188. }
  189. }
  190. [Obsolete("Use MaxColor property instead", true)]
  191. public Color TopColor
  192. {
  193. get
  194. {
  195. return MaxColor;
  196. }
  197. set
  198. {
  199. MaxColor = value;
  200. }
  201. }
  202. private Color maxColor;
  203. public Color MaxColor
  204. {
  205. get
  206. {
  207. return maxColor;
  208. }
  209. set
  210. {
  211. if (maxColor != value)
  212. {
  213. maxColor = value;
  214. Invalidate();
  215. }
  216. }
  217. }
  218. private Color minColor;
  219. public Color MinColor
  220. {
  221. get
  222. {
  223. return minColor;
  224. }
  225. set
  226. {
  227. if (minColor != value)
  228. {
  229. minColor = value;
  230. Invalidate();
  231. }
  232. }
  233. }
  234. public ColorGradientControl()
  235. {
  236. // This call is required by the Windows.Forms Form Designer.
  237. InitializeComponent();
  238. this.DoubleBuffered = true;
  239. this.ResizeRedraw = true;
  240. this.Count = 1;
  241. }
  242. private void DrawGradient(Graphics g)
  243. {
  244. g.PixelOffsetMode = PixelOffsetMode.Half;
  245. Rectangle gradientRect;
  246. float gradientAngle;
  247. switch (this.orientation)
  248. {
  249. case Orientation.Horizontal:
  250. gradientAngle = 180.0f;
  251. break;
  252. case Orientation.Vertical:
  253. gradientAngle = 90.0f;
  254. break;
  255. default:
  256. throw new InvalidEnumArgumentException();
  257. }
  258. // draw gradient
  259. gradientRect = ClientRectangle;
  260. switch (this.orientation)
  261. {
  262. case Orientation.Horizontal:
  263. gradientRect.Inflate(-triangleHalfLength, -triangleSize + 3);
  264. break;
  265. case Orientation.Vertical:
  266. gradientRect.Inflate(-triangleSize + 3, -triangleHalfLength);
  267. break;
  268. default:
  269. throw new InvalidEnumArgumentException();
  270. }
  271. if (this.customGradient != null && gradientRect.Width > 1 && gradientRect.Height > 1)
  272. {
  273. Surface gradientSurface = new Surface(gradientRect.Size);
  274. using (RenderArgs ra = new RenderArgs(gradientSurface))
  275. {
  276. Utility.DrawColorRectangle(ra.Graphics, ra.Bounds, Color.Transparent, false);
  277. if (Orientation == Orientation.Horizontal)
  278. {
  279. for (int x = 0; x < gradientSurface.Width; ++x)
  280. {
  281. double index = (double)(x * (this.customGradient.Length - 1)) / (double)(gradientSurface.Width - 1);
  282. int indexL = (int)Math.Floor(index);
  283. double t = 1.0 - (index - indexL);
  284. int indexR = (int)Math.Min(this.customGradient.Length - 1, Math.Ceiling(index));
  285. Color colorL = this.customGradient[indexL];
  286. Color colorR = this.customGradient[indexR];
  287. double a1 = colorL.A / 255.0;
  288. double r1 = colorL.R / 255.0;
  289. double g1 = colorL.G / 255.0;
  290. double b1 = colorL.B / 255.0;
  291. double a2 = colorR.A / 255.0;
  292. double r2 = colorR.R / 255.0;
  293. double g2 = colorR.G / 255.0;
  294. double b2 = colorR.B / 255.0;
  295. double at = (t * a1) + ((1.0 - t) * a2);
  296. double rt;
  297. double gt;
  298. double bt;
  299. if (at == 0)
  300. {
  301. rt = 0;
  302. gt = 0;
  303. bt = 0;
  304. }
  305. else
  306. {
  307. rt = ((t * a1 * r1) + ((1.0 - t) * a2 * r2)) / at;
  308. gt = ((t * a1 * g1) + ((1.0 - t) * a2 * g2)) / at;
  309. bt = ((t * a1 * b1) + ((1.0 - t) * a2 * b2)) / at;
  310. }
  311. int ap = Utility.Clamp((int)Math.Round(at * 255.0), 0, 255);
  312. int rp = Utility.Clamp((int)Math.Round(rt * 255.0), 0, 255);
  313. int gp = Utility.Clamp((int)Math.Round(gt * 255.0), 0, 255);
  314. int bp = Utility.Clamp((int)Math.Round(bt * 255.0), 0, 255);
  315. for (int y = 0; y < gradientSurface.Height; ++y)
  316. {
  317. ColorBgra src = gradientSurface[x, y];
  318. // we are assuming that src.A = 255
  319. int rd = ((rp * ap) + (src.R * (255 - ap))) / 255;
  320. int gd = ((gp * ap) + (src.G * (255 - ap))) / 255;
  321. int bd = ((bp * ap) + (src.B * (255 - ap))) / 255;
  322. gradientSurface[x, y] = ColorBgra.FromBgra((byte)bd, (byte)gd, (byte)rd, 255);
  323. }
  324. }
  325. g.DrawImage(ra.Bitmap, gradientRect, ra.Bounds, GraphicsUnit.Pixel);
  326. }
  327. else if (Orientation == Orientation.Vertical)
  328. {
  329. }
  330. else
  331. {
  332. throw new InvalidEnumArgumentException();
  333. }
  334. }
  335. gradientSurface.Dispose();
  336. }
  337. else
  338. {
  339. using (LinearGradientBrush lgb = new LinearGradientBrush(this.ClientRectangle,
  340. maxColor, minColor, gradientAngle, false))
  341. {
  342. g.FillRectangle(lgb, gradientRect);
  343. }
  344. }
  345. // fill background
  346. using (PdnRegion nonGradientRegion = new PdnRegion())
  347. {
  348. nonGradientRegion.MakeInfinite();
  349. nonGradientRegion.Exclude(gradientRect);
  350. using (SolidBrush sb = new SolidBrush(this.BackColor))
  351. {
  352. g.FillRegion(sb, nonGradientRegion.GetRegionReadOnly());
  353. }
  354. }
  355. // draw value triangles
  356. for (int i = 0; i < this.vals.Length; i++)
  357. {
  358. int pos = ValueToPosition(vals[i]);
  359. Brush brush;
  360. Pen pen;
  361. if (i == highlight)
  362. {
  363. brush = Brushes.Blue;
  364. pen = (Pen)Pens.White.Clone();
  365. }
  366. else
  367. {
  368. brush = Brushes.Black;
  369. pen = (Pen)Pens.Gray.Clone();
  370. }
  371. g.SmoothingMode = SmoothingMode.AntiAlias;
  372. Point a1;
  373. Point b1;
  374. Point c1;
  375. Point a2;
  376. Point b2;
  377. Point c2;
  378. switch (this.orientation)
  379. {
  380. case Orientation.Horizontal:
  381. a1 = new Point(pos - triangleHalfLength, 0);
  382. b1 = new Point(pos, triangleSize - 1);
  383. c1 = new Point(pos + triangleHalfLength, 0);
  384. a2 = new Point(a1.X, Height - 1 - a1.Y);
  385. b2 = new Point(b1.X, Height - 1 - b1.Y);
  386. c2 = new Point(c1.X, Height - 1 - c1.Y);
  387. break;
  388. case Orientation.Vertical:
  389. a1 = new Point(0, pos - triangleHalfLength);
  390. b1 = new Point(triangleSize - 1, pos);
  391. c1 = new Point(0, pos + triangleHalfLength);
  392. a2 = new Point(Width - 1 - a1.X, a1.Y);
  393. b2 = new Point(Width - 1 - b1.X, b1.Y);
  394. c2 = new Point(Width - 1 - c1.X, c1.Y);
  395. break;
  396. default:
  397. throw new InvalidEnumArgumentException();
  398. }
  399. if (this.drawNearNub)
  400. {
  401. g.FillPolygon(brush, new Point[] { a1, b1, c1, a1 });
  402. }
  403. if (this.drawFarNub)
  404. {
  405. g.FillPolygon(brush, new Point[] { a2, b2, c2, a2 });
  406. }
  407. if (pen != null)
  408. {
  409. if (this.drawNearNub)
  410. {
  411. g.DrawPolygon(pen, new Point[] { a1, b1, c1, a1 });
  412. }
  413. if (this.drawFarNub)
  414. {
  415. g.DrawPolygon(pen, new Point[] { a2, b2, c2, a2 });
  416. }
  417. pen.Dispose();
  418. }
  419. }
  420. }
  421. protected override void OnPaint(PaintEventArgs e)
  422. {
  423. base.OnPaint(e);
  424. DrawGradient(e.Graphics);
  425. }
  426. protected override void OnPaintBackground(PaintEventArgs pevent)
  427. {
  428. DrawGradient(pevent.Graphics);
  429. }
  430. /// <summary>
  431. /// Clean up any resources being used.
  432. /// </summary>
  433. protected override void Dispose(bool disposing)
  434. {
  435. if (disposing)
  436. {
  437. }
  438. base.Dispose(disposing);
  439. }
  440. private int PositionToValue(int pos)
  441. {
  442. int max;
  443. switch (this.orientation)
  444. {
  445. case Orientation.Horizontal:
  446. max = Width;
  447. break;
  448. case Orientation.Vertical:
  449. max = Height;
  450. break;
  451. default:
  452. throw new InvalidEnumArgumentException();
  453. }
  454. int val = (((max - triangleSize) - (pos - triangleHalfLength)) * 255) / (max - triangleSize);
  455. if (this.orientation == Orientation.Horizontal)
  456. {
  457. val = 255 - val;
  458. }
  459. return val;
  460. }
  461. private int ValueToPosition(int val)
  462. {
  463. int max;
  464. if (this.orientation == Orientation.Horizontal)
  465. {
  466. val = 255 - val;
  467. }
  468. switch (this.orientation)
  469. {
  470. case Orientation.Horizontal:
  471. max = Width;
  472. break;
  473. case Orientation.Vertical:
  474. max = Height;
  475. break;
  476. default:
  477. throw new InvalidEnumArgumentException();
  478. }
  479. int pos = triangleHalfLength + ((max - triangleSize) - (((val * (max - triangleSize)) / 255)));
  480. return pos;
  481. }
  482. private int WhichTriangle(int val)
  483. {
  484. int bestIndex = -1;
  485. int bestDistance = int.MaxValue;
  486. int v = PositionToValue(val);
  487. for (int i = 0; i < this.vals.Length; i++)
  488. {
  489. int distance = Math.Abs(this.vals[i] - v);
  490. if (distance < bestDistance)
  491. {
  492. bestDistance = distance;
  493. bestIndex = i;
  494. }
  495. }
  496. return bestIndex;
  497. }
  498. protected override void OnMouseDown(MouseEventArgs e)
  499. {
  500. base.OnMouseDown(e);
  501. if (e.Button == MouseButtons.Left)
  502. {
  503. int val = GetOrientedValue(e);
  504. tracking = WhichTriangle(val);
  505. Invalidate();
  506. OnMouseMove(e);
  507. }
  508. }
  509. protected override void OnMouseUp(MouseEventArgs e)
  510. {
  511. base.OnMouseUp(e);
  512. if (e.Button == MouseButtons.Left)
  513. {
  514. OnMouseMove(e);
  515. tracking = -1;
  516. Invalidate();
  517. }
  518. }
  519. private int GetOrientedValue(MouseEventArgs me)
  520. {
  521. return GetOrientedValue(new Point(me.X, me.Y));
  522. }
  523. private int GetOrientedValue(Point pt)
  524. {
  525. int pos;
  526. switch (this.orientation)
  527. {
  528. case Orientation.Horizontal:
  529. pos = pt.X;
  530. break;
  531. case Orientation.Vertical:
  532. pos = pt.Y;
  533. break;
  534. default:
  535. throw new InvalidEnumArgumentException();
  536. }
  537. return pos;
  538. }
  539. protected override void OnMouseMove(MouseEventArgs e)
  540. {
  541. base.OnMouseMove(e);
  542. int pos = GetOrientedValue(e);
  543. Point newMouseXY = new Point(e.X, e.Y);
  544. if (tracking >= 0 && newMouseXY != this.lastTrackingMouseXY)
  545. {
  546. int val = PositionToValue(pos);
  547. this.SetValue(tracking, val);
  548. this.lastTrackingMouseXY = newMouseXY;
  549. }
  550. else
  551. {
  552. int oldHighlight = highlight;
  553. highlight = WhichTriangle(pos);
  554. if (highlight != oldHighlight)
  555. {
  556. this.InvalidateTriangle(oldHighlight);
  557. this.InvalidateTriangle(highlight);
  558. }
  559. }
  560. }
  561. protected override void OnMouseLeave(EventArgs e)
  562. {
  563. int oldhighlight = highlight;
  564. highlight = -1;
  565. this.InvalidateTriangle(oldhighlight);
  566. }
  567. private void InvalidateTriangle(int index)
  568. {
  569. if (index < 0 || index >= this.vals.Length)
  570. {
  571. return;
  572. }
  573. int value = ValueToPosition(this.vals[index]);
  574. Rectangle rect;
  575. switch (this.orientation)
  576. {
  577. case Orientation.Horizontal:
  578. rect = new Rectangle(value - triangleHalfLength, 0, triangleSize, this.Height);
  579. break;
  580. case Orientation.Vertical:
  581. rect = new Rectangle(0, value - triangleHalfLength, this.Width, triangleSize);
  582. break;
  583. default:
  584. throw new InvalidEnumArgumentException();
  585. }
  586. this.Invalidate(rect, true);
  587. }
  588. #region Component Designer generated code
  589. /// <summary>
  590. /// Required method for Designer support - do not modify
  591. /// the contents of this method with the code editor.
  592. /// </summary>
  593. private void InitializeComponent()
  594. {
  595. }
  596. #endregion
  597. }
  598. }