From 156e67df9bc660d9562addfccae602021989af77 Mon Sep 17 00:00:00 2001 From: Sergey Svinolobov Date: Mon, 3 Jul 2023 19:16:02 -0400 Subject: [PATCH] implemented setter for PickedColor property implemented proper behavoir for PickedColor updates updated demo app (UWP only) by adding comparsion of Xamarin system colors --- .../Controls/ColorPicker.xaml.cs | 103 ++++++++++++++++-- .../XFColorPickerControl/MainPage.xaml | 99 +++++++++-------- .../XFColorPickerControl/MainPage.xaml.cs | 50 ++++++++- 3 files changed, 193 insertions(+), 59 deletions(-) diff --git a/XFColorPickerControl/XFColorPickerControl/Controls/ColorPicker.xaml.cs b/XFColorPickerControl/XFColorPickerControl/Controls/ColorPicker.xaml.cs index c0df5c6..64e8a4e 100644 --- a/XFColorPickerControl/XFColorPickerControl/Controls/ColorPicker.xaml.cs +++ b/XFColorPickerControl/XFColorPickerControl/Controls/ColorPicker.xaml.cs @@ -1,4 +1,6 @@ using System; +using System.Diagnostics; +using System.Linq; using SkiaSharp; using SkiaSharp.Views.Forms; using Xamarin.Forms; @@ -27,10 +29,18 @@ public static readonly BindableProperty PickedColorProperty public Color PickedColor { get { return (Color)GetValue(PickedColorProperty); } - private set { SetValue(PickedColorProperty, value); } + set + { + if (!PickedColor.Equals(value)) + { + SetValue(PickedColorProperty, value); + _lastTouchPoint = new SKPoint() { X = int.MaxValue, Y = int.MaxValue }; + SkCanvasView.InvalidateSurface(); + PickedColorChanged?.Invoke(this, PickedColor); + } + } } - public static readonly BindableProperty GradientColorStyleProperty = BindableProperty.Create( nameof(GradientColorStyle), @@ -48,7 +58,6 @@ public GradientColorStyle GradientColorStyle set { SetValue(GradientColorStyleProperty, value); } } - public static readonly BindableProperty ColorListProperty = BindableProperty.Create( nameof(ColorList), @@ -75,7 +84,6 @@ public string[] ColorList set { SetValue(ColorListProperty, value); } } - public static readonly BindableProperty ColorListDirectionProperty = BindableProperty.Create( nameof(ColorListDirection), @@ -113,7 +121,6 @@ public double PointerCircleDiameterUnits set { SetValue(PointerCircleDiameterUnitsProperty, value); } } - public static readonly BindableProperty PointerCircleBorderUnitsProperty = BindableProperty.Create( nameof(PointerCircleBorderUnits), @@ -133,14 +140,26 @@ public double PointerCircleBorderUnits set { SetValue(PointerCircleBorderUnitsProperty, value); } } - - private SKPoint _lastTouchPoint = new SKPoint(); + private SKPoint _lastTouchPoint = new SKPoint() { X = int.MaxValue, Y = int.MaxValue}; public ColorPicker() { InitializeComponent(); } + /// + /// I found this algorythm at https://stackoverflow.com/a/33782458/648919 + /// It's simple enough but really effective! + /// + private double ColorDistance(SKColor c1, SKColor c2) + { + int rmean = (c1.Red + c2.Red) / 2; + int r = c1.Red - c2.Red; + int g = c1.Green - c2.Green; + int b = c1.Blue - c2.Blue; + return Math.Sqrt((((512 + rmean) * r * r) >> 8) + 4 * g * g + (((767 - rmean) * b * b) >> 8)); + } + private void SkCanvasView_OnPaintSurface(object sender, SKPaintSurfaceEventArgs e) { var skImageInfo = e.Info; @@ -199,7 +218,7 @@ private void SkCanvasView_OnPaintSurface(object sender, SKPaintSurfaceEventArgs // Picking the Pixel Color values on the Touch Point // Represent the color of the current Touch point - SKColor touchPointColor; + SKColor touchPointColor = SKColors.White; // Efficient and fast // https://forums.xamarin.com/discussion/92899/read-a-pixel-info-from-a-canvas @@ -209,6 +228,70 @@ private void SkCanvasView_OnPaintSurface(object sender, SKPaintSurfaceEventArgs // get the pixel buffer for the bitmap IntPtr dstpixels = bitmap.GetPixels(); + // If color isn't set by touch, we need to find a color location on bitmap + if (_lastTouchPoint.X.Equals(int.MaxValue) && _lastTouchPoint.Y.Equals(int.MaxValue)) + { + // read the surface into the bitmap + var res = skSurface.ReadPixels(skImageInfo, dstpixels, skImageInfo.RowBytes, 0, 0); + touchPointColor = PickedColor.ToSKColor(); + var bpp = bitmap.BytesPerPixel; + + // Get rid of SKBitmap getters + var pixels = new byte[bitmap.ByteCount]; + bitmap.Bytes.CopyTo(pixels, 0); + + // Let's try to find our color coordinates + var exactMatch = false; + var minDistance = double.MaxValue; + + for (int y = 0; y < skCanvasHeight; y++) + { + for (int x = 0; x < skCanvasWidth; x++) + { + var c = new SKColor(pixels[(y * skCanvasWidth + x) * bpp + 2], + pixels[(y * skCanvasWidth + x) * bpp + 1], + pixels[(y * skCanvasWidth + x) * bpp + 0]); + + // Check for exact color mach first + if (c.Equals(touchPointColor)) + { + _lastTouchPoint.X = x; + _lastTouchPoint.Y = y; + exactMatch = true; + break; + } + else + { + var dist = ColorDistance(touchPointColor, c); + if (dist < 5) + { + _lastTouchPoint.X = x; + _lastTouchPoint.Y = y; + exactMatch = true; + break; + } + else if (minDistance > dist) + { + minDistance = dist; + _lastTouchPoint.X = x; + _lastTouchPoint.Y = y; + +#if false + // Small optimization based on RL tests; btw, it works + // fast enough even without these lines if (minDistance < 15) + { + exactMatch = true; + break; + } +#endif + } + } + } + + if (exactMatch) break; + } + } + // read the surface into the bitmap skSurface.ReadPixels(skImageInfo, dstpixels, @@ -253,8 +336,7 @@ private void SkCanvasView_OnPaintSurface(object sender, SKPaintSurfaceEventArgs } // Set selected color - PickedColor = touchPointColor.ToFormsColor(); - PickedColorChanged?.Invoke(this, PickedColor); + SetValue(PickedColorProperty, touchPointColor.ToFormsColor()); } private void SkCanvasView_OnTouch(object sender, SKTouchEventArgs e) @@ -272,6 +354,7 @@ private void SkCanvasView_OnTouch(object sender, SKTouchEventArgs e) // update the Canvas as you wish SkCanvasView.InvalidateSurface(); + PickedColorChanged?.Invoke(this, PickedColor); } } diff --git a/XFColorPickerControl/XFColorPickerControl/MainPage.xaml b/XFColorPickerControl/XFColorPickerControl/MainPage.xaml index 18a0857..f3b043d 100644 --- a/XFColorPickerControl/XFColorPickerControl/MainPage.xaml +++ b/XFColorPickerControl/XFColorPickerControl/MainPage.xaml @@ -12,14 +12,12 @@ + - + - - -