Browse Source

Fix picking color from reference

Equbuxu 3 years ago
parent
commit
640db3543d

+ 9 - 21
PixiEditor/Models/ImageManipulation/BitmapUtils.cs

@@ -8,7 +8,6 @@ using SkiaSharp;
 using System;
 using System.Collections.Generic;
 using System.Linq;
-using System.Runtime.CompilerServices;
 using System.Windows;
 using System.Windows.Media;
 using System.Windows.Media.Imaging;
@@ -126,29 +125,18 @@ namespace PixiEditor.Models.ImageManipulation
             return result;
         }
 
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private static Color BlendColor(Color previousColor, Color color, float opacity)
+        public static SKColor BlendColors(SKColor bottomColor, SKColor topColor)
         {
-            if ((color.A < 255 && color.A > 0) || (opacity < 1f && opacity > 0 && color.A > 0))
+            if ((topColor.Alpha < 255 && topColor.Alpha > 0))
             {
-                byte pixelA = (byte)(color.A * opacity);
-                byte r = (byte)((color.R * pixelA / 255) + (previousColor.R * previousColor.A * (255 - pixelA) / (255 * 255)));
-                byte g = (byte)((color.G * pixelA / 255) + (previousColor.G * previousColor.A * (255 - pixelA) / (255 * 255)));
-                byte b = (byte)((color.B * pixelA / 255) + (previousColor.B * previousColor.A * (255 - pixelA) / (255 * 255)));
-                byte a = (byte)(pixelA + (previousColor.A * (255 - pixelA) / 255));
-                color = Color.FromArgb(a, r, g, b);
-            }
-            else
-            {
-                color = Color.FromArgb(color.A, color.R, color.G, color.B);
-            }
-
-            if (color.A > 0)
-            {
-                return color;
+                byte r = (byte)((topColor.Red * topColor.Alpha / 255) + (bottomColor.Red * bottomColor.Alpha * (255 - topColor.Alpha) / (255 * 255)));
+                byte g = (byte)((topColor.Green * topColor.Alpha / 255) + (bottomColor.Green * bottomColor.Alpha * (255 - topColor.Alpha) / (255 * 255)));
+                byte b = (byte)((topColor.Blue * topColor.Alpha / 255) + (bottomColor.Blue * bottomColor.Alpha * (255 - topColor.Alpha) / (255 * 255)));
+                byte a = (byte)(topColor.Alpha + (bottomColor.Alpha * (255 - topColor.Alpha) / 255));
+                return new SKColor(r, g, b, a);
             }
 
-            return previousColor;
+            return topColor.Alpha == 255 ? topColor : bottomColor;
         }
 
         private static WriteableBitmap GeneratePreviewBitmap(
@@ -193,7 +181,7 @@ namespace PixiEditor.Models.ImageManipulation
 
             int newWidth = width >= height ? maxPreviewWidth : (int)Math.Ceiling(width / ((float)height / maxPreviewHeight));
             int newHeight = height > width ? maxPreviewHeight : (int)Math.Ceiling(height / ((float)width / maxPreviewWidth));
-            return previewBitmap.Resize(newWidth, newHeight, WriteableBitmapExtensions.Interpolation.NearestNeighbor);*/
+            return previewBitmap.Redesize(newWidth, newHeight, WriteableBitmapExtensions.Interpolation.NearestNeighbor);*/
         }
     }
 }

+ 73 - 32
PixiEditor/Models/Tools/Tools/ColorPickerTool.cs

@@ -1,5 +1,6 @@
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.ImageManipulation;
 using PixiEditor.Models.Layers;
 using PixiEditor.Models.Position;
 using PixiEditor.Models.Services;
@@ -17,22 +18,23 @@ namespace PixiEditor.Models.Tools.Tools
     {
         private readonly DocumentProvider _docProvider;
         private readonly BitmapManager _bitmapManager;
+        private readonly string defaultActionDisplay = "Click to pick colors from the canvas. Hold Ctrl to pick from the reference layer. Hold Ctrl and Alt to blend the reference and canvas color";
 
         public ColorPickerTool(DocumentProvider documentProvider, BitmapManager bitmapManager)
         {
-            ActionDisplay = "Press on a pixel to make it the primary color. Hold Ctrl to pick from the reference. Hold Ctrl and Alt to blend the reference and canvas color";
+            ActionDisplay = defaultActionDisplay;
             _docProvider = documentProvider;
             _bitmapManager = bitmapManager;
         }
 
         public override bool HideHighlight => true;
 
-        public override string Tooltip => "Swaps primary color with selected on canvas. (O)";
+        public override string Tooltip => "Picks the primary color from the canvas. (O)";
 
         public override void Use(List<Coordinates> coordinates)
         {
             var coords = coordinates.First();
-            var doc = _docProvider.GetSurface();
+            var doc = _docProvider.GetDocument();
             if (coords.X < 0 || coords.Y < 0 || coords.X >= doc.Width || coords.Y >= doc.Height)
                 return;
 
@@ -41,34 +43,73 @@ namespace PixiEditor.Models.Tools.Tools
 
         public SKColor GetColorAt(int x, int y)
         {
-            SKColor? color = null;
-            Document activeDocument = _docProvider.GetDocument();
-            Layer referenceLayer;
+            Layer referenceLayer = _docProvider.GetReferenceLayer();
 
-            if ((Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
-                && (referenceLayer = _docProvider.GetReferenceLayer()) is not null)
+            if (referenceLayer != null && (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)))
             {
-                double actualX = activeDocument.MouseXOnCanvas * referenceLayer.Width / activeDocument.Width;
-                double actualY = activeDocument.MouseYOnCanvas * referenceLayer.Height / activeDocument.Height;
+                double preciseX = _docProvider.GetDocument().MouseXOnCanvas;
+                double preciseY = _docProvider.GetDocument().MouseYOnCanvas;
+
+                if ((Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt)))
+                {
+                    return GetCombinedColor(x, y, preciseX, preciseY);
+                }
+                return GetReferenceColor(preciseX, preciseY);
+            }
+
+            return GetCanvasColor(x, y);
+        }
+
+        private SKColor GetCombinedColor(int x, int y, double preciseX, double preciseY)
+        {
+            SKColor top = GetCanvasColor(x, y);
+            SKColor bottom = GetReferenceColor(preciseX, preciseY);
+            return BitmapUtils.BlendColors(bottom, top);
+        }
+
+        private SKColor GetCanvasColor(int x, int y)
+        {
+            return _docProvider.GetRenderer()?.FinalSurface.GetSRGBPixel(x, y) ?? SKColors.Transparent;
+        }
 
-                x = (int)Round(actualX, MidpointRounding.ToZero);
-                y = (int)Round(actualY, MidpointRounding.ToZero);
+        private SKColor GetReferenceColor(double x, double y)
+        {
+            Document activeDocument = _docProvider.GetDocument();
+            Layer referenceLayer = _docProvider.GetReferenceLayer();
+            Coordinates refPos = CanvasSpaceToReferenceSpace(
+                    x, y,
+                    activeDocument.Width, activeDocument.Height,
+                    referenceLayer.Width, referenceLayer.Height);
+
+            if (refPos.X >= 0 && refPos.Y >= 0 && refPos.X < referenceLayer.Width && refPos.Y < referenceLayer.Height)
+                return referenceLayer.LayerBitmap.GetSRGBPixel(refPos.X, refPos.Y);
+            return SKColors.Transparent;
+        }
 
-                color = referenceLayer.LayerBitmap.GetSRGBPixel(x, y);
+        private Coordinates CanvasSpaceToReferenceSpace(double canvasX, double canvasY, int canvasW, int canvasH, int referenceW, int referenceH)
+        {
+            double canvasRatio = canvasW / (double)canvasH;
+            double referenceRatio = referenceW / (double)referenceH;
+            bool blackBarsAreOnTopAndBottom = referenceRatio > canvasRatio;
+            if (blackBarsAreOnTopAndBottom)
+            {
+                double combinedBlackBarsHeight = (1 - canvasRatio / referenceRatio) * canvasH;
+                double refScale = referenceH / ((double)canvasH - combinedBlackBarsHeight);
+
+                int outX = (int)Math.Floor(canvasX * referenceW / canvasW);
+                int outY = (int)Math.Floor((canvasY - combinedBlackBarsHeight / 2) * refScale);
 
-                //if ((Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt)) && color != null)
-                //{
-                //    // TODO: Blend colors
-                //    throw new NotImplementedException();
-                //    //SKColor? canvasColor = _docProvider.GetRenderer()?.FinalSurface.GetSRGBPixel(x, y);
-                //}
+                return new Coordinates(outX, outY);
             }
             else
             {
-                color = _docProvider.GetRenderer()?.FinalSurface.GetSRGBPixel(x, y);
-            }
+                double combinedBlackBarsWidth = (1 - referenceRatio / canvasRatio) * canvasW;
+                double refScale = referenceW / ((double)canvasW - combinedBlackBarsWidth);
 
-            return color ?? SKColors.Transparent;
+                int outX = (int)Math.Floor((canvasX - combinedBlackBarsWidth / 2) * refScale);
+                int outY = (int)Math.Floor(canvasY * referenceH / canvasH);
+                return new Coordinates(outX, outY);
+            }
         }
 
         public override void OnKeyDown(KeyEventArgs e)
@@ -94,25 +135,25 @@ namespace PixiEditor.Models.Tools.Tools
 
         private void UpdateActionDisplay()
         {
-            if (Keyboard.IsKeyDown(Key.LeftCtrl) /*|| Keyboard.IsKeyDown(Key.RightCtrl)*/)
+            if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
             {
-                //if (Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt))
-                //{
-                //    _bitmapManager.HideReferenceLayer = false;
-                //    _bitmapManager.OnlyReferenceLayer = false;
-                //    ActionDisplay = "Press on a pixel to make the blend of the reference and canvas the primary color. Release Ctrl and Alt to pick from the canvas. Release just Alt to pick from the reference";
-                //    return;
-                //}
+                if (Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt))
+                {
+                    _bitmapManager.HideReferenceLayer = false;
+                    _bitmapManager.OnlyReferenceLayer = false;
+                    ActionDisplay = "Click to pick colors from both the canvas and the reference layer blended together. Release Ctrl and Alt to pick from the canvas. Release just Alt to pick from the reference layer";
+                    return;
+                }
 
                 _bitmapManager.HideReferenceLayer = false;
                 _bitmapManager.OnlyReferenceLayer = true;
-                ActionDisplay = "Press on a pixel on the reference to make it the primary color. Release Ctrl to pick from the canvas. Hold Ctrl and Alt to blend the reference and canvas color";
+                ActionDisplay = "Click to pick colors from the reference layer. Release Ctrl to pick from the canvas. Hold Ctrl and Alt to blend the reference and canvas color";
             }
             else
             {
                 _bitmapManager.HideReferenceLayer = true;
                 _bitmapManager.OnlyReferenceLayer = false;
-                ActionDisplay = "Press on a pixel to make it the primary color. Hold Ctrl to pick from the reference.";
+                ActionDisplay = defaultActionDisplay;
             }
         }
     }