From d81627718e6a8df2048555f2f6a27a728246a7e9 Mon Sep 17 00:00:00 2001 From: diluculo Date: Wed, 28 Sep 2016 16:59:23 +0900 Subject: [PATCH 1/6] Add ThemedImageEffectFiles --- .../ShaderEffects/Converters/BindingProxy.cs | 32 ++++ .../SolidColorBrushToColorConverter.cs | 33 ++++ .../ShaderEffects/Scripts/ThemedImage.fx | 145 ++++++++++++++++++ .../ShaderEffects/ThemedImageEffect.cs | 47 ++++++ .../ShaderEffects/ThemedImageEffect.ps | Bin 0 -> 2568 bytes 5 files changed, 257 insertions(+) create mode 100644 src/Gemini/Framework/ShaderEffects/Converters/BindingProxy.cs create mode 100644 src/Gemini/Framework/ShaderEffects/Converters/SolidColorBrushToColorConverter.cs create mode 100644 src/Gemini/Framework/ShaderEffects/Scripts/ThemedImage.fx create mode 100644 src/Gemini/Framework/ShaderEffects/ThemedImageEffect.cs create mode 100644 src/Gemini/Framework/ShaderEffects/ThemedImageEffect.ps diff --git a/src/Gemini/Framework/ShaderEffects/Converters/BindingProxy.cs b/src/Gemini/Framework/ShaderEffects/Converters/BindingProxy.cs new file mode 100644 index 00000000..4516ba94 --- /dev/null +++ b/src/Gemini/Framework/ShaderEffects/Converters/BindingProxy.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; + +namespace Gemini.Framework.ShaderEffects.Converters +{ + // From http://www.thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/ + public class BindingProxy : Freezable + { + #region Overrides of Freezable + + protected override Freezable CreateInstanceCore() + { + return new BindingProxy(); + } + + #endregion + + public object Data + { + get { return (object)GetValue(DataProperty); } + set { SetValue(DataProperty, value); } + } + + // Using a DependencyProperty as the backing store for Data. This enables animation, styling, binding, etc... + public static readonly DependencyProperty DataProperty = + DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null)); + } +} diff --git a/src/Gemini/Framework/ShaderEffects/Converters/SolidColorBrushToColorConverter.cs b/src/Gemini/Framework/ShaderEffects/Converters/SolidColorBrushToColorConverter.cs new file mode 100644 index 00000000..fa372e55 --- /dev/null +++ b/src/Gemini/Framework/ShaderEffects/Converters/SolidColorBrushToColorConverter.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Data; +using System.Windows.Media; + +namespace Gemini.Framework.ShaderEffects.Converters +{ + public class SolidColorBrushToColorConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + SolidColorBrush brush = value as SolidColorBrush; + if (brush != null) + return brush.Color; + + return default(Color?); + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + if (value != null) + { + Color color = (Color)value; + return new SolidColorBrush(color); + } + + return default(SolidColorBrush); + } + } +} diff --git a/src/Gemini/Framework/ShaderEffects/Scripts/ThemedImage.fx b/src/Gemini/Framework/ShaderEffects/Scripts/ThemedImage.fx new file mode 100644 index 00000000..f7ab7241 --- /dev/null +++ b/src/Gemini/Framework/ShaderEffects/Scripts/ThemedImage.fx @@ -0,0 +1,145 @@ +/// ThemedImageEffect +/// Converts an input image into an image that blends in with the target background. + +/// The input image +sampler2D Input : register(S0); + +/// Background color of the image. +/// #FFF6F6F6 +float4 Background : register(C0); + +/// 1.0 if the image should be rendered enabled, 0.0 if it should be disabled (grayscaled). +/// 0.0 +/// 1.0 +/// 0.0 +float IsEnabled : register(C1); + +float3 rgb2hsl(in float4 RGB) +{ + float r = RGB.r; // Red, range: [0..1] + float g = RGB.g; // Green, range: [0..1] + float b = RGB.b; // Blue, range: [0..1] + + float maxChannel = (r > g && r > b) ? r : (g > b) ? g : b; + float minChannel = (r < g && r < b) ? r : (g < b) ? g : b; + + float h = (maxChannel + minChannel) / 2.0f; // Hue, range: [0..1] + float s = (maxChannel + minChannel) / 2.0f; // Saturation, range: [0..1] + float l = (maxChannel + minChannel) / 2.0f; // Lightness, range: [0..1] + + if (maxChannel == minChannel) + { + h = s = 0.0f; + } + else + { + float d = maxChannel - minChannel; + s = (l > 0.5f) ? d / (2.0f - maxChannel - minChannel) : d / (maxChannel + minChannel); + + if (r > g && r > b) // maxChannel == r + h = (g - b) / d + (g < b ? 6.0f : 0.0f); + else if (g > b) // maxChannel == g + h = (b - r) / d + 2.0f; + else // maxChannel = b + h = (r - g) / d + 4.0f; + + h /= 6.0f; + } + + return float3(h, s, l); +} + +float hue2rgb(in float p, in float q, in float t) +{ + if (t < 0.0f) t += 1.0f; + if (t > 1.0f) t -= 1.0f; + if (t < 1.0f / 6.0f) return p + (q - p) * 6.0f * t; + if (t < 1.0f / 2.0f) return q; + if (t < 2.0f / 3.0f) return p + (q - p) * (2.0f / 3.0f - t) * 6.0f; + return p; +} + +float3 hsl2rgb(in float3 HSL) +{ + float h = HSL[0]; + float s = HSL[1]; + float l = HSL[2]; + + float q = (l < 0.5f) ? l * (1.0f + s) : l + s - l * s; + float p = 2.0f * l - q; + + float r = hue2rgb(p, q, h + 1.0f / 3.0f); + float g = hue2rgb(p, q, h); + float b = hue2rgb(p, q, h - 1.0f / 3.0f); + + return float3(r, g, b); +} + +float3 rgb2gray(in float3 RGB) +{ + // https://en.wikipedia.org/wiki/Relative_luminance + // https://en.wikipedia.org/wiki/SRGB + // Convert the gamma compressed RGB values to linear RGB + RGB.r = (RGB.r <= 0.04045f) ? RGB.r / 12.92f : pow((RGB.r + 0.055f) / 1.055f, 2.4f); + RGB.g = (RGB.g <= 0.04045f) ? RGB.g / 12.92f : pow((RGB.g + 0.055f) / 1.055f, 2.4f); + RGB.b = (RGB.b <= 0.04045f) ? RGB.b / 12.92f : pow((RGB.b + 0.055f) / 1.055f, 2.4f); + float y = dot(float3(0.2126f, 0.7152f, 0.0722f), RGB.rgb); + return y <= 0.0031308f ? 12.92f * y : 1.055f * pow(y, 1.0f/2.4f) - 0.055f; +} + +float4 main(float2 uv : TEXCOORD) : COLOR +{ + // This performs two conversions. + // 1. The lightness of the image is transformed so that the constant "halo" lightness blends in with the background. + // - This has the effect of eliminating the halo visually. + // - The "halo" lightness is an immutable constant, and is not calculated from the input image. + // 2. The image is converted to grayscale if the IsEnabled parameter is 0. + + // Refer to http://stackoverflow.com/questions/36778989/vs2015-icon-guide-color-inversion + + // First, loopk up original image color + float4 pxRGBA = tex2D(Input, uv.xy); + + // For performance reason, the WPF multiplies each color channel + // by the alpha channel before sending the Input into HLSL shader. + // So let's convert the color to be non-premultiplied. + if (pxRGBA.a > 0.0) pxRGBA.rgb /= pxRGBA.a; + + // Convert the color space from RGB to HSL. + float3 pxHSL = rgb2hsl(pxRGBA); + + // Define lightness of default outlined color(#F6F6F6) + float haloHSL2 = 0.96470588235294f; + + // Lightness of Background color is bgHSL[2] + float3 bgHSL = rgb2hsl(Background); + + if (bgHSL[2] < 0.5f) + { + haloHSL2 = 1.0f - haloHSL2; + pxHSL[2] = 1.0f - pxHSL[2]; + } + + if(pxHSL[2] <= haloHSL2) + { + pxHSL[2] = bgHSL[2] / haloHSL2 * pxHSL[2]; + } + else + { + pxHSL[2] = (1.0f - bgHSL[2]) / (1.0f - haloHSL2) * (pxHSL[2] - 1.0f) + 1.0f; + } + + // Convert the color space from HSL to RGB. + pxRGBA.rgb = hsl2rgb(pxHSL); + + // if !IsEnabled, convert to grayscale. + if (IsEnabled == 0.0f) + { + pxRGBA.rgb = rgb2gray(pxRGBA.rgb); + } + + // Convert the color to a PreMultiplied color. + pxRGBA.rgb *= pxRGBA.a; + + return pxRGBA; +} \ No newline at end of file diff --git a/src/Gemini/Framework/ShaderEffects/ThemedImageEffect.cs b/src/Gemini/Framework/ShaderEffects/ThemedImageEffect.cs new file mode 100644 index 00000000..959832f1 --- /dev/null +++ b/src/Gemini/Framework/ShaderEffects/ThemedImageEffect.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Media; + +namespace Gemini.Framework.ShaderEffects +{ + /// Converts an input image into an image that blends in with the target background. + public class ThemedImageEffect : ShaderEffectBase + { + public static readonly DependencyProperty InputProperty = RegisterPixelShaderSamplerProperty("Input", typeof(ThemedImageEffect), 0); + + public static readonly DependencyProperty BackgroundProperty = DependencyProperty.Register("Background", typeof(Color), typeof(ThemedImageEffect), new UIPropertyMetadata(Color.FromArgb(255, 246, 246, 246), PixelShaderConstantCallback(0))); + + public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.Register("IsEnabled", typeof(double), typeof(ThemedImageEffect), new UIPropertyMetadata(((double)(1D)), PixelShaderConstantCallback(1))); + + public ThemedImageEffect() + { + UpdateShaderValue(InputProperty); + UpdateShaderValue(BackgroundProperty); + UpdateShaderValue(IsEnabledProperty); + } + + public Brush Input + { + get { return ((Brush)(GetValue(InputProperty))); } + set { SetValue(InputProperty, value); } + } + + /// Background color of the image. + public Color Background + { + get { return ((Color)(GetValue(BackgroundProperty))); } + set { SetValue(BackgroundProperty, value); } + } + + /// 1.0 if the image should be rendered enabled, 0.0 if it should be disabled (grayscaled). + public double IsEnabled + { + get { return ((double)(this.GetValue(IsEnabledProperty))); } + set { this.SetValue(IsEnabledProperty, value); } + } + } +} diff --git a/src/Gemini/Framework/ShaderEffects/ThemedImageEffect.ps b/src/Gemini/Framework/ShaderEffects/ThemedImageEffect.ps new file mode 100644 index 0000000000000000000000000000000000000000..00763ab6d440e86c6fa4d52732cb80b2e705cfcd GIT binary patch literal 2568 zcma)7J%}Au6h8Cw-poFKR%}EF_&^0?v4ogJ7J2)2H;4uj!NdVmg*6*ZFzzn98!L$2?pDe?SPL5)6AK%=En;b-V?@MG)XMSu&bjk``~e?(nK}R8cg~r+lGOE| z^`e}7X=U}0hh)7^8I*VZp>cTaAx+E3jQ2kEiRmG`RTTq0$t9pk;g5!cZi5$oU>mf$}?e&}j<&%gMPOC-hE zhgh&%tOG3KjBl=aQ}BzyC6-?_Um}&K4lRLyp*lf*_5pwJD{%^P2)bBn(TD!z6wCek z4o;r6Etl3NrZHz?nb?)H&Z4Pp{b7E~P29vFh%;LnY^5W(1UzVqrQVC>M^7J{tLi*C z;2YH5Hh4|PIhEIu9C)jMDMGGt5BW5%nUnbx@RKB!3_b_&`C5G_rxp+U?0i#jX!xY) z6Y%PMjZf*(yXiUu!zZ5e{il!4X6!>O4L;6*y$C($yGK1AK@nl5iPZX@ND5o2HF5WV z2WMTu{*$v=w^f(PfzRYCvZG8Jxzi8WR3?Asigga;Tzac-K4CXC zE`blQhB(&Y{8&?_xU0 zzxpgd0*tP3BU!aXViN}5Np8~0*BJ2`1Ta`uBeLTvEQ`K{Kju0{R={Fco= literal 0 HcmV?d00001 From f79e4c71fa694628e4f40d893b9b67260c2c1617 Mon Sep 17 00:00:00 2001 From: diluculo Date: Wed, 28 Sep 2016 17:04:48 +0900 Subject: [PATCH 2/6] Apply ThemedImageEffect to Toolbar --- .../Themes/VS2013/Controls/Toolbar.xaml | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/Gemini/Themes/VS2013/Controls/Toolbar.xaml b/src/Gemini/Themes/VS2013/Controls/Toolbar.xaml index 76bb1095..ec5e7cc2 100644 --- a/src/Gemini/Themes/VS2013/Controls/Toolbar.xaml +++ b/src/Gemini/Themes/VS2013/Controls/Toolbar.xaml @@ -2,7 +2,11 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:c="clr-namespace:Gemini.Modules.ToolBars.Controls" xmlns:shaderEffects="clr-namespace:Gemini.Framework.ShaderEffects" + xmlns:converters="clr-namespace:Gemini.Framework.ShaderEffects.Converters" xmlns:controls="clr-namespace:Gemini.Framework.Controls"> + + + From c32dee97e3a60d1dd6604ff267652c153752fb9a Mon Sep 17 00:00:00 2001 From: diluculo Date: Tue, 15 May 2018 17:29:55 +0900 Subject: [PATCH 5/6] Adjust margin of MenuItem and Icons --- src/Gemini/Themes/VS2013/Controls/Menu.xaml | 2 +- src/Gemini/Themes/VS2013/Controls/Window.xaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Gemini/Themes/VS2013/Controls/Menu.xaml b/src/Gemini/Themes/VS2013/Controls/Menu.xaml index f51c3e64..e4e81453 100644 --- a/src/Gemini/Themes/VS2013/Controls/Menu.xaml +++ b/src/Gemini/Themes/VS2013/Controls/Menu.xaml @@ -119,7 +119,7 @@ - + diff --git a/src/Gemini/Themes/VS2013/Controls/Window.xaml b/src/Gemini/Themes/VS2013/Controls/Window.xaml index 8fee4f7b..7d46dd9a 100644 --- a/src/Gemini/Themes/VS2013/Controls/Window.xaml +++ b/src/Gemini/Themes/VS2013/Controls/Window.xaml @@ -29,7 +29,7 @@ Height="{TemplateBinding Height}" RenderOptions.BitmapScalingMode="{Binding IconBitmapScalingMode, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type controls:MetroWindow}}}" RenderOptions.EdgeMode="{Binding IconEdgeMode, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type controls:MetroWindow}}}" - Source="{TemplateBinding Content}" > + Source="{TemplateBinding Content}" Margin="6,0,0,0"> From 3af72b028edd3c6b839b1aa396701fb61d25743b Mon Sep 17 00:00:00 2001 From: Jong Hyun Kim Date: Tue, 2 Nov 2021 13:07:47 +0900 Subject: [PATCH 6/6] Resolve conflicts --- src/Gemini/Gemini.csproj | 3 ++- src/Gemini/Themes/VS2013/Controls/Menu.xaml | 19 +++++++------------ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/Gemini/Gemini.csproj b/src/Gemini/Gemini.csproj index cc230fc5..8c17c0f2 100644 --- a/src/Gemini/Gemini.csproj +++ b/src/Gemini/Gemini.csproj @@ -31,11 +31,12 @@ + SettingsSingleFileGenerator Settings.Designer.cs - + ResXFileCodeGenerator Resources.de.Designer.cs diff --git a/src/Gemini/Themes/VS2013/Controls/Menu.xaml b/src/Gemini/Themes/VS2013/Controls/Menu.xaml index e4e81453..a60e84d3 100644 --- a/src/Gemini/Themes/VS2013/Controls/Menu.xaml +++ b/src/Gemini/Themes/VS2013/Controls/Menu.xaml @@ -151,17 +151,6 @@ Background="Transparent" Padding="2,0,1,0"> - - - - - + ContentSource="Icon"> + + + +