WPF、カラーピッカーの土台できた
前回の続き、ピックアップマーカーの○印とSV(彩度と輝度)画像を付け足した
結果
環境
- Windows 10 Home バージョン 22H2
- Visual Studio Community 2022 Version 17.5.4
- WPF
- C#
- .NET 6.0
コード
全部
一部
Picker.xaml
<Window x:Class="_20230419_ColorPicker.Picker" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:_20230419_ColorPicker" mc:Ignorable="d" Title="Picker" Height="400" Width="400"> <Grid> <StackPanel> <StackPanel Orientation="Horizontal" Height="200"> <Image x:Name="MyImageSV" Width="200" Height="200"/> <Slider x:Name="MySliderA" Maximum="255" Minimum="0" SmallChange="1" LargeChange="10" Orientation="Vertical" Value="255"/> <Slider x:Name="MySliderR" Maximum="255" Minimum="0" SmallChange="1" LargeChange="10" Orientation="Vertical"/> <Slider x:Name="MySliderG" Maximum="255" Minimum="0" SmallChange="1" LargeChange="10" Orientation="Vertical"/> <Slider x:Name="MySliderB" Maximum="255" Minimum="0" SmallChange="1" LargeChange="10" Orientation="Vertical"/> <Slider x:Name="MySliderH" Maximum="360" Minimum="0" SmallChange="1" LargeChange="10" Orientation="Vertical"/> <Slider x:Name="MySliderS" Maximum="1" Minimum="0" SmallChange="0.01" LargeChange="0.1" Orientation="Vertical"/> <Slider x:Name="MySliderV" Maximum="1" Minimum="0" SmallChange="0.01" LargeChange="0.1" Orientation="Vertical"/> <Slider Value="{Binding ElementName=MyImageSV, Path=Height}" Minimum="10" Maximum="200" LargeChange="20" TickFrequency="20" Orientation="Vertical"/> <Slider Value="{Binding Marker.MarkerSize}" Minimum="10" Maximum="200" Orientation="Vertical" LargeChange="20"/> </StackPanel> <StackPanel> <Slider Value="{Binding ElementName=MyImageSV, Path=Width}" Minimum="10" Maximum="300" LargeChange="50"/> <StackPanel Orientation="Horizontal"> <Grid> <Viewbox> <TextBlock Text="透明度"/> </Viewbox> <Border x:Name="MyBorderPickColorSample" Width="100" Background="DodgerBlue"/> </Grid> <StackPanel> <TextBlock Text="{Binding PickColor, StringFormat=pickcolor \= {0}}"/> <TextBlock Text="{Binding A, StringFormat=a 000}"/> <TextBlock Text="{Binding R, StringFormat=r 000}"/> <TextBlock Text="{Binding G, StringFormat=g 000}"/> <TextBlock Text="{Binding B, StringFormat=b 000}"/> <TextBlock Text="{Binding H, StringFormat=h 000}"/> <TextBlock Text="{Binding S, StringFormat=s 0.00}"/> <TextBlock Text="{Binding V, StringFormat=v 0.00}"/> </StackPanel> </StackPanel> </StackPanel> </StackPanel> </Grid> </Window>
Picker.xaml..cs
using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; namespace _20230419_ColorPicker { /// <summary> /// Picker.xaml の相互作用ロジック /// </summary> public partial class Picker : Window { #region 依存関係プロパティ public Color PickColor { get { return (Color)GetValue(PickColorProperty); } set { SetValue(PickColorProperty, value); } } public static readonly DependencyProperty PickColorProperty = DependencyProperty.Register(nameof(PickColor), typeof(Color), typeof(Picker), new FrameworkPropertyMetadata(Color.FromArgb(0, 0, 0, 0), FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); /// <summary> /// HSVを再計算、RGB変更時に使用 /// </summary> /// <param name="d"></param> /// <param name="e"></param> private static void OnRGB(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is Picker mw) { if (mw.IsHSVChangNow) return; mw.IsRGBChangNow = true; (mw.H, mw.S, mw.V) = MathHSV.RGB2hsv(mw.R, mw.G, mw.B); mw.UpdateSVWriteableBitmap(mw.H); mw.IsRGBChangNow = false; } } /// <summary> /// RGBを再計算、SV変更時に使用 /// </summary> /// <param name="d"></param> /// <param name="e"></param> private static void OnHSV(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is Picker mw) { if (mw.IsRGBChangNow) return; mw.IsHSVChangNow = true; (mw.R, mw.G, mw.B) = MathHSV.Hsv2rgb(mw.H, mw.S, mw.V); mw.IsHSVChangNow = false; } } private static void OnHue(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is Picker mw) { if (mw.IsRGBChangNow) return; mw.IsHSVChangNow = true; (mw.R, mw.G, mw.B) = MathHSV.Hsv2rgb(mw.H, mw.S, mw.V); mw.UpdateSVWriteableBitmap(mw.H); mw.IsHSVChangNow = false; } } public byte R { get { return (byte)GetValue(RProperty); } set { SetValue(RProperty, value); } } public static readonly DependencyProperty RProperty = DependencyProperty.Register(nameof(R), typeof(byte), typeof(Picker), new FrameworkPropertyMetadata(byte.MinValue, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnRGB))); public byte G { get { return (byte)GetValue(GProperty); } set { SetValue(GProperty, value); } } public static readonly DependencyProperty GProperty = DependencyProperty.Register(nameof(G), typeof(byte), typeof(Picker), new FrameworkPropertyMetadata(byte.MinValue, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnRGB))); public byte B { get { return (byte)GetValue(BProperty); } set { SetValue(BProperty, value); } } public static readonly DependencyProperty BProperty = DependencyProperty.Register(nameof(B), typeof(byte), typeof(Picker), new FrameworkPropertyMetadata(byte.MinValue, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnRGB))); public byte A { get { return (byte)GetValue(AProperty); } set { SetValue(AProperty, value); } } public static readonly DependencyProperty AProperty = DependencyProperty.Register(nameof(A), typeof(byte), typeof(Picker), new FrameworkPropertyMetadata(byte.MinValue, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); public double H { get { return (double)GetValue(HProperty); } set { SetValue(HProperty, value); } } public static readonly DependencyProperty HProperty = DependencyProperty.Register(nameof(H), typeof(double), typeof(Picker), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnHue))); public double S { get { return (double)GetValue(SProperty); } set { SetValue(SProperty, value); } } public static readonly DependencyProperty SProperty = DependencyProperty.Register(nameof(S), typeof(double), typeof(Picker), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnHSV))); public double V { get { return (double)GetValue(VProperty); } set { SetValue(VProperty, value); } } public static readonly DependencyProperty VProperty = DependencyProperty.Register(nameof(V), typeof(double), typeof(Picker), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnHSV))); public double MarkerSize { get { return (double)GetValue(MarkerSizeProperty); } set { SetValue(MarkerSizeProperty, value); } } public static readonly DependencyProperty MarkerSizeProperty = DependencyProperty.Register(nameof(MarkerSize), typeof(double), typeof(Picker), new FrameworkPropertyMetadata(20.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); #endregion 依存関係プロパティ ////無限ループ防止用フラグ private bool IsRGBChangNow; private bool IsHSVChangNow; public Marker Marker { get; set; } //SVimageのBitmapSourceのサイズは16あれば十分? private readonly int SVBitmapSize = 16; public WriteableBitmap SVWriteableBitmap { get; private set; } public byte[] SVPixels { get; private set; } private readonly int SVStride; #region コンストラクタ public Picker() { InitializeComponent(); //SV画像のSourceはWriterableBitmap、これのPixelsを書き換えるようにした //PixelFormats.Rgb24の1ピクセルあたりのbyte数は24/8=3 SVStride = SVBitmapSize * 3; Marker = new Marker(MyImageSV); SVPixels = new byte[SVBitmapSize * SVStride]; SVWriteableBitmap = new(SVBitmapSize, SVBitmapSize, 96, 96, PixelFormats.Rgb24, null); MyImageSV.Source = SVWriteableBitmap; DataContext = this; SetSliderBindings(); SetMyBindings(); SetMarkerBinding(); MyImageSV.Stretch = Stretch.Fill; //PickColor = Color.FromArgb(200, 100, 202, 52); PickColor = Color.FromArgb(255, 255, 0, 0); Loaded += Picker_Loaded; Closing += Picker_Closing; } //色指定あり public Picker(Color color) : this() { //Color指定だけだとAとHueしか反映されないので //Markerコンストラクタで彩度と輝度を指定 PickColor = color; var (h, s, v) = MathHSV.Color2HSV(color); Marker = new Marker(MyImageSV, s, v); //A = color.A; R = color.R; G = color.G; B = color.B; //(H, S, V) = MathHSV.Color2HSV(color); } #endregion コンストラクタ //外からの色の指定 public void SetColor(Color color) { PickColor = color; } //Windowは閉じないで非表示 private void Picker_Closing(object? sender, System.ComponentModel.CancelEventArgs e) { e.Cancel = true; this.Hide(); } private void Picker_Loaded(object sender, RoutedEventArgs e) { if (AdornerLayer.GetAdornerLayer(MyImageSV) is AdornerLayer layer) { layer.Add(Marker); } //PickColor = Color.FromArgb(255, 255, 255, 255); // //SetMarkerBinding(); } private void SetMarkerBinding() { SetBinding(SProperty, new Binding() { Source = Marker, Path = new PropertyPath(Marker.SaturationProperty) }); SetBinding(VProperty, new Binding() { Source = Marker, Path = new PropertyPath(Marker.ValueProperty) }); Marker.SetBinding(Marker.MarkerSizeProperty, new Binding() { Source = this, Path = new PropertyPath(MarkerSizeProperty) }); MarkerSize = 40; } private void UpdateSVWriteableBitmap(double hue) { int p = 0; Parallel.For(0, SVBitmapSize, y => { ParallelImageSV(p, y, SVStride, SVPixels, hue, SVBitmapSize, SVBitmapSize); }); SVWriteableBitmap.WritePixels(new Int32Rect(0, 0, SVBitmapSize, SVBitmapSize), SVPixels, SVStride, 0); } private void ParallelImageSV(int p, int y, int stride, byte[] pixels, double hue, int w, int h) { double v = y / (h - 1.0); double ww = w - 1; for (int x = 0; x < w; ++x) { p = y * stride + (x * 3); (pixels[p], pixels[p + 1], pixels[p + 2]) = MathHSV.Hsv2rgb(hue, x / ww, v); } } private void SetMyBindings() { MultiBinding mb = new(); mb.Bindings.Add(new Binding() { Source = this, Path = new PropertyPath(AProperty) }); mb.Bindings.Add(new Binding() { Source = this, Path = new PropertyPath(RProperty) }); mb.Bindings.Add(new Binding() { Source = this, Path = new PropertyPath(GProperty) }); mb.Bindings.Add(new Binding() { Source = this, Path = new PropertyPath(BProperty) }); mb.Converter = new ConverterARGB2Color(); SetBinding(PickColorProperty, mb); MyBorderPickColorSample.SetBinding(BackgroundProperty, new Binding() { Source = this, Path = new PropertyPath(PickColorProperty), Converter = new ConverterColor2Brush() }); ; } private void SetSliderBindings() { MySliderA.SetBinding(Slider.ValueProperty, new Binding() { Source = this, Path = new PropertyPath(AProperty) }); MySliderR.SetBinding(Slider.ValueProperty, new Binding() { Source = this, Path = new PropertyPath(RProperty) }); MySliderG.SetBinding(Slider.ValueProperty, new Binding() { Source = this, Path = new PropertyPath(GProperty) }); MySliderB.SetBinding(Slider.ValueProperty, new Binding() { Source = this, Path = new PropertyPath(BProperty) }); MySliderH.SetBinding(Slider.ValueProperty, new Binding() { Source = this, Path = new PropertyPath(HProperty) }); MySliderS.SetBinding(Slider.ValueProperty, new Binding() { Source = this, Path = new PropertyPath(SProperty) }); MySliderV.SetBinding(Slider.ValueProperty, new Binding() { Source = this, Path = new PropertyPath(VProperty) }); } } public class ConverterColor2Brush : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { Color c = (Color)value; return new SolidColorBrush(c); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } public class ConverterARGB2Color : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { byte a = (byte)values[0]; byte r = (byte)values[1]; byte g = (byte)values[2]; byte b = (byte)values[3]; return Color.FromArgb(a, r, g, b); } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { Color cb = (Color)value; object[] result = new object[4]; result[0] = cb.A; result[1] = cb.R; result[2] = cb.G; result[3] = cb.B; return result; } } }
PickMarker.cs
using System; using System.Globalization; using System.Windows.Controls.Primitives; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Shapes; using System.Windows; namespace _20230419_ColorPicker { public class Marker : Adorner { #region 依存関係プロパティ public double Saturation { get { return (double)GetValue(SaturationProperty); } set { SetValue(SaturationProperty, value); } } public static readonly DependencyProperty SaturationProperty = DependencyProperty.Register(nameof(Saturation), typeof(double), typeof(Marker), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); public double Value { get { return (double)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(nameof(Value), typeof(double), typeof(Marker), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); public double MarkerSize { get { return (double)GetValue(MarkerSizeProperty); } set { SetValue(MarkerSizeProperty, value); } } public static readonly DependencyProperty MarkerSizeProperty = DependencyProperty.Register(nameof(MarkerSize), typeof(double), typeof(Marker), new FrameworkPropertyMetadata(20.0, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); public SolidColorBrush Color1 { get { return (SolidColorBrush)GetValue(Color1Property); } set { SetValue(Color1Property, value); } } public static readonly DependencyProperty Color1Property = DependencyProperty.Register(nameof(Color1), typeof(SolidColorBrush), typeof(Marker), new FrameworkPropertyMetadata(Brushes.Black, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); public SolidColorBrush Color2 { get { return (SolidColorBrush)GetValue(Color2Property); } set { SetValue(Color2Property, value); } } public static readonly DependencyProperty Color2Property = DependencyProperty.Register(nameof(Color2), typeof(SolidColorBrush), typeof(Marker), new FrameworkPropertyMetadata(Brushes.White, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); #endregion 依存関係プロパティ private VisualCollection MyVisuals { get; set; } protected override int VisualChildrenCount => MyVisuals.Count; protected override Visual GetVisualChild(int index) => MyVisuals[index]; public Thumb MarkerThumb; public Canvas MyCanvas; public FrameworkElement TargetElement; private Point DiffPoint; #region コンストラクタ //通常 public Marker(FrameworkElement adornedElement) : base(adornedElement) { TargetElement = adornedElement; MyVisuals = new(this); MyCanvas = new(); MyVisuals.Add(MyCanvas); MarkerThumb = new Thumb(); SetMyCanvas(); SetMarker(); SetMarkerTemplate(); MarkerThumb.DragDelta += Marker_DragDelta; MarkerThumb.DragCompleted += (s, e) => { DiffPoint = new(); }; } //色指定で開くとき、彩度と輝度の指定が必要 public Marker(FrameworkElement adornedElement, double saturation, double value) : this(adornedElement) { Saturation = saturation; Value = value; } #endregion コンストラクタ // private void SetMyCanvas() { MyCanvas.Background = Brushes.Transparent; MyCanvas.MouseLeftButtonDown += MyCanvas_MouseLeftButtonDown; MyCanvas.Children.Add(MarkerThumb); MyCanvas.SetBinding(WidthProperty, new Binding() { Source = this, Path = new PropertyPath(ActualWidthProperty) }); MyCanvas.SetBinding(HeightProperty, new Binding() { Source = this, Path = new PropertyPath(ActualHeightProperty) }); } //SaturationとValueをクリック座標に相当する値に更新+ //更新前後の座標差を記録、これは //続けてドラッグ移動した場合に使う private void MyCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { Point pp = Mouse.GetPosition(MyCanvas); double dx = pp.X - Canvas.GetLeft(MarkerThumb) - (MarkerSize / 2.0); double dy = pp.Y - Canvas.GetTop(MarkerThumb) - (MarkerSize / 2.0); DiffPoint = new Point(dx, dy); double xx = pp.X / TargetElement.ActualWidth; if (xx < 0) xx = 0; if (xx > 1.0) xx = 1.0; Saturation = xx; double yy = pp.Y / TargetElement.ActualHeight; if (yy < 0) yy = 0; if (yy > 1.0) yy = 1.0; Value = yy; MarkerThumb.RaiseEvent(e); } private void SetMarker() { //SV変化でtopleft変化 MultiBinding mb = new(); mb.Bindings.Add(new Binding() { Source = this, Path = new PropertyPath(MarkerSizeProperty) }); mb.Bindings.Add(new Binding() { Source = this, Path = new PropertyPath(SaturationProperty) }); mb.Bindings.Add(new Binding() { Source = TargetElement, Path = new PropertyPath(ActualWidthProperty) }); mb.Converter = new ConverterTopLeft2XY(); MarkerThumb.SetBinding(Canvas.LeftProperty, mb); mb = new(); mb.Bindings.Add(new Binding() { Source = this, Path = new PropertyPath(MarkerSizeProperty) }); mb.Bindings.Add(new Binding() { Source = this, Path = new PropertyPath(ValueProperty) }); mb.Bindings.Add(new Binding() { Source = TargetElement, Path = new PropertyPath(ActualHeightProperty) }); mb.Converter = new ConverterTopLeft2XY(); MarkerThumb.SetBinding(Canvas.TopProperty, mb); } private void SetMarkerTemplate() { FrameworkElementFactory factory = new(typeof(Grid)); FrameworkElementFactory e1 = new(typeof(Ellipse)); e1.SetValue(WidthProperty, new Binding() { Source = this, Path = new PropertyPath(MarkerSizeProperty) }); e1.SetValue(HeightProperty, new Binding() { Source = this, Path = new PropertyPath(MarkerSizeProperty) }); e1.SetValue(Ellipse.StrokeProperty, new Binding() { Source = this, Path = new PropertyPath(Color1Property) }); e1.SetValue(Ellipse.FillProperty, Brushes.Transparent); FrameworkElementFactory e2 = new(typeof(Ellipse)); e2.SetValue(WidthProperty, new Binding() { Source = this, Path = new PropertyPath(MarkerSizeProperty), Converter = new ConverterDownSize() }); e2.SetValue(HeightProperty, new Binding() { Source = this, Path = new PropertyPath(MarkerSizeProperty), Converter = new ConverterDownSize() }); e2.SetValue(Ellipse.StrokeProperty, new Binding() { Source = this, Path = new PropertyPath(Color2Property), }); factory.AppendChild(e1); factory.AppendChild(e2); MarkerThumb.Template = new() { VisualTree = factory }; } private void Marker_DragDelta(object sender, DragDeltaEventArgs e) { var left = Canvas.GetLeft(MarkerThumb); var top = Canvas.GetTop(MarkerThumb); var h = e.HorizontalChange; var v = e.VerticalChange; // ドラッグ移動ではtopleftを指定ではなく、saturation,Valueを計算して指定 var dx = DiffPoint.X + (MarkerSize / 2.0); var dy = DiffPoint.Y + (MarkerSize / 2.0); double ll = left + h + dx; double xx = ll / TargetElement.ActualWidth; if (xx < 0) xx = 0; if (xx > 1.0) xx = 1.0; Saturation = xx; double tt = top + v + dy; double yy = tt / TargetElement.ActualHeight; if (yy < 0) yy = 0; if (yy > 1.0) yy = 1.0; Value = yy; } protected override Size ArrangeOverride(Size finalSize) { MyCanvas.Arrange(new Rect(finalSize)); return base.ArrangeOverride(finalSize); } } public class ConverterTopLeft2XY : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { double markerSize = (double)values[0]; double sv = (double)values[1]; double targetSize = (double)values[2]; double result = (sv * targetSize) - (markerSize / 2.0); return result; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } public class ConverterDownSize : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { double size = ((double)value) - 2.0; if (size < 0) { size = 0; } return size; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } }
SV画像は16x16ピクセル
彩度と輝度の画像をSV画像って呼んでいる
色相Hueが変化したらSV画像も更新する
SV画像にはWriteableBitmapを使っている
SとVの値は0から1の間なので、以前は画像サイズを100x100ピクセルにしてそのまま表示していた
これを今回は16x16にした
これの拡大表示はWPF(.NET?)に任せている
拡大したぶんは表示が劣化しているはずなんだけど、16x16あれば気にならない
並べたら違うってのはわかるけど、16x16を単体で見たら気づかない
2x2のたった4ピクセルでもそれなりに表示してくれるのはおもしろい
SV画像の更新
以前は更新のたびにWriteableBitmap自体を、Newで作り直していたので効率が良くなかった
今回はWriteableBitmapの中のピクセルデータのbyte配列の値だけを作り直すようにした
これでメモリ使用量が減って処理速度も上がったはず
実際にはSV画像サイズを小さくしたから誤差レベルなんだけど、気分がいい
今見てて思ったのが
生成部でParallel.Forを使っているけど16x16まで小さな画像なら普通のforでも処理速度変わらないかも
関連記事
前回のWPF記事は一昨日
gogowaten.hatenablog.com
以前のカラーピッカーは5年前
gogowaten.hatenablog.com