User avatar
stonstad
Topic Author
Posts: 241
Joined: 06 Jun 2016, 18:14
Location: Lesser Magellanic Cloud
Contact:

Updated Color Picker [Code]

13 Feb 2020, 20:23

I gave the color picker example a try. (viewtopic.php?f=12&t=187&p=996&hilit=color+picker#p996) It looks like the Noesis API has changed quite a bit since the example was initially created. I ran into compiler errors.

Here is an updated version which works with Noesis 2.2.x. I added a ColorChanged event for real-time color changes.

Image

ColorPickerControl.xaml
<UserControl
  x:Class="Example.ColorPickerControl"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  UseLayoutRounding="True">

    <UserControl.Resources>
        <Style x:Key="SpectrumSliderButtonStyle" TargetType="{x:Type RepeatButton}">
            <Setter Property="OverridesDefaultStyle" Value="True"/>
            <Setter Property="UseLayoutRounding" Value="True"/>
            <Setter Property="IsTabStop" Value="False"/>
            <Setter Property="Focusable" Value="False"/>
            <Setter Property="ClickMode" Value="Press"/>
            <Setter Property="Delay" Value="250"/>
            <Setter Property="Interval" Value="100"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type RepeatButton}">
                        <Border Background="Transparent" />
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <ControlTemplate x:Key="SpectrumSliderThumbTemplate" TargetType="{x:Type Thumb}">
            <Grid Background="Transparent">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="32"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <Path Grid.Column="0" Data="M0,0 L16,10 0,20 z" Fill="White" HorizontalAlignment="Right" Margin="0, 0, 0, -5"/>
                <Path Grid.Column="2" Data="M16,0 L0,10 16,20 z" Fill="White" HorizontalAlignment="Left" Margin="0, 0, 0, -5"/>
            </Grid>
        </ControlTemplate>
        <ControlTemplate x:Key="SpectrumSliderTemplate" TargetType="{x:Type Slider}">
            <Grid>
                <Rectangle StrokeThickness="1" Stroke="#000000" Width="32">
                    <Rectangle.Fill>
                        <LinearGradientBrush StartPoint="0.5, 0" EndPoint="0.5, 1">
                            <GradientStop Color="#FFFF0000" Offset="0"/>
                            <GradientStop Color="#FFFF00FF" Offset="0.1666666"/>
                            <GradientStop Color="#FF0000FF" Offset="0.3333333"/>
                            <GradientStop Color="#FF00FFFF" Offset="0.5"/>
                            <GradientStop Color="#FF00FF00" Offset="0.6666666"/>
                            <GradientStop Color="#FFFFFF00" Offset="0.8333333"/>
                            <GradientStop Color="#FFFF0000" Offset="1"/>
                        </LinearGradientBrush>
                    </Rectangle.Fill>
                </Rectangle>
                <Track x:Name="PART_Track">
                    <Track.Thumb>
                        <Thumb Template="{StaticResource SpectrumSliderThumbTemplate}" Margin="0,-4"/>
                    </Track.Thumb>
                    <Track.DecreaseRepeatButton>
                        <RepeatButton Style="{StaticResource SpectrumSliderButtonStyle}" Command="Slider.DecreaseLarge" />
                    </Track.DecreaseRepeatButton>
                    <Track.IncreaseRepeatButton>
                        <RepeatButton Style="{StaticResource SpectrumSliderButtonStyle}" Command="Slider.IncreaseLarge" />
                    </Track.IncreaseRepeatButton>
                </Track>
            </Grid>
        </ControlTemplate>
    </UserControl.Resources>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>

        <!-- Color selected -->
        <Grid Grid.Row="0" Grid.ColumnSpan="2">
            <StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0">
                <Rectangle Width="50" Height="50" Margin="10" StrokeThickness="1" Stroke="#000000">
                    <Rectangle.Fill>
                        <SolidColorBrush x:Name="Color" Color="#FF0000"/>
                    </Rectangle.Fill>
                </Rectangle>
                <TextBlock x:Name="Text" FontSize="30" Margin="15, 0, 0, 0" VerticalAlignment="Center" Text="Text" Foreground="White"/>
            </StackPanel>
        </Grid>

        <!-- Picker -->
        <Grid x:Name="HS" Grid.Row="1" Grid.Column="0">
            <Rectangle>
                <Rectangle.Fill>
                    <LinearGradientBrush StartPoint="0,1" EndPoint="0,0">
                        <GradientStop Color="White" Offset="0"/>
                        <GradientStop x:Name="Stop" Color="Red" Offset="1"/>
                    </LinearGradientBrush>
                </Rectangle.Fill>
            </Rectangle>
            <Rectangle StrokeThickness="1" Stroke="#000000" Height="512" Width="512">
                <Rectangle.Fill>
                    <LinearGradientBrush StartPoint="1,0" EndPoint="0,0">
                        <GradientStop Offset="0" Color="#00000000"/>
                        <GradientStop Offset="1" Color="#FF000000"/>
                    </LinearGradientBrush>
                </Rectangle.Fill>
            </Rectangle>

            <Canvas Height="24" Width="24" RenderTransformOrigin="0.5,0.5" ClipToBounds="true">
                <Canvas.RenderTransform>
                    <TranslateTransform x:Name="PickerTransform" X="0" Y="0"/>
                </Canvas.RenderTransform>
                <Grid>
                    <Ellipse Stroke="Black" Width="24" Height="24"/>
                    <Ellipse Stroke="White" Width="20" Height="20"/>
                </Grid>
            </Canvas>

        </Grid>

        <!-- Spectrum -->
        <Grid Grid.Row="1" Grid.Column="1">
            <Slider x:Name="Slider" Orientation="Vertical" IsMoveToPointEnabled="True" Minimum="0" Maximum="360" Value="0" Template="{StaticResource SpectrumSliderTemplate}" Margin="10, 0, 0, 0"/>
        </Grid>

    </Grid>
</UserControl>
ColorPicker.cs
using Noesis;
using System;
using NoesisEventArgs = Noesis.EventArgs;

namespace Example
{
    public class ColorPickerControl : UserControl
    {
        public static readonly DependencyProperty ColorBrushProperty = DependencyProperty.Register("ColorBrush",
            typeof(SolidColorBrush), typeof(ColorPickerControl), new PropertyMetadata(null));

        public event EventHandler<ColorEventArgs> ColorChanged;

        private FrameworkElement _Root;
        private Slider _Spectrum;
        private Grid _HsGrid;
        private TranslateTransform _PickerTransform;
        private TextBlock _Text;
        private SolidColorBrush _Color;
        private GradientStop _Stop;

        private float _Hue;
        private float _Saturation;
        private float _Value;

        public ColorPickerControl()
        {
            Name = "ColorPickerControl";
            InitializeComponent();
        }

        protected void InitializeComponent()
        {
            GUI.LoadComponent(this, "Assets/User Interface/Controls/Color Picker Control/ColorPickerControl.xaml");
            _Root = this.Content as FrameworkElement;

            _Spectrum = _Root.FindName("Slider") as Slider;
            _HsGrid = _Root.FindName("HS") as Grid;
            _PickerTransform = _Root.FindName("PickerTransform") as TranslateTransform;
            _Text = _Root.FindName("Text") as TextBlock;
            _Color = _Root.FindName("Color") as SolidColorBrush;
            _Stop = _Root.FindName("Stop") as GradientStop;

            _Spectrum.ValueChanged += this.OnSpectrumChange;
            _HsGrid.MouseLeftButtonDown += this.OnMouseLeftButtonDown;
            _HsGrid.MouseLeftButtonUp += this.OnMouseLeftButtonUp;
            _HsGrid.MouseMove += this.OnMouseMove;
            _HsGrid.SizeChanged += this.OnSizeChanged;

            _Hue = 0.0f;
            _Saturation = 0.5f;
            _Value = 0.5f;

            Update();
        }

        public SolidColorBrush ColorBrush
        {
            get { return (SolidColorBrush)GetValue(ColorBrushProperty); }
            set { SetValue(ColorBrushProperty, value); }
        }

        private void OnSpectrumChange(object sender, RoutedPropertyChangedEventArgs<float> e)
        {
            _Hue = e.NewValue;
            _Stop.Color = HSVToColor(_Hue, 1, 1);
            Update();
        }

        private void OnMouseLeftButtonDown(Object sender, MouseButtonEventArgs args)
        {
            Focus();
            _HsGrid.CaptureMouse();
            Point point = _HsGrid.PointFromScreen(args.GetPosition(null));
            UpdatePickerPosition(point);
        }

        private void OnMouseLeftButtonUp(Object sender, MouseButtonEventArgs args)
        {
            _HsGrid.ReleaseMouseCapture();
        }

        private void OnMouseMove(object sender, MouseEventArgs args)
        {
            if (_HsGrid.IsMouseCaptured)
            {
                Point point = _HsGrid.PointFromScreen(args.GetPosition(null));
                UpdatePickerPosition(point);
            }
        }

        private void OnSizeChanged(object sender, SizeChangedEventArgs args)
        {
            Size size = args.NewSize;
            _PickerTransform.X = _Value * size.Width - 0.5f * size.Width;
            _PickerTransform.Y = size.Height - _Saturation * size.Height - 0.5f * size.Height;
        }

        private void UpdatePickerPosition(Point pos)
        {
            Size size = _HsGrid.RenderSize;

            pos.X = Math.Max(0.0f, Math.Min(size.Width, pos.X));
            pos.Y = Math.Max(0.0f, Math.Min(size.Height, pos.Y));

            _PickerTransform.X = pos.X - 0.5f * size.Width;
            _PickerTransform.Y = pos.Y - 0.5f * size.Height;

            _Value = pos.X / size.Width;
            _Saturation = (size.Height - pos.Y) / size.Height;

            Update();
        }

        private void Update()
        {
           Color color = HSVToColor(_Hue, _Saturation, _Value);
            int red = color.R;
            int green = color.G;
            int blue = color.B;
            _Text.Text = string.Format("#{0:X2}{1:X2}{2:X2}", red, green, blue);
            _Color.Color = color;
            ColorBrush = new SolidColorBrush(color);

            ColorChanged?.Invoke(this, new ColorEventArgs(color));
        }

        private Color HSVToColor(float hue, float saturation, float value)
        {
            float chroma = value * saturation;
            float hueTag = (hue % 360) / 60;
            float x = chroma * (1 - Math.Abs(hueTag % 2.0f - 1));
            float m = value - chroma;

            switch ((int)hueTag)
            {
                case 0:
                    return Color.FromScRgb(1, chroma + m, x + m, m);
                case 1:
                    return Color.FromScRgb(1, x + m, chroma + m, m);
                case 2:
                    return Color.FromScRgb(1, m, chroma + m, x + m);
                case 3:
                    return Color.FromScRgb(1, m, x + m, chroma + m);
                case 4:
                    return Color.FromScRgb(1, x + m, m, chroma + m);
                default:
                    return Color.FromScRgb(1, chroma + m, m, x + m);
            }
        }
    }

    public class ColorEventArgs : NoesisEventArgs
    {
        public new Color Empty { get; protected set; }
        public Color Color { get; protected set; }

        public ColorEventArgs(Color color)
        {
            Color = color;
        }
    }
}
Last edited by stonstad on 03 Aug 2020, 17:55, edited 1 time in total.
 
User avatar
stonstad
Topic Author
Posts: 241
Joined: 06 Jun 2016, 18:14
Location: Lesser Magellanic Cloud
Contact:

Re: Updated Color Picker

13 Feb 2020, 20:30

Related... here is logic I use to convert between Unity, Noesis, and integer representations of color.

Shaun
Stellar Conquest (https://twitter.com/stellarconquest)

using NoesisColor = Noesis.Color;
using UnityColor = UnityEngine.Color;
using UnityColor32 = UnityEngine.Color32;
using UnityTexture2D = UnityEngine.Texture2D;

namespace Example
{
    public class ColorConversionUtility
    {
        public static UnityColor NoesisColorToUnityColor(NoesisColor color)
        {
            return new UnityColor(color.R / 255.0f, color.G / 255.0f, color.B / 255.0f, color.A / 255.0f);
        }

        public static NoesisColor UnityColorToNoesisColor(UnityColor color)
        {
            return NoesisColor.FromArgb((byte)(color.a * 255), (byte)(color.r * 255), (byte)(color.g * 255), (byte)(color.b * 255));
        }

        public static int NoesisColorToInt(NoesisColor color)
        {
            return ARGBColorToInt(color.A, color.R, color.G, color.B);
        }

        public static NoesisColor IntToNoesisColor(int argb32)
        {
            const int mask = 0x000000FF;
            byte a, r, g, b;
            a = (byte)((argb32 >> 24) & mask);
            r = (byte)((argb32 >> 16) & mask);
            g = (byte)((argb32 >> 8) & mask);
            b = (byte)(argb32 & mask);
            return new NoesisColor() { A = a, R = r, G = g, B = b };
        }

        public static UnityColor IntToUnityColor(int argb32)
        {
            const int mask = 0x000000FF;
            byte a, r, g, b;
            a = (byte)((argb32 >> 24) & mask);
            r = (byte)((argb32 >> 16) & mask);
            g = (byte)((argb32 >> 8) & mask);
            b = (byte)(argb32 & mask);
            return new UnityColor(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f);
        }

        public static byte[] IntToColorBytes(int argb32)
        {
            const int mask = 0x000000FF;
            byte a, r, g, b;
            a = (byte)((argb32 >> 24) & mask);
            r = (byte)((argb32 >> 16) & mask);
            g = (byte)((argb32 >> 8) & mask);
            b = (byte)(argb32 & mask);
            return new byte[] { a, r, g, b };
        }

        private static int ARGBColorToInt(byte a, byte r, byte g, byte b)
        {
            int argb32 = a << 24 | r << 16 | g << 8 | b;
            return argb32;
        }

        public static void ConvertToPremultipliedAlpha(UnityTexture2D source)
        {
            UnityColor[] colors = source.GetPixels();
            for (int i = 0; i < colors.Length; i++)
            {
                UnityColor color = colors[i];
                colors[i] = new UnityColor(color.r * color.a, color.g * color.a, color.b * color.a, color.a);
            }

            source.SetPixels(colors);
            source.Apply();
        }

        public static void ConvertToPremultipliedAlpha(UnityColor[] colors)
        {
            for (int i = 0; i < colors.Length; i++)
            {
                UnityColor color = colors[i];
                colors[i] = new UnityColor(color.r * color.a, color.g * color.a, color.b * color.a, color.a);
            }
        }

        public static UnityColor32 GetAverageColor(UnityTexture2D texture)
        {
            UnityColor32[] texColors = texture.GetPixels32();
            int n = texColors.Length;

            float r = 0;
            float g = 0;
            float b = 0;

            for (int i = 0; i < n; i++)
            {
                r += texColors[i].r;
                g += texColors[i].g;
                b += texColors[i].b;
            }

            return new UnityColor32((byte)(r / n), (byte)(g / n), (byte)(b / n), 0);
        }
    }
}
 
User avatar
jsantos
Site Admin
Posts: 3905
Joined: 20 Jan 2012, 17:18
Contact:

Re: Updated Color Picker

21 Feb 2020, 18:07

Thanks a lot for this great contribution!
 
Strela_999
Posts: 3
Joined: 21 Feb 2020, 16:29

Re: Updated Color Picker

25 Feb 2020, 07:36

Awesome, it really makes my life easier! I already liked the way to choose colors in the base program, but this makes it even better.

Who is online

Users browsing this forum: No registered users and 0 guests