diff --git a/src/modules/colorPicker/ColorPickerUI/Behaviors/GridEffectBehavior.cs b/src/modules/colorPicker/ColorPickerUI/Behaviors/GridEffectBehavior.cs new file mode 100644 index 0000000000..503ec31859 --- /dev/null +++ b/src/modules/colorPicker/ColorPickerUI/Behaviors/GridEffectBehavior.cs @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Windows; +using System.Windows.Interactivity; +using ColorPicker.Shaders; + +namespace ColorPicker.Behaviors +{ + public class GridEffectBehavior : Behavior + { + private static double baseZoomImageSizeInPx = 50; + + [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/dependency-property-security#:~:text=Dependency%20properties%20should%20generally%20be%20considered%20to%20be,make%20security%20guarantees%20about%20a%20dependency%20property%20value.")] + public static DependencyProperty EffectProperty = DependencyProperty.Register("Effect", typeof(GridShaderEffect), typeof(GridEffectBehavior)); + [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/dependency-property-security#:~:text=Dependency%20properties%20should%20generally%20be%20considered%20to%20be,make%20security%20guarantees%20about%20a%20dependency%20property%20value.")] + public static DependencyProperty ZoomFactorProperty = DependencyProperty.Register("ZoomFactor", typeof(double), typeof(GridEffectBehavior)); + + public GridShaderEffect Effect + { + get { return (GridShaderEffect)GetValue(EffectProperty); } + set { SetValue(EffectProperty, value); } + } + + public double ZoomFactor + { + get { return (double)GetValue(ZoomFactorProperty); } + set { SetValue(ZoomFactorProperty, value); } + } + + protected override void OnAttached() + { + AssociatedObject.Loaded += AssociatedObject_Loaded; + } + + private void AssociatedObject_Loaded(object sender, RoutedEventArgs e) + { + AssociatedObject.MouseMove += AssociatedObject_MouseMove; + } + + private void AssociatedObject_MouseMove(object sender, System.Windows.Input.MouseEventArgs e) + { + var position = e.GetPosition(AssociatedObject); + + var relativeX = position.X / AssociatedObject.ActualWidth; + var relativeY = position.Y / AssociatedObject.Height; + Effect.MousePosition = new Point(relativeX, relativeY); + if (ZoomFactor >= 4) + { + Effect.Radius = 0.04; + Effect.SquareSize = ZoomFactor; + Effect.TextureSize = baseZoomImageSizeInPx * ZoomFactor; + } + else + { + // don't show grid, too small pixels + Effect.Radius = 0.0; + Effect.SquareSize = 0; + Effect.TextureSize = 0; + } + } + } +} diff --git a/src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj b/src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj index 213742aca3..c45da430b1 100644 --- a/src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj +++ b/src/modules/colorPicker/ColorPickerUI/ColorPickerUI.csproj @@ -81,7 +81,6 @@ true - true @@ -107,11 +106,14 @@ + + + @@ -208,6 +210,7 @@ SettingsSingleFileGenerator Settings.Designer.cs + @@ -254,5 +257,8 @@ all + + + \ No newline at end of file diff --git a/src/modules/colorPicker/ColorPickerUI/Helpers/ZoomWindowHelper.cs b/src/modules/colorPicker/ColorPickerUI/Helpers/ZoomWindowHelper.cs index 0c001c0a0d..7136188d16 100644 --- a/src/modules/colorPicker/ColorPickerUI/Helpers/ZoomWindowHelper.cs +++ b/src/modules/colorPicker/ColorPickerUI/Helpers/ZoomWindowHelper.cs @@ -21,7 +21,7 @@ namespace ColorPicker.Helpers private const int ZoomWindowChangeDelayInMS = 50; private const int ZoomFactor = 2; private const int BaseZoomImageSize = 50; - private const int MaxZoomLevel = 3; + private const int MaxZoomLevel = 4; private const int MinZoomLevel = 0; private readonly IZoomViewModel _zoomViewModel; diff --git a/src/modules/colorPicker/ColorPickerUI/Shaders/Global.cs b/src/modules/colorPicker/ColorPickerUI/Shaders/Global.cs new file mode 100644 index 0000000000..51f1d84d32 --- /dev/null +++ b/src/modules/colorPicker/ColorPickerUI/Shaders/Global.cs @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace ColorPicker.Shaders +{ + internal static class Global + { + /// + /// Helper method for generating a "pack://" URI for a given relative file based on the + /// assembly that this class is in. + /// + public static Uri MakePackUri(string relativeFile) + { + string uriString = "pack://application:,,,/" + AssemblyShortName + ";component/Shaders/" + relativeFile; + return new Uri(uriString); + } + + private static string AssemblyShortName + { + get + { + if (_assemblyShortName == null) + { + var assembly = typeof(Global).Assembly; + + // Pull out the short name. + _assemblyShortName = assembly.ToString().Split(',')[0]; + } + + return _assemblyShortName; + } + } + + private static string _assemblyShortName; + } +} diff --git a/src/modules/colorPicker/ColorPickerUI/Shaders/GridShader.cso b/src/modules/colorPicker/ColorPickerUI/Shaders/GridShader.cso new file mode 100644 index 0000000000..db23c058d3 Binary files /dev/null and b/src/modules/colorPicker/ColorPickerUI/Shaders/GridShader.cso differ diff --git a/src/modules/colorPicker/ColorPickerUI/Shaders/GridShader.fx b/src/modules/colorPicker/ColorPickerUI/Shaders/GridShader.fx new file mode 100644 index 0000000000..20d4fd27e2 --- /dev/null +++ b/src/modules/colorPicker/ColorPickerUI/Shaders/GridShader.fx @@ -0,0 +1,81 @@ +float2 mousePosition : register(C1); +float radius : register(C2); +float squareSize : register(c3); +float textureSize : register(c4); + +sampler2D inputSampler : register(S0); + +float4 main(float2 uv : TEXCOORD) : COLOR +{ + // do not draw grid where the mouse is + if (uv.x == mousePosition.y && uv.y == mousePosition.y) + { + return tex2D(inputSampler, uv); + } + + float gridColor = 1.0f; + float mainRectangleColor = 1.0f; + + float4 originalColor = tex2D(inputSampler, uv); + float4 colorAtMousePosition = tex2D(inputSampler, mousePosition); + + if (originalColor.r > 0.8 && originalColor.g > 0.8 && originalColor.b > 0.8) + { + gridColor = 0.0f; + } + + if (colorAtMousePosition.r > 0.8 && colorAtMousePosition.g > 0.8 && colorAtMousePosition.b > 0.8) + { + mainRectangleColor = 0.0f; + } + + float4 color = tex2D(inputSampler, uv); + float distanceFromMouse = length(mousePosition - uv); + float distanceFactor; + + int pixelPositionX = textureSize * uv.x; + int pixelPositionY = textureSize * uv.y; + + int mousePositionX = mousePosition.x * textureSize; + int mousePositionY = mousePosition.y * textureSize; + + int2 topLeftRectangle = int2(mousePositionX - (mousePositionX % squareSize) - 1, mousePositionY - (mousePositionY % squareSize) - 1); + + // do not draw grid inside square even when grid (avoid drawing grid in that area later + if (((pixelPositionX >= topLeftRectangle.x + 1 && pixelPositionX <= topLeftRectangle.x + squareSize) && (pixelPositionY == topLeftRectangle.y + 1 || pixelPositionY == topLeftRectangle.y + squareSize)) || + ((pixelPositionY >= topLeftRectangle.y + 1 && pixelPositionY <= topLeftRectangle.y + squareSize) && (pixelPositionX == topLeftRectangle.x + 1 || pixelPositionX == topLeftRectangle.x + squareSize))) + { + return originalColor; + } + + if (distanceFromMouse <= radius) + { + // draw grid + if (pixelPositionX % squareSize == 0 || pixelPositionY % squareSize == 0) + { + if (gridColor == 1.0f) + { + color.r = color.r + ((1.0 - color.r) * (1.0 - (distanceFromMouse / radius))); + color.g = color.g + ((1.0 - color.g) * (1.0 - (distanceFromMouse / radius))); + color.b = color.b + ((1.0 - color.b) * (1.0 - (distanceFromMouse / radius))); + } + else + { + color.r = color.r * (distanceFromMouse / radius); + color.g = color.g * (distanceFromMouse / radius); + color.b = color.b * (distanceFromMouse / radius); + } + } + } + + if (((pixelPositionX >= topLeftRectangle.x && pixelPositionX <= topLeftRectangle.x + squareSize + 2) && (pixelPositionY == topLeftRectangle.y || pixelPositionY == topLeftRectangle.y + squareSize + 2)) || + ((pixelPositionY >= topLeftRectangle.y && pixelPositionY <= topLeftRectangle.y + squareSize + 2) && (pixelPositionX == topLeftRectangle.x || pixelPositionX == topLeftRectangle.x + squareSize + 2))) + { + originalColor.r = mainRectangleColor; + originalColor.g = mainRectangleColor; + originalColor.b = mainRectangleColor; + return originalColor; + } + + return color; +} \ No newline at end of file diff --git a/src/modules/colorPicker/ColorPickerUI/Shaders/GridShaderEffect.cs b/src/modules/colorPicker/ColorPickerUI/Shaders/GridShaderEffect.cs new file mode 100644 index 0000000000..4db23cbfff --- /dev/null +++ b/src/modules/colorPicker/ColorPickerUI/Shaders/GridShaderEffect.cs @@ -0,0 +1,101 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Effects; + +namespace ColorPicker.Shaders +{ + public class GridShaderEffect : ShaderEffect + { + private static readonly PixelShader Shader = + new PixelShader() + { + UriSource = Global.MakePackUri("GridShader.cso"), + }; + + public static readonly DependencyProperty InputProperty = ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(GridShaderEffect), 0); + public static readonly DependencyProperty MousePositionProperty = DependencyProperty.Register("MousePosition", typeof(Point), typeof(GridShaderEffect), new UIPropertyMetadata(new Point(0D, 0D), PixelShaderConstantCallback(1))); + public static readonly DependencyProperty RadiusProperty = DependencyProperty.Register("Radius", typeof(double), typeof(GridShaderEffect), new UIPropertyMetadata((double)0D, PixelShaderConstantCallback(2))); + public static readonly DependencyProperty SquareSizeProperty = DependencyProperty.Register("SquareSize", typeof(double), typeof(GridShaderEffect), new UIPropertyMetadata((double)0D, PixelShaderConstantCallback(3))); + public static readonly DependencyProperty TextureSizeProperty = DependencyProperty.Register("TextureSize", typeof(double), typeof(GridShaderEffect), new UIPropertyMetadata((double)0D, PixelShaderConstantCallback(4))); + + public GridShaderEffect() + { + PixelShader = Shader; + + UpdateShaderValue(InputProperty); + UpdateShaderValue(MousePositionProperty); + UpdateShaderValue(RadiusProperty); + UpdateShaderValue(SquareSizeProperty); + UpdateShaderValue(TextureSizeProperty); + } + + public Brush Input + { + get + { + return (Brush)GetValue(InputProperty); + } + + set + { + SetValue(InputProperty, value); + } + } + + public Point MousePosition + { + get + { + return (Point)GetValue(MousePositionProperty); + } + + set + { + SetValue(MousePositionProperty, value); + } + } + + public double Radius + { + get + { + return (double)GetValue(RadiusProperty); + } + + set + { + SetValue(RadiusProperty, value); + } + } + + public double SquareSize + { + get + { + return (double)GetValue(SquareSizeProperty); + } + + set + { + SetValue(SquareSizeProperty, value); + } + } + + public double TextureSize + { + get + { + return (double)GetValue(TextureSizeProperty); + } + + set + { + SetValue(TextureSizeProperty, value); + } + } + } +} diff --git a/src/modules/colorPicker/ColorPickerUI/Views/MainView.xaml b/src/modules/colorPicker/ColorPickerUI/Views/MainView.xaml index 3aa1574748..32a8558e2e 100644 --- a/src/modules/colorPicker/ColorPickerUI/Views/MainView.xaml +++ b/src/modules/colorPicker/ColorPickerUI/Views/MainView.xaml @@ -20,13 +20,12 @@ - - + + + + - - - diff --git a/src/modules/colorPicker/ColorPickerUI/Views/ZoomView.xaml b/src/modules/colorPicker/ColorPickerUI/Views/ZoomView.xaml index 0a83c4b2c0..ed0822dcbb 100644 --- a/src/modules/colorPicker/ColorPickerUI/Views/ZoomView.xaml +++ b/src/modules/colorPicker/ColorPickerUI/Views/ZoomView.xaml @@ -5,12 +5,20 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:ColorPicker.Views" mc:Ignorable="d" + xmlns:shaders="clr-namespace:ColorPicker.Shaders" xmlns:e="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:behaviors="clr-namespace:ColorPicker.Behaviors" - d:DesignHeight="450" d:DesignWidth="800" BorderBrush="Black" BorderThickness="2" Focusable="False"> - - - - - + d:DesignHeight="450" d:DesignWidth="800" Background="Transparent" Focusable="False"> + + + + + + + + + + + + diff --git a/src/modules/colorPicker/ColorPickerUI/ZoomWindow.xaml b/src/modules/colorPicker/ColorPickerUI/ZoomWindow.xaml index 003d886b31..c07cf8f113 100644 --- a/src/modules/colorPicker/ColorPickerUI/ZoomWindow.xaml +++ b/src/modules/colorPicker/ColorPickerUI/ZoomWindow.xaml @@ -7,7 +7,7 @@ xmlns:e="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:behaviors="clr-namespace:ColorPicker.Behaviors" mc:Ignorable="d" - Title="Zoom window" WindowStyle="None" SizeToContent="WidthAndHeight" Topmost="True" ShowInTaskbar="False" ResizeMode="NoResize" Focusable="False"> + Title="Zoom window" WindowStyle="None" SizeToContent="WidthAndHeight" Topmost="True" AllowsTransparency="True" Background="Transparent" ShowInTaskbar="False" ResizeMode="NoResize" Focusable="False">