From 6e898ae52d0d040a5ee8802fc133aba5b9aaf2b9 Mon Sep 17 00:00:00 2001 From: martinchrzan Date: Thu, 3 Sep 2020 23:31:27 +0200 Subject: [PATCH] Added pixel reveal effect, increased max zoom level to 4, added rounded corners to main and zoom window (#6242) --- .../Behaviors/GridEffectBehavior.cs | 64 +++++++++++ .../ColorPickerUI/ColorPickerUI.csproj | 8 +- .../ColorPickerUI/Helpers/ZoomWindowHelper.cs | 2 +- .../ColorPickerUI/Shaders/Global.cs | 39 +++++++ .../ColorPickerUI/Shaders/GridShader.cso | Bin 0 -> 2208 bytes .../ColorPickerUI/Shaders/GridShader.fx | 81 ++++++++++++++ .../ColorPickerUI/Shaders/GridShaderEffect.cs | 101 ++++++++++++++++++ .../ColorPickerUI/Views/MainView.xaml | 9 +- .../ColorPickerUI/Views/ZoomView.xaml | 20 ++-- .../colorPicker/ColorPickerUI/ZoomWindow.xaml | 2 +- 10 files changed, 312 insertions(+), 14 deletions(-) create mode 100644 src/modules/colorPicker/ColorPickerUI/Behaviors/GridEffectBehavior.cs create mode 100644 src/modules/colorPicker/ColorPickerUI/Shaders/Global.cs create mode 100644 src/modules/colorPicker/ColorPickerUI/Shaders/GridShader.cso create mode 100644 src/modules/colorPicker/ColorPickerUI/Shaders/GridShader.fx create mode 100644 src/modules/colorPicker/ColorPickerUI/Shaders/GridShaderEffect.cs 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 0000000000000000000000000000000000000000..db23c058d3adc3cb5e86b4b7c2e1a0d3701e4833 GIT binary patch literal 2208 zcmZuyO^aMr5Iygsdpa^W5VH)}OJiUNF$rX)CbJNnfI2=1Zo-ghNkb+*=?@TBulHGW zz#nk4a5KH}ALt?r7Y4F)D|rY*WZa1>U(Ttz?{!b`LGh~UR-O8|b?-%1*ZQeu+-M!uZ-h=&v z!MJ~Wba*smF8!O^SFdf~ynbc->W%9+u>2BWd$t0*^06Yn_-&W}@@MHgrpG7JH{bg# zpAIGST8O89f!A1cj%oZccZ($^~DM$^5WMQCe;*uu@7`F(HtA16KtEb;EH}0}zBiU@Qe%GPw zuJN8@w`7Cq`+V^p*D~wFXmp%<{ z6%;f2SX_^}sTdYBcRb3c7PGN!`s^`fU$F_wmt^s(iKoZx$j|8;b4`U+0(B5phMaP3Sk4#b<27pSIAY1KmP{<0m&{{p zp6ej{j`iV>xDOfqR8tjuvPpS|wqCz_qP}Up(Rt3=8}A4E6|uZ00D@*`wa1R_{eMkt z&4kYv?;PUnklaVPCHQlmcMfYRt)7e`C!(&ET9dLz5fxw|bi1DOc4;vbpHM_g?nmbq2}XTu2u0SgoDq z7iXreL380)h1oln@&ALBvKPMQd70c|sb{O1EMmNrxz({Bb&#L#A>oL0_xPNr>P1-9 zqq&xi>{Xx1#6dVuT{r3^lS 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">