Skip to content

Commit 28db6bf

Browse files
committed
Add BlurEffectBrush and ImageEffectBrush
1 parent 22f9d4e commit 28db6bf

9 files changed

Lines changed: 466 additions & 0 deletions

File tree

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ Install-Package DevWinUI
144144
## 🔥 DevWinUI.Controls 🔥
145145
### ⚡ What’s Inside? ⚡
146146

147+
- ✨ BlurEffectBrush
148+
- ✨ ImageEffectBrush
147149
- ✨ BlurEffectControl
148150
- ✨ AnimatedGradient
149151
- ✨ ShimmerTextBlock
@@ -277,6 +279,12 @@ Install-Package DevWinUI.ContextMenu
277279

278280
## 🕰️ History 🕰️
279281

282+
### ImageEffectBrush
283+
![DevWinUI](https://raw.githubusercontent.com/ghost1372/DevWinUI-Resources/refs/heads/main/DevWinUI-Docs/ImageEffectBrush.gif)
284+
285+
### BlurEffectBrush
286+
![DevWinUI](https://raw.githubusercontent.com/ghost1372/DevWinUI-Resources/refs/heads/main/DevWinUI-Docs/BlurEffectBrush.png)
287+
280288
### BlurEffectControl
281289
![DevWinUI](https://raw.githubusercontent.com/ghost1372/DevWinUI-Resources/refs/heads/main/DevWinUI-Docs/BlurEffectControl.gif)
282290

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
namespace DevWinUI;
2+
public partial class BlurEffectBrush : XamlCompositionBrushBase
3+
{
4+
private BlurEffectManager _manager;
5+
private FrameworkElement _dummyElement;
6+
7+
#region Dependency Properties
8+
public bool IsBlurEnabled
9+
{
10+
get { return (bool)GetValue(IsBlurEnabledProperty); }
11+
set { SetValue(IsBlurEnabledProperty, value); }
12+
}
13+
14+
public static readonly DependencyProperty IsBlurEnabledProperty =
15+
DependencyProperty.Register(nameof(IsBlurEnabled), typeof(bool), typeof(BlurEffectBrush), new PropertyMetadata(true, OnBlurPropertyChanged));
16+
17+
public bool IsTintEnabled
18+
{
19+
get { return (bool)GetValue(IsTintEnabledProperty); }
20+
set { SetValue(IsTintEnabledProperty, value); }
21+
}
22+
23+
public static readonly DependencyProperty IsTintEnabledProperty =
24+
DependencyProperty.Register(nameof(IsTintEnabled), typeof(bool), typeof(BlurEffectBrush), new PropertyMetadata(false, OnBlurPropertyChanged));
25+
26+
public BlurTintTarget TintTargetMode
27+
{
28+
get { return (BlurTintTarget)GetValue(TintTargetModeProperty); }
29+
set { SetValue(TintTargetModeProperty, value); }
30+
}
31+
32+
public static readonly DependencyProperty TintTargetModeProperty =
33+
DependencyProperty.Register(nameof(TintTargetMode), typeof(BlurTintTarget), typeof(BlurEffectBrush), new PropertyMetadata(BlurTintTarget.Foreground, OnBlurPropertyChanged));
34+
35+
public ICompositionSurface SurfaceSource
36+
{
37+
get { return (ICompositionSurface)GetValue(SurfaceSourceProperty); }
38+
set { SetValue(SurfaceSourceProperty, value); }
39+
}
40+
41+
public static readonly DependencyProperty SurfaceSourceProperty =
42+
DependencyProperty.Register(nameof(SurfaceSource), typeof(ICompositionSurface), typeof(BlurEffectBrush), new PropertyMetadata(null, OnBlurPropertyChanged));
43+
44+
public CompositionSurfaceBrush SurfaceBrushSource
45+
{
46+
get { return (CompositionSurfaceBrush)GetValue(SurfaceBrushSourceProperty); }
47+
set { SetValue(SurfaceBrushSourceProperty, value); }
48+
}
49+
50+
public static readonly DependencyProperty SurfaceBrushSourceProperty =
51+
DependencyProperty.Register(nameof(SurfaceBrushSource), typeof(CompositionSurfaceBrush), typeof(BlurEffectBrush), new PropertyMetadata(null, OnBlurPropertyChanged));
52+
53+
public Visual VisualSource
54+
{
55+
get { return (Visual)GetValue(VisualSourceProperty); }
56+
set { SetValue(VisualSourceProperty, value); }
57+
}
58+
59+
public static readonly DependencyProperty VisualSourceProperty =
60+
DependencyProperty.Register(nameof(VisualSource), typeof(Visual), typeof(BlurEffectBrush), new PropertyMetadata(null, OnBlurPropertyChanged));
61+
62+
public CompositionBrush CustomSourceBrush
63+
{
64+
get { return (CompositionBrush)GetValue(CustomSourceBrushProperty); }
65+
set { SetValue(CustomSourceBrushProperty, value); }
66+
}
67+
68+
public static readonly DependencyProperty CustomSourceBrushProperty =
69+
DependencyProperty.Register(nameof(CustomSourceBrush), typeof(CompositionBrush), typeof(BlurEffectBrush), new PropertyMetadata(null, OnBlurPropertyChanged));
70+
71+
public double BlurAmount
72+
{
73+
get => (double)GetValue(BlurAmountProperty);
74+
set => SetValue(BlurAmountProperty, value);
75+
}
76+
77+
public static readonly DependencyProperty BlurAmountProperty =
78+
DependencyProperty.Register(nameof(BlurAmount), typeof(double), typeof(BlurEffectBrush), new PropertyMetadata(10.0, OnBlurPropertyChanged));
79+
80+
public Color TintColor
81+
{
82+
get => (Color)GetValue(TintColorProperty);
83+
set => SetValue(TintColorProperty, value);
84+
}
85+
86+
public static readonly DependencyProperty TintColorProperty =
87+
DependencyProperty.Register(nameof(TintColor), typeof(Color), typeof(BlurEffectBrush), new PropertyMetadata(Colors.Transparent, OnBlurPropertyChanged));
88+
89+
public bool UseNoise
90+
{
91+
get => (bool)GetValue(UseNoiseProperty);
92+
set => SetValue(UseNoiseProperty, value);
93+
}
94+
95+
public static readonly DependencyProperty UseNoiseProperty =
96+
DependencyProperty.Register(nameof(UseNoise), typeof(bool), typeof(BlurEffectBrush), new PropertyMetadata(false, OnBlurPropertyChanged));
97+
98+
public string NoiseUri
99+
{
100+
get => (string)GetValue(NoiseUriProperty);
101+
set => SetValue(NoiseUriProperty, value);
102+
}
103+
104+
public static readonly DependencyProperty NoiseUriProperty =
105+
DependencyProperty.Register(nameof(NoiseUri), typeof(string), typeof(BlurEffectBrush), new PropertyMetadata("ms-appx:///Assets/Noise/Noise.jpg", OnBlurPropertyChanged));
106+
107+
public EffectBorderMode BorderMode
108+
{
109+
get => (EffectBorderMode)GetValue(BorderModeProperty);
110+
set => SetValue(BorderModeProperty, value);
111+
}
112+
113+
public static readonly DependencyProperty BorderModeProperty =
114+
DependencyProperty.Register(nameof(BorderMode), typeof(EffectBorderMode), typeof(BlurEffectBrush), new PropertyMetadata(EffectBorderMode.Hard, OnBlurPropertyChanged));
115+
116+
public EffectOptimization Optimization
117+
{
118+
get => (EffectOptimization)GetValue(OptimizationProperty);
119+
set => SetValue(OptimizationProperty, value);
120+
}
121+
122+
public static readonly DependencyProperty OptimizationProperty =
123+
DependencyProperty.Register(nameof(Optimization), typeof(EffectOptimization), typeof(BlurEffectBrush), new PropertyMetadata(EffectOptimization.Balanced, OnBlurPropertyChanged));
124+
125+
public BlendEffectMode BlendMode
126+
{
127+
get => (BlendEffectMode)GetValue(BlendModeProperty);
128+
set => SetValue(BlendModeProperty, value);
129+
}
130+
131+
public static readonly DependencyProperty BlendModeProperty =
132+
DependencyProperty.Register(nameof(BlendMode), typeof(BlendEffectMode), typeof(BlurEffectBrush), new PropertyMetadata(BlendEffectMode.SoftLight, OnBlurPropertyChanged));
133+
134+
public BlendEffectMode NoiseBlendMode
135+
{
136+
get => (BlendEffectMode)GetValue(NoiseBlendModeProperty);
137+
set => SetValue(NoiseBlendModeProperty, value);
138+
}
139+
140+
public static readonly DependencyProperty NoiseBlendModeProperty =
141+
DependencyProperty.Register(nameof(NoiseBlendMode), typeof(BlendEffectMode), typeof(BlurEffectBrush), new PropertyMetadata(BlendEffectMode.Screen, OnBlurPropertyChanged));
142+
143+
public BlurSourceType SourceType
144+
{
145+
get => (BlurSourceType)GetValue(SourceTypeProperty);
146+
set => SetValue(SourceTypeProperty, value);
147+
}
148+
public static readonly DependencyProperty SourceTypeProperty =
149+
DependencyProperty.Register(nameof(SourceType), typeof(BlurSourceType), typeof(BlurEffectBrush), new PropertyMetadata(BlurSourceType.Backdrop, OnBlurPropertyChanged));
150+
151+
public CompositionStretch NoiseStretch
152+
{
153+
get { return (CompositionStretch)GetValue(NoiseStretchProperty); }
154+
set { SetValue(NoiseStretchProperty, value); }
155+
}
156+
157+
public static readonly DependencyProperty NoiseStretchProperty =
158+
DependencyProperty.Register(nameof(NoiseStretch), typeof(CompositionStretch), typeof(BlurEffectBrush), new PropertyMetadata(CompositionStretch.UniformToFill, OnBlurPropertyChanged));
159+
160+
#endregion
161+
protected override void OnConnected()
162+
{
163+
if (CompositionBrush == null)
164+
{
165+
_dummyElement = new Grid(); // Not used visually, just for compositor access
166+
_manager = new BlurEffectManager(_dummyElement)
167+
{
168+
IsTintEnabled = this.IsTintEnabled,
169+
TintTargetMode = this.TintTargetMode,
170+
TintColor = this.TintColor,
171+
SurfaceSource = this.SurfaceSource,
172+
SurfaceBrushSource = this.SurfaceBrushSource,
173+
VisualSource = this.VisualSource,
174+
CustomSourceBrush = this.CustomSourceBrush,
175+
NoiseUri = this.NoiseUri,
176+
SourceType = this.SourceType,
177+
BlurAmount = this.BlurAmount,
178+
UseNoise = this.UseNoise,
179+
BorderMode = this.BorderMode,
180+
Optimization = this.Optimization,
181+
BlendMode = this.BlendMode,
182+
NoiseBlendMode = this.NoiseBlendMode
183+
};
184+
185+
_manager.Refresh();
186+
CompositionBrush = _manager.GetBrush();
187+
}
188+
}
189+
190+
protected override void OnDisconnected()
191+
{
192+
_manager?.DisableBlur();
193+
_manager = null;
194+
195+
CompositionBrush?.Dispose();
196+
CompositionBrush = null;
197+
}
198+
199+
private static void OnBlurPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
200+
{
201+
if (d is BlurEffectBrush b && b._manager != null)
202+
{
203+
b._manager.IsTintEnabled = b.IsTintEnabled;
204+
b._manager.TintTargetMode = b.TintTargetMode;
205+
b._manager.TintColor = b.TintColor;
206+
b._manager.SurfaceSource = b.SurfaceSource;
207+
b._manager.SurfaceBrushSource = b.SurfaceBrushSource;
208+
b._manager.VisualSource = b.VisualSource;
209+
b._manager.CustomSourceBrush = b.CustomSourceBrush;
210+
b._manager.NoiseUri = b.NoiseUri;
211+
b._manager.SourceType = b.SourceType;
212+
b._manager.BlurAmount = b.BlurAmount;
213+
b._manager.UseNoise = b.UseNoise;
214+
b._manager.BorderMode = b.BorderMode;
215+
b._manager.Optimization = b.Optimization;
216+
b._manager.BlendMode = b.BlendMode;
217+
b._manager.NoiseBlendMode = b.NoiseBlendMode;
218+
219+
b._manager.Refresh();
220+
}
221+
}
222+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
using Windows.Graphics.Effects;
2+
3+
namespace DevWinUI;
4+
public partial class ImageEffectBrush : XamlCompositionBrushBase
5+
{
6+
private LoadedImageSurface _surface;
7+
private CompositionSurfaceBrush _surfaceBrush;
8+
9+
public string ImageUri
10+
{
11+
get { return (string)GetValue(ImageUriStringProperty); }
12+
set { SetValue(ImageUriStringProperty, value); }
13+
}
14+
15+
public static readonly DependencyProperty ImageUriStringProperty =
16+
DependencyProperty.Register(nameof(ImageUri), typeof(string), typeof(ImageEffectBrush),new PropertyMetadata(string.Empty, OnImageUriChanged));
17+
18+
private static void OnImageUriChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
19+
{
20+
var brush = (ImageEffectBrush)d;
21+
// Unbox and update surface if CompositionBrush exists
22+
if (brush._surfaceBrush != null)
23+
{
24+
var newSurface = LoadedImageSurface.StartLoadFromUri(new Uri((string)e.NewValue));
25+
brush._surface = newSurface;
26+
brush._surfaceBrush.Surface = newSurface;
27+
}
28+
}
29+
30+
protected override void OnConnected()
31+
{
32+
// return if Uri String is null or empty
33+
if (String.IsNullOrEmpty(ImageUri))
34+
{
35+
return;
36+
}
37+
38+
Compositor compositor = CompositionTarget.GetCompositorForCurrentThread();
39+
40+
// Use LoadedImageSurface API to get ICompositionSurface from image uri provided
41+
_surface = LoadedImageSurface.StartLoadFromUri(new Uri(ImageUri));
42+
43+
// Load Surface onto SurfaceBrush
44+
_surfaceBrush = compositor.CreateSurfaceBrush(_surface);
45+
_surfaceBrush.Stretch = CompositionStretch.UniformToFill;
46+
47+
// CompositionCapabilities: Are Tint+Temperature and Saturation supported?
48+
var capabilities = new CompositionCapabilities();
49+
bool usingFallback = !capabilities.AreEffectsSupported();
50+
if (usingFallback)
51+
{
52+
// If Effects are not supported, Fallback to image without effects
53+
CompositionBrush = _surfaceBrush;
54+
return;
55+
}
56+
57+
// Define Effect graph
58+
IGraphicsEffect graphicsEffect = new SaturationEffect
59+
{
60+
Name = "Saturation",
61+
Saturation = 0.3f,
62+
Source = new TemperatureAndTintEffect
63+
{
64+
Name = "TempAndTint",
65+
Temperature = 0,
66+
Source = new CompositionEffectSourceParameter("Surface"),
67+
}
68+
};
69+
70+
// Create EffectFactory and EffectBrush
71+
CompositionEffectFactory effectFactory = compositor.CreateEffectFactory(graphicsEffect, new[] { "TempAndTint.Temperature" });
72+
CompositionEffectBrush effectBrush = effectFactory.CreateBrush();
73+
effectBrush.SetSourceParameter("Surface", _surfaceBrush);
74+
75+
// Set EffectBrush to paint Xaml UIElement
76+
CompositionBrush = effectBrush;
77+
78+
// Trivial looping animation to demonstrate animated effect
79+
ScalarKeyFrameAnimation tempAnim = compositor.CreateScalarKeyFrameAnimation();
80+
tempAnim.InsertKeyFrame(0, 0);
81+
tempAnim.InsertKeyFrame(0.5f, 1f);
82+
tempAnim.InsertKeyFrame(1, 0);
83+
tempAnim.Duration = TimeSpan.FromSeconds(5);
84+
tempAnim.IterationBehavior = Microsoft.UI.Composition.AnimationIterationBehavior.Forever;
85+
effectBrush.Properties.StartAnimation("TempAndTint.Temperature", tempAnim);
86+
}
87+
88+
protected override void OnDisconnected()
89+
{
90+
// Dispose Surface and CompositionBrushes if XamlCompBrushBase is removed from tree
91+
if (_surface != null)
92+
{
93+
_surface.Dispose();
94+
_surface = null;
95+
}
96+
if (CompositionBrush != null)
97+
{
98+
CompositionBrush.Dispose();
99+
CompositionBrush = null;
100+
}
101+
}
102+
}

dev/DevWinUI.Gallery/Assets/NavViewMenu/AppData.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,13 @@
402402
"ImagePath": "ms-appx:///Assets/Fluent/Acrylic.png",
403403
"UseBuiltInNavigationViewInfoBadgeStyle": true,
404404
"Items": [
405+
{
406+
"UniqueId": "DevWinUIGallery.Views.CompositionBrushPage",
407+
"Title": "Composition Brush",
408+
"Subtitle": "Paint XAML UIElements with CompositionBrushes (and load Surfaces from XAML to CompositionBrushes)",
409+
"IsNew": true,
410+
"ImagePath": "ms-appx:///Assets/Fluent/InkToolbar.png"
411+
},
405412
{
406413
"UniqueId": "DevWinUIGallery.Views.BlurEffectControlPage",
407414
"Title": "Blur Effect Control",
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<Grid>
2+
<Grid.Background>
3+
<ImageBrush ImageSource="ms-appx:///Assets/Landscapes/Landscape-12.jpg"
4+
Stretch="UniformToFill" />
5+
</Grid.Background>
6+
<Grid Padding="10">
7+
<Path HorizontalAlignment="Center"
8+
VerticalAlignment="Center"
9+
Data="M200 20 a200 200 0 1 0 1 0m-1 100a100 100 0 1 1 -1 0"
10+
Stretch="Uniform">
11+
<Path.Fill>
12+
<dev:BlurEffectBrush FallbackColor="#80C0C0C0" />
13+
</Path.Fill>
14+
</Path>
15+
</Grid>
16+
</Grid>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Grid>
2+
<Grid Padding="10">
3+
<Path HorizontalAlignment="Center"
4+
VerticalAlignment="Center"
5+
Data="M200 20 a200 200 0 1 0 1 0m-1 100a100 100 0 1 1 -1 0"
6+
Stretch="Uniform">
7+
<Path.Fill>
8+
<dev:ImageEffectBrush ImageUri="ms-appx:///Assets/Landscapes/Landscape-12.jpg" />
9+
</Path.Fill>
10+
</Path>
11+
</Grid>
12+
</Grid>
13+
14+
<Grid>
15+
<Grid.Background>
16+
<dev:ImageEffectBrush ImageUri="ms-appx:///Assets/Landscapes/Landscape-12.jpg" />
17+
</Grid.Background>
18+
</Grid>

0 commit comments

Comments
 (0)