79849

WPF Inner Glow Effect

Question:

Does anyone know how to make an Inner Glow effect in WPF <strong>without</strong> using expression blend or deprecated BitmapEffects?

Sample image:

<img alt="enter image description here" class="b-lazy" data-src="https://i.stack.imgur.com/2AJ60.png" data-original="https://i.stack.imgur.com/2AJ60.png" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" />

For instance, here is some xaml for a button with an image and some text. I want this button to have an inner glow (not an outer glow):

<Button Click="HandleDeleteRows" Style="{StaticResource ButtonCellStyle}"> <DockPanel> <Image Style="{StaticResource DeleteButtonImage}" /> <TextBlock Style="{StaticResource DeleteButtonCaption}" /> </DockPanel> </Button>

Answer1:

While my simplified example above is solved by PompolutZ's answer, I wasn't in a position to override the control template of the control I wanted to apply the style to in my real world example - so I took to defining my own Effect, following instructions <a href="http://windowsclient.net/wpf/wpf35/wpf-35sp1-more-effects.aspx" rel="nofollow">here</a>.

<strong>Step 1</strong> - Write an HLSL .FX file that will do your desired effect. I gave up on the glow as being too complicated, since it required edge detection. I decided to go with a slew of standard colour, brightness, gamma, and saturation adjustments that were fairly easy to implement and would let me create some good visual cues. They were pretty easy to implement using common sense and looking up pixel shading algorithms online.

ColourAdjust.fx:

sampler2D implicitInput : register(s0); float saturation : register(c0); float gamma : register(c1); float brightness : register(c2); float red_adjust : register(c3); float green_adjust : register(c4); float blue_adjust : register(c5); static const float max_gamma = 100; float4 main(float2 uv : TEXCOORD) : COLOR { float4 color = tex2D(implicitInput, uv); float4 result; // Apply greyscale desaturation float gray = color.r * 0.3 + color.g * 0.59 + color.b *0.11; result.r = (color.r - gray) * saturation + gray; result.g = (color.g - gray) * saturation + gray; result.b = (color.b - gray) * saturation + gray; // Apply Gamma Adjustment (if it's not approximately 0.5 - which means no adjustment) float gammafactor = gamma == 0 ? max_gamma : log(gamma) / log(0.5); result.r = pow(result.r, gammafactor); result.g = pow(result.g, gammafactor); result.b = pow(result.b, gammafactor); //Apply linear brightness adjustment result.r += brightness + red_adjust; result.g += brightness + green_adjust; result.b += brightness + blue_adjust; //Clamp brightness adjustment result to bounds 0 <= val <= 1 result.r = (result.r > 1 ? 1 : (result.r < 0 ? 0 : result.r)); result.g = (result.g > 1 ? 1 : (result.g < 0 ? 0 : result.g)); result.b = (result.b > 1 ? 1 : (result.b < 0 ? 0 : result.b)); result.a = color.a; return result; }

<strong>Step 2</strong> - I had to download a local copy of the <a href="http://msdn.microsoft.com/en-us/library/aa139765.aspx" rel="nofollow">DirectX SDK</a> so that I could compile the above HLSL code into a PS file, which is what's used by WPF - giving me ColourAdjust.ps.

> > fxc.exe /T ps_2_0 /E PS /ColourAdjust.ps ColourAdjust.fx

<strong>Step 3</strong> - Write a ShaderEffect class that will expose the effect parameters via DependencyProperties. Here is ColourAdjustEffect.cs:

using System; using System.Reflection; using System.Windows; using System.Windows.Media; using System.Windows.Media.Effects; namespace WPF.Utilities.UI { public class ColourAdjustEffect : ShaderEffect { private static PixelShader _pixelShader = new PixelShader() { UriSource = new Uri("pack://application:,,,/" + Assembly.GetExecutingAssembly() + ";component/Effects/ColourAdjust.ps") }; public static readonly DependencyProperty InputProperty = ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(ColourAdjustEffect), 0); public static readonly DependencyProperty SaturationProperty = DependencyProperty.Register("Saturation", typeof(double), typeof(ColourAdjustEffect), new UIPropertyMetadata(1.0, PixelShaderConstantCallback(0), CoerceFactor)); public static readonly DependencyProperty GammaProperty = DependencyProperty.Register("Gamma", typeof(double), typeof(ColourAdjustEffect), new UIPropertyMetadata(0.5, PixelShaderConstantCallback(1), CoerceFactor)); public static readonly DependencyProperty BrightnessAdjustmentProperty = DependencyProperty.Register("BrightnessAdjustment", typeof(double), typeof(ColourAdjustEffect), new UIPropertyMetadata(0.0, PixelShaderConstantCallback(2), CoerceBrightnessAdjustment)); public static readonly DependencyProperty RedAdjustmentProperty = DependencyProperty.Register("RedAdjustment", typeof(double), typeof(ColourAdjustEffect), new UIPropertyMetadata(0.0, PixelShaderConstantCallback(3), CoerceBrightnessAdjustment)); public static readonly DependencyProperty GreenAdjustmentProperty = DependencyProperty.Register("GreenAdjustment", typeof(double), typeof(ColourAdjustEffect), new UIPropertyMetadata(0.0, PixelShaderConstantCallback(4), CoerceBrightnessAdjustment)); public static readonly DependencyProperty BlueAdjustmentProperty = DependencyProperty.Register("BlueAdjustment", typeof(double), typeof(ColourAdjustEffect), new UIPropertyMetadata(0.0, PixelShaderConstantCallback(5), CoerceBrightnessAdjustment)); public ColourAdjustEffect() { PixelShader = _pixelShader; UpdateShaderValue(InputProperty); UpdateShaderValue(SaturationProperty); UpdateShaderValue(GammaProperty); UpdateShaderValue(BrightnessAdjustmentProperty); UpdateShaderValue(RedAdjustmentProperty); UpdateShaderValue(GreenAdjustmentProperty); UpdateShaderValue(BlueAdjustmentProperty); } public Brush Input { get { return (Brush)GetValue(InputProperty); } set { SetValue(InputProperty, value); } } /// <summary>A value between 0 and 1 to alter the amount of colour left in the image. 0 is entirely greyscale, and 1 is unaffected. Default is 1.</summary> public double Saturation { get { return (double)GetValue(SaturationProperty); } set { SetValue(SaturationProperty, value); } } /// <summary>A value between 0 and 1 to alter the lightness of the greyscale without altering true black or true white. /// 0 shifts shades closer to true black, and 1 shifts shades closer to true white. Default is 0.5.</summary> public double Gamma { get { return (double)GetValue(GammaProperty); } set { SetValue(GammaProperty, value); } } /// <summary>A value between -1 and 1 to linearly move the end result closer to true black or true white respectively. /// -1 will result in an entirely black image, +1 will result in an entirely white image. Default is 0.</summary> public double BrightnessAdjustment { get { return (double)GetValue(BrightnessAdjustmentProperty); } set { SetValue(BrightnessAdjustmentProperty, value); } } /// <summary>A value between -1 and 1 to linearly increase the Red component of the result. /// -1 will remove all Red from the image, +1 will maximize all Red in the image. Default is 0.</summary> public double RedAdjustment { get { return (double)GetValue(RedAdjustmentProperty); } set { SetValue(RedAdjustmentProperty, value); } } /// <summary>A value between -1 and 1 to linearly increase the Green component of the result. /// -1 will remove all Green from the image, +1 will maximize all Green in the image. Default is 0.</summary> public double GreenAdjustment { get { return (double)GetValue(GreenAdjustmentProperty); } set { SetValue(GreenAdjustmentProperty, value); } } /// <summary>A value between -1 and 1 to linearly increase the Blue component of the result. /// -1 will remove all Blue from the image, +1 will maximize all Blue in the image. Default is 0.</summary> public double BlueAdjustment { get { return (double)GetValue(BlueAdjustmentProperty); } set { SetValue(BlueAdjustmentProperty, value); } } private static object CoerceFactor(DependencyObject d, object value) { double newFactor = (double)value; if( newFactor < 0.0 ) return 0.0; if( newFactor > 1.0 ) return 1.0; return newFactor; } private static object CoerceBrightnessAdjustment(DependencyObject d, object value) { double newFactor = (double)value; if( newFactor < -1.0 ) return -1.0; if( newFactor > 1.0 ) return 1.0; return newFactor; } } }

<strong>Step 4</strong>: Use your effect in the xaml:

<Setter Property="Effect"> <Setter.Value> <ui:ColourAdjustEffect Saturation="0" Gamma="0.6" BrightnessAdjustment="-0.2" RedAdjustment="0.04" /> </Setter.Value> </Setter>

So while I didn't get my glow effect, I had enough parameters to play with that I could get a 'highlighting' visual cue, which was my real goal. Here's some of the things I was able to do with it:

<img alt="enter image description here" class="b-lazy" data-src="https://i.stack.imgur.com/1cHjY.png" data-original="https://i.stack.imgur.com/1cHjY.png" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" />

Answer2:

Maybe you can try something like this (Note! This is not finished solutions just an idea):

<Style x:Key="{x:Type Button}" TargetType="{x:Type Button}"> <Style.Setters> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type Button}"> <Grid> <Border CornerRadius="10" BorderThickness="20"> <Border.BorderBrush> <LinearGradientBrush StartPoint="0, 0.5" EndPoint="1, 0.5"> <GradientStop Color="LightGreen" Offset="0.0" /> <GradientStop Color="Transparent" Offset="0.15" /> </LinearGradientBrush> </Border.BorderBrush> </Border> <Border CornerRadius="10" BorderThickness="20"> <Border.BorderBrush> <LinearGradientBrush StartPoint="0, 0.5" EndPoint="1, 0.5"> <GradientStop Color="LightGreen" Offset="1.0" /> <GradientStop Color="Transparent" Offset="0.85" /> </LinearGradientBrush> </Border.BorderBrush> </Border> <Border CornerRadius="10" BorderThickness="20"> <Border.BorderBrush> <LinearGradientBrush StartPoint="0.5, 0" EndPoint="0.5, 1"> <GradientStop Color="LightGreen" Offset="0.0" /> <GradientStop Color="Transparent" Offset="0.15" /> </LinearGradientBrush> </Border.BorderBrush> </Border> <Border CornerRadius="10" BorderThickness="20"> <Border.BorderBrush> <LinearGradientBrush StartPoint="0.5 0" EndPoint="0.5, 1"> <GradientStop Color="LightGreen" Offset="1.0" /> <GradientStop Color="Transparent" Offset="0.85" /> </LinearGradientBrush> </Border.BorderBrush> </Border> <Border BorderBrush="White" BorderThickness="2" CornerRadius="5" Margin="18"></Border> <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style.Setters> </Style>

We can make "glow" effect by playing with borders around some content in Grid. I feel it won't be so flexible of course as a InnerGlow BitmapEffect but anyway it is obsolete.

Recommend

  • A-Frame / THREE.js, no textures simplified gltf[glb] models
  • why am I getting type errors when hovering over map after layers are set?
  • Pull a specific string from an HTTP request in node.js
  • Warp texture with opengles
  • How do I tell frag-shader which texture (of many previously loaded textures) to use per vertex of an
  • PDF File generated by POST Request Not Opening
  • Using floor() function in GLSL when sampling a texture leaves glitch
  • Texture from Blender doesn't appear in Unity 3D
  • HLSL Shader to Subtract Background Image
  • OpenGL | Render Vertex at UV coordinate
  • WebGL textureCube bias causing seams
  • WebGL heightmap using vertex shader, using 32 bits instead of 8 bits
  • Javascript google transliterate API not served over https
  • WPF Inner Glow Effect
  • visual studio 2008 error C2371: 'int8_t' : redefinition; different basic types (http_parse
  • Combining variables in dataframe
  • ERR_SSL_SERVER_CERT_BAD_FORMAT in Chromium 6.3
  • Why does my class cost so much memory?
  • Angrybots Environment Floors
  • Unable to compute a CMAC using Cygwin and OpenSSL
  • UITableView not displaying parsed data
  • Msql: Counting growth over time
  • How to use SpEL to inject result of method call in Spring?
  • Is there a way of avoiding so many list(chain(*list_of_list))?
  • Get Windows Version
  • Aggregating based on “near” row values
  • What if the best way to return Option types by WCF service
  • What is the difference in Angular2 between inject a provider in @Component and @Module?
  • Trying to find the last non-empty cell in a specific row/range over multiple sheets
  • Range Multiplication VB.NET (What is wrong with this code?)
  • Self join tutorial #10 on sqlzoo
  • JOOQ nested condition
  • Why is this button causing my layout to break?
  • joining two bezier curves
  • Implementation of State Monad
  • sending/ receiving email in Java
  • How to delete a row from a dynamic generate table using jquery?
  • Proper way to use connect-multiparty with express.js?
  • Checking variable from a different class in C#