Subversion Repositories AndroidProjects

Rev

Rev 819 | Blame | Compare with Previous | Last modification | View Log | RSS feed

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;

using System.Runtime.InteropServices;

using BauzoidNET.math;

namespace BauzoidNET.controls
{
    public partial class ColorWidget: UserControl
    {
        private enum MouseDownState
        {
            NONE,
            WHEEL,
            BOX
        }

        private Bitmap mHueWheel = null;
        private Bitmap mGradientBox = null;

        private float mCurrentHue = 0;
        private float mCurrentSaturation = 0.5f;
        private float mCurrentValue = 0.5f;

        private MouseDownState mMouseDownState = MouseDownState.NONE;

        private float mWheelThicknessFactor = 0.2f;
        private int mMarkerSize = 4;

        public ColorWidget()
        {
            InitializeComponent();


            /*Vector3 hsv = MathUtil.rgbToHsv(new Vector3(0.0f, 0.0f, 1.0f));

            Vector3 rgb = MathUtil.hsvToRgb(hsv);

            Vector3 rgb2 = MathUtil.hsvToRgb(new Vector3(240.0f, 1.0f, 1.0f));*/



            createHueWheel();
            createGradientBox();
            colorWheelBox.Invalidate();
        }

        private void createGradientBox()
        {
            mGradientBox = new Bitmap(GradientBoxSize, GradientBoxSize);
            Rectangle rect = new Rectangle(0, 0, mGradientBox.Width, mGradientBox.Height);
            BitmapData data = mGradientBox.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

            IntPtr ptr = data.Scan0;
            int bytes = data.Stride * mGradientBox.Height;
            var rgbValues = new byte[bytes];

            int width = mGradientBox.Width;
            int height = mGradientBox.Height;

            for (int x = 0; x < width; x++)
            {
                for (int y = 0; y < height; y++)
                {
                    float s = (float)x / (float)width;
                    float v = 1.0f-(float)y / (float)height;

                    Vector3 rgbColor = MathUtil.hsvToRgb(new Vector3(mCurrentHue, s, v));

                    //rgbValues[i++] = (byte)(rgbColor.x * 255);
                    //rgbValues[i++] = (byte)(rgbColor.y * 255);
                    //rgbValues[i++] = (byte)(rgbColor.z * 255);
                   
                    byte a = 255;
                    byte r = (byte)(rgbColor.x * 255);
                    byte g = (byte)(rgbColor.y * 255);
                    byte b = (byte)(rgbColor.z * 255);

                    rgbValues[(x * 4 + y * data.Stride) + 0] = b;
                    rgbValues[(x * 4 + y * data.Stride) + 1] = g;
                    rgbValues[(x * 4 + y * data.Stride) + 2] = r;
                    rgbValues[(x * 4 + y * data.Stride) + 3] = a;
                }
            }

            //Vector3 test = MathUtil.hsvToRgb(new Vector3(mCurrentHue, 1.0f, 1.0f));
            //System.Diagnostics.Debug.WriteLine("Hue: " + mCurrentHue + " RGB: " + test.x + ", " + test.y + ", " + test.z);

            Marshal.Copy(rgbValues, 0, ptr, bytes);
            mGradientBox.UnlockBits(data);
        }

        private void createHueWheel()
        {
            mHueWheel = new Bitmap(Width*2, Height*2);

            Rectangle rect = new Rectangle(0, 0, Width, Height);
            BitmapData data = mHueWheel.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

            IntPtr ptr = data.Scan0;
            int bytes = data.Stride * mHueWheel.Height;
            var rgbValues = new byte[bytes];

            int width = mHueWheel.Width;
            int height = mHueWheel.Height;

            int centerX = width / 2;
            int centerY = height / 2;

            float s = 1.0f;
            float v = 1.0f;

            for (int x = 0; x < width; x++)
            {
                for (int y = 0; y < height; y++)
                {
                    byte a = 0;
                    byte r = 64;
                    byte g = 64;
                    byte b = 64;

                    Vector2 vec = new Vector2(centerX - x, centerY - y);
                    float dist = (float)System.Math.Sqrt(vec.x * vec.x + vec.y * vec.y);
                    if ((dist >= InnerRadius*2) && (dist <= OuterRadius*2))
                    //if (isInsideWheel((int)x/2, (int)y/2))
                    {
                        float theta = MathUtil.radToDeg((float)Math.Atan2(vec.y, -vec.x));

                        // rotate by 90 degrees
                        theta -= 90.0f;
                        if (theta < -180.0f)
                            theta += 360.0f;

/*                        theta -= Math.PI / 2;
                        if (theta < -Math.PI)
                            theta += (Math.PI * 2);*/


                        //float h = (float)((theta + System.Math.PI) / (2 * System.Math.PI));
                        float h = (theta + 180.0f);
                        //System.Diagnostics.Debug.WriteLine("h = " + h);
                        Vector3 rgbColor = MathUtil.hsvToRgb(new Vector3(h, s, v));

                        a = 255;
                        r = (byte)(rgbColor.x * 255);
                        g = (byte)(rgbColor.y * 255);
                        b = (byte)(rgbColor.z * 255);

                        /*rgbValues[i++] = (byte)(rgbColor.x * 255);
                        rgbValues[i++] = (byte)(rgbColor.y * 255);
                        rgbValues[i++] = (byte)(rgbColor.z * 255);*/

                    }

                    rgbValues[(x * 4 + y * data.Stride) + 0] = r;
                    rgbValues[(x * 4 + y * data.Stride) + 1] = g;
                    rgbValues[(x * 4 + y * data.Stride) + 2] = b;
                    rgbValues[(x * 4 + y * data.Stride) + 3] = a;
                }
            }

            Marshal.Copy(rgbValues, 0, ptr, bytes);
            mHueWheel.UnlockBits(data);

            drawCircle(Graphics.FromImage(mHueWheel), InnerRadius - 2);
            drawCircle(Graphics.FromImage(mHueWheel), OuterRadius + 2);
        }

        private void drawCircle(Graphics g, int radius)
        {
            int diameter = radius * 2;
            g.DrawEllipse(new Pen(Color.FromArgb(192, 192, 192), 2), new Rectangle((Width - diameter), (Height - diameter), diameter * 2, diameter * 2));
        }

        private void colorWheelBox_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;

            g.InterpolationMode = InterpolationMode.HighQualityBilinear;
            g.SmoothingMode = SmoothingMode.AntiAlias;

            Rectangle rect = new Rectangle(0, 0, Width, Height);
            g.DrawImage(mHueWheel, rect);

            int boxOffset = 2;

            int x = getGradientBoxLeft();
            int y = getGradientBoxTop();

            Rectangle rect2 = new Rectangle(x, y, GradientBoxSize, GradientBoxSize);
            g.DrawImage(mGradientBox, rect2);
            g.DrawRectangle(new Pen(Color.FromArgb(192, 192, 192), 0.5f),
                new Rectangle(x - boxOffset, y - boxOffset, GradientBoxSize + boxOffset * 2, GradientBoxSize + boxOffset * 2));

            drawHueMarker(g);
            drawSaturationValueMarker(g);
        }

        private int getGradientBoxLeft()
        {
            return (int)((Width - GradientBoxSize) / 2) + 1;
        }

        private int getGradientBoxTop()
        {
            return (int)((Height - GradientBoxSize) / 2) + 1;
        }

        private void drawHueMarker(Graphics g)
        {
            int center_radius = (OuterRadius + InnerRadius) / 2;
            float radHue = (float)(MathUtil.degToRad(mCurrentHue - 150.0f));

            float x = (float)Math.Cos(radHue) * center_radius + Width / 2;
            float y = (float)Math.Sin(radHue) * center_radius + Height / 2;

            Vector3 rgbColor = MathUtil.hsvToRgb(new Vector3((float)(mCurrentHue), 1.0f, 1.0f));

            /*System.Diagnostics.Debug.WriteLine("==");
            System.Diagnostics.Debug.WriteLine("Marker Hue: " + mCurrentHue + " RGB: " + rgbColor.x + ", " + rgbColor.y + ", " + rgbColor.z);
            System.Diagnostics.Debug.WriteLine("==");*/


            /*System.Diagnostics.Debug.WriteLine("==");
            System.Diagnostics.Debug.WriteLine("HSV: " + mCurrentHue);
            System.Diagnostics.Debug.WriteLine("RGB: " + rgbColor.x + ", " + rgbColor.y + ", " + rgbColor.z);
            System.Diagnostics.Debug.WriteLine("==");*/


            Color color = Color.FromArgb((int)((1-rgbColor.x) * 255), (int)((1-rgbColor.y) * 255), (int)((1-rgbColor.z) * 255));

            g.DrawEllipse(new Pen(color, 2), new Rectangle((int)x - MarkerSize, (int)y - MarkerSize, MarkerSize * 2, MarkerSize * 2));
        }

        private void drawSaturationValueMarker(Graphics g)
        {
            Vector3 rgbColor = MathUtil.hsvToRgb(new Vector3(mCurrentHue, mCurrentSaturation, mCurrentValue));
            Color color = Color.FromArgb((int)((1 - rgbColor.x) * 255), (int)((1 - rgbColor.y) * 255), (int)((1 - rgbColor.z) * 255));

            int x = (int)(getGradientBoxLeft() + (float)(GradientBoxSize-1) * mCurrentSaturation);
            int y = (int)(getGradientBoxTop() + (float)(GradientBoxSize-1) * (1.0f - mCurrentValue));

            g.DrawEllipse(new Pen(color, 2), new Rectangle((int)x - MarkerSize, (int)y - MarkerSize, MarkerSize * 2, MarkerSize * 2));
        }

        private void colorWheelBox_Resize(object sender, EventArgs e)
        {
            createHueWheel();
        }

        private void colorWheelBox_MouseDown(object sender, MouseEventArgs e)
        {
            if (isInsideWheel(e.X, e.Y))
            {
                mMouseDownState = MouseDownState.WHEEL;
                updateHue(e.X, e.Y);
            }
            else if (isInsideGradientBox(e.X, e.Y))
            {
                mMouseDownState = MouseDownState.BOX;
                updateSaturationValue(e.X, e.Y);
            }
            else
            {
                mMouseDownState = MouseDownState.NONE;
            }
        }

        private void colorWheelBox_MouseMove(object sender, MouseEventArgs e)
        {
            if (mMouseDownState == MouseDownState.WHEEL)
            {
                updateHue(e.X, e.Y);
            }
            else if (mMouseDownState == MouseDownState.BOX)
            {
                updateSaturationValue(e.X, e.Y);
            }
        }

        private void colorWheelBox_MouseUp(object sender, MouseEventArgs e)
        {
            if (mMouseDownState == MouseDownState.WHEEL)
            {
                updateHue(e.X, e.Y);
            }
            else if (mMouseDownState == MouseDownState.BOX)
            {
                updateSaturationValue(e.X, e.Y);
            }
            mMouseDownState = MouseDownState.NONE;
        }

        private void updateHue(int x, int y)
        {
            int centerX = x - Width / 2;
            int centerY = y - Height / 2;

            float theta = MathUtil.radToDeg((float)Math.Atan2(centerY, centerX));
            theta -= 90.0f;
            if (theta < -180.0f)
                theta += 360.0f;

            mCurrentHue = theta + 180.0f + 60.0f;
            if (mCurrentHue >= 360.0f)
                mCurrentHue -= 360.0f;

            /*theta -= Math.PI / 2;
            if (theta < -Math.PI)
                theta += (Math.PI * 2);*/

            // theta can go from -pi to pi

            //mCurrentHue = MathUtil.clamp((float)((theta + System.Math.PI) / (2 * System.Math.PI)), 0, 1);
            //System.Diagnostics.Debug.WriteLine("Hue: " + mCurrentHue);

            createGradientBox();
            colorWheelBox.Invalidate();

            OnColorChange(EventArgs.Empty);
        }

        private void updateSaturationValue(int x, int y)
        {
            int left = getGradientBoxLeft();
            int top = getGradientBoxTop();

            int _x = x - left;
            int _y = y - top;

            mCurrentSaturation = MathUtil.clamp(((float)_x / (float)GradientBoxSize), 0.0f, 1.0f);
            mCurrentValue = 1.0f-MathUtil.clamp(((float)_y / (float)GradientBoxSize), 0.0f, 1.0f);
           
            colorWheelBox.Invalidate();

            OnColorChange(EventArgs.Empty);
        }

        private void setFromColor(Color color)
        {
            Vector3 c = new Vector3((float)color.R / 255.0f, (float)color.G / 255.0f, (float)color.B / 255.0f);
            Vector3 hsv = MathUtil.rgbToHsv(c);

            mCurrentHue = hsv.x;
            mCurrentSaturation = hsv.y;
            mCurrentValue = hsv.z;

            /*System.Diagnostics.Debug.WriteLine("=====");
            System.Diagnostics.Debug.WriteLine("HSV Set: " + hsv.x + ", " + hsv.y + ", " + hsv.z);
            System.Diagnostics.Debug.WriteLine("=====");*/


            createGradientBox();
            colorWheelBox.Invalidate();
        }

        private float getDistanceFromCenter(int x, int y)
        {
            int centerX = x - Width / 2;
            int centerY = y - Height / 2;

            return (float)System.Math.Sqrt(centerX * centerX + centerY * centerY);
        }

        private bool isInsideWheel(int x, int y)
        {
            float dist = getDistanceFromCenter(x, y);
            return isInsideWheel(dist);
        }

        private bool isInsideGradientBox(int x, int y)
        {
            int left = getGradientBoxLeft();
            int top = getGradientBoxTop();

            return ((x >= left) && (x <= (left + GradientBoxSize)) && (y >= top) && (y <= (top + GradientBoxSize)));
        }

        private bool isInsideWheel(float distanceFromCenter)
        {
            return ((distanceFromCenter >= InnerRadius) && (distanceFromCenter <= OuterRadius));
        }

        public int OuterRadius
        {
            get { return (Math.Min(Width, Height) / 2 - 4); }
        }

        public int InnerRadius
        {
            get { return (int)(OuterRadius - (float)OuterRadius * mWheelThicknessFactor); }
        }

        public int GradientBoxSize
        {
            get { return (int)Math.Sqrt(InnerRadius * InnerRadius / 2) * 2 - 6; }
        }

        public float Hue
        {
            get { return mCurrentHue; }
        }

        public float Saturation
        {
            get { return mCurrentSaturation; }
        }

        public float Value
        {
            get { return mCurrentValue; }
        }

        public Color CurrentColor
        {
            get
            {
                Vector3 rgbColor = MathUtil.hsvToRgb(new Vector3(mCurrentHue, mCurrentSaturation, mCurrentValue));
                return Color.FromArgb((int)((rgbColor.x) * 255), (int)((rgbColor.y) * 255), (int)((rgbColor.z) * 255));
            }

            set
            {
                setFromColor(value);
            }
        }

        [Description("Size of the Marker"), Category("Appearance")]
        public int MarkerSize
        {
            get { return mMarkerSize; }
            set { mMarkerSize = value; }
        }

        [Description("Wheel size relative to wheel radius"), Category("Appearance")]
        public float WheelThicknessFactor
        {
            get { return mWheelThicknessFactor; }
            set
            {
                mWheelThicknessFactor = value;
                createHueWheel();
                colorWheelBox.Invalidate();
            }
        }

        [Category("Action")]
        [Description("Fires when the color is changed")]
        public event EventHandler ColorChange;

        protected virtual void OnColorChange(EventArgs e)
        {
            EventHandler handler = this.ColorChange;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        private void ColorWidget_Load(object sender, EventArgs e)
        {
            OnColorChange(EventArgs.Empty);
        }
    }
}