Skip to content

Commit e64b417

Browse files
committed
Add BlendedImage
1 parent cc30076 commit e64b417

File tree

5 files changed

+240
-21
lines changed

5 files changed

+240
-21
lines changed

dev/DevWinUI.Controls/Controls/Composition/CompositionShadow/CompositionShadow.cs

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -144,19 +144,7 @@ private async Task UpdateColorFromImageAsync()
144144
return;
145145
}
146146

147-
Uri uri = null;
148-
149-
switch (ImageSourceForEdgeColor)
150-
{
151-
case Uri u:
152-
uri = u;
153-
break;
154-
155-
case string s when Uri.TryCreate(s, UriKind.RelativeOrAbsolute, out var uriFromString):
156-
uri = uriFromString;
157-
break;
158-
}
159-
147+
Uri uri = GeneralHelper.GetUriFromObjectSource(ImageSourceForEdgeColor);
160148
if (uri != null)
161149
{
162150
var device = CanvasDevice.GetSharedDevice();
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
namespace DevWinUI;
2+
3+
[TemplatePart(Name = nameof(PART_ImageGrid), Type = typeof(Grid))]
4+
public partial class BlendedImage : Control
5+
{
6+
private const string PART_ImageGrid = "PART_ImageGrid";
7+
private Grid grid;
8+
9+
private Compositor _compositor;
10+
private ICompositionGenerator _generator;
11+
private CanvasGeometry _leftGeometry;
12+
private CanvasGeometry _rightGeometry;
13+
private float _topOffset;
14+
private IGaussianMaskSurface _leftGaussianSurface;
15+
private IGaussianMaskSurface _rightGaussianSurface;
16+
private float _currentWidth;
17+
private float _currentHeight;
18+
19+
public double BlurRadius
20+
{
21+
get { return (double)GetValue(BlurRadiusProperty); }
22+
set { SetValue(BlurRadiusProperty, value); }
23+
}
24+
25+
public static readonly DependencyProperty BlurRadiusProperty =
26+
DependencyProperty.Register(nameof(BlurRadius), typeof(double), typeof(BlendedImage), new PropertyMetadata(100.0d, OnPropertyChanged));
27+
28+
public double SplitValue
29+
{
30+
get { return (double)GetValue(SplitValueProperty); }
31+
set { SetValue(SplitValueProperty, value); }
32+
}
33+
34+
public static readonly DependencyProperty SplitValueProperty =
35+
DependencyProperty.Register(nameof(SplitValue), typeof(double), typeof(BlendedImage), new PropertyMetadata(50.0d, OnPropertyChanged));
36+
37+
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
38+
{
39+
var ctl = (BlendedImage)d;
40+
if (ctl != null)
41+
{
42+
ctl.Redraw();
43+
}
44+
}
45+
46+
public object PrimaryImageSource
47+
{
48+
get { return (object)GetValue(PrimaryImageSourceProperty); }
49+
set { SetValue(PrimaryImageSourceProperty, value); }
50+
}
51+
52+
public static readonly DependencyProperty PrimaryImageSourceProperty =
53+
DependencyProperty.Register(nameof(PrimaryImageSource), typeof(object), typeof(BlendedImage), new PropertyMetadata(null));
54+
55+
public object SecondaryImageSource
56+
{
57+
get { return (object)GetValue(SecondaryImageSourceProperty); }
58+
set { SetValue(SecondaryImageSourceProperty, value); }
59+
}
60+
61+
public static readonly DependencyProperty SecondaryImageSourceProperty =
62+
DependencyProperty.Register(nameof(SecondaryImageSource), typeof(object), typeof(BlendedImage), new PropertyMetadata(null));
63+
64+
public BlendedImage()
65+
{
66+
DefaultStyleKey = typeof(BlendedImage);
67+
}
68+
69+
protected override void OnApplyTemplate()
70+
{
71+
base.OnApplyTemplate();
72+
73+
grid = GetTemplateChild(PART_ImageGrid) as Grid;
74+
75+
_compositor = CompositionTarget.GetCompositorForCurrentThread();
76+
_generator = _compositor.CreateCompositionGenerator();
77+
78+
SizeChanged -= OnSizeChanged;
79+
SizeChanged += OnSizeChanged;
80+
}
81+
82+
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
83+
{
84+
if (ActualWidth <= 0 || ActualHeight <= 0)
85+
return;
86+
87+
_currentWidth = (float)ActualWidth;
88+
_currentHeight = (float)ActualHeight;
89+
90+
RebuildSurfaces();
91+
}
92+
private async void RebuildSurfaces()
93+
{
94+
if (grid == null || _generator == null)
95+
return;
96+
97+
var size = new Vector2(_currentWidth, _currentHeight);
98+
_topOffset = -_currentHeight / 2f;
99+
100+
// Create visuals again
101+
var leftImageVisual = _compositor.CreateSpriteVisual();
102+
leftImageVisual.Size = size;
103+
104+
var leftImageSurface =
105+
await _generator.CreateImageSurfaceAsync(
106+
GeneralHelper.GetUriFromObjectSource(PrimaryImageSource),
107+
size.ToSize(),
108+
ImageSurfaceOptions.Default
109+
);
110+
111+
_leftGeometry = CanvasGeometry.CreateRectangle(
112+
_generator.Device,
113+
new Rect(0f, 0f, 2f * (_currentWidth * (float)SplitValue) / 100f, 2f * _currentHeight)
114+
);
115+
116+
var leftOffset = -(_currentWidth * (float)SplitValue) / 100f;
117+
118+
var leftMaskedBrush = _compositor.CreateMaskBrush();
119+
leftMaskedBrush.Source = _compositor.CreateSurfaceBrush(leftImageSurface);
120+
_leftGaussianSurface = _generator.CreateGaussianMaskSurface(size.ToSize(), _leftGeometry, new Vector2(leftOffset, _topOffset), (float)BlurRadius);
121+
leftMaskedBrush.Mask = _compositor.CreateSurfaceBrush(_leftGaussianSurface);
122+
leftImageVisual.Brush = leftMaskedBrush;
123+
124+
// Right side
125+
var rightImageVisual = _compositor.CreateSpriteVisual();
126+
rightImageVisual.Size = size;
127+
128+
var rightImageSurface =
129+
await _generator.CreateImageSurfaceAsync(
130+
GeneralHelper.GetUriFromObjectSource(SecondaryImageSource),
131+
size.ToSize(),
132+
ImageSurfaceOptions.Default
133+
);
134+
135+
_rightGeometry = CanvasGeometry.CreateRectangle(
136+
_generator.Device,
137+
new Rect(0, 0, 2f * (_currentWidth * (100f - (float)SplitValue)) / 100f, 2f * _currentHeight)
138+
);
139+
140+
var rightMaskedBrush = _compositor.CreateMaskBrush();
141+
rightMaskedBrush.Source = _compositor.CreateSurfaceBrush(rightImageSurface);
142+
_rightGaussianSurface = _generator.CreateGaussianMaskSurface(size.ToSize(), _rightGeometry, new Vector2(-leftOffset, _topOffset), (float)BlurRadius);
143+
rightMaskedBrush.Mask = _compositor.CreateSurfaceBrush(_rightGaussianSurface);
144+
rightImageVisual.Brush = rightMaskedBrush;
145+
146+
// Replace child visual
147+
var container = _compositor.CreateContainerVisual();
148+
container.Size = size;
149+
container.Children.InsertAtTop(leftImageVisual);
150+
container.Children.InsertAtTop(rightImageVisual);
151+
152+
ElementCompositionPreview.SetElementChildVisual(grid, container);
153+
}
154+
155+
private void Redraw()
156+
{
157+
if (_leftGeometry == null || _rightGeometry == null)
158+
{
159+
return;
160+
}
161+
162+
var leftOffset = -(_currentWidth * (float)SplitValue) / 100f;
163+
_leftGeometry = CanvasGeometry.CreateRectangle(_generator.Device, new Rect(0f, 0f, 2f * (_currentWidth * (float)SplitValue) / 100f, 2f * _currentHeight));
164+
_leftGaussianSurface.Redraw(_leftGeometry, new Vector2(leftOffset, _topOffset), (float)BlurRadius);
165+
_rightGeometry = CanvasGeometry.CreateRectangle(_generator.Device, new Rect(0, 0, 2f * (_currentWidth * (100f - (float)SplitValue)) / 100, 2f * _currentHeight));
166+
_rightGaussianSurface.Redraw(_rightGeometry, new Vector2(-leftOffset, _topOffset), (float)BlurRadius);
167+
}
168+
}
169+

dev/DevWinUI.Controls/Controls/Win2D/CircleIcon/CircleIcon.cs

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,23 @@ public partial class CircleIcon : Control
99
private const string PART_Canvas = "PART_Canvas";
1010
private CanvasControl canvas;
1111

12+
public Color ClearColor
13+
{
14+
get { return (Color)GetValue(ClearColorProperty); }
15+
set { SetValue(ClearColorProperty, value); }
16+
}
17+
18+
public static readonly DependencyProperty ClearColorProperty =
19+
DependencyProperty.Register(nameof(ClearColor), typeof(Color), typeof(CircleIcon), new PropertyMetadata(Colors.Transparent, OnClearColorChanged));
20+
21+
private static void OnClearColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
22+
{
23+
var ctl = (CircleIcon)d;
24+
if (ctl != null)
25+
{
26+
ctl.UpdateClearColor();
27+
}
28+
}
1229
public double BorderStrokeThickness
1330
{
1431
get { return (double)GetValue(BorderStrokeThicknessProperty); }
@@ -53,14 +70,7 @@ private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChan
5370
ctl.UpdateCanvas();
5471
}
5572
}
56-
57-
private void UpdateCanvas()
58-
{
59-
if (canvas == null)
60-
return;
61-
62-
canvas.Invalidate();
63-
}
73+
6474
public CircleIcon()
6575
{
6676
DefaultStyleKey = typeof(CircleIcon);
@@ -87,10 +97,27 @@ protected override void OnApplyTemplate()
8797
new RadialGradientStopData() { Color = endColor, Position = 1 }
8898
};
8999
}
100+
101+
UpdateClearColor();
102+
90103
canvas.Draw -= OnCanvasDraw;
91104
canvas.Draw += OnCanvasDraw;
92105
}
106+
private void UpdateClearColor()
107+
{
108+
if (canvas == null)
109+
return;
110+
111+
canvas.ClearColor = ClearColor;
112+
}
113+
114+
private void UpdateCanvas()
115+
{
116+
if (canvas == null)
117+
return;
93118

119+
canvas.Invalidate();
120+
}
94121
private void OnCanvasDraw(CanvasControl sender, CanvasDrawEventArgs args)
95122
{
96123
var width = (float)sender.ActualWidth;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4+
xmlns:local="using:DevWinUI">
5+
6+
<Style TargetType="local:BlendedImage" BasedOn="{StaticResource DefaultBlendedImageStyle}" />
7+
8+
<Style x:Key="DefaultBlendedImageStyle" TargetType="local:BlendedImage">
9+
<Setter Property="Template">
10+
<Setter.Value>
11+
<ControlTemplate TargetType="local:BlendedImage">
12+
<Grid x:Name="PART_ImageGrid" />
13+
</ControlTemplate>
14+
</Setter.Value>
15+
</Setter>
16+
</Style>
17+
</ResourceDictionary>

dev/DevWinUI/Helpers/GeneralHelper.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,4 +300,22 @@ public static string LoadNativeString(uint id, int bufferSize = 256)
300300

301301
return len > 0 ? new string(buffer[..len]) : $"[{id}]";
302302
}
303+
304+
public static Uri GetUriFromObjectSource(object value)
305+
{
306+
Uri result = null;
307+
308+
switch (value)
309+
{
310+
case Uri uri:
311+
result = uri;
312+
break;
313+
314+
case string text when Uri.TryCreate(text, UriKind.RelativeOrAbsolute, out var parsedUri):
315+
result = parsedUri;
316+
break;
317+
}
318+
319+
return result;
320+
}
303321
}

0 commit comments

Comments
 (0)