using System;
using System.Runtime.CompilerServices;
using System.Drawing;
using System.Windows.Forms;
using System.ComponentModel;
namespace SpriteWave
{
public enum BGRA
{
Blue, Green, Red, Alpha
};
public enum ColorMode
{
RGB, HSV, Invalid
};
public class ColorPicker : Form, IPalettePicker
{
public delegate void CursorHandler(int x, int y);
public delegate void ScrollHandler(int delta);
private const int nChans = 4;
private const float dimmed = 0.8f;
private const float scrollUnit = 40f;
private const int tableH = 80;
private float[] _chn; // ordered by BGRA
private int[] _order;
//private ColorMode _mode;
private readonly string[] _labels;
private IPalette _refPal;
private int _refPalIdx;
private Bitmap _slider;
private Bitmap _dot;
//private Bitmap[] _modeImg;
private ColorBox _boxA;
private ColorBox _boxXY;
private ColorBox _boxZ;
private ColorBox _boxSample;
private Label[] _axisLabel;
private Label[] _chnLabel;
private TextBox[] _chnBox;
private bool _allowTextEvent = true;
private Button _cycle;
//private Button _switchMode;
private PalettePanel _paletteTable;
private MainForm _main;
public Color Color
{
get {
byte b = (byte)(_chn[0] * 255f);
byte g = (byte)(_chn[1] * 255f);
byte r = (byte)(_chn[2] * 255f);
return Color.FromArgb(255, r, g, b);
}
}
public Rectangle DotRect
{
get {
int dotX = (int)((1f - _chn[_order[1]]) * (float)_boxXY.Width);
int dotY = (int)((1f - _chn[_order[2]]) * (float)_boxXY.Height);
int off = _dot.Width / -2;
return new Rectangle(
dotX + off,
dotY + off,
_dot.Width,
_dot.Height
);
}
}
public Rectangle SliderRect(PictureBox ctrl, int idx)
{
int h = (int)((1f - _chn[_order[idx]]) * (float)ctrl.Height);
int off = (ctrl.Width - _slider.Width) / 2;
return new Rectangle(
off,
h + off,
_slider.Width,
_slider.Height
);
}
private Bitmap _sample;
private Rectangle _sampleRect = new Rectangle(0, 0, 8, 8);
public ColorPicker(MainForm main, int boxSize, IPalette refPal, int refPalIdx)
{
_main = main;
_sample = new Bitmap(_sampleRect.Width, _sampleRect.Height);
//_mode = ColorMode.RGB;
_chn = new[] {
1f, 1f, 1f, 1f
};
_order = new[] {
(int)BGRA.Alpha,
(int)BGRA.Red,
(int)BGRA.Green,
(int)BGRA.Blue
};
_labels = new string[nChans];
_labels[(int)BGRA.Red] = "R";
_labels[(int)BGRA.Green] = "G";
_labels[(int)BGRA.Blue] = "B";
_labels[(int)BGRA.Alpha] = "A";
this.Name = "colorPicker";
this.Text = "Color Picker";
int width = boxSize + 200, height = boxSize + 50;
bool createPalPanel = refPalIdx < 0;
if (createPalPanel)
{
refPalIdx = 0;
_paletteTable = new PalettePanel(this, refPal);
_paletteTable.Location = new Point(0, height);
_paletteTable.Size = new Size(width, tableH);
height += tableH;
}
this.ClientSize = new Size(width, height);
this.FormBorderStyle = FormBorderStyle.FixedSingle;
var resources = new ComponentResourceManager(typeof(ColorPicker));
_slider = (Bitmap)(resources.GetObject("slider"));
_dot = (Bitmap)(resources.GetObject("dot"));
/*
_modeImg = new Bitmap[2];
_modeImg[(int)ColorMode.RGB] = (Bitmap)(resources.GetObject("rgb"));
_modeImg[(int)ColorMode.HSV] = (Bitmap)(resources.GetObject("hsv"));
*/
_boxA = new ColorBox();
_boxA.Name = "alphaBox";
_boxA.Location = new Point(20, 20);
_boxA.Size = new Size(20, boxSize);
_boxA.Paint += this.paintLeftSlider;
_boxA.MouseWheel += (s, e) => this.scrollLeftSlider(e.Delta / 120);
SetColorBoxClick(_boxA, this.moveLeftSlider);
_boxXY = new ColorBox();
_boxXY.Name = "xyBox";
_boxXY.Location = new Point(60, 20);
_boxXY.Size = new Size(boxSize, boxSize);
_boxXY.Paint += this.paintDot;
SetColorBoxClick(_boxXY, this.moveDot);
_boxZ = new ColorBox();
_boxZ.Name = "zBox";
_boxZ.Location = new Point(80 + boxSize, 20);
_boxZ.Size = new Size(20, boxSize);
_boxZ.Paint += this.paintRightSlider;
_boxZ.MouseWheel += (s, e) => this.scrollRightSlider(e.Delta / 120);
SetColorBoxClick(_boxZ, this.moveRightSlider);
_boxSample = new ColorBox();
_boxSample.Name = "sampleBox";
_boxSample.Location = new Point(114 + boxSize, 20 + boxSize - 72);
_boxSample.Size = new Size(72, 72);
_cycle = new Button();
_cycle.Name = "cycleChans";
_cycle.Location = new Point(114 + boxSize, 20);
_cycle.Size = new Size(32, 32);
_cycle.Image = (Bitmap)resources.GetObject("cycle");
_cycle.Click += (s, e) => Cycle();
/*
_switchMode = new Button();
_switchMode.Name = "switchMode";
_switchMode.Location = new Point(152 + boxSize, 20);
_switchMode.Size = new Size(32, 32);
_switchMode.Image = _modeImg[(int)ColorMode.HSV];
_switchMode.Click += (s, e) => SwitchMode();
*/
_axisLabel = new Label[nChans];
for (int i = 0; i < nChans; i++)
{
_axisLabel[i] = new Label();
_axisLabel[i].Name = "axisLabel" + i;
_axisLabel[i].Font = new Font(Control.DefaultFont, FontStyle.Bold);
_axisLabel[i].Size = new Size(15, 20);
this.Controls.Add(_axisLabel[i]);
}
int midY = 13 + (boxSize / 2);
_axisLabel[0].Location = new Point(4, midY);
_axisLabel[1].Location = new Point(52 + (boxSize / 2), 24 + boxSize);
_axisLabel[2].Location = new Point(44, midY);
_axisLabel[3].Location = new Point(64 + boxSize, midY);
_chnLabel = new Label[nChans];
for (int i = 0; i < nChans; i++)
{
_chnLabel[i] = new Label();
_chnLabel[i].Name = "chnLabel" + i;
_chnLabel[i].Font = new Font(Control.DefaultFont, FontStyle.Bold);
_chnLabel[i].Location = new Point(114 + boxSize, 67 + i * 32);
_chnLabel[i].Size = new Size(15, 20);
this.Controls.Add(_chnLabel[i]);
}
_chnBox = new TextBox[nChans];
for (int i = 0; i < nChans; i++)
{
_chnBox[i] = new TextBox();
_chnBox[i].Name = "chnBox" + i;
_chnBox[i].Location = new Point(138 + boxSize, 64 + i * 32);
_chnBox[i].Size = new Size(45, 20);
_chnBox[i].TextChanged += this.updateField;
this.Controls.Add(_chnBox[i]);
}
RefreshInputFields();
this.Controls.Add(_boxA);
this.Controls.Add(_boxXY);
this.Controls.Add(_boxZ);
this.Controls.Add(_boxSample);
this.Controls.Add(_cycle);
//this.Controls.Add(_switchMode);
if (createPalPanel)
{
this.Controls.Add(_paletteTable);
_paletteTable.AdjustContents(ToolBoxOrientation.None);
}
this.KeyPreview = true;
this.KeyUp += (s, e) => ResetIcon();
this.KeyDown += (s, e) => {
if (e.KeyCode == Keys.Escape && _paletteTable != null)
{
_paletteTable.CurrentCell = -1;
_paletteTable.Draw();
}
};
Utils.ControlFunc updateFormIcon = (ctrl, args) => {ctrl.MouseUp += (s, e) => ResetIcon(); return null;};
Utils.ApplyRecursiveControlFunc(this, updateFormIcon);
SelectColorFrom(refPal, refPalIdx);
ResetIcon();
}
private void SetColorBoxClick(ColorBox box, Action<int, int> moveSlider)
{
box.MouseDown += (s, e) => moveSlider(e.X, e.Y);
box.MouseMove += (s, e) => { if (e.Button != MouseButtons.None && s == box) moveSlider(e.X, e.Y); };
box.MouseUp += (s, e) => { moveSlider(e.X, e.Y); UpdateColorSource(); };
}
public void SelectFromTable(PalettePanel panel, int cellIdx)
{
panel.CurrentCell = cellIdx;
SelectColorFrom(_refPal, cellIdx);
}
public void ResetIcon()
{
if (_paletteTable != null)
_paletteTable.Draw();
Utils.ClearBitmap(_sample, Color, _sampleRect);
this.Icon = Icon.FromHandle(_sample.GetHicon());
}
public void SelectColorFrom(IPalette pal, int palIdx)
{
_refPal = pal;
_refPalIdx = palIdx;
uint rgba = _refPal[_refPalIdx];
_chn[0] = (float)(rgba >> 24) / 255f;
_chn[1] = (float)((rgba >> 16) & 0xff) / 255f;
_chn[2] = (float)((rgba >> 8) & 0xff) / 255f;
_chn[3] = (float)(rgba & 0xff) / 255f;
RefreshInputFields();
Render();
}
private void UpdateColorSource()
{
_refPal[_refPalIdx] = Utils.ComposeBGRA(_chn[0], _chn[1], _chn[2], _chn[3]);
// Definitely not a hack /s
var cl = _main.spriteWnd.Collage;
if (cl != null)
cl.Render();
_main.PerformLayout();
}
public void RefreshInputFields()
{
_allowTextEvent = false;
for (int i = 0; i < nChans; i++)
{
_axisLabel[i].Text = _labels[_order[i]];
_chnLabel[3-i].Text = _labels[_order[i]];
_chnBox[3-i].Text = Math.Floor(_chn[_order[i]] * 255f).ToString();
}
_allowTextEvent = true;
}
public void SetAxis(int idx, float f)
{
_chn[_order[idx]] = Math.Min(Math.Max(f, 0), 1);
}
public void SetAxis(int idx, int num, int denom)
{
SetAxis(idx, 1f - ((float)num / (float)denom));
}
public void updateField(object sender, EventArgs e)
{
if (!_allowTextEvent)
return;
string name = (sender as Control).Name;
if (name.Length != 7 || name.Substring(0, 6) != "chnBox")
return;
int idx = name[6] - '0';
if (idx < 0 || idx > 3)
return;
float f = 0f;
try {
int n = Convert.ToInt32(_chnBox[idx].Text);
if (n < 0 || n > 255)
throw new InvalidOperationException();
f = (float)n / 255f;
}
catch (Exception ex) {
if (ex is FormatException ||
ex is OverflowException ||
ex is InvalidOperationException
)
return;
throw;
}
_chn[_order[3 - idx]] = f;
UpdateColorSource();
Render();
}
public void Cycle()
{
Action<int[], int> tick = (arr, idx) => arr[idx] = (arr[idx] + 1) % 3;
tick(_order, 1);
tick(_order, 2);
tick(_order, 3);
RefreshInputFields();
Render();
}
/*
public void SwitchMode()
{
Func<ColorMode, ColorMode> cycleMode = (m) =>
{
m++;
if (m == ColorMode.Invalid)
m = 0;
return m;
};
_mode = cycleMode(_mode);
_switchMode.Image = _modeImg[(int)cycleMode(_mode)];
}
*/
public void Render()
{
RenderAlphaBar();
RenderMainBox();
RenderRightBar();
RenderSampleBox();
if (_paletteTable != null)
_paletteTable.Draw();
this.Invalidate();
}
private struct AlphaPixel
{
public int blockSize;
public float[] shades;
public float blue;
public float green;
public float red;
public float alpha;
public AlphaPixel(int size, float[] sh, float b, float g, float r, float a)
{
blockSize = size;
shades = new[] { sh[0], sh[1] };
blue = b;
green = g;
red = r;
alpha = a;
}
}
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void RenderAlphaPixel(AlphaPixel info, byte[] buf, int w, int h, int idx)
{
const int frame = ColorBox.Border;
int x = idx % w;
int y = idx / w;
int xOdd = (x / info.blockSize) % 2;
int yOdd = (y / info.blockSize) % 2;
float shade = info.shades[xOdd ^ yOdd];
float lum = 255f;
if (x < frame || x >= w - frame || y < frame || y >= h - frame)
lum = 255f * dimmed;
int i = idx * 4;
buf[i + (int)BGRA.Blue] = (byte)((shade + (info.blue - shade) * info.alpha) * lum);
buf[i + (int)BGRA.Green] = (byte)((shade + (info.green - shade) * info.alpha) * lum);
buf[i + (int)BGRA.Red] = (byte)((shade + (info.red - shade) * info.alpha) * lum);
buf[i + (int)BGRA.Alpha] = 255;
}
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void RenderOpaquePixel(float b, float g, float r, byte[] buf, int w, int h, int idx)
{
const int frame = ColorBox.Border;
int x = idx % w;
int y = idx / w;
if (x < frame || x >= w - frame || y < frame || y >= h - frame)
{
b *= dimmed;
g *= dimmed;
r *= dimmed;
}
int i = idx * 4;
buf[i + (int)BGRA.Blue] = (byte)(b * 255f);
buf[i + (int)BGRA.Green] = (byte)(g * 255f);
buf[i + (int)BGRA.Red] = (byte)(r * 255f);
buf[i + (int)BGRA.Alpha] = 255; // axis0
}
private void RenderSampleBox()
{
_boxSample.Lock();
var buf = _boxSample.Buffer;
int w = _boxSample.Width;
int h = _boxSample.Height;
var info = new AlphaPixel(
w / 8,
Utils.AlphaShades,
_chn[0],
_chn[1],
_chn[2],
_chn[3]
);
for (int idx = 0; idx < buf.Length / 4; idx++)
RenderAlphaPixel(info, buf, w, h, idx);
_boxSample.Unlock();
}
private void RenderAlphaBar()
{
_boxA.Lock();
var buf = _boxA.Buffer;
int w = _boxA.Width;
int h = _boxA.Height;
var info = new AlphaPixel(
(w + 1) / 2,
Utils.AlphaShades,
_chn[0],
_chn[1],
_chn[2],
1f
);
for (int i = 0; i < buf.Length; i += 4)
{
int idx = i / 4;
info.alpha = 1f - (float)(idx / w) / (float)h;
RenderAlphaPixel(info, buf, w, h, idx);
}
_boxA.Unlock();
}
private void RenderMainBox()
{
_boxXY.Lock();
var buf = _boxXY.Buffer;
int w = _boxXY.Width;
int h = _boxXY.Height;
float[] axes = new float[3];
int a1 = _order[1];
int a2 = _order[2];
axes[_order[3]] = _chn[_order[3]];
for (int i = 0; i < buf.Length; i += 4)
{
axes[a1] = 1f - (float)((i / 4) % w) / (float)w;
axes[a2] = 1f - (float)((i / 4) / w) / (float)h;
RenderOpaquePixel(axes[0], axes[1], axes[2], buf, w, h, i / 4);
}
_boxXY.Unlock();
}
private void RenderRightBar()
{
_boxZ.Lock();
var buf = _boxZ.Buffer;
int w = _boxZ.Width;
int h = _boxZ.Height;
float[] axes = new float[3];
axes[_order[1]] = _chn[_order[1]];
axes[_order[2]] = _chn[_order[2]];
int a3 = _order[3];
for (int i = 0; i < buf.Length; i += 4)
{
axes[a3] = 1f - (float)((i / 4) / w) / (float)h;
RenderOpaquePixel(axes[0], axes[1], axes[2], buf, w, h, i / 4);
}
_boxZ.Unlock();
}
private void RefreshLeftSlider()
{
RenderSampleBox();
RefreshInputFields();
_boxA.Invalidate();
this.Invalidate();
}
private void RefreshDot()
{
RenderAlphaBar();
RenderRightBar();
RenderSampleBox();
RefreshInputFields();
_boxXY.Invalidate();
this.Invalidate();
}
private void RefreshRightSlider()
{
RenderAlphaBar();
RenderMainBox();
RenderSampleBox();
RefreshInputFields();
_boxZ.Invalidate();
this.Invalidate();
}
private void moveLeftSlider(int x, int y)
{
SetAxis(0, y, _boxA.Height);
RefreshLeftSlider();
}
private void moveDot(int x, int y)
{
SetAxis(1, x, _boxXY.Height);
SetAxis(2, y, _boxXY.Width);
RefreshDot();
}
private void moveRightSlider(int x, int y)
{
SetAxis(3, y, _boxZ.Height);
RefreshRightSlider();
}
private void scrollLeftSlider(int amount)
{
float delta = (float)amount / scrollUnit;
SetAxis(0, _chn[_order[0]] + delta);
RefreshLeftSlider();
}
private void scrollRightSlider(int amount)
{
float delta = (float)amount / scrollUnit;
SetAxis(3, _chn[_order[3]] + delta);
RefreshRightSlider();
}
private void paintLeftSlider(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(_slider, SliderRect(_boxA, 0));
}
private void paintDot(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(_dot, DotRect);
}
private void paintRightSlider(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(_slider, SliderRect(_boxZ, 3));
}
public void PaintUnderUI(Graphics g)
{
Func<PictureBox, Rectangle, Rectangle> addLoc = (ctrl, box)
=> new Rectangle(
ctrl.Location.X + box.X,
ctrl.Location.Y + box.Y,
box.Width,
box.Height
);
g.DrawImage(_slider, addLoc(_boxA, SliderRect(_boxA, 0)));
g.DrawImage(_dot, addLoc(_boxXY, DotRect));
g.DrawImage(_slider, addLoc(_boxZ, SliderRect(_boxZ, 3)));
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
PaintUnderUI(e.Graphics);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
_slider.Dispose();
_dot.Dispose();
_boxA.Dispose();
_boxXY.Dispose();
_boxZ.Dispose();
_boxSample.Dispose();
_sample.Dispose();
if (_paletteTable == null)
_main.ClearColorPicker();
}
}
}