123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720 |
- using System;
- using System.ComponentModel;
- using System.Drawing;
- using System.Drawing.Drawing2D;
- using System.Windows.Forms;
- namespace PaintDotNet
- {
- public sealed class ColorGradientControl
- : UserControl
- {
- private Point lastTrackingMouseXY = new Point(-1, -1);
- private int tracking = -1;
- private int highlight = -1;
- private const int triangleSize = 7;
- private const int triangleHalfLength = (triangleSize - 1) / 2;
- private Orientation orientation = Orientation.Vertical;
- private Color[] customGradient = null;
- private bool drawNearNub = true;
- public bool DrawNearNub
- {
- get
- {
- return this.drawNearNub;
- }
- set
- {
- this.drawNearNub = value;
- Invalidate();
- }
- }
- private bool drawFarNub = true;
- public bool DrawFarNub
- {
- get
- {
- return this.drawFarNub;
- }
- set
- {
- this.drawFarNub = value;
- Invalidate();
- }
- }
- private int[] vals;
- // value from [0,255] that specifies the hsv "value" component
- // where we should draw little triangles that show the value
- public int Value
- {
- get
- {
- return GetValue(0);
- }
- set
- {
- SetValue(0, value);
- }
- }
- public Color[] CustomGradient
- {
- get
- {
- if (this.customGradient == null)
- {
- return null;
- }
- else
- {
- return (Color[])this.customGradient.Clone();
- }
- }
- set
- {
- if (value != this.customGradient)
- {
- if (value == null)
- {
- this.customGradient = null;
- }
- else
- {
- this.customGradient = (Color[])value.Clone();
- }
- Invalidate();
- }
- }
- }
- public Orientation Orientation
- {
- get
- {
- return this.orientation;
- }
- set
- {
- if (value != this.orientation)
- {
- this.orientation = value;
- Invalidate();
- }
- }
- }
- public int Count
- {
- get
- {
- return vals.Length;
- }
- set
- {
- if (value < 0 || value > 16)
- {
- throw new ArgumentOutOfRangeException("value", value, "Count must be between 0 and 16");
- }
- vals = new int[value];
- if (value > 1)
- {
- for (int i = 0; i < value; i++)
- {
- vals[i] = i * 255 / (value - 1);
- }
- }
- else if (value == 1)
- {
- vals[0] = 128;
- }
- OnValueChanged(0);
- Invalidate();
- }
- }
- public int GetValue(int index)
- {
- if (index < 0 || index >= vals.Length)
- {
- throw new ArgumentOutOfRangeException("index", index, "Index must be within the bounds of the array");
- }
- int val = vals[index];
- return val;
- }
- public void SetValue(int index, int val)
- {
- int min = -1;
- int max = 256;
- if (index < 0 || index >= vals.Length)
- {
- throw new ArgumentOutOfRangeException("index", index, "Index must be within the bounds of the array");
- }
- if (index - 1 >= 0)
- {
- min = vals[index - 1];
- }
- if (index + 1 < vals.Length)
- {
- max = vals[index + 1];
- }
- if (vals[index] != val)
- {
- int newVal = Utility.Clamp(val, min + 1, max - 1);
- vals[index] = newVal;
- OnValueChanged(index);
- Invalidate();
- }
- Update();
- }
- public event IndexEventHandler ValueChanged;
- private void OnValueChanged(int index)
- {
- if (ValueChanged != null)
- {
- ValueChanged(this, new IndexEventArgs(index));
- }
- }
- [Obsolete("Use MinColor property instead", true)]
- public Color BottomColor
- {
- get
- {
- return MinColor;
- }
- set
- {
- MinColor = value;
- }
- }
- [Obsolete("Use MaxColor property instead", true)]
- public Color TopColor
- {
- get
- {
- return MaxColor;
- }
- set
- {
- MaxColor = value;
- }
- }
- private Color maxColor;
- public Color MaxColor
- {
- get
- {
- return maxColor;
- }
- set
- {
- if (maxColor != value)
- {
- maxColor = value;
- Invalidate();
- }
- }
- }
- private Color minColor;
- public Color MinColor
- {
- get
- {
- return minColor;
- }
- set
- {
- if (minColor != value)
- {
- minColor = value;
- Invalidate();
- }
- }
- }
- public ColorGradientControl()
- {
- // This call is required by the Windows.Forms Form Designer.
- InitializeComponent();
- this.DoubleBuffered = true;
- this.ResizeRedraw = true;
- this.Count = 1;
- }
- private void DrawGradient(Graphics g)
- {
- g.PixelOffsetMode = PixelOffsetMode.Half;
- Rectangle gradientRect;
- float gradientAngle;
- switch (this.orientation)
- {
- case Orientation.Horizontal:
- gradientAngle = 180.0f;
- break;
- case Orientation.Vertical:
- gradientAngle = 90.0f;
- break;
- default:
- throw new InvalidEnumArgumentException();
- }
- // draw gradient
- gradientRect = ClientRectangle;
- switch (this.orientation)
- {
- case Orientation.Horizontal:
- gradientRect.Inflate(-triangleHalfLength, -triangleSize + 3);
- break;
- case Orientation.Vertical:
- gradientRect.Inflate(-triangleSize + 3, -triangleHalfLength);
- break;
- default:
- throw new InvalidEnumArgumentException();
- }
- if (this.customGradient != null && gradientRect.Width > 1 && gradientRect.Height > 1)
- {
- Surface gradientSurface = new Surface(gradientRect.Size);
- using (RenderArgs ra = new RenderArgs(gradientSurface))
- {
- Utility.DrawColorRectangle(ra.Graphics, ra.Bounds, Color.Transparent, false);
- if (Orientation == Orientation.Horizontal)
- {
- for (int x = 0; x < gradientSurface.Width; ++x)
- {
- double index = (double)(x * (this.customGradient.Length - 1)) / (double)(gradientSurface.Width - 1);
- int indexL = (int)Math.Floor(index);
- double t = 1.0 - (index - indexL);
- int indexR = (int)Math.Min(this.customGradient.Length - 1, Math.Ceiling(index));
- Color colorL = this.customGradient[indexL];
- Color colorR = this.customGradient[indexR];
- double a1 = colorL.A / 255.0;
- double r1 = colorL.R / 255.0;
- double g1 = colorL.G / 255.0;
- double b1 = colorL.B / 255.0;
- double a2 = colorR.A / 255.0;
- double r2 = colorR.R / 255.0;
- double g2 = colorR.G / 255.0;
- double b2 = colorR.B / 255.0;
- double at = (t * a1) + ((1.0 - t) * a2);
- double rt;
- double gt;
- double bt;
- if (at == 0)
- {
- rt = 0;
- gt = 0;
- bt = 0;
- }
- else
- {
- rt = ((t * a1 * r1) + ((1.0 - t) * a2 * r2)) / at;
- gt = ((t * a1 * g1) + ((1.0 - t) * a2 * g2)) / at;
- bt = ((t * a1 * b1) + ((1.0 - t) * a2 * b2)) / at;
- }
- int ap = Utility.Clamp((int)Math.Round(at * 255.0), 0, 255);
- int rp = Utility.Clamp((int)Math.Round(rt * 255.0), 0, 255);
- int gp = Utility.Clamp((int)Math.Round(gt * 255.0), 0, 255);
- int bp = Utility.Clamp((int)Math.Round(bt * 255.0), 0, 255);
- for (int y = 0; y < gradientSurface.Height; ++y)
- {
- ColorBgra src = gradientSurface[x, y];
- // we are assuming that src.A = 255
- int rd = ((rp * ap) + (src.R * (255 - ap))) / 255;
- int gd = ((gp * ap) + (src.G * (255 - ap))) / 255;
- int bd = ((bp * ap) + (src.B * (255 - ap))) / 255;
- gradientSurface[x, y] = ColorBgra.FromBgra((byte)bd, (byte)gd, (byte)rd, 255);
- }
- }
- g.DrawImage(ra.Bitmap, gradientRect, ra.Bounds, GraphicsUnit.Pixel);
- }
- else if (Orientation == Orientation.Vertical)
- {
-
- }
- else
- {
- throw new InvalidEnumArgumentException();
- }
- }
- gradientSurface.Dispose();
- }
- else
- {
- using (LinearGradientBrush lgb = new LinearGradientBrush(this.ClientRectangle,
- maxColor, minColor, gradientAngle, false))
- {
- g.FillRectangle(lgb, gradientRect);
- }
- }
- // fill background
- using (PdnRegion nonGradientRegion = new PdnRegion())
- {
- nonGradientRegion.MakeInfinite();
- nonGradientRegion.Exclude(gradientRect);
- using (SolidBrush sb = new SolidBrush(this.BackColor))
- {
- g.FillRegion(sb, nonGradientRegion.GetRegionReadOnly());
- }
- }
- // draw value triangles
- for (int i = 0; i < this.vals.Length; i++)
- {
- int pos = ValueToPosition(vals[i]);
- Brush brush;
- Pen pen;
- if (i == highlight)
- {
- brush = Brushes.Blue;
- pen = (Pen)Pens.White.Clone();
- }
- else
- {
- brush = Brushes.Black;
- pen = (Pen)Pens.Gray.Clone();
- }
- g.SmoothingMode = SmoothingMode.AntiAlias;
- Point a1;
- Point b1;
- Point c1;
- Point a2;
- Point b2;
- Point c2;
- switch (this.orientation)
- {
- case Orientation.Horizontal:
- a1 = new Point(pos - triangleHalfLength, 0);
- b1 = new Point(pos, triangleSize - 1);
- c1 = new Point(pos + triangleHalfLength, 0);
- a2 = new Point(a1.X, Height - 1 - a1.Y);
- b2 = new Point(b1.X, Height - 1 - b1.Y);
- c2 = new Point(c1.X, Height - 1 - c1.Y);
- break;
- case Orientation.Vertical:
- a1 = new Point(0, pos - triangleHalfLength);
- b1 = new Point(triangleSize - 1, pos);
- c1 = new Point(0, pos + triangleHalfLength);
- a2 = new Point(Width - 1 - a1.X, a1.Y);
- b2 = new Point(Width - 1 - b1.X, b1.Y);
- c2 = new Point(Width - 1 - c1.X, c1.Y);
- break;
- default:
- throw new InvalidEnumArgumentException();
- }
- if (this.drawNearNub)
- {
- g.FillPolygon(brush, new Point[] { a1, b1, c1, a1 });
- }
- if (this.drawFarNub)
- {
- g.FillPolygon(brush, new Point[] { a2, b2, c2, a2 });
- }
- if (pen != null)
- {
- if (this.drawNearNub)
- {
- g.DrawPolygon(pen, new Point[] { a1, b1, c1, a1 });
- }
- if (this.drawFarNub)
- {
- g.DrawPolygon(pen, new Point[] { a2, b2, c2, a2 });
- }
- pen.Dispose();
- }
- }
- }
- protected override void OnPaint(PaintEventArgs e)
- {
- base.OnPaint(e);
- DrawGradient(e.Graphics);
- }
- protected override void OnPaintBackground(PaintEventArgs pevent)
- {
- DrawGradient(pevent.Graphics);
- }
- /// <summary>
- /// Clean up any resources being used.
- /// </summary>
- protected override void Dispose(bool disposing)
- {
- if (disposing)
- {
- }
- base.Dispose(disposing);
- }
- private int PositionToValue(int pos)
- {
- int max;
- switch (this.orientation)
- {
- case Orientation.Horizontal:
- max = Width;
- break;
- case Orientation.Vertical:
- max = Height;
- break;
- default:
- throw new InvalidEnumArgumentException();
- }
- int val = (((max - triangleSize) - (pos - triangleHalfLength)) * 255) / (max - triangleSize);
- if (this.orientation == Orientation.Horizontal)
- {
- val = 255 - val;
- }
- return val;
- }
- private int ValueToPosition(int val)
- {
- int max;
- if (this.orientation == Orientation.Horizontal)
- {
- val = 255 - val;
- }
- switch (this.orientation)
- {
- case Orientation.Horizontal:
- max = Width;
- break;
- case Orientation.Vertical:
- max = Height;
- break;
- default:
- throw new InvalidEnumArgumentException();
- }
- int pos = triangleHalfLength + ((max - triangleSize) - (((val * (max - triangleSize)) / 255)));
- return pos;
- }
- private int WhichTriangle(int val)
- {
- int bestIndex = -1;
- int bestDistance = int.MaxValue;
- int v = PositionToValue(val);
- for (int i = 0; i < this.vals.Length; i++)
- {
- int distance = Math.Abs(this.vals[i] - v);
- if (distance < bestDistance)
- {
- bestDistance = distance;
- bestIndex = i;
- }
- }
- return bestIndex;
- }
- protected override void OnMouseDown(MouseEventArgs e)
- {
- base.OnMouseDown(e);
- if (e.Button == MouseButtons.Left)
- {
- int val = GetOrientedValue(e);
- tracking = WhichTriangle(val);
- Invalidate();
- OnMouseMove(e);
- }
- }
- protected override void OnMouseUp(MouseEventArgs e)
- {
- base.OnMouseUp(e);
- if (e.Button == MouseButtons.Left)
- {
- OnMouseMove(e);
- tracking = -1;
- Invalidate();
- }
- }
- private int GetOrientedValue(MouseEventArgs me)
- {
- return GetOrientedValue(new Point(me.X, me.Y));
- }
- private int GetOrientedValue(Point pt)
- {
- int pos;
- switch (this.orientation)
- {
- case Orientation.Horizontal:
- pos = pt.X;
- break;
- case Orientation.Vertical:
- pos = pt.Y;
- break;
- default:
- throw new InvalidEnumArgumentException();
- }
- return pos;
- }
- protected override void OnMouseMove(MouseEventArgs e)
- {
- base.OnMouseMove(e);
- int pos = GetOrientedValue(e);
- Point newMouseXY = new Point(e.X, e.Y);
- if (tracking >= 0 && newMouseXY != this.lastTrackingMouseXY)
- {
- int val = PositionToValue(pos);
- this.SetValue(tracking, val);
- this.lastTrackingMouseXY = newMouseXY;
- }
- else
- {
- int oldHighlight = highlight;
- highlight = WhichTriangle(pos);
- if (highlight != oldHighlight)
- {
- this.InvalidateTriangle(oldHighlight);
- this.InvalidateTriangle(highlight);
- }
- }
- }
- protected override void OnMouseLeave(EventArgs e)
- {
- int oldhighlight = highlight;
- highlight = -1;
- this.InvalidateTriangle(oldhighlight);
- }
- private void InvalidateTriangle(int index)
- {
- if (index < 0 || index >= this.vals.Length)
- {
- return;
- }
- int value = ValueToPosition(this.vals[index]);
- Rectangle rect;
- switch (this.orientation)
- {
- case Orientation.Horizontal:
- rect = new Rectangle(value - triangleHalfLength, 0, triangleSize, this.Height);
- break;
- case Orientation.Vertical:
- rect = new Rectangle(0, value - triangleHalfLength, this.Width, triangleSize);
- break;
- default:
- throw new InvalidEnumArgumentException();
- }
- this.Invalidate(rect, true);
- }
- #region Component Designer generated code
- /// <summary>
- /// Required method for Designer support - do not modify
- /// the contents of this method with the code editor.
- /// </summary>
- private void InitializeComponent()
- {
- }
- #endregion
- }
- }
|