diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj index d48bda9a5a3..a6ae7bae87b 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj +++ b/Microsoft.Toolkit.Uwp.SampleApp/Microsoft.Toolkit.Uwp.SampleApp.csproj @@ -269,9 +269,16 @@ PreserveNewest + + + + + + + @@ -505,6 +512,15 @@ FocusBehaviorPage.xaml + + GeometryMaskSurfaceBrushPage.xaml + + + ImageMaskSurfaceBrushPage.xaml + + + ImageSurfaceBrushPage.xaml + MetadataControlPage.xaml @@ -515,6 +531,9 @@ RichSuggestBoxPage.xaml + + GeometrySurfaceBrushPage.xaml + TilesBrushPage.xaml @@ -632,6 +651,10 @@ + + + + Designer @@ -984,6 +1007,18 @@ Designer + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + Designer MSBuild:Compile @@ -1007,6 +1042,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + MSBuild:Compile Designer diff --git a/Microsoft.Toolkit.Uwp.SampleApp/Models/Sample.cs b/Microsoft.Toolkit.Uwp.SampleApp/Models/Sample.cs index e26a5076895..ab40135fce7 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/Models/Sample.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/Models/Sample.cs @@ -480,156 +480,158 @@ public async Task PreparePropertyDescriptorAsync() if (_propertyDescriptor == null) { - // Get Xaml code - using (var codeStream = await StreamHelper.GetPackagedFileStreamAsync(XamlCodeFile.StartsWith('/') ? XamlCodeFile : $"SamplePages/{Name}/{XamlCodeFile}")) + try { - XamlCode = await codeStream.ReadTextAsync(Encoding.UTF8); + // Get Xaml code + using (var codeStream = await StreamHelper.GetPackagedFileStreamAsync(XamlCodeFile.StartsWith('/') ? XamlCodeFile : $"SamplePages/{Name}/{XamlCodeFile}")) + { + XamlCode = await codeStream.ReadTextAsync(Encoding.UTF8); - // Look for @[] values and generate associated properties - var regularExpression = new Regex("(?<=\\\")@\\[(?.+?)(:(?.+?):(?.+?)(:(?.+?))?(:(?.*))*)?\\]@?(?=\\\")"); + // Look for @[] values and generate associated properties + var regularExpression = new Regex("(?<=\\\")@\\[(?.+?)(:(?.+?):(?.+?)(:(?.+?))?(:(?.*))*)?\\]@?(?=\\\")"); - _propertyDescriptor = new PropertyDescriptor { Expando = new ExpandoObject() }; - var proxy = (IDictionary)_propertyDescriptor.Expando; + _propertyDescriptor = new PropertyDescriptor { Expando = new ExpandoObject() }; + var proxy = (IDictionary)_propertyDescriptor.Expando; - foreach (Match match in regularExpression.Matches(XamlCode)) - { - var label = match.Groups["name"].Value; - var name = label.Replace(" ", string.Empty); // Allow us to have nicer display names, but create valid properties. - var type = match.Groups["type"].Value; - var value = match.Groups["value"].Value; + foreach (Match match in regularExpression.Matches(XamlCode)) + { + var label = match.Groups["name"].Value; + var name = label.Replace(" ", string.Empty); // Allow us to have nicer display names, but create valid properties. + var type = match.Groups["type"].Value; + var value = match.Groups["value"].Value; - var existingOption = _propertyDescriptor.Options.Where(o => o.Name == name).FirstOrDefault(); + var existingOption = _propertyDescriptor.Options.Where(o => o.Name == name).FirstOrDefault(); - if (existingOption == null && string.IsNullOrWhiteSpace(type)) - { - throw new NotSupportedException($"Unrecognized short identifier '{name}'; Define type and parameters of property in first occurrence in {XamlCodeFile}."); - } + if (existingOption == null && string.IsNullOrWhiteSpace(type)) + { + throw new NotSupportedException($"Unrecognized short identifier '{name}'; Define type and parameters of property in first occurrence in {XamlCodeFile}."); + } - if (Enum.TryParse(type, out PropertyKind kind)) - { - if (existingOption != null) + if (Enum.TryParse(type, out PropertyKind kind)) { - if (existingOption.Kind != kind) + if (existingOption != null) { - throw new NotSupportedException($"Multiple options with same name but different type not supported: {XamlCodeFile}:{name}"); - } + if (existingOption.Kind != kind) + { + throw new NotSupportedException($"Multiple options with same name but different type not supported: {XamlCodeFile}:{name}"); + } - continue; - } + continue; + } - PropertyOptions options; + PropertyOptions options; - switch (kind) - { - case PropertyKind.Slider: - case PropertyKind.DoubleSlider: - try - { - var sliderOptions = new SliderPropertyOptions { DefaultValue = double.Parse(value, CultureInfo.InvariantCulture) }; - var parameters = match.Groups["parameters"].Value; - var split = parameters.Split('-'); - int minIndex = 0; - int minMultiplier = 1; - if (string.IsNullOrEmpty(split[0])) + switch (kind) + { + case PropertyKind.Slider: + case PropertyKind.DoubleSlider: + try { - minIndex = 1; - minMultiplier = -1; + var sliderOptions = new SliderPropertyOptions { DefaultValue = double.Parse(value, CultureInfo.InvariantCulture) }; + var parameters = match.Groups["parameters"].Value; + var split = parameters.Split('-'); + int minIndex = 0; + int minMultiplier = 1; + if (string.IsNullOrEmpty(split[0])) + { + minIndex = 1; + minMultiplier = -1; + } + + sliderOptions.MinValue = minMultiplier * double.Parse(split[minIndex], CultureInfo.InvariantCulture); + sliderOptions.MaxValue = double.Parse(split[minIndex + 1], CultureInfo.InvariantCulture); + if (split.Length > 2 + minIndex) + { + sliderOptions.Step = double.Parse(split[split.Length - 1], CultureInfo.InvariantCulture); + } + + options = sliderOptions; } - - sliderOptions.MinValue = minMultiplier * double.Parse(split[minIndex], CultureInfo.InvariantCulture); - sliderOptions.MaxValue = double.Parse(split[minIndex + 1], CultureInfo.InvariantCulture); - if (split.Length > 2 + minIndex) + catch (Exception ex) { - sliderOptions.Step = double.Parse(split[split.Length - 1], CultureInfo.InvariantCulture); + Debug.WriteLine($"Unable to extract slider info from {value}({ex.Message})"); + TrackingManager.TrackException(ex); + continue; } - options = sliderOptions; - } - catch (Exception ex) - { - Debug.WriteLine($"Unable to extract slider info from {value}({ex.Message})"); - TrackingManager.TrackException(ex); - continue; - } + break; - break; - - case PropertyKind.TimeSpan: - try - { - var sliderOptions = new SliderPropertyOptions { DefaultValue = TimeSpan.FromMilliseconds(double.Parse(value, CultureInfo.InvariantCulture)) }; - var parameters = match.Groups["parameters"].Value; - var split = parameters.Split('-'); - int minIndex = 0; - int minMultiplier = 1; - if (string.IsNullOrEmpty(split[0])) + case PropertyKind.TimeSpan: + try { - minIndex = 1; - minMultiplier = -1; + var sliderOptions = new SliderPropertyOptions { DefaultValue = TimeSpan.FromMilliseconds(double.Parse(value, CultureInfo.InvariantCulture)) }; + var parameters = match.Groups["parameters"].Value; + var split = parameters.Split('-'); + int minIndex = 0; + int minMultiplier = 1; + if (string.IsNullOrEmpty(split[0])) + { + minIndex = 1; + minMultiplier = -1; + } + + sliderOptions.MinValue = minMultiplier * double.Parse(split[minIndex], CultureInfo.InvariantCulture); + sliderOptions.MaxValue = double.Parse(split[minIndex + 1], CultureInfo.InvariantCulture); + if (split.Length > 2 + minIndex) + { + sliderOptions.Step = double.Parse(split[split.Length - 1], CultureInfo.InvariantCulture); + } + + options = sliderOptions; } - - sliderOptions.MinValue = minMultiplier * double.Parse(split[minIndex], CultureInfo.InvariantCulture); - sliderOptions.MaxValue = double.Parse(split[minIndex + 1], CultureInfo.InvariantCulture); - if (split.Length > 2 + minIndex) + catch (Exception ex) { - sliderOptions.Step = double.Parse(split[split.Length - 1], CultureInfo.InvariantCulture); + Debug.WriteLine($"Unable to extract slider info from {value}({ex.Message})"); + TrackingManager.TrackException(ex); + continue; } - options = sliderOptions; - } - catch (Exception ex) - { - Debug.WriteLine($"Unable to extract slider info from {value}({ex.Message})"); - TrackingManager.TrackException(ex); - continue; - } - - break; + break; - case PropertyKind.Enum: - try - { - options = new PropertyOptions(); - var split = value.Split('.'); - var typeName = string.Join(".", split.Take(split.Length - 1)); - var enumType = LookForTypeByName(typeName); - options.DefaultValue = Enum.Parse(enumType, split.Last()); - } - catch (Exception ex) - { - Debug.WriteLine($"Unable to parse enum from {value}({ex.Message})"); - TrackingManager.TrackException(ex); - continue; - } + case PropertyKind.Enum: + try + { + options = new PropertyOptions(); + var split = value.Split('.'); + var typeName = string.Join(".", split.Take(split.Length - 1)); + var enumType = LookForTypeByName(typeName); + options.DefaultValue = Enum.Parse(enumType, split.Last()); + } + catch (Exception ex) + { + Debug.WriteLine($"Unable to parse enum from {value}({ex.Message})"); + TrackingManager.TrackException(ex); + continue; + } - break; + break; - case PropertyKind.Bool: - try - { - options = new PropertyOptions { DefaultValue = bool.Parse(value) }; - } - catch (Exception ex) - { - Debug.WriteLine($"Unable to parse bool from {value}({ex.Message})"); - continue; - } + case PropertyKind.Bool: + try + { + options = new PropertyOptions { DefaultValue = bool.Parse(value) }; + } + catch (Exception ex) + { + Debug.WriteLine($"Unable to parse bool from {value}({ex.Message})"); + continue; + } - break; + break; - case PropertyKind.Brush: - try - { - options = new PropertyOptions { DefaultValue = value }; - } - catch (Exception ex) - { - Debug.WriteLine($"Unable to parse bool from {value}({ex.Message})"); - TrackingManager.TrackException(ex); - continue; - } + case PropertyKind.Brush: + try + { + options = new PropertyOptions { DefaultValue = value }; + } + catch (Exception ex) + { + Debug.WriteLine($"Unable to parse bool from {value}({ex.Message})"); + TrackingManager.TrackException(ex); + continue; + } - break; + break; case PropertyKind.Thickness: try @@ -659,24 +661,29 @@ public async Task PreparePropertyDescriptorAsync() continue; } - break; + break; - default: - options = new PropertyOptions { DefaultValue = value }; - break; - } + default: + options = new PropertyOptions { DefaultValue = value }; + break; + } - options.Label = label; - options.Name = name; - options.OriginalString = match.Value; - options.Kind = kind; - options.IsTwoWayBinding = options.OriginalString.EndsWith("@"); - proxy[name] = new ValueHolder(options.DefaultValue); + options.Label = label; + options.Name = name; + options.OriginalString = match.Value; + options.Kind = kind; + options.IsTwoWayBinding = options.OriginalString.EndsWith("@"); + proxy[name] = new ValueHolder(options.DefaultValue); - _propertyDescriptor.Options.Add(options); + _propertyDescriptor.Options.Add(options); + } } } } + catch (Exception e) + { + var msg = e.Message; + } } } diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/CanvasPathGeometry/CanvasPathGeometryPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/CanvasPathGeometry/CanvasPathGeometryPage.xaml.cs index 3cbdacb187c..8015c596a70 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/CanvasPathGeometry/CanvasPathGeometryPage.xaml.cs +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/CanvasPathGeometry/CanvasPathGeometryPage.xaml.cs @@ -9,6 +9,7 @@ using Microsoft.Graphics.Canvas.Geometry; using Microsoft.Graphics.Canvas.UI.Xaml; using Microsoft.Toolkit.Uwp.UI; +using Microsoft.Toolkit.Uwp.UI.Media; using Microsoft.Toolkit.Uwp.UI.Media.Geometry; using Windows.System; using Windows.UI; diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GeometryMaskSurfaceBrush/GeometryMaskSurfaceBrush.png b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GeometryMaskSurfaceBrush/GeometryMaskSurfaceBrush.png new file mode 100644 index 00000000000..44a3b5ff836 Binary files /dev/null and b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GeometryMaskSurfaceBrush/GeometryMaskSurfaceBrush.png differ diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GeometryMaskSurfaceBrush/GeometryMaskSurfaceBrushPage.xaml b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GeometryMaskSurfaceBrush/GeometryMaskSurfaceBrushPage.xaml new file mode 100644 index 00000000000..7f0e3a59ca2 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GeometryMaskSurfaceBrush/GeometryMaskSurfaceBrushPage.xaml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GeometryMaskSurfaceBrush/GeometryMaskSurfaceBrushPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GeometryMaskSurfaceBrush/GeometryMaskSurfaceBrushPage.xaml.cs new file mode 100644 index 00000000000..bac5cdfe5a1 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GeometryMaskSurfaceBrush/GeometryMaskSurfaceBrushPage.xaml.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Windows.UI.Xaml.Controls; + +// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238 +namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages +{ + /// + /// An empty page that can be used on its own or navigated to within a Frame. + /// + public sealed partial class GeometryMaskSurfaceBrushPage : Page + { + public GeometryMaskSurfaceBrushPage() + { + this.InitializeComponent(); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GeometryMaskSurfaceBrush/GeometryMaskSurfaceBrushXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GeometryMaskSurfaceBrush/GeometryMaskSurfaceBrushXaml.bind new file mode 100644 index 00000000000..f9261043039 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GeometryMaskSurfaceBrush/GeometryMaskSurfaceBrushXaml.bind @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GeometrySurfaceBrush/GeometrySurfaceBrush.png b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GeometrySurfaceBrush/GeometrySurfaceBrush.png new file mode 100644 index 00000000000..7ba968db969 Binary files /dev/null and b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GeometrySurfaceBrush/GeometrySurfaceBrush.png differ diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GeometrySurfaceBrush/GeometrySurfaceBrushPage.xaml b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GeometrySurfaceBrush/GeometrySurfaceBrushPage.xaml new file mode 100644 index 00000000000..9b1f4326ff7 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GeometrySurfaceBrush/GeometrySurfaceBrushPage.xaml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GeometrySurfaceBrush/GeometrySurfaceBrushPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GeometrySurfaceBrush/GeometrySurfaceBrushPage.xaml.cs new file mode 100644 index 00000000000..e2f9531467e --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GeometrySurfaceBrush/GeometrySurfaceBrushPage.xaml.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Windows.UI.Xaml.Controls; + +// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238 +namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages +{ + /// + /// An empty page that can be used on its own or navigated to within a Frame. + /// + public sealed partial class GeometrySurfaceBrushPage : Page + { + public GeometrySurfaceBrushPage() + { + this.InitializeComponent(); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GeometrySurfaceBrush/GeometrySurfaceBrushXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GeometrySurfaceBrush/GeometrySurfaceBrushXaml.bind new file mode 100644 index 00000000000..b2e26d667b3 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/GeometrySurfaceBrush/GeometrySurfaceBrushXaml.bind @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageMaskSurfaceBrush/ImageMaskSurfaceBrush.png b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageMaskSurfaceBrush/ImageMaskSurfaceBrush.png new file mode 100644 index 00000000000..9123372ad51 Binary files /dev/null and b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageMaskSurfaceBrush/ImageMaskSurfaceBrush.png differ diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageMaskSurfaceBrush/ImageMaskSurfaceBrushPage.xaml b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageMaskSurfaceBrush/ImageMaskSurfaceBrushPage.xaml new file mode 100644 index 00000000000..75d5b09ef4b --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageMaskSurfaceBrush/ImageMaskSurfaceBrushPage.xaml @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageMaskSurfaceBrush/ImageMaskSurfaceBrushPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageMaskSurfaceBrush/ImageMaskSurfaceBrushPage.xaml.cs new file mode 100644 index 00000000000..64bd9606512 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageMaskSurfaceBrush/ImageMaskSurfaceBrushPage.xaml.cs @@ -0,0 +1,77 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Controls.Primitives; + +// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238 +namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages +{ + /// + /// An empty page that can be used on its own or navigated to within a Frame. + /// + public sealed partial class ImageMaskSurfaceBrushPage : Page + { + private Dictionary _maskImages; + private Dictionary _targetImages; + + public ImageMaskSurfaceBrushPage() + { + this.InitializeComponent(); + + _maskImages = new Dictionary + { + ["Image 1"] = "ms-appx:///SamplePages/ImageMaskSurfaceBrush/MaskImage1.png", + ["Image 2"] = "ms-appx:///SamplePages/ImageMaskSurfaceBrush/MaskImage2.png", + ["Image 3"] = "ms-appx:///SamplePages/ImageMaskSurfaceBrush/MaskImage3.png", + }; + + MaskImages.ItemsSource = _maskImages.Keys; + + // Select the first Image as mask + MaskImages.SelectedIndex = 0; + + _targetImages = new Dictionary + { + ["Image 1"] = "ms-appx:///Assets/Photos/PaintedHillsPathway.jpg", + ["Image 2"] = "ms-appx:///Assets/Photos/ShootingOnAutoOnTheDrone.jpg", + ["Image 3"] = "ms-appx:///Assets/Photos/NorthernCascadesReflection.jpg", + ["Image 4"] = "ms-appx:///Assets/Photos/Van.jpg", + }; + + TargetImages.ItemsSource = _targetImages.Keys; + + // Select the first Image as target image. + TargetImages.SelectedIndex = 0; + } + + private void OnMaskImageChanged(object sender, SelectionChangedEventArgs e) + { + var image = MaskImages.SelectedValue as string; + if (string.IsNullOrWhiteSpace(image)) + { + return; + } + + MaskImageBrush.Source = _maskImages[image]; + } + + private void OnTargetImageChanged(object sender, SelectionChangedEventArgs e) + { + var image = TargetImages.SelectedValue as string; + if (string.IsNullOrWhiteSpace(image)) + { + return; + } + + TargetImageBrush.Source = _targetImages[image]; + } + + private void OnBlurRadiusChanged(object sender, RangeBaseValueChangedEventArgs e) + { + MaskImageOptions.BlurRadius = e.NewValue; + } + } +} diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageMaskSurfaceBrush/ImageMaskSurfaceBrushXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageMaskSurfaceBrush/ImageMaskSurfaceBrushXaml.bind new file mode 100644 index 00000000000..60560bf1e83 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageMaskSurfaceBrush/ImageMaskSurfaceBrushXaml.bind @@ -0,0 +1,22 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageMaskSurfaceBrush/MaskImage1.png b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageMaskSurfaceBrush/MaskImage1.png new file mode 100644 index 00000000000..a92ab65d842 Binary files /dev/null and b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageMaskSurfaceBrush/MaskImage1.png differ diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageMaskSurfaceBrush/MaskImage2.png b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageMaskSurfaceBrush/MaskImage2.png new file mode 100644 index 00000000000..3b619a37a0e Binary files /dev/null and b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageMaskSurfaceBrush/MaskImage2.png differ diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageMaskSurfaceBrush/MaskImage3.png b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageMaskSurfaceBrush/MaskImage3.png new file mode 100644 index 00000000000..ad4cd9ebef7 Binary files /dev/null and b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageMaskSurfaceBrush/MaskImage3.png differ diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageSurfaceBrush/ImageSurfaceBrush.png b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageSurfaceBrush/ImageSurfaceBrush.png new file mode 100644 index 00000000000..31334d7b2b7 Binary files /dev/null and b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageSurfaceBrush/ImageSurfaceBrush.png differ diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageSurfaceBrush/ImageSurfaceBrushPage.xaml b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageSurfaceBrush/ImageSurfaceBrushPage.xaml new file mode 100644 index 00000000000..b31d88889cd --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageSurfaceBrush/ImageSurfaceBrushPage.xaml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageSurfaceBrush/ImageSurfaceBrushPage.xaml.cs b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageSurfaceBrush/ImageSurfaceBrushPage.xaml.cs new file mode 100644 index 00000000000..1761b4b184a --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageSurfaceBrush/ImageSurfaceBrushPage.xaml.cs @@ -0,0 +1,90 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media; + +// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238 +namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages +{ + /// + /// An empty page that can be used on its own or navigated to within a Frame. + /// + public sealed partial class ImageSurfaceBrushPage : Page + { + public ImageSurfaceBrushPage() + { + this.InitializeComponent(); + + StretchOptions.ItemsSource = new List + { + "None", + "Fill", + "Uniform", + "UniformToFill" + }; + + HAOptions.ItemsSource = new List + { + "Left", + "Center", + "Right" + }; + + VAOptions.ItemsSource = new List + { + "Top", + "Center", + "Bottom" + }; + + StretchOptions.SelectionChanged += OnStretchOptionsChanged; + StretchOptions.SelectedIndex = 2; + + HAOptions.SelectionChanged += OnHorizontalAlignmentChanged; + HAOptions.SelectedIndex = 1; + + VAOptions.SelectionChanged += OnVerticalAlignmentChanged; + VAOptions.SelectedIndex = 1; + } + + private void OnStretchOptionsChanged(object sender, SelectionChangedEventArgs e) + { + var stretch = StretchOptions.SelectedValue as string; + if (!string.IsNullOrWhiteSpace(stretch)) + { + if (Enum.TryParse(typeof(Stretch), stretch, out object stretchEnum)) + { + ImageOptions.Stretch = (Stretch)stretchEnum; + } + } + } + + private void OnHorizontalAlignmentChanged(object sender, SelectionChangedEventArgs e) + { + var align = HAOptions.SelectedValue as string; + if (!string.IsNullOrWhiteSpace(align)) + { + if (Enum.TryParse(typeof(AlignmentX), align, out object alignEnum)) + { + ImageOptions.HorizontalAlignment = (AlignmentX)alignEnum; + } + } + } + + private void OnVerticalAlignmentChanged(object sender, SelectionChangedEventArgs e) + { + var align = VAOptions.SelectedValue as string; + if (!string.IsNullOrWhiteSpace(align)) + { + if (Enum.TryParse(typeof(AlignmentY), align, out object alignEnum)) + { + ImageOptions.VerticalAlignment = (AlignmentY)alignEnum; + } + } + } + } +} diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageSurfaceBrush/ImageSurfaceBrushXaml.bind b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageSurfaceBrush/ImageSurfaceBrushXaml.bind new file mode 100644 index 00000000000..60560bf1e83 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/ImageSurfaceBrush/ImageSurfaceBrushXaml.bind @@ -0,0 +1,22 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json index c1f45a20762..9c4258d58ab 100644 --- a/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json +++ b/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/samples.json @@ -1156,6 +1156,46 @@ "Icon": "/SamplePages/TilesBrush/TilesBrush.png", "ApiCheck": "Windows.UI.Xaml.Media.XamlCompositionBrushBase", "DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/brushes/TilesBrush.md" + }, + { + "Name": "GeometrySurfaceBrush", + "Type": "GeometrySurfaceBrushPage", + "About": "Brush which fills the contents with a CanvasCoreGeometry.", + "CodeUrl": "https://github.com/ratishphilip/WindowsCommunityToolkit/tree/feature/rendersurfaces/Microsoft.Toolkit.Uwp.UI.Media/Brushes/GeometrySurfaceBrush.cs", + "XamlCodeFile": "GeometrySurfaceBrushXaml.bind", + "Icon": "/SamplePages/GeometrySurfaceBrush/GeometrySurfaceBrush.png", + "ApiCheck": "Windows.UI.Xaml.Media.XamlCompositionBrushBase", + "DocumentationUrl": "" + }, + { + "Name": "GeometryMaskSurfaceBrush", + "Type": "GeometryMaskSurfaceBrushPage", + "About": "Brush which fills the contents with a CanvasCoreGeometry.", + "CodeUrl": "https://github.com/ratishphilip/WindowsCommunityToolkit/tree/feature/rendersurfaces/Microsoft.Toolkit.Uwp.UI.Media/Brushes/GeometryMaskSurfaceBrush.cs", + "XamlCodeFile": "GeometryMaskSurfaceBrushXaml.bind", + "Icon": "/SamplePages/GeometryMaskSurfaceBrush/GeometryMaskSurfaceBrush.png", + "ApiCheck": "Windows.UI.Xaml.Media.XamlCompositionBrushBase", + "DocumentationUrl": "" + }, + { + "Name": "ImageSurfaceBrush", + "Type": "ImageSurfaceBrushPage", + "About": "Brush which fills the contents with a Image.", + "CodeUrl": "https://github.com/ratishphilip/WindowsCommunityToolkit/tree/feature/rendersurfaces/Microsoft.Toolkit.Uwp.UI.Media/Brushes/ImageSurfaceBrush.cs", + "XamlCodeFile": "ImageSurfaceBrushXaml.bind", + "Icon": "/SamplePages/ImageSurfaceBrush/ImageSurfaceBrush.png", + "ApiCheck": "Windows.UI.Xaml.Media.XamlCompositionBrushBase", + "DocumentationUrl": "" + }, + { + "Name": "ImageMaskSurfaceBrush", + "Type": "ImageMaskSurfaceBrushPage", + "About": "Brush which fills the contents with a Image.", + "CodeUrl": "https://github.com/ratishphilip/WindowsCommunityToolkit/tree/feature/rendersurfaces/Microsoft.Toolkit.Uwp.UI.Media/Brushes/ImageMaskSurfaceBrush.cs", + "XamlCodeFile": "ImageMaskSurfaceBrushXaml.bind", + "Icon": "/SamplePages/ImageMaskSurfaceBrush/ImageMaskSurfaceBrush.png", + "ApiCheck": "Windows.UI.Xaml.Media.XamlCompositionBrushBase", + "DocumentationUrl": "" } ] }, diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Brushes/Base/RenderCanvasBrushBase.cs b/Microsoft.Toolkit.Uwp.UI.Media/Brushes/Base/RenderCanvasBrushBase.cs new file mode 100644 index 00000000000..aada20fffbe --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Brushes/Base/RenderCanvasBrushBase.cs @@ -0,0 +1,127 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.CompilerServices; +using Microsoft.Graphics.Canvas.Brushes; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Media; + +namespace Microsoft.Toolkit.Uwp.UI.Media +{ + /// + /// Abstract base class for SolidColorCanvasBrush, LinearGradientCanvasBrush and RadialGradientCanvasBrush which are XAML equivalents of Win2d brushes. + /// + public abstract class RenderCanvasBrushBase : DependencyObject, IDisposable + { + /// + /// Event which indicates that the components of the brush have changed. + /// + public event EventHandler Updated; + + private bool _disposedValue; + + /// + /// Gets or sets the associated CanvasBrush. + /// + public ICanvasBrush CanvasBrush { get; protected set; } + + /// + /// Opacity Dependency Property + /// + public static readonly DependencyProperty OpacityProperty = DependencyProperty.Register( + "Opacity", + typeof(double), + typeof(RenderCanvasBrushBase), + new PropertyMetadata(1d, OnPropertyChanged)); + + /// + /// Gets or sets the opacity of the brush. + /// + public double Opacity + { + get => (double)GetValue(OpacityProperty); + set => SetValue(OpacityProperty, value); + } + + /// + /// Transform Dependency Property + /// + public static readonly DependencyProperty TransformProperty = DependencyProperty.Register( + "Transform", + typeof(MatrixTransform), + typeof(RenderCanvasBrushBase), + new PropertyMetadata(new MatrixTransform { Matrix = Matrix.Identity }, OnPropertyChanged)); + + /// + /// Gets or sets the transform matrix applied to the brush. + /// + public MatrixTransform Transform + { + get => (MatrixTransform)GetValue(TransformProperty); + set => SetValue(TransformProperty, value); + } + + /// + /// Refreshes the CanvasBrush when any of the properties are updated + /// + protected virtual void OnUpdated() + { + RaiseUpdatedEvent(); + } + + /// + /// Raises the Updated event. + /// + protected void RaiseUpdatedEvent() + { + Updated?.Invoke(this, null); + } + + /// + /// Call this method to redraw its Geometry (usually when event is raised). + /// + public void Refresh() + { + OnUpdated(); + } + + /// + /// Disposes the resources used by the CanvasCoreGeometry and its derivatives + /// + /// Flag to indicate if we are disposing the managed objects. + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + } + + _disposedValue = true; + } + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + /// + /// Method that is called whenever the dependency properties of the Brush changes + /// + /// The object whose property has changed + /// Event arguments + private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var brush = (RenderCanvasBrushBase)d; + + // Recreate the canvas brush on any property change. + brush.Refresh(); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Brushes/Base/RenderSurfaceBrushBase.cs b/Microsoft.Toolkit.Uwp.UI.Media/Brushes/Base/RenderSurfaceBrushBase.cs new file mode 100644 index 00000000000..d3794ded81e --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Brushes/Base/RenderSurfaceBrushBase.cs @@ -0,0 +1,206 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Windows.UI.Composition; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Media; + +namespace Microsoft.Toolkit.Uwp.UI.Media +{ + /// + /// Base class for RenderSurface brushes. + /// + public abstract class RenderSurfaceBrushBase : XamlCompositionBrushBase + { + /// + /// Event which indicates that the components of the brush have changed. + /// + public event EventHandler Updated; + + /// + /// Gets or sets the CompositionSurface associated with the brush. + /// + protected IRenderSurface RenderSurface { get; set; } + + /// + /// The initialization instance. + /// + private readonly AsyncMutex connectedMutex = new(); + + /// + /// Gets the associated CompositionBrush + /// + internal CompositionBrush Brush + { + get + { + return CompositionBrush; + } + } + + /// + /// SurfaceWidth Dependency Property + /// + public static readonly DependencyProperty SurfaceWidthProperty = DependencyProperty.Register( + "SurfaceWidth", + typeof(double), + typeof(RenderSurfaceBrushBase), + new PropertyMetadata(0d, OnPropertyChanged)); + + /// + /// Gets or sets the width of the Brush Surface. + /// + public double SurfaceWidth + { + get => (double)GetValue(SurfaceWidthProperty); + set => SetValue(SurfaceWidthProperty, value); + } + + /// + /// SurfaceHeight Dependency Property + /// + public static readonly DependencyProperty SurfaceHeightProperty = DependencyProperty.Register( + "SurfaceHeight", + typeof(double), + typeof(RenderSurfaceBrushBase), + new PropertyMetadata(0d, OnPropertyChanged)); + + /// + /// Gets or sets the height of the Brush Surface. + /// + public double SurfaceHeight + { + get => (double)GetValue(SurfaceHeightProperty); + set => SetValue(SurfaceHeightProperty, value); + } + + /// + /// Gets or sets the instance. + /// + protected ICompositionGenerator Generator { get; set; } + + /// + /// Method that is called whenever the dependency properties of the Brush changes. + /// + /// The object whose property has changed. + /// Event arguments. + private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var brush = (RenderSurfaceBrushBase)d; + + if (brush.SurfaceWidth < 0d) + { + throw new ArgumentException("SurfaceWidth must be a positive number!"); + } + + if (brush.SurfaceHeight < 0d) + { + throw new ArgumentException("SurfaceHeight must be a positive number!"); + } + + // Recreate the Render Surface Brush on any property change. + brush.Refresh(); + } + + /// + /// Gets the CompositionGenerator Instance. + /// + protected void GetGeneratorInstance() + { + if (Window.Current != null) + { + Generator = CompositionGenerator.Instance; + + Generator.DeviceReplaced += OnDeviceReplaced; + } + } + + /// + protected override async void OnConnected() + { + using (await connectedMutex.LockAsync()) + { + if (CompositionBrush == null) + { + GetGeneratorInstance(); + + OnSurfaceBrushUpdated(true); + } + } + + base.OnConnected(); + } + + /// + protected override async void OnDisconnected() + { + using (await connectedMutex.LockAsync()) + { + if (Generator != null) + { + Generator.DeviceReplaced -= OnDeviceReplaced; + Generator = null; + } + + // Dispose Brush resources + OnSurfaceBrushDisposed(); + + // Dispose of composition resources when no longer in use. + if (CompositionBrush != null) + { + CompositionBrush.Dispose(); + CompositionBrush = null; + } + } + + base.OnDisconnected(); + } + + /// + /// Handler for the DeviceReplaced event. + /// + /// Sender. + /// EventArgs. + private void OnDeviceReplaced(object sender, object e) + { + OnDisconnected(); + OnConnected(); + } + + /// + /// Invoked whenever any brush property is updated. + /// + /// Indicates whether the surface needs to be created. + protected virtual void OnSurfaceBrushUpdated(bool createSurface = false) + { + Updated?.Invoke(this, null); + } + + /// + /// Invoked whenever any brush property is updated. + /// + protected virtual void OnSurfaceBrushDisposed() + { + } + + /// + /// Redraws the brush content + /// + public void Refresh() + { + OnSurfaceBrushUpdated(); + } + + /// + /// Checks if the URI starts with http: or https:. + /// + /// URI. + /// True if it does, otherwise false. + protected static bool IsHttpUri(Uri uri) + { + return uri != null && uri.IsAbsoluteUri && (uri.Scheme == "http" || uri.Scheme == "https"); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Brushes/GeometryMaskSurfaceBrush.cs b/Microsoft.Toolkit.Uwp.UI.Media/Brushes/GeometryMaskSurfaceBrush.cs new file mode 100644 index 00000000000..80b77351e51 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Brushes/GeometryMaskSurfaceBrush.cs @@ -0,0 +1,238 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Numerics; +using Microsoft.Toolkit.Uwp.Helpers; +using Microsoft.Toolkit.Uwp.UI.Media.Geometry; +using Windows.Foundation; +using Windows.System; +using Windows.UI.Composition; +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.UI.Media +{ + /// + /// Represents a brush which defines a as a mask to be applied on a derivative. + /// + public sealed class GeometryMaskSurfaceBrush : RenderSurfaceBrushBase + { + private CompositionMaskBrush _maskBrush; + + private WeakEventListener _maskUpdateListener; + private WeakEventListener _targetUpdateListener; + + /// + /// Target Dependency Property + /// + public static readonly DependencyProperty TargetProperty = DependencyProperty.Register( + "Target", + typeof(RenderSurfaceBrushBase), + typeof(GeometryMaskSurfaceBrush), + new PropertyMetadata(null, OnTargetChanged)); + + /// + /// Gets or sets the target on which the Mask is applied. + /// + public RenderSurfaceBrushBase Target + { + get => (RenderSurfaceBrushBase)GetValue(TargetProperty); + set => SetValue(TargetProperty, value); + } + + /// + /// Handles changes to the Target property. + /// + /// . + /// DependencyProperty changed event arguments. + private static void OnTargetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var geometryMaskSurfaceBrush = (GeometryMaskSurfaceBrush)d; + geometryMaskSurfaceBrush.OnTargetChanged(); + } + + /// + /// Instance handler for the changes to the Target dependency property. + /// + private void OnTargetChanged() + { + _targetUpdateListener?.Detach(); + _targetUpdateListener = null; + + if (Target != null) + { + _targetUpdateListener = new WeakEventListener(Target) + { + OnEventAction = async (instance, source, args) => + { + await DispatcherQueue.GetForCurrentThread().EnqueueAsync(() => + { + OnSurfaceBrushUpdated(); + }); + } + }; + + Target.Updated += _targetUpdateListener.OnEvent; + + OnSurfaceBrushUpdated(); + } + } + + /// + /// Mask Dependency Property + /// + public static readonly DependencyProperty MaskProperty = DependencyProperty.Register( + "Mask", + typeof(CanvasCoreGeometry), + typeof(GeometryMaskSurfaceBrush), + new PropertyMetadata(null, OnMaskChanged)); + + /// + /// Gets or sets the Mask Geometry that is applied on the Target Geometry. + /// + public CanvasCoreGeometry Mask + { + get => (CanvasCoreGeometry)GetValue(MaskProperty); + set => SetValue(MaskProperty, value); + } + + /// + /// Handles changes to the Mask property. + /// + /// . + /// DependencyProperty changed event arguments. + private static void OnMaskChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var geometryMaskSurfaceBrush = (GeometryMaskSurfaceBrush)d; + geometryMaskSurfaceBrush.OnMaskChanged(); + } + + /// + /// Instance handler for the changes to the Mask dependency property. + /// + private void OnMaskChanged() + { + _maskUpdateListener?.Detach(); + _maskUpdateListener = null; + + if (Mask != null) + { + _maskUpdateListener = new WeakEventListener(Mask) + { + OnEventAction = async (instance, source, args) => + { + await DispatcherQueue.GetForCurrentThread().EnqueueAsync(() => + { + OnSurfaceBrushUpdated(); + }); + } + }; + + Mask.Updated += _maskUpdateListener.OnEvent; + + OnSurfaceBrushUpdated(); + } + } + + /// + /// OffsetX Dependency Property + /// + public static readonly DependencyProperty OffsetXProperty = DependencyProperty.Register( + "OffsetX", + typeof(double), + typeof(GeometryMaskSurfaceBrush), + new PropertyMetadata(0d, OnPropertyChanged)); + + /// + /// Gets or sets the offset on the x-axis from the top left corner of the Brush Surface where the Geometry is rendered. + /// + public double OffsetX + { + get => (double)GetValue(OffsetXProperty); + set => SetValue(OffsetXProperty, value); + } + + /// + /// OffsetY Dependency Property + /// + public static readonly DependencyProperty OffsetYProperty = DependencyProperty.Register( + "OffsetY", + typeof(double), + typeof(GeometryMaskSurfaceBrush), + new PropertyMetadata(0d, OnPropertyChanged)); + + /// + /// Gets or sets the offset on the y-axis from the top left corner of the Brush Surface where the Geometry is rendered. + /// + public double OffsetY + { + get => (double)GetValue(OffsetYProperty); + set => SetValue(OffsetYProperty, value); + } + + /// + /// BlurRadius Dependency Property + /// + public static readonly DependencyProperty BlurRadiusProperty = DependencyProperty.Register( + "BlurRadius", + typeof(double), + typeof(GeometryMaskSurfaceBrush), + new PropertyMetadata(0d, OnPropertyChanged)); + + /// + /// Gets or sets the radius of Gaussian Blur to be applied on the mask. + /// + public double BlurRadius + { + get => (double)GetValue(BlurRadiusProperty); + set => SetValue(BlurRadiusProperty, value); + } + + /// + /// Method that is called whenever the dependency properties of the Brush changes. + /// + /// The object whose property has changed. + /// Event arguments. + private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var brush = (GeometryMaskSurfaceBrush)d; + + // Recreate the canvas brush on any property change. + brush.OnSurfaceBrushUpdated(); + } + + /// + protected override void OnSurfaceBrushUpdated(bool createSurface = false) + { + if (Generator == null) + { + GetGeneratorInstance(); + } + + if (Target == null || Target.Brush == null || Mask == null || Generator == null) + { + return; + } + + var offset = new Vector2((float)OffsetX, (float)OffsetY); + + _maskBrush = Window.Current.Compositor.CreateMaskBrush(); + _maskBrush.Source = Target.Brush; + + if (createSurface || (RenderSurface == null)) + { + CompositionBrush?.Dispose(); + RenderSurface = Generator.CreateGaussianMaskSurface(new Size(SurfaceWidth, SurfaceHeight), Mask.Geometry, offset, (float)BlurRadius); + _maskBrush.Mask = Window.Current.Compositor.CreateSurfaceBrush(RenderSurface.Surface); + CompositionBrush = _maskBrush; + } + else + { + ((IGaussianMaskSurface)RenderSurface).Redraw(new Size(SurfaceWidth, SurfaceHeight), Mask.Geometry, offset, (float)BlurRadius); + } + + base.OnSurfaceBrushUpdated(createSurface); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Brushes/GeometrySurfaceBrush.cs b/Microsoft.Toolkit.Uwp.UI.Media/Brushes/GeometrySurfaceBrush.cs new file mode 100644 index 00000000000..be803da5dc8 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Brushes/GeometrySurfaceBrush.cs @@ -0,0 +1,384 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Microsoft.Graphics.Canvas.Brushes; +using Microsoft.Graphics.Canvas.Geometry; +using Microsoft.Toolkit.Uwp.Helpers; +using Microsoft.Toolkit.Uwp.UI.Media.Geometry; +using Windows.Foundation; +using Windows.System; +using Windows.UI; +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.UI.Media +{ + /// + /// Brush which renders the provided . + /// + public sealed class GeometrySurfaceBrush : RenderSurfaceBrushBase + { + private WeakEventListener _geometryUpdateListener; + private WeakEventListener _strokeUpdateListener; + private WeakEventListener _fillBrushUpdateListener; + private WeakEventListener _bgBrushUpdateListener; + private WeakEventListener _strokeStyleUpdateListener; + + /// + /// Geometry Dependency Property + /// + public static readonly DependencyProperty GeometryProperty = DependencyProperty.Register( + "Geometry", + typeof(CanvasCoreGeometry), + typeof(GeometrySurfaceBrush), + new PropertyMetadata(null, OnGeometryChanged)); + + /// + /// Gets or sets the that is used to paint the surface of the brush. + /// + public CanvasCoreGeometry Geometry + { + get => (CanvasCoreGeometry)GetValue(GeometryProperty); + set => SetValue(GeometryProperty, value); + } + + /// + /// Handles changes to the Geometry property. + /// + /// . + /// DependencyProperty changed event arguments. + private static void OnGeometryChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var maskSurfaceBrush = (GeometrySurfaceBrush)d; + maskSurfaceBrush.OnGeometryChanged(); + } + + /// + /// Instance handler for the changes to the Geometry dependency property. + /// + private void OnGeometryChanged() + { + _geometryUpdateListener?.Detach(); + _geometryUpdateListener = null; + + if (Geometry != null) + { + _geometryUpdateListener = new WeakEventListener(Geometry) + { + OnEventAction = async (instance, source, args) => + { + await DispatcherQueue.GetForCurrentThread().EnqueueAsync(() => + { + OnSurfaceBrushUpdated(); + }); + } + }; + + Geometry.Updated += _geometryUpdateListener.OnEvent; + + OnSurfaceBrushUpdated(); + } + } + + /// + /// Stroke Dependency Property + /// + public static readonly DependencyProperty StrokeProperty = DependencyProperty.Register( + "Stroke", + typeof(RenderCanvasBrushBase), + typeof(GeometrySurfaceBrush), + new PropertyMetadata(null, OnStrokeChanged)); + + /// + /// Gets or sets the brush for rendering the outline stroke of the geometry. + /// + public RenderCanvasBrushBase Stroke + { + get => (RenderCanvasBrushBase)GetValue(StrokeProperty); + set => SetValue(StrokeProperty, value); + } + + /// + /// Handles changes to the Stroke property. + /// + /// . + /// DependencyProperty changed event arguments. + private static void OnStrokeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var geometrySurfaceBrush = (GeometrySurfaceBrush)d; + geometrySurfaceBrush.OnStrokeChanged(); + } + + /// + /// Instance handler for the changes to the Stroke dependency property. + /// + private void OnStrokeChanged() + { + _strokeUpdateListener?.Detach(); + _strokeUpdateListener = null; + + if (Stroke != null) + { + _strokeUpdateListener = new WeakEventListener(Stroke) + { + OnEventAction = async (instance, source, args) => + { + await DispatcherQueue.GetForCurrentThread().EnqueueAsync(() => + { + OnSurfaceBrushUpdated(); + }); + } + }; + + Stroke.Updated += _strokeUpdateListener.OnEvent; + + OnSurfaceBrushUpdated(); + } + } + + /// + /// StrokeThickness Dependency Property + /// + public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register( + "StrokeThickness", + typeof(double), + typeof(GeometrySurfaceBrush), + new PropertyMetadata(0d, OnStrokeThicknessChanged)); + + /// + /// Gets or sets the thickness of the outline of the geometry. + /// + public double StrokeThickness + { + get => (double)GetValue(StrokeThicknessProperty); + set => SetValue(StrokeThicknessProperty, value); + } + + /// + /// Handles changes to the StrokeThickness property. + /// + /// . + /// DependencyProperty changed event arguments. + private static void OnStrokeThicknessChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var geometrySurfaceBrush = (GeometrySurfaceBrush)d; + geometrySurfaceBrush.OnStrokeThicknessChanged(); + } + + /// + /// Instance handler for the changes to the StrokeThickness dependency property. + /// + private void OnStrokeThicknessChanged() + { + if (StrokeThickness < 0d) + { + throw new ArgumentException("StrokeThickness must be a non-negative number."); + } + + OnSurfaceBrushUpdated(); + } + + /// + /// RenderStrokeStyle Dependency Property + /// + public static readonly DependencyProperty RenderStrokeStyleProperty = DependencyProperty.Register( + "RenderStrokeStyle", + typeof(StrokeStyle), + typeof(GeometrySurfaceBrush), + new PropertyMetadata(null, OnRenderStrokeStyleChanged)); + + /// + /// Gets or sets the style of the outline for the Geometry. + /// + public StrokeStyle RenderStrokeStyle + { + get => (StrokeStyle)GetValue(RenderStrokeStyleProperty); + set => SetValue(RenderStrokeStyleProperty, value); + } + + /// + /// Handles changes to the RenderStrokeStyle property. + /// + /// . + /// DependencyProperty changed event arguments. + private static void OnRenderStrokeStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var geometrySurfaceBrush = (GeometrySurfaceBrush)d; + geometrySurfaceBrush.OnRenderStrokeStyleChanged(); + } + + /// + /// Instance handler for the changes to the RenderStrokeStyle dependency property. + /// + private void OnRenderStrokeStyleChanged() + { + _strokeStyleUpdateListener?.Detach(); + _strokeStyleUpdateListener = null; + + if (RenderStrokeStyle != null) + { + _strokeStyleUpdateListener = new WeakEventListener(RenderStrokeStyle) + { + OnEventAction = async (instance, source, args) => + { + await DispatcherQueue.GetForCurrentThread().EnqueueAsync(() => + { + OnSurfaceBrushUpdated(); + }); + } + }; + + RenderStrokeStyle.Updated += _strokeStyleUpdateListener.OnEvent; + + OnSurfaceBrushUpdated(); + } + } + + /// + /// FillBrush Dependency Property + /// + public static readonly DependencyProperty FillBrushProperty = DependencyProperty.Register( + "FillBrush", + typeof(RenderCanvasBrushBase), + typeof(GeometrySurfaceBrush), + new PropertyMetadata(null, OnFillBrushChanged)); + + /// + /// Gets or sets the brush which will be used to fill the geometry. + /// + public RenderCanvasBrushBase FillBrush + { + get => (RenderCanvasBrushBase)GetValue(FillBrushProperty); + set => SetValue(FillBrushProperty, value); + } + + /// + /// Handles changes to the FillBrush property. + /// + /// . + /// DependencyProperty changed event arguments. + private static void OnFillBrushChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var geometrySurfaceBrush = (GeometrySurfaceBrush)d; + geometrySurfaceBrush.OnFillBrushChanged(); + } + + /// + /// Instance handler for the changes to the FillBrush dependency property. + /// + private void OnFillBrushChanged() + { + _fillBrushUpdateListener?.Detach(); + _fillBrushUpdateListener = null; + + if (FillBrush != null) + { + _fillBrushUpdateListener = new WeakEventListener(FillBrush) + { + OnEventAction = async (instance, source, args) => + { + await DispatcherQueue.GetForCurrentThread().EnqueueAsync(() => + { + OnSurfaceBrushUpdated(); + }); + } + }; + + FillBrush.Updated += _fillBrushUpdateListener.OnEvent; + + OnSurfaceBrushUpdated(); + } + } + + /// + /// BackgroundBrush Dependency Property + /// + public static readonly DependencyProperty BackgroundBrushProperty = DependencyProperty.Register( + "BackgroundBrush", + typeof(RenderCanvasBrushBase), + typeof(GeometrySurfaceBrush), + new PropertyMetadata(null, OnBackgroundBrushChanged)); + + /// + /// Gets or sets the brush with which the background of the Geometry surface will be rendered. + /// + public RenderCanvasBrushBase BackgroundBrush + { + get => (RenderCanvasBrushBase)GetValue(BackgroundBrushProperty); + set => SetValue(BackgroundBrushProperty, value); + } + + /// + /// Handles changes to the BackgroundBrush property. + /// + /// . + /// DependencyProperty changed event arguments. + private static void OnBackgroundBrushChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var geometrySurfaceBrush = (GeometrySurfaceBrush)d; + geometrySurfaceBrush.OnBackgroundBrushChanged(); + } + + /// + /// Instance handler for the changes to the BackgroundBrush dependency property. + /// + private void OnBackgroundBrushChanged() + { + _bgBrushUpdateListener?.Detach(); + _bgBrushUpdateListener = null; + + if (BackgroundBrush != null) + { + _bgBrushUpdateListener = new WeakEventListener(BackgroundBrush) + { + OnEventAction = async (instance, source, args) => + { + await DispatcherQueue.GetForCurrentThread().EnqueueAsync(() => + { + OnSurfaceBrushUpdated(); + }); + } + }; + + BackgroundBrush.Updated += _bgBrushUpdateListener.OnEvent; + + OnSurfaceBrushUpdated(); + } + } + + /// + protected override void OnSurfaceBrushUpdated(bool createSurface = false) + { + if (Generator == null) + { + GetGeneratorInstance(); + } + + if (Geometry == null || Generator == null) + { + return; + } + + var transparentBrush = new CanvasSolidColorBrush(Generator.Device, Colors.Transparent); + var strokeBrush = (Stroke == null) ? transparentBrush : Stroke.CanvasBrush; + var fillBrush = (FillBrush == null) ? transparentBrush : FillBrush.CanvasBrush; + var bgBrush = (BackgroundBrush == null) ? transparentBrush : BackgroundBrush.CanvasBrush; + var strokeStyle = (RenderStrokeStyle == null) ? new CanvasStrokeStyle() : RenderStrokeStyle.GetCanvasStrokeStyle(); + var canvasStroke = new CanvasStroke(strokeBrush, (float)StrokeThickness, strokeStyle); + + if (createSurface || (RenderSurface == null)) + { + CompositionBrush?.Dispose(); + RenderSurface = Generator.CreateGeometrySurface(new Size(SurfaceWidth, SurfaceHeight), Geometry.Geometry, canvasStroke, fillBrush, bgBrush); + CompositionBrush = Window.Current.Compositor.CreateSurfaceBrush(RenderSurface.Surface); + } + else + { + ((IGeometrySurface)RenderSurface).Redraw(new Size(SurfaceWidth, SurfaceHeight), Geometry.Geometry, canvasStroke, fillBrush, bgBrush); + } + + base.OnSurfaceBrushUpdated(createSurface); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Brushes/ImageMaskSurfaceBrush.cs b/Microsoft.Toolkit.Uwp.UI.Media/Brushes/ImageMaskSurfaceBrush.cs new file mode 100644 index 00000000000..61f1237aed2 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Brushes/ImageMaskSurfaceBrush.cs @@ -0,0 +1,266 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Microsoft.Toolkit.Uwp.Helpers; +using Windows.Foundation; +using Windows.System; +using Windows.UI.Composition; +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.UI.Media +{ + /// + /// Represents a brush which uses an Image to create a mask to be applied on a derivative. + /// + public sealed class ImageMaskSurfaceBrush : RenderSurfaceBrushBase + { + private WeakEventListener _targetUpdateListener; + private WeakEventListener _imageSurfaceOptionsUpdateListener; + + private CompositionMaskBrush _maskBrush; + private Uri _uri; + + /// + /// Target Dependency Property + /// + public static readonly DependencyProperty TargetProperty = DependencyProperty.Register( + "Target", + typeof(RenderSurfaceBrushBase), + typeof(ImageMaskSurfaceBrush), + new PropertyMetadata(null, OnTargetChanged)); + + /// + /// Gets or sets the RenderSurfaceBrush upon which the mask is applied. + /// + public RenderSurfaceBrushBase Target + { + get => (RenderSurfaceBrushBase)GetValue(TargetProperty); + set => SetValue(TargetProperty, value); + } + + /// + /// Handles changes to the Target property. + /// + /// + /// DependencyProperty changed event arguments + private static void OnTargetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var imageMaskSurfaceBrush = (ImageMaskSurfaceBrush)d; + imageMaskSurfaceBrush.OnTargetChanged(); + } + + /// + /// Instance handler for the changes to the Target dependency property. + /// + private void OnTargetChanged() + { + _targetUpdateListener?.Detach(); + _targetUpdateListener = null; + + if (Target != null) + { + _targetUpdateListener = new WeakEventListener(Target) + { + OnEventAction = async (instance, source, args) => + { + await DispatcherQueue.GetForCurrentThread().EnqueueAsync(() => + { + OnSurfaceBrushUpdated(true); + }); + } + }; + + Target.Updated += _targetUpdateListener.OnEvent; + + OnSurfaceBrushUpdated(true); + } + } + + /// + /// Mask Dependency Property + /// + public static readonly DependencyProperty MaskProperty = DependencyProperty.Register( + "Mask", + typeof(object), + typeof(ImageMaskSurfaceBrush), + new PropertyMetadata(null, OnMaskChanged)); + + /// + /// Gets or sets the URI of the image that is used to create the mask. + /// + public object Mask + { + get => (object)GetValue(MaskProperty); + set => SetValue(MaskProperty, value); + } + + /// + /// Handles changes to the Mask property. + /// + /// + /// DependencyProperty changed event arguments + private static void OnMaskChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var imageMaskSurfaceBrush = (ImageMaskSurfaceBrush)d; + imageMaskSurfaceBrush.OnMaskChanged(); + } + + /// + /// Instance handler for the changes to the Mask dependency property. + /// + private void OnMaskChanged() + { + if (Mask == null) + { + return; + } + + var uri = Mask as Uri; + if (uri == null) + { + var url = Mask as string ?? Mask.ToString(); + if (!Uri.TryCreate(url, UriKind.RelativeOrAbsolute, out uri)) + { + _uri = null; + return; + } + } + + if (!IsHttpUri(uri) && !uri.IsAbsoluteUri) + { + _uri = new Uri("ms-appx:///" + uri.OriginalString.TrimStart('/')); + } + + _uri = uri; + + OnSurfaceBrushUpdated(true); + } + + /// + /// ImageOptions Dependency Property + /// + public static readonly DependencyProperty ImageOptionsProperty = DependencyProperty.Register( + "ImageOptions", + typeof(ImageSurfaceOptions), + typeof(ImageMaskSurfaceBrush), + new PropertyMetadata(null, OnImageOptionsChanged)); + + /// + /// Gets or sets the additional options that can be used to configure the image used to create the brush. + /// + public ImageSurfaceOptions ImageOptions + { + get => (ImageSurfaceOptions)GetValue(ImageOptionsProperty); + set => SetValue(ImageOptionsProperty, value); + } + + /// + /// Handles changes to the ImageOptions property. + /// + /// + /// DependencyProperty changed event arguments + private static void OnImageOptionsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var imageMaskSurfaceBrush = (ImageMaskSurfaceBrush)d; + imageMaskSurfaceBrush.OnImageOptionsChanged(); + } + + /// + /// Instance handler for the changes to the ImageOptions dependency property. + /// + private void OnImageOptionsChanged() + { + _imageSurfaceOptionsUpdateListener?.Detach(); + _imageSurfaceOptionsUpdateListener = null; + + if (ImageOptions != null) + { + _imageSurfaceOptionsUpdateListener = new WeakEventListener(ImageOptions) + { + OnEventAction = async (instance, source, args) => + { + await DispatcherQueue.GetForCurrentThread().EnqueueAsync(() => + { + OnSurfaceBrushUpdated(); + }); + } + }; + + ImageOptions.Updated += _imageSurfaceOptionsUpdateListener.OnEvent; + + OnSurfaceBrushUpdated(); + } + } + + /// + /// Padding Dependency Property + /// + public static readonly DependencyProperty PaddingProperty = DependencyProperty.Register( + "Padding", + typeof(Thickness), + typeof(ImageMaskSurfaceBrush), + new PropertyMetadata(new Thickness(0d), OnPaddingChanged)); + + /// + /// Gets or sets the padding between the IImageMaskSurface outer bounds and the bounds of the area where the mask, created from the loaded image's alpha values, should be rendered. + /// + public Thickness Padding + { + get => (Thickness)GetValue(PaddingProperty); + set => SetValue(PaddingProperty, value); + } + + /// + /// Handles changes to the Padding property. + /// + /// + /// DependencyProperty changed event arguments + private static void OnPaddingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var imageMaskSurfaceBrush = (ImageMaskSurfaceBrush)d; + imageMaskSurfaceBrush.OnPaddingChanged(); + } + + /// + /// Instance handler for the changes to the Padding dependency property. + /// + private void OnPaddingChanged() + { + OnSurfaceBrushUpdated(); + } + + /// + protected async override void OnSurfaceBrushUpdated(bool createSurface = false) + { + if (Generator == null) + { + GetGeneratorInstance(); + } + + if (Target == null || Target.Brush == null || _uri == null || Generator == null) + { + return; + } + + if (createSurface || (RenderSurface == null)) + { + CompositionBrush?.Dispose(); + + _maskBrush = Window.Current.Compositor.CreateMaskBrush(); + _maskBrush.Source = Target.Brush; + + RenderSurface = await Generator.CreateImageMaskSurfaceAsync(_uri, new Size(SurfaceWidth, SurfaceHeight), Padding, ImageOptions ?? ImageSurfaceOptions.DefaultImageMaskOptions); + _maskBrush.Mask = Window.Current.Compositor.CreateSurfaceBrush(RenderSurface.Surface); + CompositionBrush = _maskBrush; + } + else + { + ((IImageMaskSurface)RenderSurface)?.Redraw(new Size(SurfaceWidth, SurfaceHeight), Padding, ImageOptions ?? ImageSurfaceOptions.DefaultImageMaskOptions); + } + + base.OnSurfaceBrushUpdated(createSurface); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Brushes/ImageSurfaceBrush.cs b/Microsoft.Toolkit.Uwp.UI.Media/Brushes/ImageSurfaceBrush.cs new file mode 100644 index 00000000000..6f6a4c46841 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Brushes/ImageSurfaceBrush.cs @@ -0,0 +1,204 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Microsoft.Toolkit.Uwp.Helpers; +using Windows.Foundation; +using Windows.System; +using Windows.UI; +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.UI.Media +{ + /// + /// Creates a Render Surface brush using an image + /// + public class ImageSurfaceBrush : RenderSurfaceBrushBase + { + private WeakEventListener _imageSurfaceOptionsUpdateListener; + + private Uri _uri; + + /// + /// Background Dependency Property + /// + public static readonly DependencyProperty BackgroundProperty = DependencyProperty.Register( + "Background", + typeof(Color), + typeof(ImageSurfaceBrush), + new PropertyMetadata(Colors.Transparent, OnBackgroundChanged)); + + /// + /// Gets or sets the color that is rendered in the transparent areas of the Image. The default value is Colors.Transparent. + /// + public Color Background + { + get => (Color)GetValue(BackgroundProperty); + set => SetValue(BackgroundProperty, value); + } + + /// + /// Handles changes to the Background property. + /// + /// + /// DependencyProperty changed event arguments + private static void OnBackgroundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var imageSurfaceBrush = (ImageSurfaceBrush)d; + imageSurfaceBrush.OnBackgroundChanged(); + } + + /// + /// Instance handler for the changes to the Background dependency property. + /// + private void OnBackgroundChanged() + { + OnSurfaceBrushUpdated(); + } + + /// + /// Source Dependency Property + /// + public static readonly DependencyProperty SourceProperty = DependencyProperty.Register( + "Source", + typeof(object), + typeof(ImageSurfaceBrush), + new PropertyMetadata(null, OnSourceChanged)); + + /// + /// Gets or sets the object representing the image source. + /// + public object Source + { + get => (object)GetValue(SourceProperty); + set => SetValue(SourceProperty, value); + } + + /// + /// Handles changes to the Source property. + /// + /// + /// DependencyProperty changed event arguments + private static void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var target = (ImageSurfaceBrush)d; + target.OnSourceChanged(); + } + + /// + /// Instance handler for the changes to the Source dependency property. + /// + private void OnSourceChanged() + { + if (Source == null) + { + return; + } + + var uri = Source as Uri; + + if (uri == null) + { + var url = Source as string ?? Source.ToString(); + if (!Uri.TryCreate(url, UriKind.RelativeOrAbsolute, out uri)) + { + _uri = null; + return; + } + } + + if (!IsHttpUri(uri) && !uri.IsAbsoluteUri) + { + _uri = new Uri("ms-appx:///" + uri.OriginalString.TrimStart('/')); + } + + _uri = uri; + + OnSurfaceBrushUpdated(true); + } + + /// + /// ImageOptions Dependency Property + /// + public static readonly DependencyProperty ImageOptionsProperty = DependencyProperty.Register( + "ImageOptions", + typeof(ImageSurfaceOptions), + typeof(ImageSurfaceBrush), + new PropertyMetadata(null, OnImageOptionsChanged)); + + /// + /// Gets or sets the additional options that can be used to configure the image used to create the brush. + /// + public ImageSurfaceOptions ImageOptions + { + get => (ImageSurfaceOptions)GetValue(ImageOptionsProperty); + set => SetValue(ImageOptionsProperty, value); + } + + /// + /// Handles changes to the ImageOptions property. + /// + /// + /// DependencyProperty changed event arguments + private static void OnImageOptionsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var imageSurfaceBrush = (ImageSurfaceBrush)d; + imageSurfaceBrush.OnImageOptionsChanged(); + } + + /// + /// Instance handler for the changes to the ImageOptions dependency property. + /// + private void OnImageOptionsChanged() + { + _imageSurfaceOptionsUpdateListener?.Detach(); + _imageSurfaceOptionsUpdateListener = null; + + if (ImageOptions != null) + { + _imageSurfaceOptionsUpdateListener = new WeakEventListener(ImageOptions) + { + OnEventAction = async (instance, source, args) => + { + await DispatcherQueue.GetForCurrentThread().EnqueueAsync(() => + { + OnSurfaceBrushUpdated(); + }); + } + }; + + ImageOptions.Updated += _imageSurfaceOptionsUpdateListener.OnEvent; + + OnSurfaceBrushUpdated(); + } + } + + /// + protected async override void OnSurfaceBrushUpdated(bool createSurface = false) + { + if (Generator == null) + { + GetGeneratorInstance(); + } + + if (_uri == null || Generator == null) + { + return; + } + + if (createSurface || (RenderSurface == null)) + { + CompositionBrush?.Dispose(); + RenderSurface = await Generator?.CreateImageSurfaceAsync(_uri, new Size(SurfaceWidth, SurfaceHeight), ImageOptions ?? ImageSurfaceOptions.Default); + CompositionBrush = Window.Current.Compositor.CreateSurfaceBrush(RenderSurface.Surface); + } + else + { + ((IImageSurface)RenderSurface)?.Redraw(new Size(SurfaceWidth, SurfaceHeight), ImageOptions ?? ImageSurfaceOptions.Default); + } + + base.OnSurfaceBrushUpdated(createSurface); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Brushes/LinearGradientCanvasBrush.cs b/Microsoft.Toolkit.Uwp.UI.Media/Brushes/LinearGradientCanvasBrush.cs new file mode 100644 index 00000000000..8d0e2261aa7 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Brushes/LinearGradientCanvasBrush.cs @@ -0,0 +1,216 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Numerics; +using Microsoft.Graphics.Canvas; +using Microsoft.Graphics.Canvas.Brushes; +using Microsoft.Toolkit.Uwp.UI.Media.Geometry; +using Windows.Foundation; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Markup; +using Windows.UI.Xaml.Media; + +namespace Microsoft.Toolkit.Uwp.UI.Media.Brushes +{ + /// + /// XAML equivalent of Win2d's CanvasLinearGradientBrush class which paints in linear gradient. + /// + [ContentProperty(Name = nameof(Stops))] + public class LinearGradientCanvasBrush : RenderCanvasBrushBase + { + /// + /// AlphaMode Dependency Property + /// + public static readonly DependencyProperty AlphaModeProperty = DependencyProperty.Register( + "AlphaMode", + typeof(CanvasAlphaMode), + typeof(LinearGradientCanvasBrush), + new PropertyMetadata(CanvasAlphaMode.Premultiplied, OnPropertyChanged)); + + /// + /// Gets or sets the way in which the Alpha channel affects color channels. + /// + public CanvasAlphaMode AlphaMode + { + get => (CanvasAlphaMode)GetValue(AlphaModeProperty); + set => SetValue(AlphaModeProperty, value); + } + + /// + /// BufferPrecision Dependency Property + /// + public static readonly DependencyProperty BufferPrecisionProperty = DependencyProperty.Register( + "BufferPrecision", + typeof(CanvasBufferPrecision), + typeof(LinearGradientCanvasBrush), + new PropertyMetadata(CanvasBufferPrecision.Precision8UIntNormalized, OnPropertyChanged)); + + /// + /// Gets or sets the precision used for computation. + /// + public CanvasBufferPrecision BufferPrecision + { + get => (CanvasBufferPrecision)GetValue(BufferPrecisionProperty); + set => SetValue(BufferPrecisionProperty, value); + } + + /// + /// EdgeBehavior Dependency Property + /// + public static readonly DependencyProperty EdgeBehaviorProperty = DependencyProperty.Register( + "EdgeBehavior", + typeof(CanvasEdgeBehavior), + typeof(LinearGradientCanvasBrush), + new PropertyMetadata(CanvasEdgeBehavior.Clamp, OnPropertyChanged)); + + /// + /// Gets or sets the behavior of the pixels which fall outside of the gradient's typical rendering area. + /// + public CanvasEdgeBehavior EdgeBehavior + { + get => (CanvasEdgeBehavior)GetValue(EdgeBehaviorProperty); + set => SetValue(EdgeBehaviorProperty, value); + } + + /// + /// EndPoint Dependency Property + /// + public static readonly DependencyProperty EndPointProperty = DependencyProperty.Register( + "EndPoint", + typeof(Point), + typeof(LinearGradientCanvasBrush), + new PropertyMetadata(default(Point), OnPropertyChanged)); + + /// + /// Gets or sets the point on the Canvas where the gradient stops. + /// + public Point EndPoint + { + get => (Point)GetValue(EndPointProperty); + set => SetValue(EndPointProperty, value); + } + + /// + /// PostInterpolationSpace Dependency Property + /// + public static readonly DependencyProperty PostInterpolationSpaceProperty = DependencyProperty.Register( + "PostInterpolationSpace", + typeof(CanvasColorSpace), + typeof(LinearGradientCanvasBrush), + new PropertyMetadata(CanvasColorSpace.Srgb, OnPropertyChanged)); + + /// + /// Gets or sets the the color space to be used after interpolation. + /// + public CanvasColorSpace PostInterpolationSpace + { + get => (CanvasColorSpace)GetValue(PostInterpolationSpaceProperty); + set => SetValue(PostInterpolationSpaceProperty, value); + } + + /// + /// PreInterpolationSpace Dependency Property + /// + public static readonly DependencyProperty PreInterpolationSpaceProperty = DependencyProperty.Register( + "PreInterpolationSpace", + typeof(CanvasColorSpace), + typeof(LinearGradientCanvasBrush), + new PropertyMetadata(CanvasColorSpace.Srgb, OnPropertyChanged)); + + /// + /// Gets or sets the the color space to be used before interpolation. + /// + public CanvasColorSpace PreInterpolationSpace + { + get => (CanvasColorSpace)GetValue(PreInterpolationSpaceProperty); + set => SetValue(PreInterpolationSpaceProperty, value); + } + + /// + /// StartPoint Dependency Property + /// + public static readonly DependencyProperty StartPointProperty = DependencyProperty.Register( + "StartPoint", + typeof(Point), + typeof(LinearGradientCanvasBrush), + new PropertyMetadata(default(Point), OnPropertyChanged)); + + /// + /// Gets or sets the point on the Canvas where the gradient starts. + /// + public Point StartPoint + { + get => (Point)GetValue(StartPointProperty); + set => SetValue(StartPointProperty, value); + } + + /// + /// Stops Dependency Property + /// + public static readonly DependencyProperty StopsProperty = DependencyProperty.Register( + "Stops", + typeof(GradientStopCollection), + typeof(LinearGradientCanvasBrush), + new PropertyMetadata(null, OnPropertyChanged)); + + /// + /// Gets or sets the gradient stops that comprise the brush. + /// + public GradientStopCollection Stops + { + get => (GradientStopCollection)GetValue(StopsProperty); + set => SetValue(StopsProperty, value); + } + + /// + /// Method that is called whenever the dependency properties of the Brush changes + /// + /// The object whose property has changed. + /// Event arguments. + private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var brush = (LinearGradientCanvasBrush)d; + + // Recreate the canvas brush on any property change. + brush.OnUpdated(); + } + + /// + protected override void OnUpdated() + { + if (Stops == null) + { + return; + } + + var canvasGradientStops = new List(); + foreach (var stop in Stops) + { + canvasGradientStops.Add(new CanvasGradientStop() + { + Color = stop.Color, + Position = (float)stop.Offset + }); + } + + CanvasBrush = new CanvasLinearGradientBrush( + CompositionGenerator.Instance.Device, + canvasGradientStops.ToArray(), + EdgeBehavior, + AlphaMode, + PreInterpolationSpace, + PostInterpolationSpace, + BufferPrecision) + { + StartPoint = StartPoint.ToVector2(), + EndPoint = EndPoint.ToVector2(), + Opacity = (float)Opacity, + Transform = Transform.ToMatrix3x2() + }; + + base.OnUpdated(); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Brushes/RadialGradientCanvasBrush.cs b/Microsoft.Toolkit.Uwp.UI.Media/Brushes/RadialGradientCanvasBrush.cs new file mode 100644 index 00000000000..0adfab9934b --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Brushes/RadialGradientCanvasBrush.cs @@ -0,0 +1,263 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Numerics; +using Microsoft.Graphics.Canvas; +using Microsoft.Graphics.Canvas.Brushes; +using Microsoft.Toolkit.Uwp.UI.Media.Geometry; +using Windows.Foundation; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Markup; +using Windows.UI.Xaml.Media; + +namespace Microsoft.Toolkit.Uwp.UI.Media +{ + /// + /// XAML equivalent of Win2d's CanvasRadialGradientBrush class which paints in radial gradient. + /// + [ContentProperty(Name = nameof(Stops))] + public class RadialGradientCanvasBrush : RenderCanvasBrushBase + { + /// + /// AlphaMode Dependency Property + /// + public static readonly DependencyProperty AlphaModeProperty = DependencyProperty.Register( + "AlphaMode", + typeof(CanvasAlphaMode), + typeof(RadialGradientCanvasBrush), + new PropertyMetadata(CanvasAlphaMode.Premultiplied, OnPropertyChanged)); + + /// + /// Gets or sets the way in which the Alpha channel affects color channels. + /// + public CanvasAlphaMode AlphaMode + { + get => (CanvasAlphaMode)GetValue(AlphaModeProperty); + set => SetValue(AlphaModeProperty, value); + } + + /// + /// BufferPrecision Dependency Property + /// + public static readonly DependencyProperty BufferPrecisionProperty = DependencyProperty.Register( + "BufferPrecision", + typeof(CanvasBufferPrecision), + typeof(RadialGradientCanvasBrush), + new PropertyMetadata(CanvasBufferPrecision.Precision8UIntNormalized, OnPropertyChanged)); + + /// + /// Gets or sets the precision used for computation. + /// + public CanvasBufferPrecision BufferPrecision + { + get => (CanvasBufferPrecision)GetValue(BufferPrecisionProperty); + set => SetValue(BufferPrecisionProperty, value); + } + + /// + /// Center Dependency Property + /// + public static readonly DependencyProperty CenterProperty = DependencyProperty.Register( + "Center", + typeof(Point), + typeof(RadialGradientCanvasBrush), + new PropertyMetadata(default(Point), OnPropertyChanged)); + + /// + /// Gets or sets the center of the brush's radial gradient. + /// + public Point Center + { + get => (Point)GetValue(CenterProperty); + set => SetValue(CenterProperty, value); + } + + /// + /// EdgeBehavior Dependency Property + /// + public static readonly DependencyProperty EdgeBehaviorProperty = DependencyProperty.Register( + "EdgeBehavior", + typeof(CanvasEdgeBehavior), + typeof(RadialGradientCanvasBrush), + new PropertyMetadata(CanvasEdgeBehavior.Clamp, OnPropertyChanged)); + + /// + /// Gets or sets the behavior of the pixels which fall outside of the gradient's typical rendering area. + /// + public CanvasEdgeBehavior EdgeBehavior + { + get => (CanvasEdgeBehavior)GetValue(EdgeBehaviorProperty); + set => SetValue(EdgeBehaviorProperty, value); + } + + /// + /// EndPoint Dependency Property + /// + public static readonly DependencyProperty EndPointProperty = DependencyProperty.Register( + "EndPoint", + typeof(Point), + typeof(RadialGradientCanvasBrush), + new PropertyMetadata(default(Point), OnPropertyChanged)); + + /// + /// OriginOffset Dependency Property + /// + public static readonly DependencyProperty OriginOffsetProperty = DependencyProperty.Register( + "OriginOffset", + typeof(Point), + typeof(RadialGradientCanvasBrush), + new PropertyMetadata(default(Point), OnPropertyChanged)); + + /// + /// Gets or sets the displacement from Center, used to form the brush's radial gradient. + /// + public Point OriginOffset + { + get => (Point)GetValue(OriginOffsetProperty); + set => SetValue(OriginOffsetProperty, value); + } + + /// + /// PostInterpolationSpace Dependency Property + /// + public static readonly DependencyProperty PostInterpolationSpaceProperty = DependencyProperty.Register( + "PostInterpolationSpace", + typeof(CanvasColorSpace), + typeof(RadialGradientCanvasBrush), + new PropertyMetadata(CanvasColorSpace.Srgb, OnPropertyChanged)); + + /// + /// Gets or sets the the color space to be used after interpolation. + /// + public CanvasColorSpace PostInterpolationSpace + { + get => (CanvasColorSpace)GetValue(PostInterpolationSpaceProperty); + set => SetValue(PostInterpolationSpaceProperty, value); + } + + /// + /// PreInterpolationSpace Dependency Property + /// + public static readonly DependencyProperty PreInterpolationSpaceProperty = DependencyProperty.Register( + "PreInterpolationSpace", + typeof(CanvasColorSpace), + typeof(RadialGradientCanvasBrush), + new PropertyMetadata(CanvasColorSpace.Srgb, OnPropertyChanged)); + + /// + /// Gets or sets the the color space to be used before interpolation. + /// + public CanvasColorSpace PreInterpolationSpace + { + get => (CanvasColorSpace)GetValue(PreInterpolationSpaceProperty); + set => SetValue(PreInterpolationSpaceProperty, value); + } + + /// + /// RadiusX Dependency Property + /// + public static readonly DependencyProperty RadiusXProperty = DependencyProperty.Register( + "RadiusX", + typeof(double), + typeof(RadialGradientCanvasBrush), + new PropertyMetadata(0d, OnPropertyChanged)); + + /// + /// Gets or sets the horizontal radius of the brush's radial gradient. + /// + public double RadiusX + { + get => (double)GetValue(RadiusXProperty); + set => SetValue(RadiusXProperty, value); + } + + /// + /// RadiusY Dependency Property + /// + public static readonly DependencyProperty RadiusYProperty = DependencyProperty.Register( + "RadiusY", + typeof(double), + typeof(RadialGradientCanvasBrush), + new PropertyMetadata(0d, OnPropertyChanged)); + + /// + /// Gets or sets the vertical radius of the brush's radial gradient. + /// + public double RadiusY + { + get => (double)GetValue(RadiusYProperty); + set => SetValue(RadiusYProperty, value); + } + + /// + /// Stops Dependency Property + /// + public static readonly DependencyProperty StopsProperty = DependencyProperty.Register( + "Stops", + typeof(GradientStopCollection), + typeof(RadialGradientCanvasBrush), + new PropertyMetadata(null, OnPropertyChanged)); + + /// + /// Gets or sets the gradient stops that comprise the brush. + /// + public GradientStopCollection Stops + { + get => (GradientStopCollection)GetValue(StopsProperty); + set => SetValue(StopsProperty, value); + } + + /// + /// Method that is called whenever the dependency properties of the Brush changes. + /// + /// The object whose property has changed. + /// Event arguments. + private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var brush = (RadialGradientCanvasBrush)d; + + // Recreate the canvas brush on any property change. + brush.OnUpdated(); + } + + /// + protected override void OnUpdated() + { + if (Stops == null) + { + return; + } + + var canvasGradientStops = new List(); + foreach (var stop in Stops) + { + canvasGradientStops.Add(new CanvasGradientStop() + { + Color = stop.Color, + Position = (float)stop.Offset + }); + } + + CanvasBrush = new CanvasRadialGradientBrush( + CompositionGenerator.Instance.Device, + canvasGradientStops.ToArray(), + EdgeBehavior, + AlphaMode, + PreInterpolationSpace, + PostInterpolationSpace, + BufferPrecision) + { + Center = Center.ToVector2(), + RadiusX = (float)RadiusX, + RadiusY = (float)RadiusY, + OriginOffset = OriginOffset.ToVector2(), + Opacity = (float)Opacity, + Transform = Transform.ToMatrix3x2() + }; + + base.OnUpdated(); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Brushes/SolidColorCanvasBrush.cs b/Microsoft.Toolkit.Uwp.UI.Media/Brushes/SolidColorCanvasBrush.cs new file mode 100644 index 00000000000..54a39a97a65 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Brushes/SolidColorCanvasBrush.cs @@ -0,0 +1,54 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Graphics.Canvas.Brushes; +using Windows.UI; +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.UI.Media +{ + /// + /// XAML equivalent of Win2d's CanvasSolidColorBrush class which paints in solid color. + /// + public class SolidColorCanvasBrush : RenderCanvasBrushBase + { + /// + /// Color Dependency Property + /// + public static readonly DependencyProperty ColorProperty = DependencyProperty.Register( + "Color", + typeof(Color), + typeof(SolidColorCanvasBrush), + new PropertyMetadata(Colors.Transparent, OnPropertyChanged)); + + /// + /// Gets or sets the color of the brush. + /// + public Color Color + { + get => (Color)GetValue(ColorProperty); + set => SetValue(ColorProperty, value); + } + + /// + /// Method that is called whenever the dependency properties of the Brush changes + /// + /// The object whose property has changed + /// Event arguments + private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var brush = (SolidColorCanvasBrush)d; + + // Recreate the canvas brush on any property change. + brush.OnUpdated(); + } + + /// + protected override void OnUpdated() + { + CanvasBrush = new CanvasSolidColorBrush(CompositionGenerator.Instance.Device, Color); + base.OnUpdated(); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasCircleGeometry.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasCircleGeometry.cs new file mode 100644 index 00000000000..cf6d773dcf9 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasCircleGeometry.cs @@ -0,0 +1,94 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Numerics; +using Microsoft.Graphics.Canvas.Geometry; +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry +{ + /// + /// Represents a circle geometry object with the specified extents. + /// + public class CanvasCircleGeometry : CanvasCoreGeometry + { + /// + /// CenterX Dependency Property + /// + public static readonly DependencyProperty CenterXProperty = DependencyProperty.Register( + "CenterX", + typeof(double), + typeof(CanvasCircleGeometry), + new PropertyMetadata(0d, OnPropertyChanged)); + + /// + /// Gets or sets the the x coordinate of the center. + /// + public double CenterX + { + get => (double)GetValue(CenterXProperty); + set => SetValue(CenterXProperty, value); + } + + /// + /// CenterY Dependency Property + /// + public static readonly DependencyProperty CenterYProperty = DependencyProperty.Register( + "CenterY", + typeof(double), + typeof(CanvasCircleGeometry), + new PropertyMetadata(0d, OnPropertyChanged)); + + /// + /// Gets or sets the y coordinate of the Center. + /// + public double CenterY + { + get => (double)GetValue(CenterYProperty); + set => SetValue(CenterYProperty, value); + } + + /// + /// Radius Dependency Property + /// + public static readonly DependencyProperty RadiusProperty = DependencyProperty.Register( + "Radius", + typeof(double), + typeof(CanvasCircleGeometry), + new PropertyMetadata(0d, OnPropertyChanged)); + + /// + /// Gets or sets the radius value of the . + /// + public double Radius + { + get => (double)GetValue(RadiusProperty); + set => SetValue(RadiusProperty, value); + } + + /// + /// Method that is called whenever the dependency properties of the Brush . + /// + /// The object whose property has changed + /// Event arguments + private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var geometry = (CanvasCircleGeometry)d; + + // Recreate the geometry on any property change. + geometry.OnUpdateGeometry(); + } + + /// + protected override void OnUpdateGeometry() + { + Geometry?.Dispose(); + + var center = new Vector2((float)CenterX, (float)CenterY); + Geometry = CanvasGeometry.CreateCircle(CompositionGenerator.Instance.Device, center, (float)Radius); + + base.OnUpdateGeometry(); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasCombinedGeometry.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasCombinedGeometry.cs new file mode 100644 index 00000000000..bdf9308ccc0 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasCombinedGeometry.cs @@ -0,0 +1,197 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Numerics; +using Microsoft.Graphics.Canvas.Geometry; +using Microsoft.Toolkit.Uwp.Helpers; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Media; + +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry +{ + /// + /// Represents a Geometry defined by the combination of two objects. + /// + public class CanvasCombinedGeometry : CanvasCoreGeometry + { + private WeakEventListener _geometry1UpdateListener; + private WeakEventListener _geometry2UpdateListener; + + /// + /// Geometry1 Dependency Property + /// + public static readonly DependencyProperty Geometry1Property = DependencyProperty.Register( + "Geometry1", + typeof(CanvasCoreGeometry), + typeof(CanvasCombinedGeometry), + new PropertyMetadata(null, OnGeometry1Changed)); + + /// + /// Gets or sets the first object to combine. + /// + public CanvasCoreGeometry Geometry1 + { + get => (CanvasCoreGeometry)GetValue(Geometry1Property); + set => SetValue(Geometry1Property, value); + } + + /// + /// Handles changes to the Geometry1 property. + /// + /// . + /// DependencyProperty changed event arguments. + private static void OnGeometry1Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var combinedGeometry = (CanvasCombinedGeometry)d; + combinedGeometry.OnGeometry1Changed(); + } + + /// + /// Instance handler for the changes to the Geometry1 dependency property. + /// + private void OnGeometry1Changed() + { + _geometry1UpdateListener?.Detach(); + _geometry1UpdateListener = null; + + if (Geometry1 != null) + { + _geometry1UpdateListener = new WeakEventListener(Geometry1) + { + OnEventAction = async (instance, source, args) => + { + await Window.Current.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => + { + OnUpdateGeometry(); + }); + } + }; + + Geometry1.Updated += _geometry1UpdateListener.OnEvent; + + OnUpdateGeometry(); + } + } + + /// + /// Geometry2 Dependency Property + /// + public static readonly DependencyProperty Geometry2Property = DependencyProperty.Register( + "Geometry2", + typeof(CanvasCoreGeometry), + typeof(CanvasCombinedGeometry), + new PropertyMetadata(null, OnGeometry2Changed)); + + /// + /// Gets or sets the second to combine. + /// + public CanvasCoreGeometry Geometry2 + { + get => (CanvasCoreGeometry)GetValue(Geometry2Property); + set => SetValue(Geometry2Property, value); + } + + /// + /// Handles changes to the Geometry2 property. + /// + /// . + /// DependencyProperty changed event arguments. + private static void OnGeometry2Changed(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var combinedGeometry = (CanvasCombinedGeometry)d; + combinedGeometry.OnGeometry2Changed(); + } + + /// + /// Instance handler for the changes to the Geometry2 dependency property. + /// + private void OnGeometry2Changed() + { + _geometry2UpdateListener?.Detach(); + _geometry2UpdateListener = null; + + if (Geometry2 != null) + { + _geometry2UpdateListener = new WeakEventListener(Geometry2) + { + OnEventAction = async (instance, source, args) => + { + await Window.Current.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => + { + OnUpdateGeometry(); + }); + } + }; + + Geometry2.Updated += _geometry2UpdateListener.OnEvent; + + OnUpdateGeometry(); + } + } + + /// + /// Transform Dependency Property + /// + public static readonly DependencyProperty TransformProperty = DependencyProperty.Register( + "Transform", + typeof(MatrixTransform), + typeof(CanvasCombinedGeometry), + new PropertyMetadata(Matrix3x2.Identity.ToMatrixTransform(), OnPropertyChanged)); + + /// + /// Gets or sets the MatrixTransform to be applied to Geometry2 before combining with Geometry1. + /// + public MatrixTransform Transform + { + get => (MatrixTransform)GetValue(TransformProperty); + set => SetValue(TransformProperty, value); + } + + /// + /// GeometryCombineMode Dependency Property + /// + public static readonly DependencyProperty GeometryCombineModeProperty = DependencyProperty.Register( + "GeometryCombineMode", + typeof(CanvasGeometryCombine), + typeof(CanvasCombinedGeometry), + new PropertyMetadata(CanvasGeometryCombine.Union, OnPropertyChanged)); + + /// + /// Gets or sets the method by which the geometries specified by and are meant to be combined. + /// + public CanvasGeometryCombine GeometryCombineMode + { + get => (CanvasGeometryCombine)GetValue(GeometryCombineModeProperty); + set => SetValue(GeometryCombineModeProperty, value); + } + + /// + /// Method that is called whenever the dependency properties of the Brush changes + /// + /// The object whose property has changed + /// Event arguments + private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var geometry = (CanvasCombinedGeometry)d; + + // Recreate the geometry on any property change. + geometry.OnUpdateGeometry(); + } + + /// + protected override void OnUpdateGeometry() + { + if (Geometry1?.Geometry == null || Geometry2?.Geometry == null) + { + Geometry = null; + return; + } + + Geometry = Geometry1.Geometry.CombineWith(Geometry2.Geometry, Transform.ToMatrix3x2(), GeometryCombineMode); + + RaiseUpdatedEvent(); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasCoreGeometry.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasCoreGeometry.cs new file mode 100644 index 00000000000..788e30b7ab7 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasCoreGeometry.cs @@ -0,0 +1,109 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.CompilerServices; +using Microsoft.Graphics.Canvas.Geometry; +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry +{ + /// + /// Provides a base class for objects that define geometric shapes using . + /// + public abstract class CanvasCoreGeometry : DependencyObject, ICanvasPathGeometry, IDisposable + { + /// + /// Event to notify that the properties of this class have been updated. + /// + public event EventHandler Updated; + + private bool _disposedValue; + + /// + /// Geometry Dependency Property + /// + public static readonly DependencyProperty GeometryProperty = DependencyProperty.Register( + "Geometry", + typeof(CanvasGeometry), + typeof(CanvasCoreGeometry), + new PropertyMetadata(null, OnGeometryChanged)); + + /// + /// Gets or sets the associated Win2d CanvasGeometry. + /// + public CanvasGeometry Geometry + { + get => (CanvasGeometry)GetValue(GeometryProperty); + protected set => SetValue(GeometryProperty, value); + } + + /// + /// Handles changes to the Geometry property. + /// + /// CanvasCoreGeometry. + /// DependencyProperty changed event arguments. + private static void OnGeometryChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var coreGeometry = (CanvasCoreGeometry)d; + coreGeometry.OnGeometryChanged(); + } + + /// + /// Instance handler for the changes to the Geometry dependency property. + /// + protected virtual void OnGeometryChanged() + { + } + + /// + /// Method to be called when any of the parameters affecting the Geometry is updated. + /// + protected virtual void OnUpdateGeometry() + { + Updated?.Invoke(this, null); + } + + /// + /// Raises the Updated event. + /// + protected void RaiseUpdatedEvent() + { + Updated?.Invoke(this, null); + } + + /// + /// Call this method to redraw its Geometry (usually when event is raised). + /// + public void Refresh() + { + OnUpdateGeometry(); + } + + /// + /// Disposes the resources used by the CanvasCoreGeometry and its derivatives + /// + /// Flag to indicate if we are disposing the managed objects. + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + Geometry.Dispose(); + } + + _disposedValue = true; + } + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasDrawingSessionExtensions.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasDrawingSessionExtensions.cs index bd297f71f89..c4f3a607916 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasDrawingSessionExtensions.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasDrawingSessionExtensions.cs @@ -304,4 +304,4 @@ public static void FillSquircle(this CanvasDrawingSession session, float x, floa session.FillGeometry(geometry, offset, brush); } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasEllipseGeometry.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasEllipseGeometry.cs new file mode 100644 index 00000000000..15d6cda6ca0 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasEllipseGeometry.cs @@ -0,0 +1,109 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Numerics; +using Microsoft.Graphics.Canvas.Geometry; +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry +{ + /// + /// Represents an Ellipse geometry object with the specified extents. + /// + public class CanvasEllipseGeometry : CanvasCoreGeometry + { + /// + /// CenterX Dependency Property + /// + public static readonly DependencyProperty CenterXProperty = DependencyProperty.Register( + "CenterX", + typeof(double), + typeof(CanvasEllipseGeometry), + new PropertyMetadata(0d, OnPropertyChanged)); + + /// + /// Gets or sets the coordinate of the center of the on the x-axis. + /// + public double CenterX + { + get => (double)GetValue(CenterXProperty); + set => SetValue(CenterXProperty, value); + } + + /// + /// CenterY Dependency Property + /// + public static readonly DependencyProperty CenterYProperty = DependencyProperty.Register( + "CenterY", + typeof(double), + typeof(CanvasEllipseGeometry), + new PropertyMetadata(0d, OnPropertyChanged)); + + /// + /// Gets or sets the coordinate of the center of the on the y-axis. + /// + public double CenterY + { + get => (double)GetValue(CenterYProperty); + set => SetValue(CenterYProperty, value); + } + + /// + /// RadiusX Dependency Property + /// + public static readonly DependencyProperty RadiusXProperty = DependencyProperty.Register( + "RadiusX", + typeof(float), + typeof(CanvasEllipseGeometry), + new PropertyMetadata(0d, OnPropertyChanged)); + + /// + /// Gets or sets the x-radius value of the . + /// + public float RadiusX + { + get => (float)GetValue(RadiusXProperty); + set => SetValue(RadiusXProperty, value); + } + + /// + /// RadiusY Dependency Property + /// + public static readonly DependencyProperty RadiusYProperty = DependencyProperty.Register( + "RadiusY", + typeof(float), + typeof(CanvasEllipseGeometry), + new PropertyMetadata(0d, OnPropertyChanged)); + + /// + /// Gets or sets the y-radius value of the . + /// + public float RadiusY + { + get => (float)GetValue(RadiusYProperty); + set => SetValue(RadiusYProperty, value); + } + + /// + /// Method that is called whenever the dependency properties of the Brush changes + /// + /// The object whose property has changed + /// Event arguments + private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var geometry = (CanvasEllipseGeometry)d; + + // Recreate the geometry on any property change. + geometry.OnUpdateGeometry(); + } + + /// + protected override void OnUpdateGeometry() + { + Geometry = CanvasGeometry.CreateEllipse(CompositionGenerator.Instance.Device, new Vector2((float)CenterX, (float)CenterY), RadiusX, RadiusY); + + RaiseUpdatedEvent(); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasPathBuilderExtensions.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasPathBuilderExtensions.cs index d9878de510d..4bce7681c9b 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasPathBuilderExtensions.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasPathBuilderExtensions.cs @@ -7,7 +7,6 @@ using System.Linq; using System.Numerics; using Microsoft.Graphics.Canvas.Geometry; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { @@ -459,4 +458,4 @@ public static CanvasPathBuilder BuildPathWithLines(this CanvasPathBuilder builde return BuildPathWithLines(builder, canvasFigureLoop, vectors); } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasPathGeometry.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasPathGeometry.cs index 37a717eb359..48a8318212f 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasPathGeometry.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasPathGeometry.cs @@ -2,37 +2,103 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#pragma warning disable CS0419 // Ambiguous reference in cref attribute + +using System; using System.Numerics; -using System.Runtime.CompilerServices; -using System.Text; using Microsoft.Graphics.Canvas; using Microsoft.Graphics.Canvas.Brushes; using Microsoft.Graphics.Canvas.Geometry; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers; using Windows.UI; +using Windows.UI.Xaml; namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// - /// Helper Class for creating Win2d objects. + /// Represents a complex vector-based shape geometry that may be composed of arcs, curves, ellipses, lines, rectangles, rounded rectangles, squircles. + /// Also provides several helper methods to create Win2d objects. /// - public static class CanvasPathGeometry + public class CanvasPathGeometry : CanvasCoreGeometry { /// - /// Parses the Path data string and converts it to CanvasGeometry. + /// Data Dependency Property + /// + public static readonly DependencyProperty DataProperty = DependencyProperty.Register( + "Data", + typeof(string), + typeof(CanvasPathGeometry), + new PropertyMetadata(string.Empty, OnDataChanged)); + + /// + /// Gets or sets the path data for the associated Win2d defined in the Win2d Path Mini Language. + /// + public string Data + { + get => (string)GetValue(DataProperty); + set => SetValue(DataProperty, value); + } + + /// + /// Handles changes to the Data property. + /// + /// CanvasPathGeometry. + /// DependencyProperty changed event arguments. + private static void OnDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var pathGeometry = (CanvasPathGeometry)d; + pathGeometry.OnDataChanged(); + } + + /// + /// Instance handler for the changes to the Data dependency property. + /// + protected virtual void OnDataChanged() + { + OnUpdateGeometry(); + } + + /// + /// Initializes a new instance of the class. /// - /// Path data + public CanvasPathGeometry() + { + this.Geometry = null; + } + + /// + protected override void OnUpdateGeometry() + { + // Dispose previous CanvasGeometry (if any) + Geometry?.Dispose(); + Geometry = null; + + try + { + Geometry = CreateGeometry(CompositionGenerator.Instance.Device, Data); + } + catch (Exception) + { + Geometry = null; + } + + RaiseUpdatedEvent(); + } + + /// + /// Parses the Path data string and converts it to . + /// + /// Path data. /// public static CanvasGeometry CreateGeometry(string pathData) { - return CreateGeometry(null, pathData); + return CreateGeometry(CompositionGenerator.Instance.Device, pathData); } /// - /// Parses the Path data string and converts it to CanvasGeometry. + /// Parses the Path data string and converts it to . /// - /// - /// Path data + /// . + /// Path data. /// public static CanvasGeometry CreateGeometry(ICanvasResourceCreator resourceCreator, string pathData) { @@ -46,13 +112,13 @@ public static CanvasGeometry CreateGeometry(ICanvasResourceCreator resourceCreat /// /// Creates a Squircle geometry with the specified extents. /// - /// Resource creator - /// X offset of the TopLeft corner of the Squircle - /// Y offset of the TopLeft corner of the Squircle - /// Width of the Squircle - /// Height of the Squircle - /// Corner Radius on the x-axis - /// Corner Radius on the y-axis + /// Resource creator. + /// X offset of the TopLeft corner of the Squircle. + /// Y offset of the TopLeft corner of the Squircle. + /// Width of the Squircle. + /// Height of the Squircle. + /// Corner Radius on the x-axis. + /// Corner Radius on the y-axis. /// public static CanvasGeometry CreateSquircle(ICanvasResourceCreator resourceCreator, float x, float y, float width, float height, float radiusX, float radiusY) { @@ -62,10 +128,10 @@ public static CanvasGeometry CreateSquircle(ICanvasResourceCreator resourceCreat } /// - /// Parses the given Brush data string and converts it to ICanvasBrush. + /// Parses the given Brush data string and converts it to . /// - /// ICanvasResourceCreator - /// Brush data in string format + /// ICanvasResourceCreator. + /// Brush data in string format. /// public static ICanvasBrush CreateBrush(ICanvasResourceCreator resourceCreator, string brushData) { @@ -76,10 +142,10 @@ public static ICanvasBrush CreateBrush(ICanvasResourceCreator resourceCreator, s } /// - /// Parses the given Stroke data string and converts it to ICanvasStroke. + /// Parses the given Stroke data string and converts it to . /// - /// ICanvasResourceCreator - /// Stroke data in string format + /// . + /// Stroke data in string format. /// public static ICanvasStroke CreateStroke(ICanvasResourceCreator resourceCreator, string strokeData) { @@ -90,10 +156,10 @@ public static ICanvasStroke CreateStroke(ICanvasResourceCreator resourceCreator, } /// - /// Parses the give CanvasStrokeStyle data string and converts it to CanvasStrokeStyle. + /// Parses the give CanvasStrokeStyle data string and converts it to . /// - /// CanvasStrokeStyle data in string format - /// object + /// data in string format. + /// public static CanvasStrokeStyle CreateStrokeStyle(string styleData) { using (new CultureShield("en-US")) @@ -103,14 +169,14 @@ public static CanvasStrokeStyle CreateStrokeStyle(string styleData) } /// - /// Converts the color string in Hexadecimal or HDR color format to the corresponding Color object. + /// Converts the color string in Hexadecimal or HDR color format to the corresponding object. /// The hexadecimal color string should be in #RRGGBB or #AARRGGBB format. /// The '#' character is optional. /// The HDR color string should be in R G B A format. /// (R, G, B & A should have value in the range between 0 and 1, inclusive) /// - /// Color string in Hexadecimal or HDR format - /// Color + /// Color string in Hexadecimal or HDR format. + /// public static Color CreateColor(string colorString) { using (new CultureShield("en-US")) @@ -120,15 +186,15 @@ public static Color CreateColor(string colorString) } /// - /// Converts a Vector4 High Dynamic Range Color to Color object. + /// Converts a Vector4 High Dynamic Range Color to object. /// Negative components of the Vector4 will be sanitized by taking the absolute /// value of the component. The HDR Color components should have value in /// the range between 0 and 1, inclusive. If they are more than 1, they /// will be clamped at 1. /// Vector4's X, Y, Z, W components match to Color's R, G, B, A components respectively. /// - /// High Dynamic Range Color - /// Color + /// High Dynamic Range Color. + /// public static Color CreateColor(Vector4 hdrColor) { using (new CultureShield("en-US")) @@ -137,4 +203,6 @@ public static Color CreateColor(Vector4 hdrColor) } } } -} \ No newline at end of file +} + +#pragma warning restore CS0419 // Ambiguous reference in cref attribute diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasRectangleGeometry.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasRectangleGeometry.cs new file mode 100644 index 00000000000..c249ea4c654 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasRectangleGeometry.cs @@ -0,0 +1,108 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Graphics.Canvas.Geometry; +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry +{ + /// + /// Represents a rectangular geometry object with the specified extents. + /// + public class CanvasRectangleGeometry : CanvasCoreGeometry + { + /// + /// X Dependency Property + /// + public static readonly DependencyProperty XProperty = DependencyProperty.Register( + "X", + typeof(double), + typeof(CanvasRectangleGeometry), + new PropertyMetadata(0d, OnPropertyChanged)); + + /// + /// Gets or sets the x-coordinate of the upper-left corner of the rectangle geometry. + /// + public double X + { + get => (double)GetValue(XProperty); + set => SetValue(XProperty, value); + } + + /// + /// Y Dependency Property + /// + public static readonly DependencyProperty YProperty = DependencyProperty.Register( + "Y", + typeof(double), + typeof(CanvasRectangleGeometry), + new PropertyMetadata(0d, OnPropertyChanged)); + + /// + /// Gets or sets the y-coordinate of the upper-left corner of the rectangle geometry. + /// + public double Y + { + get => (double)GetValue(YProperty); + set => SetValue(YProperty, value); + } + + /// + /// Width Dependency Property + /// + public static readonly DependencyProperty WidthProperty = DependencyProperty.Register( + "Width", + typeof(double), + typeof(CanvasRectangleGeometry), + new PropertyMetadata(0d, OnPropertyChanged)); + + /// + /// Gets or sets the width of the rectangle geometry. + /// + public double Width + { + get => (double)GetValue(WidthProperty); + set => SetValue(WidthProperty, value); + } + + /// + /// Height Dependency Property + /// + public static readonly DependencyProperty HeightProperty = DependencyProperty.Register( + "Height", + typeof(double), + typeof(CanvasRectangleGeometry), + new PropertyMetadata(0d, OnPropertyChanged)); + + /// + /// Gets or sets the height of the rectangle geometry. + /// + public double Height + { + get => (double)GetValue(HeightProperty); + set => SetValue(HeightProperty, value); + } + + /// + /// Method that is called whenever the dependency properties of the Brush changes + /// + /// The object whose property has changed + /// Event arguments + private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var geometry = (CanvasRectangleGeometry)d; + + // Recreate the geometry on any property change. + geometry.OnUpdateGeometry(); + } + + /// + protected override void OnUpdateGeometry() + { + Geometry = CanvasGeometry.CreateRectangle(CompositionGenerator.Instance.Device, (float)X, (float)Y, (float)Width, (float)Height); + + RaiseUpdatedEvent(); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasRoundedRectangleGeometry.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasRoundedRectangleGeometry.cs new file mode 100644 index 00000000000..417dc487d23 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasRoundedRectangleGeometry.cs @@ -0,0 +1,72 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Graphics.Canvas.Geometry; +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry +{ + /// + /// Represents a rounded rectangle geometry object with the specified extents. + /// + public class CanvasRoundedRectangleGeometry : CanvasRectangleGeometry + { + /// + /// RadiusX Dependency Property + /// + public static readonly DependencyProperty RadiusXProperty = DependencyProperty.Register( + "RadiusX", + typeof(double), + typeof(CanvasRoundedRectangleGeometry), + new PropertyMetadata(0d, OnPropertyChanged)); + + /// + /// Gets or sets the radius of the corners in the x-axis. + /// + public double RadiusX + { + get => (double)GetValue(RadiusXProperty); + set => SetValue(RadiusXProperty, value); + } + + /// + /// RadiusY Dependency Property + /// + public static readonly DependencyProperty RadiusYProperty = DependencyProperty.Register( + "RadiusY", + typeof(double), + typeof(CanvasRoundedRectangleGeometry), + new PropertyMetadata(0d, OnPropertyChanged)); + + /// + /// Gets or sets the radius of the corners in the x-axis. + /// + public double RadiusY + { + get => (double)GetValue(RadiusYProperty); + set => SetValue(RadiusYProperty, value); + } + + /// + /// Method that is called whenever the dependency properties of the Brush changes + /// + /// The object whose property has changed + /// Event arguments + private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var geometry = (CanvasRoundedRectangleGeometry)d; + + // Recreate the geometry on any property change. + geometry.OnUpdateGeometry(); + } + + /// + protected override void OnUpdateGeometry() + { + Geometry = CanvasGeometry.CreateRoundedRectangle(CompositionGenerator.Instance.Device, (float)X, (float)Y, (float)Width, (float)Height, (float)RadiusX, (float)RadiusY); + + RaiseUpdatedEvent(); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasSquircleGeometry.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasSquircleGeometry.cs new file mode 100644 index 00000000000..f2dc25a9c36 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasSquircleGeometry.cs @@ -0,0 +1,72 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Toolkit.Uwp.UI.Media.Geometry; +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry +{ + /// + /// Represents a Squircle geometry object with the specified extents. + /// + public class CanvasSquircleGeometry : CanvasRectangleGeometry + { + /// + /// RadiusX Dependency Property + /// + public static readonly DependencyProperty RadiusXProperty = DependencyProperty.Register( + "RadiusX", + typeof(double), + typeof(CanvasSquircleGeometry), + new PropertyMetadata(0d, OnPropertyChanged)); + + /// + /// Gets or sets the radius of the corners in the x-axis. + /// + public double RadiusX + { + get => (double)GetValue(RadiusXProperty); + set => SetValue(RadiusXProperty, value); + } + + /// + /// RadiusY Dependency Property + /// + public static readonly DependencyProperty RadiusYProperty = DependencyProperty.Register( + "RadiusY", + typeof(double), + typeof(CanvasSquircleGeometry), + new PropertyMetadata(0d, OnPropertyChanged)); + + /// + /// Gets or sets the radius of the corners in the x-axis. + /// + public double RadiusY + { + get => (double)GetValue(RadiusYProperty); + set => SetValue(RadiusYProperty, value); + } + + /// + /// Method that is called whenever the dependency properties of the Brush changes + /// + /// The object whose property has changed + /// Event arguments + private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var geometry = (CanvasSquircleGeometry)d; + + // Recreate the geometry on any property change. + geometry.OnUpdateGeometry(); + } + + /// + protected override void OnUpdateGeometry() + { + Geometry = CanvasPathGeometry.CreateSquircle(CompositionGenerator.Instance.Device, (float)X, (float)Y, (float)Width, (float)Height, (float)RadiusX, (float)RadiusY); + + RaiseUpdatedEvent(); + } + } +} \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasStroke.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasStroke.cs index 8704bbb3be4..2b2655cd39b 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasStroke.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CanvasStroke.cs @@ -40,6 +40,16 @@ public Matrix3x2 Transform set => SetTransform(value); } + /// + /// Initializes a new instance of the class. + /// + public CanvasStroke() + { + Brush = null; + Width = 0; + Style = new CanvasStrokeStyle(); + } + /// /// Initializes a new instance of the class. /// @@ -109,4 +119,4 @@ private Matrix3x2 GetTransform() return Brush?.Transform ?? Matrix3x2.Identity; } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CompositorGeometryExtensions.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CompositorGeometryExtensions.cs index 1f6f0f8f518..c06592cbbb9 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CompositorGeometryExtensions.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CompositorGeometryExtensions.cs @@ -93,4 +93,4 @@ public static CompositionGeometricClip CreateGeometricClip(this Compositor compo return compositor.CreateGeometricClip(geometry); } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Core/CanvasRoundRect.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Core/CanvasRoundRect.cs index 5dae1483a6f..44a1bc2b3a5 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Core/CanvasRoundRect.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Core/CanvasRoundRect.cs @@ -5,7 +5,7 @@ using System; using System.Numerics; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Structure which encapsulates the details of each of the core points of the path of the rounded rectangle which is calculated based on @@ -277,4 +277,4 @@ private void ComputeCoordinates(float originX, float originY) TopLeftY = topLeftY + originY; } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Core/GeometryTypeDefinitions.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Core/GeometryTypeDefinitions.cs index c50c8625f98..c7d4e63b36c 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Core/GeometryTypeDefinitions.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Core/GeometryTypeDefinitions.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Enum for the various PathFigures. @@ -56,4 +56,4 @@ internal enum GradientStopAttributeType MainHdr, AdditionalHdr } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Core/PathElementFactory.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Core/PathElementFactory.cs index 16c911f1f3a..aadd11a4311 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Core/PathElementFactory.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Core/PathElementFactory.cs @@ -4,9 +4,8 @@ using System; using System.Text.RegularExpressions; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Factory class to instantiate various PathElements. @@ -154,4 +153,4 @@ private static ICanvasPathElement CreatePathElement(PathElementType elementType) }; } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Core/RegexFactory.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Core/RegexFactory.cs index 8ee81f4bad4..92406b0611d 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Core/RegexFactory.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Core/RegexFactory.cs @@ -8,7 +8,7 @@ [assembly: InternalsVisibleTo("UnitTests.UWP")] -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Contains all the Regular Expressions which are used for parsing the Win2d Path Mini Language. @@ -599,4 +599,4 @@ internal static Regex GetAttributesRegex(GradientStopAttributeType gsAttrType) return GradientStopAttributeRegexes[gsAttrType]; } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CultureShield.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CultureShield.cs index 763e34019bf..702cd9d2c51 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CultureShield.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/CultureShield.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Globalization; namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry @@ -56,4 +55,4 @@ public void Dispose() CultureInfo.CurrentCulture = _prevCulture; } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Brush/AbstractCanvasBrushElement.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Brush/AbstractCanvasBrushElement.cs index 46d1fd086d3..f38c958faff 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Brush/AbstractCanvasBrushElement.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Brush/AbstractCanvasBrushElement.cs @@ -5,9 +5,8 @@ using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas; using Microsoft.Graphics.Canvas.Brushes; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Brush +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Abstract base class for all Brush Elements @@ -69,4 +68,4 @@ public virtual void Initialize(Capture capture) /// Match object protected abstract void GetAttributes(Match match); } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Brush/ICanvasBrushElement.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Brush/ICanvasBrushElement.cs index 75c34abcb8f..f51a2633df5 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Brush/ICanvasBrushElement.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Brush/ICanvasBrushElement.cs @@ -6,7 +6,7 @@ using Microsoft.Graphics.Canvas; using Microsoft.Graphics.Canvas.Brushes; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Brush +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Interface for a Brush Element @@ -37,4 +37,4 @@ internal interface ICanvasBrushElement /// ICanvasBrush ICanvasBrush CreateBrush(ICanvasResourceCreator resourceCreator); } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Brush/LinearGradientBrushElement.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Brush/LinearGradientBrushElement.cs index b27fe460aff..00249484980 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Brush/LinearGradientBrushElement.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Brush/LinearGradientBrushElement.cs @@ -9,11 +9,9 @@ using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas; using Microsoft.Graphics.Canvas.Brushes; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers; using Windows.UI; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Brush +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Represents a CanvasLinearGradientBrush with GradientStops @@ -203,4 +201,4 @@ protected override void GetAttributes(Match match) } } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Brush/LinearGradientHdrBrushElement.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Brush/LinearGradientHdrBrushElement.cs index 49a1eef4553..19c3d98a1f2 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Brush/LinearGradientHdrBrushElement.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Brush/LinearGradientHdrBrushElement.cs @@ -9,9 +9,8 @@ using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas; using Microsoft.Graphics.Canvas.Brushes; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Brush +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Represents a CanvasLinearGradientBrush with GradientStopHdrs @@ -209,4 +208,4 @@ protected override void GetAttributes(Match match) } } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Brush/RadialGradientBrushElement.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Brush/RadialGradientBrushElement.cs index 728d0782177..190e305645d 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Brush/RadialGradientBrushElement.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Brush/RadialGradientBrushElement.cs @@ -9,11 +9,9 @@ using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas; using Microsoft.Graphics.Canvas.Brushes; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers; using Windows.UI; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Brush +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Represents a CanvasRadialGradientBrush with GradientStops @@ -227,4 +225,4 @@ protected override void GetAttributes(Match match) } } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Brush/RadialGradientHdrBrushElement.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Brush/RadialGradientHdrBrushElement.cs index 8ee430428e6..0b947f155ec 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Brush/RadialGradientHdrBrushElement.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Brush/RadialGradientHdrBrushElement.cs @@ -9,9 +9,8 @@ using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas; using Microsoft.Graphics.Canvas.Brushes; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Brush +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Represents a CanvasRadialGradientBrush with GradientStopHdrs @@ -234,4 +233,4 @@ protected override void GetAttributes(Match match) } } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Brush/SolidColorBrushElement.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Brush/SolidColorBrushElement.cs index 078b4523836..feeedc9fa6f 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Brush/SolidColorBrushElement.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Brush/SolidColorBrushElement.cs @@ -5,11 +5,9 @@ using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas; using Microsoft.Graphics.Canvas.Brushes; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers; using Windows.UI; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Brush +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Represents a CanvasSolidColorBrush @@ -70,4 +68,4 @@ protected override void GetAttributes(Match match) } } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/AbstractPathElement.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/AbstractPathElement.cs index 0a1f9bb6de6..02951aa6836 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/AbstractPathElement.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/AbstractPathElement.cs @@ -5,9 +5,8 @@ using System.Numerics; using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas.Geometry; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Abstract base class for all Path Elements @@ -104,4 +103,4 @@ public virtual void InitializeAdditional(Capture capture, int index, bool isRela /// Match object protected abstract void GetAttributes(Match match); } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/ArcElement.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/ArcElement.cs index 984aa224591..ef9f4cc8951 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/ArcElement.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/ArcElement.cs @@ -6,9 +6,8 @@ using System.Numerics; using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas.Geometry; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Class representing the Arc Element in a Path Geometry @@ -98,4 +97,4 @@ protected override void GetAttributes(Match match) float.TryParse(match.Groups["Y"].Value, out _y); } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/CanvasEllipseFigure.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/CanvasEllipseFigure.cs index 9868ae2eddc..a4f769f3414 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/CanvasEllipseFigure.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/CanvasEllipseFigure.cs @@ -6,9 +6,8 @@ using System.Numerics; using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas.Geometry; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Class representing the Ellipse Figure in a Path Geometry @@ -80,4 +79,4 @@ protected override void GetAttributes(Match match) float.TryParse(match.Groups["Y"].Value, out _y); } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/CanvasPathFigure.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/CanvasPathFigure.cs index 8c38c6a5a54..40b63b19b0d 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/CanvasPathFigure.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/CanvasPathFigure.cs @@ -8,9 +8,8 @@ using System.Numerics; using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas.Geometry; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Class which contains a collection of ICanvasPathElements @@ -133,4 +132,4 @@ protected override void GetAttributes(Match match) // Do nothing } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/CanvasPolygonFigure.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/CanvasPolygonFigure.cs index 4bf4cc8184f..c448d49688c 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/CanvasPolygonFigure.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/CanvasPolygonFigure.cs @@ -6,9 +6,9 @@ using System.Numerics; using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas.Geometry; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; +using Microsoft.Toolkit.Uwp.UI.Media.Geometry; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Class representing the Polygon Figure in a Path Geometry @@ -82,4 +82,4 @@ protected override void GetAttributes(Match match) float.TryParse(match.Groups["Y"].Value, out _y); } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/CanvasRectangleFigure.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/CanvasRectangleFigure.cs index 883ec7f847e..c45569e3f32 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/CanvasRectangleFigure.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/CanvasRectangleFigure.cs @@ -6,9 +6,9 @@ using System.Numerics; using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas.Geometry; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; +using Microsoft.Toolkit.Uwp.UI.Media.Geometry; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Class representing the Rectangle Figure in a Path Geometry @@ -87,4 +87,4 @@ protected override void GetAttributes(Match match) _height = Math.Abs(_height); } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/CanvasRoundRectangleFigure.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/CanvasRoundRectangleFigure.cs index 3a0d7a1193c..7e8e1e5b4ec 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/CanvasRoundRectangleFigure.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/CanvasRoundRectangleFigure.cs @@ -6,9 +6,9 @@ using System.Numerics; using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas.Geometry; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; +using Microsoft.Toolkit.Uwp.UI.Media.Geometry; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Class representing the RoundRectangle Figure in a Path Geometry @@ -101,4 +101,4 @@ protected override void GetAttributes(Match match) _radiusY = Math.Abs(_radiusY); } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/ClosePathElement.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/ClosePathElement.cs index e66c387e4cb..bcbf2fb938e 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/ClosePathElement.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/ClosePathElement.cs @@ -5,9 +5,8 @@ using System.Numerics; using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas.Geometry; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Class representing the ClosePath command in a Path Geometry @@ -98,4 +97,4 @@ protected override void GetAttributes(Match match) // Do Nothing } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/CubicBezierElement.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/CubicBezierElement.cs index d4113e8f76b..708520b95eb 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/CubicBezierElement.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/CubicBezierElement.cs @@ -5,9 +5,8 @@ using System.Numerics; using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas.Geometry; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Class representing the Cubic Bezier Element in a Path Geometry @@ -102,4 +101,4 @@ protected override void GetAttributes(Match match) float.TryParse(match.Groups["Y"].Value, out _y); } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/FillRuleElement.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/FillRuleElement.cs index 6d7948bf4a1..40d7c332501 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/FillRuleElement.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/FillRuleElement.cs @@ -7,7 +7,7 @@ using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas.Geometry; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Class representing the Fill Rule Element in a Path Geometry @@ -75,4 +75,4 @@ protected override void GetAttributes(Match match) Enum.TryParse(match.Groups["FillValue"].Value, out _fillValue); } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/HorizontalLineElement.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/HorizontalLineElement.cs index bbd3201ff9f..4dec26cacb4 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/HorizontalLineElement.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/HorizontalLineElement.cs @@ -5,9 +5,8 @@ using System.Numerics; using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas.Geometry; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Class representing the Horizontal Line Element in a Path Geometry @@ -66,4 +65,4 @@ protected override void GetAttributes(Match match) float.TryParse(match.Groups["X"].Value, out _x); } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/ICanvasPathElement.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/ICanvasPathElement.cs index 2bee7f24960..f9cfcd283fd 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/ICanvasPathElement.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/ICanvasPathElement.cs @@ -3,11 +3,10 @@ // See the LICENSE file in the project root for more information. using System.Numerics; -using System.Text; using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas.Geometry; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Interface for a Path Element which serves @@ -62,4 +61,4 @@ internal interface ICanvasPathElement /// The current point on the path after the path element is added Vector2 CreatePath(CanvasPathBuilder pathBuilder, Vector2 currentPoint, ref ICanvasPathElement lastElement); } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/LineElement.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/LineElement.cs index 2f52bbce991..00e97dd3c09 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/LineElement.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/LineElement.cs @@ -5,9 +5,8 @@ using System.Numerics; using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas.Geometry; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Class representing the Line Element in a Path Geometry @@ -71,4 +70,4 @@ protected override void GetAttributes(Match match) float.TryParse(match.Groups["Y"].Value, out _y); } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/MoveToElement.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/MoveToElement.cs index 73aa61b4571..64feb9d1721 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/MoveToElement.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/MoveToElement.cs @@ -5,9 +5,8 @@ using System.Numerics; using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas.Geometry; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Class representing the MoveTo Command in a Path Geometry @@ -84,4 +83,4 @@ protected override void GetAttributes(Match match) float.TryParse(match.Groups["Y"].Value, out _y); } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/QuadraticBezierElement.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/QuadraticBezierElement.cs index 3dd5ce32bc8..46ea0a89915 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/QuadraticBezierElement.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/QuadraticBezierElement.cs @@ -5,9 +5,8 @@ using System.Numerics; using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas.Geometry; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Class representing the Quadratic Bezier Element in a Path Geometry @@ -94,4 +93,4 @@ protected override void GetAttributes(Match match) float.TryParse(match.Groups["Y"].Value, out _y); } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/SmoothCubicBezierElement.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/SmoothCubicBezierElement.cs index d04112accae..1c16ec40fc5 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/SmoothCubicBezierElement.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/SmoothCubicBezierElement.cs @@ -5,9 +5,9 @@ using System.Numerics; using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas.Geometry; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; +using Microsoft.Toolkit.Uwp.UI.Media.Geometry; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Class representing the Smooth Cubic Bezier Element in a Path Geometry @@ -114,4 +114,4 @@ protected override void GetAttributes(Match match) float.TryParse(match.Groups["Y"].Value, out _y); } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/SmoothQuadraticBezierElement.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/SmoothQuadraticBezierElement.cs index 538b2997b6f..3d61b4fba57 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/SmoothQuadraticBezierElement.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/SmoothQuadraticBezierElement.cs @@ -5,9 +5,9 @@ using System.Numerics; using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas.Geometry; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; +using Microsoft.Toolkit.Uwp.UI.Media.Geometry; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Class representing the Smooth Quadratic Bezier Element in a Path Geometry @@ -103,4 +103,4 @@ protected override void GetAttributes(Match match) float.TryParse(match.Groups["Y"].Value, out _y); } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/VerticalLineElement.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/VerticalLineElement.cs index 538bc570556..ea3efa3b923 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/VerticalLineElement.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Path/VerticalLineElement.cs @@ -5,9 +5,8 @@ using System.Numerics; using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas.Geometry; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Class representing the Vertical Line Element in a Path Geometry @@ -66,4 +65,4 @@ protected override void GetAttributes(Match match) float.TryParse(match.Groups["Y"].Value, out _y); } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Stroke/AbstractCanvasStrokeElement.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Stroke/AbstractCanvasStrokeElement.cs index fa086874b9d..ff933b4b956 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Stroke/AbstractCanvasStrokeElement.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Stroke/AbstractCanvasStrokeElement.cs @@ -4,9 +4,8 @@ using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Stroke +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Abstract base class for Stroke Element. @@ -62,4 +61,4 @@ protected virtual void Validate() /// Match object protected abstract void GetAttributes(Match match); } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Stroke/CanvasStrokeElement.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Stroke/CanvasStrokeElement.cs index e526074af53..cc20c7ab27a 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Stroke/CanvasStrokeElement.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Stroke/CanvasStrokeElement.cs @@ -5,11 +5,8 @@ using System; using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Brush; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Stroke +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Represents a Stroke Element. @@ -102,4 +99,4 @@ protected override void Validate() } } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Stroke/CanvasStrokeStyleElement.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Stroke/CanvasStrokeStyleElement.cs index 2780bc78732..aca39407bc2 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Stroke/CanvasStrokeStyleElement.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Stroke/CanvasStrokeStyleElement.cs @@ -7,9 +7,8 @@ using System.Linq; using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas.Geometry; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Stroke +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Represents a CanvasStrokeStyle Element. @@ -179,4 +178,4 @@ public void Initialize(Match match) } } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Stroke/ICanvasStrokeElement.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Stroke/ICanvasStrokeElement.cs index 92029aaa7aa..c6d99fbd8d2 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Stroke/ICanvasStrokeElement.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Stroke/ICanvasStrokeElement.cs @@ -5,7 +5,7 @@ using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Stroke +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Interface for Stroke Element @@ -34,4 +34,4 @@ internal interface ICanvasStrokeElement /// ICanvasStroke ICanvasStroke CreateStroke(ICanvasResourceCreator resourceCreator); } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Stroke/ICanvasStrokeStyleElement.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Stroke/ICanvasStrokeStyleElement.cs index ec1fca39225..71e25056cc0 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Stroke/ICanvasStrokeStyleElement.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Elements/Stroke/ICanvasStrokeStyleElement.cs @@ -5,7 +5,7 @@ using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas.Geometry; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Stroke +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Interface for the CanvasStrokeStyle Element @@ -33,4 +33,4 @@ internal interface ICanvasStrokeStyleElement /// Match object void Initialize(Match match); } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/ICanvasPathGeometry.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/ICanvasPathGeometry.cs new file mode 100644 index 00000000000..899096159f5 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/ICanvasPathGeometry.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Graphics.Canvas.Geometry; + +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry +{ + internal interface ICanvasPathGeometry + { + /// + /// Gets the associated . + /// + CanvasGeometry Geometry { get; } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/ICanvasStroke.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/ICanvasStroke.cs index 9c9a0b7f41f..74be76ca1ab 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/ICanvasStroke.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/ICanvasStroke.cs @@ -5,8 +5,9 @@ using System.Numerics; using Microsoft.Graphics.Canvas.Brushes; using Microsoft.Graphics.Canvas.Geometry; +using Microsoft.Toolkit.Uwp.UI.Media.Geometry; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry +namespace Microsoft.Toolkit.Uwp.UI.Media { /// /// Interface to represent the Stroke which can be used to render an outline on a . @@ -33,4 +34,4 @@ public interface ICanvasStroke /// Matrix3x2 Transform { get; set; } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Parsers/CanvasBrushParser.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Parsers/CanvasBrushParser.cs index 2a66728c9bc..b4d478c75ba 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Parsers/CanvasBrushParser.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Parsers/CanvasBrushParser.cs @@ -6,10 +6,8 @@ using System.Runtime.CompilerServices; using Microsoft.Graphics.Canvas; using Microsoft.Graphics.Canvas.Brushes; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Brush; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Parser for ICanvasBrush. @@ -119,4 +117,4 @@ internal static ICanvasBrush Parse(ICanvasResourceCreator resourceCreator, strin return brushElement.CreateBrush(resourceCreator); } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Parsers/CanvasGeometryParser.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Parsers/CanvasGeometryParser.cs index 76780d1e334..50c912e8757 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Parsers/CanvasGeometryParser.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Parsers/CanvasGeometryParser.cs @@ -10,10 +10,8 @@ using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas; using Microsoft.Graphics.Canvas.Geometry; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Parser for CanvasGeometry. @@ -66,7 +64,7 @@ internal static CanvasGeometry Parse(ICanvasResourceCreator resourceCreator, str // Process the 'Additional' Group which contains just the attributes figures.AddRange(from Capture capture in figureMatch.Groups["Additional"].Captures - select PathElementFactory.CreateAdditionalPathFigure(type, capture, figureRootIndex + capture.Index, figure.IsRelative)); + select PathElementFactory.CreateAdditionalPathFigure(type, capture, figureRootIndex + capture.Index, figure.IsRelative)); } } @@ -128,10 +126,11 @@ static void ThrowForInvalidCharacters(List pathFigures, stri return CanvasGeometry.CreatePath(pathBuilder); static void ThrowForZeroCount() => throw new ArgumentException("PATH_ERR000:Invalid Path data! No matching path data found!"); + static void ThrowForNotOneCount() => throw new ArgumentException("PATH_ERR001:Multiple FillRule elements present in Path Data!\n" + "There should be only one FillRule within the Path Data. " + "You can either remove additional FillRule elements or split the Path Data " + "into multiple Path Data and call the CanvasPathGeometry.CreateGeometry() method on each of them."); } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Parsers/CanvasStrokeParser.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Parsers/CanvasStrokeParser.cs index e7c2fea9026..f9102b086a4 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Parsers/CanvasStrokeParser.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Parsers/CanvasStrokeParser.cs @@ -5,10 +5,8 @@ using System; using System.Runtime.CompilerServices; using Microsoft.Graphics.Canvas; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Stroke; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Parser for CanvasStroke @@ -95,4 +93,4 @@ internal static ICanvasStroke Parse(ICanvasResourceCreator resourceCreator, stri return strokeElement.CreateStroke(resourceCreator); } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Parsers/CanvasStrokeStyleParser.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Parsers/CanvasStrokeStyleParser.cs index 25bce614f4e..54b4be50608 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Parsers/CanvasStrokeStyleParser.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Parsers/CanvasStrokeStyleParser.cs @@ -6,10 +6,8 @@ using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using Microsoft.Graphics.Canvas.Geometry; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Stroke; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Parser for the CanvasStrokeStyle @@ -90,4 +88,4 @@ internal static ICanvasStrokeStyleElement Parse(Match match) return new CanvasStrokeStyleElement(match); } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Parsers/ColorParser.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Parsers/ColorParser.cs index e6bf1cb7192..a356fc9abed 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Parsers/ColorParser.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Parsers/ColorParser.cs @@ -6,10 +6,9 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Text.RegularExpressions; -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; using Windows.UI; -namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Parsers +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry { /// /// Parser for Color @@ -143,4 +142,4 @@ internal static Color Parse(Match match) return Colors.Transparent; } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Scalar.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Scalar.cs index 773b4f46744..6067794e1a8 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Scalar.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Scalar.cs @@ -61,4 +61,4 @@ internal static class Scalar /// internal const float RadiansToDegrees = 180f / Pi; } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/StrokeStyle.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/StrokeStyle.cs new file mode 100644 index 00000000000..c555501bbbe --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/StrokeStyle.cs @@ -0,0 +1,318 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Graphics.Canvas.Geometry; +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry +{ + /// + /// Class which defines various properties which govern how the stroke is rendered. + /// + public class StrokeStyle : DependencyObject, IDisposable + { + private bool _disposedValue; + private CanvasStrokeStyle _canvasStrokeStyle; + + /// + /// Event to notify that the properties of this class have been updated. + /// + public event EventHandler Updated; + + /// + /// CustomDashStyle Dependency Property + /// + public static readonly DependencyProperty CustomDashStyleProperty = DependencyProperty.Register( + "CustomDashStyle", + typeof(string), + typeof(StrokeStyle), + new PropertyMetadata(string.Empty, OnCustomDashStyleChanged)); + + /// + /// Gets or sets the an array describing a custom dash pattern. This overrides the DashStyle property, which is only used when CustomDashStyle is set to null. + /// A custom dash style is an array whose elements specify the length of each dash and space in the pattern. + /// The first element sets the length of a dash, the second element sets the length of a space, the third element sets the length of a dash, and so on. + /// The length of each dash and space in the dash pattern is the product of the element value in the array and the stroke width. + /// This array must contain an even number of elements. + /// If the dash style is configured to contain a dash which is zero-length, that dash will only be visible with a cap style other than Flat. + /// + public string CustomDashStyle + { + get => (string)GetValue(CustomDashStyleProperty); + set => SetValue(CustomDashStyleProperty, value); + } + + /// + /// Handles changes to the CustomDashStyle property. + /// + /// + /// DependencyProperty changed event arguments + private static void OnCustomDashStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var strokeStyle = (StrokeStyle)d; + strokeStyle.OnCustomDashStyleChanged(); + } + + /// + /// Instance handler for the changes to the CustomDashStyle dependency property. + /// + private void OnCustomDashStyleChanged() + { + var result = new List(); + + if (!string.IsNullOrWhiteSpace(CustomDashStyle)) + { + var arr = CustomDashStyle.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + + if (arr.Any()) + { + if (arr.Length % 2 != 0) + { + throw new ArgumentException("CustomDashStyle must contain an even number of elements!"); + } + + foreach (var token in arr) + { + if (float.TryParse(token, out float num)) + { + result.Add(num); + } + else + { + throw new ArgumentException($"Invalid value! Cannot convert '{token}' to 'System.Single'!"); + } + } + } + } + + ParsedCustomDashStyle = result.Any() ? result.ToArray() : null; + + OnUpdated(); + } + + /// + /// Gets the custom dash style parsed from the input string. + /// + public float[] ParsedCustomDashStyle { get; private set; } = null; + + /// + /// DashCap Dependency Property + /// + public static readonly DependencyProperty DashCapProperty = DependencyProperty.Register( + "DashCap", + typeof(CanvasCapStyle), + typeof(StrokeStyle), + new PropertyMetadata(CanvasCapStyle.Square, OnPropertyChanged)); + + /// + /// Gets or sets how the ends of each dash are drawn. Defaults to Square. + /// If this is set to Flat, dots will have zero size so only dashes are visible. + /// + public CanvasCapStyle DashCap + { + get => (CanvasCapStyle)GetValue(DashCapProperty); + set => SetValue(DashCapProperty, value); + } + + /// + /// DashOffset Dependency Property + /// + public static readonly DependencyProperty DashOffsetProperty = DependencyProperty.Register( + "DashOffset", + typeof(double), + typeof(StrokeStyle), + new PropertyMetadata(0d, OnPropertyChanged)); + + /// + /// Gets or sets how far into the dash sequence the stroke will start. + /// + public double DashOffset + { + get => (double)GetValue(DashOffsetProperty); + set => SetValue(DashOffsetProperty, value); + } + + /// + /// DashStyle Dependency Property + /// + public static readonly DependencyProperty DashStyleProperty = DependencyProperty.Register( + "DashStyle", + typeof(CanvasDashStyle), + typeof(StrokeStyle), + new PropertyMetadata(CanvasDashStyle.Solid, OnPropertyChanged)); + + /// + /// Gets or sets the stroke's dash pattern. This is ignored if CustomDashStyle has been set. + /// + public CanvasDashStyle DashStyle + { + get => (CanvasDashStyle)GetValue(DashStyleProperty); + set => SetValue(DashStyleProperty, value); + } + + /// + /// EndCap Dependency Property + /// + public static readonly DependencyProperty EndCapProperty = DependencyProperty.Register( + "EndCap", + typeof(CanvasCapStyle), + typeof(StrokeStyle), + new PropertyMetadata(CanvasCapStyle.Flat, OnPropertyChanged)); + + /// + /// Gets or sets the type of shape used at the end of a stroke. Defaults to Flat. + /// + public CanvasCapStyle EndCap + { + get => (CanvasCapStyle)GetValue(EndCapProperty); + set => SetValue(EndCapProperty, value); + } + + /// + /// LineJoin Dependency Property + /// + public static readonly DependencyProperty LineJoinProperty = DependencyProperty.Register( + "LineJoin", + typeof(CanvasLineJoin), + typeof(StrokeStyle), + new PropertyMetadata(CanvasLineJoin.Miter, OnPropertyChanged)); + + /// + /// Gets or sets the type of joint used at the vertices of a shape's outline. + /// + public CanvasLineJoin LineJoin + { + get => (CanvasLineJoin)GetValue(LineJoinProperty); + set => SetValue(LineJoinProperty, value); + } + + /// + /// MiterLimit Dependency Property + /// + public static readonly DependencyProperty MiterLimitProperty = DependencyProperty.Register( + "MiterLimit", + typeof(double), + typeof(StrokeStyle), + new PropertyMetadata(10d, OnPropertyChanged)); + + /// + /// Gets or sets the limit on the ratio of the miter length to half the stroke's thickness. + /// + public double MiterLimit + { + get => (double)GetValue(MiterLimitProperty); + set => SetValue(MiterLimitProperty, value); + } + + /// + /// StartCap Dependency Property + /// + public static readonly DependencyProperty StartCapProperty = DependencyProperty.Register( + "StartCap", + typeof(CanvasCapStyle), + typeof(StrokeStyle), + new PropertyMetadata(CanvasCapStyle.Flat, OnPropertyChanged)); + + /// + /// Gets or sets the type of shape used at the beginning of a stroke. Defaults to Flat. + /// + public CanvasCapStyle StartCap + { + get => (CanvasCapStyle)GetValue(StartCapProperty); + set => SetValue(StartCapProperty, value); + } + + /// + /// TransformBehavior Dependency Property + /// + public static readonly DependencyProperty TransformBehaviorProperty = DependencyProperty.Register( + "TransformBehavior", + typeof(CanvasStrokeTransformBehavior), + typeof(StrokeStyle), + new PropertyMetadata(CanvasStrokeTransformBehavior.Normal, OnPropertyChanged)); + + /// + /// Gets or sets how the world transform, dots per inch (DPI), and stroke width affect the shape of the pen. + /// + public CanvasStrokeTransformBehavior TransformBehavior + { + get => (CanvasStrokeTransformBehavior)GetValue(TransformBehaviorProperty); + set => SetValue(TransformBehaviorProperty, value); + } + + /// + /// Method that is called whenever the dependency properties of the StrokeStyle changes + /// + /// The object whose property has changed + /// Event arguments + private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var strokeStyle = (StrokeStyle)d; + + // Recreate the canvas brush on any property change. + strokeStyle.OnUpdated(); + } + + private void OnUpdated() + { + _canvasStrokeStyle = new CanvasStrokeStyle() + { + CustomDashStyle = ParsedCustomDashStyle, + DashCap = DashCap, + DashOffset = (float)DashOffset, + DashStyle = DashStyle, + EndCap = EndCap, + LineJoin = LineJoin, + MiterLimit = (float)MiterLimit, + StartCap = StartCap, + TransformBehavior = TransformBehavior + }; + + Updated?.Invoke(this, null); + } + + /// + /// Initializes a new instance of the class. + /// + public StrokeStyle() + { + ParsedCustomDashStyle = new float[0]; + } + + private void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + // Dispose managed state (managed objects) + ParsedCustomDashStyle = null; + } + + // Free unmanaged resources (unmanaged objects), if any, and override finalizer + _disposedValue = true; + } + } + + /// + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + /// + /// Gets the CanvasStrokeStyle. + /// + /// + public CanvasStrokeStyle GetCanvasStrokeStyle() + { + return _canvasStrokeStyle; + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Utils.cs b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Utils.cs index b38e130e749..afbb05f730a 100644 --- a/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Utils.cs +++ b/Microsoft.Toolkit.Uwp.UI.Media/Geometry/Utils.cs @@ -4,7 +4,6 @@ using System; using System.Numerics; -using System.Runtime.InteropServices; using Windows.Foundation; using Windows.UI; using Windows.UI.Xaml; @@ -31,7 +30,7 @@ public static class Utils /// The first double to compare. /// The second double to compare. /// - /// bool - the result of the AreClose comparison. + /// bool - the result of the AreClose comparision. /// public static bool IsCloseTo(this double value1, double value2) { @@ -55,7 +54,7 @@ public static bool IsCloseTo(this double value1, double value2) /// The first double to compare. /// The second double to compare. /// - /// bool - the result of the LessThan comparison. + /// bool - the result of the LessThan comparision. /// public static bool IsLessThan(this double value1, double value2) { @@ -68,7 +67,7 @@ public static bool IsLessThan(this double value1, double value2) /// The first double to compare. /// The second double to compare. /// - /// bool - the result of the GreaterThan comparison. + /// bool - the result of the GreaterThan comparision. /// public static bool IsGreaterThan(this double value1, double value2) { @@ -81,7 +80,7 @@ public static bool IsGreaterThan(this double value1, double value2) /// /// The double to compare to 1. /// - /// bool - the result of the AreClose comparison. + /// bool - the result of the AreClose comparision. /// public static bool IsOne(this double value) { @@ -94,7 +93,7 @@ public static bool IsOne(this double value) /// /// The double to compare to 0. /// - /// bool - the result of the AreClose comparison. + /// bool - the result of the AreClose comparision. /// public static bool IsZero(this double value) { @@ -107,7 +106,7 @@ public static bool IsZero(this double value) /// The first float to compare. /// The second float to compare. /// - /// bool - the result of the AreClose comparison. + /// bool - the result of the AreClose comparision. /// public static bool IsCloseTo(this float value1, float value2) { @@ -131,7 +130,7 @@ public static bool IsCloseTo(this float value1, float value2) /// The first float to compare. /// The second float to compare. /// - /// bool - the result of the LessThan comparison. + /// bool - the result of the LessThan comparision. /// public static bool IsLessThan(this float value1, float value2) { @@ -144,7 +143,7 @@ public static bool IsLessThan(this float value1, float value2) /// The first float to compare. /// The second float to compare. /// - /// bool - the result of the GreaterThan comparison. + /// bool - the result of the GreaterThan comparision. /// public static bool IsGreaterThan(this float value1, float value2) { @@ -157,7 +156,7 @@ public static bool IsGreaterThan(this float value1, float value2) /// /// The float to compare to 1. /// - /// bool - the result of the AreClose comparison. + /// bool - the result of the AreClose comparision. /// public static bool IsOne(this float value) { @@ -170,7 +169,7 @@ public static bool IsOne(this float value) /// /// The float to compare to 0. /// - /// bool - the result of the AreClose comparison. + /// bool - the result of the AreClose comparision. /// public static bool IsZero(this float value) { @@ -813,5 +812,40 @@ public static Rect GetOptimumSize(double srcWidth, double srcHeight, double dest return new Rect(left, top, targetWidth, targetHeight); } + + /// + /// Converts to + /// + /// + /// + public static Matrix3x2 ToMatrix3x2(this MatrixTransform transform) + { + return new Matrix3x2( + (float)transform.Matrix.M11, + (float)transform.Matrix.M12, + (float)transform.Matrix.M21, + (float)transform.Matrix.M22, + (float)transform.Matrix.OffsetX, + (float)transform.Matrix.OffsetY); + } + + /// + /// Converts to + /// + /// + /// + public static MatrixTransform ToMatrixTransform(this Matrix3x2 matrix) + { + return new MatrixTransform() + { + Matrix = new Matrix( + matrix.M11, + matrix.M12, + matrix.M21, + matrix.M22, + matrix.M31, + matrix.M32) + }; + } } -} \ No newline at end of file +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Surface/CompositionGenerator.cs b/Microsoft.Toolkit.Uwp.UI.Media/Surface/CompositionGenerator.cs new file mode 100644 index 00000000000..25fc0e5d8ce --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Surface/CompositionGenerator.cs @@ -0,0 +1,1147 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Linq; +using System.Numerics; +using System.Threading.Tasks; +using Microsoft.Graphics.Canvas; +using Microsoft.Graphics.Canvas.Brushes; +using Microsoft.Graphics.Canvas.Effects; +using Microsoft.Graphics.Canvas.Geometry; +using Microsoft.Graphics.Canvas.UI.Composition; +using Microsoft.Toolkit.Uwp.UI.Media.Geometry; +using Windows.Foundation; +using Windows.Graphics.DirectX; +using Windows.UI; +using Windows.UI.Composition; +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.UI.Media +{ + /// + /// Core class which is used to create various RenderSurfaces like GeometrySurface, GeometryMaskSurface, GaussianMaskSurface, ImageSurface, ImageMaskSurface + /// + public sealed class CompositionGenerator : ICompositionGeneratorInternal + { + /// + /// Device Replaced event + /// + public event EventHandler DeviceReplaced; + + private static Lazy _instance = new(() => new CompositionGenerator(), System.Threading.LazyThreadSafetyMode.PublicationOnly); + + private readonly object _disposingLock; + private bool _useSharedCanvasDevice; + private CompositionGraphicsDevice _compositionDevice; + + /// + /// Gets the instance. + /// + public static ICompositionGenerator Instance => _instance.Value; + + /// + public Compositor Compositor { get; private set; } + + /// + public CanvasDevice Device { get; private set; } + + /// + /// Initializes a new instance of the class. + /// Constructor + /// + private CompositionGenerator() + { + Compositor = Window.Current?.Compositor; + if (Compositor == null) + { + // Since the compositor is null, we cannot proceed with the initialization of the CompositionGenerator. Therefore, throw an exception so that the value of + // the GeneratorInstance.IsValueCreated property remains false, and subsequent calls to the GeneratorInstance.Value property, either by the thread where the exception was + // thrown or by other threads, cause the initialization method to run again. + throw new ArgumentException("Cannot instantiate CompositionGenerator. Window.Current.Compositor is null."); + } + + // Disposing Lock + _disposingLock = new object(); + + InitializeDevices(); + } + + /// + /// Renders the on the based on the given options. + /// + /// The object to lock to prevent multiple threads from accessing the surface at the same time. + /// on which the CanvasBitmap has to be rendered. + /// created by loading the image from the . + /// Describes the image's resize and alignment options in the allocated space. + private static void RenderBitmap( + object surfaceLock, + CompositionDrawingSurface surface, + CanvasBitmap canvasBitmap, + ImageSurfaceOptions options) + { + var surfaceSize = surface.Size; + + // If the canvasBitmap is null, then just fill the surface with the SurfaceBackgroundColor + if (canvasBitmap == null) + { + // No need to render if the width and/or height of the surface is zero + if (surfaceSize.IsEmpty || surfaceSize.Width.IsZero() || surfaceSize.Height.IsZero()) + { + return; + } + + // Since multiple threads could be trying to get access to the device/surface + // at the same time, we need to do any device/surface work under a lock. + lock (surfaceLock) + { + using var session = CanvasComposition.CreateDrawingSession(surface); + + // Clear the surface with the SurfaceBackgroundColor + session.Clear(options.SurfaceBackgroundColor); + + // No need to proceed further + return; + } + } + + // Since multiple threads could be trying to get access to the device/surface + // at the same time, we need to do any device/surface work under a lock. + lock (surfaceLock) + { + // Is AutoResize Enabled? + if (options.AutoResize) + { + // If AutoResize is allowed and the canvasBitmap size and surface size are + // not matching then resize the surface to match the canvasBitmap size. + // + // NOTE: HorizontalAlignment, Vertical Alignment and Stretch will be + // handled by the CompositionSurfaceBrush created using this surface. + if (canvasBitmap.Size != surfaceSize) + { + // Resize the surface + CanvasComposition.Resize(surface, canvasBitmap.Size); + surfaceSize = canvasBitmap.Size; + } + + // No need to render if the width and/or height of the surface is zero + if (surfaceSize.IsEmpty || surface.Size.Width.IsZero() || surface.Size.Height.IsZero()) + { + return; + } + + // Draw the image to the surface + using var session = CanvasComposition.CreateDrawingSession(surface); + + // Clear the surface with the SurfaceBackgroundColor + session.Clear(options.SurfaceBackgroundColor); + + // Render the image + session.DrawImage( + canvasBitmap, // CanvasBitmap + new Rect(0, 0, surfaceSize.Width, surfaceSize.Height), // Target Rectangle + new Rect(0, 0, canvasBitmap.Size.Width, canvasBitmap.Size.Height), // Source Rectangle + (float)options.Opacity, // Opacity + options.Interpolation); // Interpolation + } + else + { + // No need to render if the width and/or height of the surface is zero + if (surfaceSize.IsEmpty || surface.Size.Width.IsZero() || surface.Size.Height.IsZero()) + { + return; + } + + // Get the optimum size that can fit the surface + var targetRect = Utils.GetOptimumSize( + canvasBitmap.Size.Width, + canvasBitmap.Size.Height, + surfaceSize.Width, + surfaceSize.Height, + options.Stretch, + options.HorizontalAlignment, + options.VerticalAlignment); + + // Draw the image to the surface + using var session = CanvasComposition.CreateDrawingSession(surface); + + // Clear the surface with the SurfaceBackgroundColor + session.Clear(options.SurfaceBackgroundColor); + + // Render the image + session.DrawImage( + canvasBitmap, // CanvasBitmap + targetRect, // Target Rectangle + canvasBitmap.Bounds, // Source Rectangle + (float)options.Opacity, // Opacity + options.Interpolation); // Interpolation + } + } + } + + /// + /// Renders the mask using the 's alpha values on the based on the given options. + /// + /// + /// The object to lock to prevent multiple threads from accessing the surface at the same time. + /// on which the CanvasBitmap has to be rendered. + /// created by loading the image from the . + /// The padding between the IImageMaskSurface outer bounds and the bounds of the area where the mask, created from the loaded image's alpha values, should be rendered. + /// Describes the image's resize and alignment options in the allocated space. + private static void RenderBitmapMask( + CanvasDevice device, + object surfaceLock, + CompositionDrawingSurface surface, + CanvasBitmap canvasBitmap, + Thickness padding, + ImageSurfaceOptions options) + { + var surfaceSize = surface.Size; + + // If the canvasBitmap is null, then just fill the surface with transparent color + if (canvasBitmap == null) + { + // No need to render if the width and/or height of the surface is zero + if (surfaceSize.IsEmpty || surfaceSize.Width.IsZero() || surfaceSize.Height.IsZero()) + { + return; + } + + // Since multiple threads could be trying to get access to the device/surface + // at the same time, we need to do any device/surface work under a lock. + lock (surfaceLock) + { + using var session = CanvasComposition.CreateDrawingSession(surface); + + // Clear the surface with the transparent color + session.Clear(options.SurfaceBackgroundColor); + + // No need to proceed further + return; + } + } + + // Since multiple threads could be trying to get access to the device/surface + // at the same time, we need to do any device/surface work under a lock. + lock (surfaceLock) + { + // No need to render if the width and/or height of the surface is zero + if (surfaceSize.IsEmpty || surface.Size.Width.IsZero() || surface.Size.Height.IsZero()) + { + return; + } + + // Get the available size on the surface + var paddingSize = padding.CollapseThickness(); + var availableWidth = Math.Max(0, surfaceSize.Width - paddingSize.Width); + var availableHeight = Math.Max(0, surfaceSize.Height - paddingSize.Height); + + if (availableWidth.IsZero() || availableHeight.IsZero()) + { + return; + } + + // Get the optimum size that can fit the available size on the surface + var targetRect = Utils.GetOptimumSize( + canvasBitmap.Size.Width, + canvasBitmap.Size.Height, + availableWidth, + availableHeight, + options.Stretch, + options.HorizontalAlignment, + options.VerticalAlignment); + + // Add the padding to the targetRect + targetRect.X += padding.Left; + targetRect.Y += padding.Top; + + // Resize the image to the target size + var imageCmdList = new CanvasCommandList(device); + using (var ds = imageCmdList.CreateDrawingSession()) + { + ds.DrawImage(canvasBitmap, targetRect, canvasBitmap.Bounds, 1f, options.Interpolation); + } + + // Fill the entire surface with White + var surfaceBounds = new Rect(0, 0, (float)surfaceSize.Width, (float)surfaceSize.Height); + + var rectCmdList = new CanvasCommandList(device); + using (var ds = rectCmdList.CreateDrawingSession()) + { + ds.FillRectangle(surfaceBounds, Colors.White); + } + + // Create the mask using the image's alpha values + var alphaEffect = new AlphaMaskEffect + { + Source = rectCmdList, + AlphaMask = imageCmdList + }; + + // Apply Gaussian blur on the mask to create the final mask + var blurEffect = new GaussianBlurEffect + { + Source = alphaEffect, + BlurAmount = (float)options.BlurRadius + }; + + // Draw the final mask to the surface + using var session = CanvasComposition.CreateDrawingSession(surface); + + // Clear the surface with the SurfaceBackgroundColor + session.Clear(options.SurfaceBackgroundColor); + + // Render the mask + session.DrawImage( + blurEffect, // CanvasBitmap + surfaceBounds, // Target Rectangle + surfaceBounds, // Source Rectangle + (float)options.Opacity, // Opacity + options.Interpolation); // Interpolation + } + } + + /// + /// Reloads the and fields. + /// + /// Indicates whether the DeviceReplacedEvent should be raised. + private void InitializeDevices(bool raiseEvent = false) + { + lock (_disposingLock) + { + if (!(Device is null)) + { + Device.DeviceLost -= OnDeviceLost; + } + + if (!(_compositionDevice is null)) + { + _compositionDevice.RenderingDeviceReplaced -= OnRenderingDeviceReplaced; + } + + // Canvas Device + _useSharedCanvasDevice = true; + Device = CanvasDevice.GetSharedDevice(); + if (Device is null) + { + Device = new CanvasDevice(); + _useSharedCanvasDevice = false; + } + + // Composition Graphics Device + _compositionDevice = CanvasComposition.CreateCompositionGraphicsDevice(Compositor, Device); + + _compositionDevice.RenderingDeviceReplaced += OnRenderingDeviceReplaced; + + Device.DeviceLost += OnDeviceLost; + _compositionDevice.RenderingDeviceReplaced += OnRenderingDeviceReplaced; + + if (raiseEvent) + { + // Raise the device replaced event + RaiseDeviceReplacedEvent(); + } + } + } + + /// + /// Invokes when the current is lost and raises the DeviceReplacedEvent. + /// + private void OnDeviceLost(CanvasDevice sender, object args) + { + InitializeDevices(true); + } + + /// + /// Invokes when the current changes rendering device and raises the DeviceReplacedEvent. + /// + private void OnRenderingDeviceReplaced(CompositionGraphicsDevice sender, RenderingDeviceReplacedEventArgs args) + { + InitializeDevices(true); + } + + /// + /// Raises the DeviceReplacedEvent. + /// + private void RaiseDeviceReplacedEvent() + { + var deviceEvent = DeviceReplaced; + deviceEvent?.Invoke(this, new EventArgs()); + } + + /// + public IGeometryMaskSurface CreateGeometryMaskSurface() + { + return CreateGeometryMaskSurface(default, null, Vector2.Zero); + } + + /// + public IGeometryMaskSurface CreateGeometryMaskSurface(Size size, CanvasGeometry geometry) + { + return CreateGeometryMaskSurface(size, geometry, Vector2.Zero); + } + + /// + public IGeometryMaskSurface CreateGeometryMaskSurface(Size size, CanvasGeometry geometry, Vector2 offset) + { + // Initialize the mask + IGeometryMaskSurface mask = new GeometryMaskSurface(this, size, geometry, offset); + + // Render the mask + mask.Redraw(); + + return mask; + } + + /// + public IGaussianMaskSurface CreateGaussianMaskSurface() + { + return CreateGaussianMaskSurface(default, null, Vector2.Zero, 0); + } + + /// + public IGaussianMaskSurface CreateGaussianMaskSurface(Size size, CanvasGeometry geometry, Vector2 offset, float blurRadius) + { + // Initialize the mask + IGaussianMaskSurface gaussianMaskSurface = new GaussianMaskSurface(this, size, geometry, offset, blurRadius); + + // Render the mask + gaussianMaskSurface.Redraw(); + + return gaussianMaskSurface; + } + + /// + public IGeometrySurface CreateGeometrySurface() + { + // Initialize the geometrySurface + IGeometrySurface geometrySurface = new GeometrySurface(this, default, null, null, Colors.Transparent, Colors.Transparent); + + // Render the geometrySurface + geometrySurface.Redraw(); + + return geometrySurface; + } + + /// + public IGeometrySurface CreateGeometrySurface(Size size, CanvasGeometry geometry, ICanvasStroke stroke) + { + // Initialize the geometrySurface + IGeometrySurface geometrySurface = new GeometrySurface(this, size, geometry, stroke, Colors.Transparent, Colors.Transparent); + + // Render the geometrySurface + geometrySurface.Redraw(); + + return geometrySurface; + } + + /// + public IGeometrySurface CreateGeometrySurface(Size size, CanvasGeometry geometry, Color fillColor) + { + // Initialize the geometrySurface + IGeometrySurface geometrySurface = new GeometrySurface(this, size, geometry, null, fillColor, Colors.Transparent); + + // Render the geometrySurface + geometrySurface.Redraw(); + + return geometrySurface; + } + + /// + public IGeometrySurface CreateGeometrySurface(Size size, CanvasGeometry geometry, ICanvasStroke stroke, Color fillColor) + { + // Initialize the geometrySurface + IGeometrySurface geometrySurface = new GeometrySurface(this, size, geometry, stroke, fillColor, Colors.Transparent); + + // Render the geometrySurface + geometrySurface.Redraw(); + + return geometrySurface; + } + + /// + public IGeometrySurface CreateGeometrySurface(Size size, CanvasGeometry geometry, Color fillColor, Color backgroundColor) + { + // Initialize the geometrySurface + IGeometrySurface geometrySurface = new GeometrySurface(this, size, geometry, null, fillColor, backgroundColor); + + // Render the geometrySurface + geometrySurface.Redraw(); + + return geometrySurface; + } + + /// + public IGeometrySurface CreateGeometrySurface(Size size, CanvasGeometry geometry, ICanvasStroke stroke, Color fillColor, Color backgroundColor) + { + // Initialize the geometrySurface + IGeometrySurface geometrySurface = new GeometrySurface(this, size, geometry, stroke, fillColor, backgroundColor); + + // Render the geometrySurface + geometrySurface.Redraw(); + + return geometrySurface; + } + + /// + public IGeometrySurface CreateGeometrySurface(Size size, CanvasGeometry geometry, ICanvasBrush fillBrush) + { + // Create the background brush + var backgroundBrush = new CanvasSolidColorBrush(Device, Colors.Transparent); + + // Initialize the geometrySurface + IGeometrySurface geometrySurface = new GeometrySurface(this, size, geometry, null, fillBrush, backgroundBrush); + + // Render the geometrySurface + geometrySurface.Redraw(); + + return geometrySurface; + } + + /// + public IGeometrySurface CreateGeometrySurface(Size size, CanvasGeometry geometry, ICanvasStroke stroke, ICanvasBrush fillBrush) + { + // Create the background brush + var backgroundBrush = new CanvasSolidColorBrush(Device, Colors.Transparent); + + // Initialize the geometrySurface + IGeometrySurface geometrySurface = new GeometrySurface(this, size, geometry, stroke, fillBrush, backgroundBrush); + + // Render the geometrySurface + geometrySurface.Redraw(); + + return geometrySurface; + } + + /// + public IGeometrySurface CreateGeometrySurface(Size size, CanvasGeometry geometry, ICanvasBrush fillBrush, ICanvasBrush backgroundBrush) + { + // Initialize the geometrySurface + IGeometrySurface geometrySurface = new GeometrySurface(this, size, geometry, null, fillBrush, backgroundBrush); + + // Render the geometrySurface + geometrySurface.Redraw(); + + return geometrySurface; + } + + /// + public IGeometrySurface CreateGeometrySurface(Size size, CanvasGeometry geometry, ICanvasStroke stroke, ICanvasBrush fillBrush, ICanvasBrush backgroundBrush) + { + // Initialize the geometrySurface + IGeometrySurface geometrySurface = new GeometrySurface(this, size, geometry, stroke, fillBrush, backgroundBrush); + + // Render the geometrySurface + geometrySurface.Redraw(); + + return geometrySurface; + } + + /// + public IGeometrySurface CreateGeometrySurface(Size size, CanvasGeometry geometry, ICanvasBrush fillBrush, Color backgroundColor) + { + // Create the background brush + var backgroundBrush = new CanvasSolidColorBrush(Device, backgroundColor); + + // Initialize the geometrySurface + IGeometrySurface geometrySurface = new GeometrySurface(this, size, geometry, null, fillBrush, backgroundBrush); + + // Render the geometrySurface + geometrySurface.Redraw(); + + return geometrySurface; + } + + /// + public IGeometrySurface CreateGeometrySurface(Size size, CanvasGeometry geometry, ICanvasStroke stroke, ICanvasBrush fillBrush, Color backgroundColor) + { + // Create the background brush + var backgroundBrush = new CanvasSolidColorBrush(Device, backgroundColor); + + // Initialize the geometrySurface + IGeometrySurface geometrySurface = new GeometrySurface(this, size, geometry, stroke, fillBrush, backgroundBrush); + + // Render the geometrySurface + geometrySurface.Redraw(); + + return geometrySurface; + } + + /// + public IGeometrySurface CreateGeometrySurface(Size size, CanvasGeometry geometry, Color fillColor, ICanvasBrush backgroundBrush) + { + // Create the foreground brush + var foregroundBrush = new CanvasSolidColorBrush(Device, fillColor); + + // Initialize the geometrySurface + IGeometrySurface geometrySurface = new GeometrySurface(this, size, geometry, null, foregroundBrush, backgroundBrush); + + // Render the geometrySurface + geometrySurface.Redraw(); + + return geometrySurface; + } + + /// + public IGeometrySurface CreateGeometrySurface(Size size, CanvasGeometry geometry, ICanvasStroke stroke, Color fillColor, ICanvasBrush backgroundBrush) + { + // Create the foreground brush + var foregroundBrush = new CanvasSolidColorBrush(Device, fillColor); + + // Initialize the geometrySurface + IGeometrySurface geometrySurface = new GeometrySurface(this, size, geometry, stroke, foregroundBrush, backgroundBrush); + + // Render the geometrySurface + geometrySurface.Redraw(); + + return geometrySurface; + } + + /// + public async Task CreateImageSurfaceAsync(Uri uri, Size size, ImageSurfaceOptions options) + { + // Initialize the IImageSurface + var imageSurface = new ImageSurface(this, uri, size, options); + + // Render the image onto the surface + await imageSurface.RedrawAsync(); + + return imageSurface; + } + + /// + public IImageSurface CreateImageSurface(CanvasBitmap bitmap, Size size, ImageSurfaceOptions options) + { + // Create a new IImageSurface using the given imageSurface + var imageSurface = new ImageSurface(this, bitmap, size, options); + + // Render the image onto the surface + imageSurface.Redraw(); + + return imageSurface; + } + + /// + public IImageSurface CreateImageSurface(IImageSurface imageSurface) + { + if (imageSurface != null) + { + // Create a new IImageSurface using the given imageSurface + var newImageSurface = new ImageSurface(this, imageSurface.SurfaceBitmap, imageSurface.Size, imageSurface.Options); + + // Render the image onto the surface + newImageSurface.Redraw(); + + return newImageSurface; + } + + // return an empty ImageSurface + return CreateImageSurface(null, new Size(0, 0), ImageSurfaceOptions.Default); + } + + /// + public IImageMaskSurface CreateImageMaskSurface(IImageMaskSurface imageMaskSurface) + { + if (imageMaskSurface != null) + { + // Create a new IImageMaskSurface using the given imageMaskSurface + var newImageSurface = new ImageMaskSurface( + this, + imageMaskSurface.SurfaceBitmap, + imageMaskSurface.Size, + imageMaskSurface.MaskPadding, + imageMaskSurface.Options); + + // Render the image onto the surface + newImageSurface.Redraw(); + + return newImageSurface; + } + + // return an empty ImageSurface + return CreateImageMaskSurface(surfaceBitmap: null, new Size(0, 0), new Thickness(0), ImageSurfaceOptions.DefaultImageMaskOptions); + } + + /// + public IImageMaskSurface CreateImageMaskSurface(CanvasBitmap surfaceBitmap, Size size, Thickness padding, float blurRadius) + { + return CreateImageMaskSurface(surfaceBitmap, size, padding, ImageSurfaceOptions.GetDefaultImageMaskOptionsForBlur(blurRadius)); + } + + /// + public IImageMaskSurface CreateImageMaskSurface(CanvasBitmap surfaceBitmap, Size size, Thickness padding, ImageSurfaceOptions options) + { + var imageMaskSurface = new ImageMaskSurface(this, surfaceBitmap, size, padding, options); + + // Render the image onto the surface + imageMaskSurface.Redraw(); + + return imageMaskSurface; + } + + /// + public IImageMaskSurface CreateImageMaskSurface(IImageSurface imageSurface, Size size, Thickness padding, ImageSurfaceOptions options) + { + if (imageSurface != null) + { + // Create a new IImageSurface using the given imageSurface + return CreateImageMaskSurface(imageSurface.SurfaceBitmap, size, padding, options); + } + + // Create an empty ImageMaskSurface + return CreateImageMaskSurface(surfaceBitmap: null, size, padding, options); + } + + /// + public async Task CreateImageMaskSurfaceAsync(Uri uri, Size size, Thickness padding, ImageSurfaceOptions options) + { + var imageMaskSurface = new ImageMaskSurface(this, uri, size, padding, options); + + // Render the image onto the surface + await imageMaskSurface.RedrawAsync(); + + return imageMaskSurface; + } + + /// + public void CreateReflection(ContainerVisual visual, float reflectionDistance = 0f, float reflectionLength = 0.7f, ReflectionLocation location = ReflectionLocation.Bottom) + { + // Create the visual layer that will contained the visual's reflection + var reflectionLayer = Compositor.CreateLayerVisual(); + reflectionLayer.Size = visual.Size; + reflectionLayer.CenterPoint = new Vector3(visual.Size * 0.5f, 0); + + // Create the effect to create the opacity mask + var effect = new CompositeEffect + { + // CanvasComposite.DestinationIn - Intersection of source and mask. + // Equation: O = MA * S + // where O - Output pixel, MA - Mask Alpha, S - Source pixel. + Mode = CanvasComposite.DestinationIn, + Sources = + { + new CompositionEffectSourceParameter("source"), + new CompositionEffectSourceParameter("mask") + } + }; + + var effectFactory = Compositor.CreateEffectFactory(effect); + var effectBrush = effectFactory.CreateBrush(); + + // Create the gradient brush for the effect + var gradientBrush = new CanvasLinearGradientBrush(Device, Colors.White, Colors.Transparent); + + // Based on the reflection location, + // Set the Offset, RotationAxis and RotationAngleInDegrees of the reflectionLayer and + // set the StartPoint and EndPoint of the gradientBrush + switch (location) + { + case ReflectionLocation.Bottom: + reflectionLayer.RotationAxis = new Vector3(1, 0, 0); + reflectionLayer.RotationAngleInDegrees = 180; + reflectionLayer.Offset = new Vector3(0, visual.Size.Y + reflectionDistance, 0); + gradientBrush.StartPoint = new Vector2(visual.Size.X * 0.5f, 0); + gradientBrush.EndPoint = new Vector2(visual.Size.X * 0.5f, visual.Size.Y * reflectionLength); + break; + case ReflectionLocation.Top: + reflectionLayer.RotationAxis = new Vector3(1, 0, 0); + reflectionLayer.RotationAngleInDegrees = -180; + reflectionLayer.Offset = new Vector3(0, -visual.Size.Y - reflectionDistance, 0); + gradientBrush.StartPoint = new Vector2(visual.Size.X * 0.5f, visual.Size.Y); + gradientBrush.EndPoint = new Vector2(visual.Size.X * 0.5f, visual.Size.Y * (1f - reflectionLength)); + break; + case ReflectionLocation.Left: + reflectionLayer.RotationAxis = new Vector3(0, 1, 0); + reflectionLayer.RotationAngleInDegrees = -180; + reflectionLayer.Offset = new Vector3(-visual.Size.X - reflectionDistance, 0, 0); + gradientBrush.StartPoint = new Vector2(visual.Size.X, visual.Size.Y * 0.5f); + gradientBrush.EndPoint = new Vector2(visual.Size.X * (1f - reflectionLength), visual.Size.Y * 0.5f); + break; + case ReflectionLocation.Right: + reflectionLayer.RotationAxis = new Vector3(0, 1, 0); + reflectionLayer.RotationAngleInDegrees = 180; + reflectionLayer.Offset = new Vector3(visual.Size.X + reflectionDistance, 0, 0); + gradientBrush.StartPoint = new Vector2(0, visual.Size.Y * 0.5f); + gradientBrush.EndPoint = new Vector2(visual.Size.X * reflectionLength, visual.Size.Y * 0.5f); + break; + } + + // Create a mask filled with gradientBrush + var mask = CreateGeometrySurface(visual.Size.ToSize(), null, Colors.Transparent, gradientBrush); + + // Set the 'mask' parameter of the effectBrush + effectBrush.SetSourceParameter("mask", Compositor.CreateSurfaceBrush(mask.Surface)); + + // Set the effect for the reflection layer + reflectionLayer.Effect = effectBrush; + + // Now we need to duplicate the visual tree of the visual + ArrangeVisualReflection(visual, reflectionLayer, true); + + visual.Children.InsertAtTop(reflectionLayer); + } + + /// + public CompositionDrawingSurface CreateDrawingSurface(object surfaceLock, Size size) + { + var surfaceSize = size; + if (surfaceSize.IsEmpty) + { + // We start out with a size of 0,0 for the surface, because we don't know + // the size of the image at this time. We resize the surface later. + surfaceSize = new Size(0, 0); + } + + // Since multiple threads could be trying to get access to the device/surface + // at the same time, we need to do any device/surface work under a lock. + lock (surfaceLock) + { + return _compositionDevice.CreateDrawingSurface(surfaceSize, DirectXPixelFormat.B8G8R8A8UIntNormalized, DirectXAlphaMode.Premultiplied); + } + } + + /// + public void ResizeDrawingSurface(object surfaceLock, CompositionDrawingSurface surface, Size size) + { + // Cannot resize to Size.Empty. Will throw exception! + if (size.IsEmpty) + { + return; + } + + // Ensuring that the size contains positive values + size.Width = Math.Max(0, size.Width); + size.Height = Math.Max(0, size.Height); + + // Since multiple threads could be trying to get access to the device/surface + // at the same time, we need to do any device/surface work under a lock. + lock (surfaceLock) + { + CanvasComposition.Resize(surface, size); + } + } + + /// + public void RedrawGeometryMaskSurface(object surfaceLock, CompositionDrawingSurface surface, Size size, CanvasGeometry geometry, Vector2 offset) + { + // If the surface is not created, create it + surface ??= CreateDrawingSurface(surfaceLock, size); + + // No need to render if the width and/or height of the surface is zero + if (surface.Size.Width.IsZero() || surface.Size.Height.IsZero()) + { + return; + } + + // Since multiple threads could be trying to get access to the device/surface + // at the same time, we need to do any device/surface work under a lock. + lock (surfaceLock) + { + // Render the mask to the surface + using var session = CanvasComposition.CreateDrawingSession(surface); + session.Clear(Colors.Transparent); + if (geometry != null) + { + // If the geometry is not null then fill the geometry area at the given offset + // with White color. The rest of the area on the surface will be transparent. + // When this mask is applied to a visual, only the area that is white will be visible. + session.FillGeometry(geometry, offset, Colors.White); + } + else + { + // If the geometry is null, then the entire mask with a padding, provided by the offset, + // should be filled with White. If the color is White. + // When this mask is applied to a visual, only the area that is white will be visible. + var width = Math.Max(0, (float)size.Width - (2 * offset.X)); + var height = Math.Max(0, (float)size.Height - (2 * offset.Y)); + var maskRect = new Rect(offset.X, offset.Y, width, height); + session.FillRectangle(maskRect, Colors.White); + } + } + } + + /// + public void RedrawGaussianMaskSurface(object surfaceLock, CompositionDrawingSurface surface, Size size, CanvasGeometry geometry, Vector2 offset, float blurRadius) + { + // If the surface is not created, create it + surface ??= CreateDrawingSurface(surfaceLock, size); + + // No need to render if the width and/or height of the surface is zero + if (surface.Size.Width.IsZero() || surface.Size.Height.IsZero()) + { + return; + } + + // Since multiple threads could be trying to get access to the device/surface + // at the same time, we need to do any device/surface work under a lock. + lock (surfaceLock) + { + // Render the mask to the surface + using var session = CanvasComposition.CreateDrawingSession(surface); + var cl = new CanvasCommandList(Device); + using (var ds = cl.CreateDrawingSession()) + { + ds.Clear(Colors.Transparent); + if (geometry != null) + { + // If the geometry is not null then fill the geometry area with the White color at the specified offset. + // The rest of the area on the surface will be transparent. + // When this mask is applied to a visual, only the area that is white will be visible. + ds.FillGeometry(geometry, offset, Colors.White); + } + else + { + // If the geometry is null, then the entire mask with a padding, provided by the offset, + // should be filled with white color. + // When this mask is applied to a visual, only the area that is white will be visible. + var width = Math.Max(0, (float)size.Width - (2 * offset.X)); + var height = Math.Max(0, (float)size.Height - (2 * offset.Y)); + var maskRect = new Rect(offset.X, offset.Y, width, height); + ds.FillRectangle(maskRect, Colors.White); + } + } + + // Apply the Gaussian blur + var blurGeometry = new GaussianBlurEffect() + { + BlurAmount = blurRadius, + Source = cl, + BorderMode = EffectBorderMode.Soft, + Optimization = EffectOptimization.Quality + }; + + // Clear previously rendered mask (if any) + session.Clear(Colors.Transparent); + + // Render the mask + session.DrawImage(blurGeometry); + } + } + + /// + public void RedrawGeometrySurface(object surfaceLock, CompositionDrawingSurface surface, Size size, CanvasGeometry geometry, ICanvasStroke stroke, ICanvasBrush fillBrush, ICanvasBrush backgroundBrush) + { + // If the surface is not created, create it + surface ??= CreateDrawingSurface(surfaceLock, size); + + // No need to render if the width and/or height of the surface is zero + if (surface.Size.Width.IsZero() || surface.Size.Height.IsZero()) + { + return; + } + + // Since multiple threads could be trying to get access to the device/surface + // at the same time, we need to do any device/surface work under a lock. + lock (surfaceLock) + { + // Render the geometry to the surface + using var session = CanvasComposition.CreateDrawingSession(surface); + + // Clear the surface + session.Clear(Colors.Transparent); + + // First fill the background + if (backgroundBrush is CanvasSolidColorBrush brush) + { + // If the backgroundBrush is a SolidColorBrush then use the Clear() + // method to fill the surface with background color. It is faster. + // Clear the surface with the background color + session.Clear(brush.Color); + } + else + { + // Fill the surface with the background brush. + session.FillRectangle(0, 0, (float)size.Width, (float)size.Height, backgroundBrush); + } + + // If the geometry is not null then render the geometry + if (geometry != null) + { + // If there is a stroke, then scale back the geometry to fit the stroke in the + // surface. + if (stroke != null) + { + var scaleX = (float)((surface.Size.Width - stroke.Width) / surface.Size.Width); + var scaleY = (float)((surface.Size.Height - stroke.Width) / surface.Size.Height); + + geometry = geometry.Transform( + Matrix3x2.CreateScale(new Vector2(scaleX, scaleY), surface.Size.ToVector2() * 0.5f)); + } + + // If fillBrush is defined then fill the geometry area + if (fillBrush != null) + { + session.FillGeometry(geometry, fillBrush); + } + + // If stroke is defined then outline the geometry area + if (stroke != null) + { + session.DrawGeometry(geometry, stroke.Brush, stroke.Width, stroke.Style); + } + } + } + } + + /// + public void RedrawImageSurface(object surfaceLock, CompositionDrawingSurface surface, ImageSurfaceOptions options, CanvasBitmap canvasBitmap) + { + // Render the image to the surface + RenderBitmap(surfaceLock, surface, canvasBitmap, options); + } + + /// + public async Task RedrawImageSurfaceAsync(object surfaceLock, CompositionDrawingSurface surface, Uri uri, ImageSurfaceOptions options, CanvasBitmap canvasBitmap) + { + if ((canvasBitmap == null) && (uri != null)) + { + try + { + canvasBitmap = await CanvasBitmap.LoadAsync(Device, uri); + } + catch (Exception) + { + // Do nothing here as RenderBitmap method will fill the surface + // with options.SurfaceBackgroundColor as the image failed to load + // from Uri + } + } + + // Render the image to the surface + RenderBitmap(surfaceLock, surface, canvasBitmap, options); + + return canvasBitmap; + } + + /// + public void RedrawImageMaskSurface(object surfaceLock, CompositionDrawingSurface surface, Thickness padding, ImageSurfaceOptions options, CanvasBitmap surfaceBitmap) + { + // Render the image mask to the surface + RenderBitmapMask(Device, surfaceLock, surface, surfaceBitmap, padding, options); + } + + /// + public async Task RedrawImageMaskSurfaceAsync(object surfaceLock, CompositionDrawingSurface surface, Uri uri, Thickness padding, ImageSurfaceOptions options, CanvasBitmap surfaceBitmap) + { + if ((surfaceBitmap == null) && (uri != null)) + { + try + { + surfaceBitmap = await CanvasBitmap.LoadAsync(Device, uri); + } + catch (Exception) + { + // Do nothing here as RenderBitmap method will fill the surface with options.SurfaceBackgroundColor as the image failed to load from the provided Uri. + } + } + + // Render the image mask to the surface + RenderBitmapMask(Device, surfaceLock, surface, surfaceBitmap, padding, options); + + return surfaceBitmap; + } + + /// + public void Dispose() + { + lock (_disposingLock) + { + Compositor = null; + + if (Device != null) + { + // Only dispose the canvas device if we own the device. + if (!_useSharedCanvasDevice) + { + Device.Dispose(); + } + + Device = null; + } + + if (_compositionDevice != null) + { + _compositionDevice.RenderingDeviceReplaced -= OnRenderingDeviceReplaced; + + // Only dispose the composition graphics device if we own the device. + if (!_useSharedCanvasDevice) + { + _compositionDevice.Dispose(); + } + + _compositionDevice = null; + } + + System.Threading.Interlocked.Exchange(ref _instance, null); + } + } + + /// + /// Creates a duplicate of the visual tree of the given visual and arranges them within the reflectedParent. + /// + /// Visual whose visual tree has to be duplicated + /// Visual in which will host the duplicated visual tree + /// Flag to indicate whether the given visual is the root of the visual tree to be duplicated. + private void ArrangeVisualReflection(ContainerVisual visual, ContainerVisual reflectedParent, bool isRoot = false) + { + if (visual == null) + { + return; + } + + ContainerVisual reflectedVisual; + + if (visual is LayerVisual layerVisual) + { + reflectedVisual = Compositor.CreateLayerVisual(); + ((LayerVisual)reflectedVisual).Effect = layerVisual.Effect; + } + else if (visual is SpriteVisual spriteVisual) + { + reflectedVisual = Compositor.CreateSpriteVisual(); + ((SpriteVisual)reflectedVisual).Brush = spriteVisual.Brush; + ((SpriteVisual)reflectedVisual).Shadow = spriteVisual.Shadow; + } + else + { + reflectedVisual = Compositor.CreateContainerVisual(); + } + + // Copy the Visual properties + reflectedVisual.AnchorPoint = visual.AnchorPoint; + reflectedVisual.BackfaceVisibility = visual.BackfaceVisibility; + reflectedVisual.BorderMode = visual.BorderMode; + reflectedVisual.CenterPoint = visual.CenterPoint; + reflectedVisual.Clip = visual.Clip; + reflectedVisual.CompositeMode = visual.CompositeMode; + reflectedVisual.ImplicitAnimations = visual.ImplicitAnimations; + reflectedVisual.IsVisible = visual.IsVisible; + reflectedVisual.Offset = isRoot ? Vector3.One : visual.Offset; + reflectedVisual.Opacity = visual.Opacity; + reflectedVisual.Orientation = visual.Orientation; + reflectedVisual.RotationAngle = visual.RotationAngle; + reflectedVisual.RotationAngleInDegrees = visual.RotationAngleInDegrees; + reflectedVisual.RotationAxis = visual.RotationAxis; + reflectedVisual.Scale = visual.Scale; + reflectedVisual.Size = visual.Size; + reflectedVisual.TransformMatrix = visual.TransformMatrix; + + // Add the reflectedVisual to the reflectedParent's Children (at the Top) + reflectedParent.Children.InsertAtTop(reflectedVisual); + + if (!visual.Children.Any()) + { + return; + } + + // Iterate each of the visual's Children and add them to + // the reflectedVisual's Children (at the Top so that the + // correct order is obtained) + foreach (var child in visual.Children) + { + ArrangeVisualReflection((ContainerVisual)child, reflectedVisual); + } + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Surface/CompositorExtensions.cs b/Microsoft.Toolkit.Uwp.UI.Media/Surface/CompositorExtensions.cs new file mode 100644 index 00000000000..578344d6195 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Surface/CompositorExtensions.cs @@ -0,0 +1,387 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Microsoft.Graphics.Canvas; +using Microsoft.Graphics.Canvas.Effects; +using Windows.UI; +using Windows.UI.Composition; +using Windows.UI.Xaml.Media; + +namespace Microsoft.Toolkit.Uwp.UI.Media +{ + /// + /// Extension methods for + /// + public static class CompositorExtensions + { + /// + /// Creates the from the specified render surface. + /// + /// + /// An object deriving from , , , or + /// + public static CompositionSurfaceBrush CreateSurfaceBrush(this Compositor compositor, IRenderSurface renderSurface) + { + return compositor.CreateSurfaceBrush(renderSurface.Surface); + } + + /// + /// Creates a custom shaped Effect Brush using BackdropBrush and an . + /// + /// Compositor + /// . + /// Color to blend in the BackdropBrush. + /// Blur Amount of the BackdropBrush. + /// Backdrop Brush (optional). If not provided, then compositor creates it. + /// + public static CompositionEffectBrush CreateMaskedBackdropBrush(this Compositor compositor, IGeometryMaskSurface mask, Color blendColor, float blurAmount, CompositionBackdropBrush backdropBrush = null) + { + return CreateBackdropBrush(compositor, mask, blendColor, blurAmount, backdropBrush); + } + + /// + /// Creates a custom shaped Effect Brush using BackdropBrush and an . + /// + /// Compositor + /// + /// Color to blend in the BackdropBrush + /// Blur Amount of the Backdrop Brush + /// Backdrop Brush (optional). If not provided, then compositor creates it. + /// + public static CompositionEffectBrush CreateGaussianMaskedBackdropBrush(this Compositor compositor, IGaussianMaskSurface mask, Color blendColor, float blurRadius, CompositionBackdropBrush backdropBrush = null) + { + return CreateBackdropBrush(compositor, mask, blendColor, blurRadius, backdropBrush); + } + + /// + /// Creates a custom shaped Effect Brush using BackdropBrush and an or an . + /// + /// Compositor. + /// or . + /// Color to blend in the BackdropBrush. + /// Blur Amount of the Backdrop Brush. + /// Backdrop Brush (optional). If not provided, then compositor creates it. + /// CompositionEffectBrush + internal static CompositionEffectBrush CreateBackdropBrush(Compositor compositor, IRenderSurface mask, Color blendColor, float blurAmount, CompositionBackdropBrush backdropBrush = null) + { + // Blur Effect + var blurEffect = new GaussianBlurEffect() + { + Name = "Blur", + BlurAmount = blurAmount, + BorderMode = EffectBorderMode.Hard, + Optimization = EffectOptimization.Balanced, + Source = new CompositionEffectSourceParameter("backdrop"), + }; + + // Blend Effect + var blendEffect = new Graphics.Canvas.Effects.BlendEffect + { + Foreground = new ColorSourceEffect + { + Name = "Color", + Color = blendColor + }, + Background = blurEffect, + Mode = BlendEffectMode.Multiply + }; + + // Composite Effect + var effect = new CompositeEffect + { + Mode = CanvasComposite.DestinationIn, + Sources = + { + blendEffect, + new CompositionEffectSourceParameter("mask") + } + }; + + // Create Effect Factory + var factory = compositor.CreateEffectFactory(effect, new[] { $"Blur.{nameof(GaussianBlurEffect.BlurAmount)}", $"Color.{nameof(ColorSourceEffect.Color)}" }); + + // Create Effect Brush + var brush = factory.CreateBrush(); + + // Set the BackDropBrush + // If no backdrop brush is provided, create one + brush.SetSourceParameter("backdrop", backdropBrush ?? compositor.CreateBackdropBrush()); + + // Set the Mask + // Create SurfaceBrush from IGeometryMaskSurface + var maskBrush = compositor.CreateSurfaceBrush(mask.Surface); + brush.SetSourceParameter("mask", maskBrush); + + return brush; + } + + /// + /// Creates a custom shaped Frosted Glass Effect Brush using BackdropBrush and a . + /// + /// Compositor + /// + /// Color to blend in the BackdropBrush + /// Blur Amount of the Backdrop Brush + /// Backdrop Brush (optional). If not provided, then compositor creates it. + /// MultiplyAmount of the + /// Source1Amount of the + /// Source2Amount of the + /// + public static CompositionEffectBrush CreateFrostedGlassBrush( + this Compositor compositor, + IGeometryMaskSurface mask, + Color blendColor, + float blurAmount, + CompositionBackdropBrush backdropBrush = null, + float multiplyAmount = 0, + float colorAmount = 0.5f, + float backdropAmount = 0.5f) + { + // Create a frosty glass effect + var frostEffect = new GaussianBlurEffect + { + Name = "Blur", + BlurAmount = blurAmount, + BorderMode = EffectBorderMode.Hard, + Source = new ArithmeticCompositeEffect + { + Name = "Source", + MultiplyAmount = multiplyAmount, + Source1Amount = backdropAmount, + Source2Amount = colorAmount, + Source1 = new CompositionEffectSourceParameter("backdrop"), + Source2 = new ColorSourceEffect + { + Name = "BlendColor", + Color = blendColor + } + } + }; + + // Composite Effect + var effect = new CompositeEffect + { + Mode = CanvasComposite.DestinationIn, + Sources = + { + frostEffect, + new CompositionEffectSourceParameter("mask") + } + }; + + // Create Effect Factory + var factory = compositor.CreateEffectFactory(effect, new[] { $"Blur.{nameof(GaussianBlurEffect.BlurAmount)}", $"BlendColor.{nameof(ColorSourceEffect.Color)}" }); + + // Create Effect Brush + var brush = factory.CreateBrush(); + + // Set the BackDropBrush + // If no backdrop brush is provided, create one + brush.SetSourceParameter("backdrop", backdropBrush ?? compositor.CreateBackdropBrush()); + + // Set the Mask + // Create SurfaceBrush from CompositionMask + var maskBrush = compositor.CreateSurfaceBrush(mask.Surface); + brush.SetSourceParameter("mask", maskBrush); + + return brush; + } + + /// + /// Updates the 's Stretch and Alignment options. + /// + /// CompositionSurfaceBrush. + /// Stretch mode. + /// Horizontal Alignment. + /// Vertical Alignment. + /// The animation to use to update the horizontal alignment of the surface brush. + /// The animation to use to update the vertical alignment of the surface brush. + public static void UpdateSurfaceBrushOptions( + this CompositionSurfaceBrush surfaceBrush, + Stretch stretch, + AlignmentX alignX, + AlignmentY alignY, + ScalarKeyFrameAnimation alignXAnimation = null, + ScalarKeyFrameAnimation alignYAnimation = null) + { + // Stretch Mode + surfaceBrush.Stretch = stretch switch + { + Stretch.None => CompositionStretch.None, + Stretch.Fill => CompositionStretch.Fill, + Stretch.Uniform => CompositionStretch.Uniform, + Stretch.UniformToFill => CompositionStretch.UniformToFill, + _ => throw new ArgumentException("Invalid stretch value") + + }; + + // Horizontal Alignment + var finalAlignX = alignX switch + { + AlignmentX.Left => 0, + AlignmentX.Center => 0.5f, + AlignmentX.Right => 1f, + _ => surfaceBrush.HorizontalAlignmentRatio + }; + + // If animation is available, animate to the new value + // otherwise set it explicitly + if (alignXAnimation == null) + { + surfaceBrush.HorizontalAlignmentRatio = finalAlignX; + } + else + { + alignXAnimation.InsertKeyFrame(1f, finalAlignX); + surfaceBrush.StartAnimation("HorizontalAlignmentRatio", alignXAnimation); + } + + // Vertical Alignment + var finalAlignY = alignY switch + { + AlignmentY.Top => 0, + AlignmentY.Center => 0.5f, + AlignmentY.Bottom => 1f, + _ => surfaceBrush.VerticalAlignmentRatio + }; + + // If animation is available, animate to the new value + // otherwise set it explicitly + if (alignYAnimation == null) + { + surfaceBrush.VerticalAlignmentRatio = finalAlignY; + } + else + { + alignYAnimation.InsertKeyFrame(1f, finalAlignY); + surfaceBrush.StartAnimation("VerticalAlignmentRatio", alignYAnimation); + } + } + + /// + /// This extension method creates a scoped batch and handles the completed event + /// the subscribing and unsubscribing process internally. + /// + /// Example usage: + /// _compositor.CreateScopedBatch(CompositionBatchTypes.Animation, + /// () => // Action + /// { + /// transitionVisual.StartAnimation("Scale.XY", _scaleUpAnimation); + /// }, + /// () => // Post Action + /// { + /// BackBtn.IsEnabled = true; + /// }); + /// + /// + /// Compositor. + /// Composition Batch Type. + /// Action to perform within the scoped batch. + /// Action to perform once the batch completes. + public static void CreateScopedBatch(this Compositor compositor, CompositionBatchTypes batchType, Action action, Action postAction = null) + { + if (action == null) + { + throw new ArgumentException("Cannot create a scoped batch on an action with null value!", nameof(action)); + } + + // Create ScopedBatch + var batch = compositor.CreateScopedBatch(batchType); + + // Handler for the Completed Event + void BatchCompletedHandler(object s, CompositionBatchCompletedEventArgs ea) + { + var scopedBatch = s as CompositionScopedBatch; + + // Unsubscribe the handler from the Completed event + if (scopedBatch != null) + { + scopedBatch.Completed -= BatchCompletedHandler; + } + + try + { + // Invoke the post action + postAction?.Invoke(); + } + finally + { + scopedBatch?.Dispose(); + } + } + + // Subscribe to the Completed event + batch.Completed += BatchCompletedHandler; + + // Invoke the action + action(); + + // End Batch + batch.End(); + } + + /// + /// This extension method creates a scoped batch and handles the completed event + /// the subscribing and unsubscribing process internally. + /// + /// Example usage: + /// _compositor.CreateScopedBatch(CompositionBatchTypes.Animation, + /// (batch) => // Action + /// { + /// transitionVisual.StartAnimation("Scale.XY", _scaleUpAnimation); + /// }, + /// (batch) => // Post Action + /// { + /// BackBtn.IsEnabled = true; + /// }); + /// + /// + /// Compositor. + /// Composition Batch Type. + /// Action to perform within the scoped batch. + /// Action to perform once the batch completes. + public static void CreateScopedBatch(this Compositor compositor, CompositionBatchTypes batchType, Action action, Action postAction = null) + { + if (action == null) + { + throw new ArgumentException("Cannot create a scoped batch on an action with null value!", nameof(action)); + } + + // Create ScopedBatch + var batch = compositor.CreateScopedBatch(batchType); + + // Handler for the Completed Event + void BatchCompletedHandler(object s, CompositionBatchCompletedEventArgs ea) + { + var scopedBatch = s as CompositionScopedBatch; + + // Unsubscribe the handler from the Completed event + if (scopedBatch != null) + { + scopedBatch.Completed -= BatchCompletedHandler; + } + + try + { + // Invoke the post action + postAction?.Invoke(scopedBatch); + } + finally + { + scopedBatch?.Dispose(); + } + } + + // Subscribe to the Completed event + batch.Completed += BatchCompletedHandler; + + // Invoke the action + action(batch); + + // End Batch + batch.End(); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Surface/GaussianMaskSurface.cs b/Microsoft.Toolkit.Uwp.UI.Media/Surface/GaussianMaskSurface.cs new file mode 100644 index 00000000000..e9ef45098aa --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Surface/GaussianMaskSurface.cs @@ -0,0 +1,184 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Numerics; +using Microsoft.Graphics.Canvas.Geometry; +using Windows.Foundation; +using Windows.UI.Composition; + +namespace Microsoft.Toolkit.Uwp.UI.Media +{ + /// + /// Class for rendering custom shaped geometries onto so that they can be used as masks on Composition Visuals. + /// These geometries have a Gaussian Blur applied to them. + /// + internal sealed class GaussianMaskSurface : IGaussianMaskSurface + { + private readonly object _surfaceLock; + private ICompositionGeneratorInternal _generator; + private CompositionDrawingSurface _surface; + + /// + public ICompositionGenerator Generator => _generator; + + /// + public ICompositionSurface Surface => _surface; + + /// + public CanvasGeometry Geometry { get; private set; } + + /// + public Size Size { get; private set; } + + /// + public Vector2 Offset { get; private set; } + + /// + public float BlurRadius { get; private set; } + + /// + /// Initializes a new instance of the class. + /// Constructor + /// + /// object + /// Size of the + /// Geometry of the + /// The offset from the top left corner of the where the is rendered. + /// Radius of Gaussian Blur to be applied on the + public GaussianMaskSurface(ICompositionGeneratorInternal generator, Size size, CanvasGeometry geometry, Vector2 offset, float blurRadius) + { + _generator = generator ?? throw new ArgumentException("Generator cannot be null!", nameof(generator)); + + _surfaceLock = new object(); + Geometry = geometry; + Offset = offset; + BlurRadius = Math.Abs(blurRadius); + + // Create Mask Surface + _surface = _generator.CreateDrawingSurface(_surfaceLock, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + + // Subscribe to DeviceReplaced event + _generator.DeviceReplaced += OnDeviceReplaced; + } + + /// + public void Redraw() + { + // Redraw the mask surface + RedrawSurface(); + } + + /// + public void Redraw(float blurRadius) + { + Redraw(Size, Geometry, Offset, blurRadius); + } + + /// + public void Redraw(CanvasGeometry geometry) + { + Redraw(Size, geometry, Offset, BlurRadius); + } + + /// + public void Redraw(CanvasGeometry geometry, Vector2 offset) + { + Redraw(Size, geometry, offset, BlurRadius); + } + + /// + public void Redraw(CanvasGeometry geometry, Vector2 offset, float blurRadius) + { + Redraw(Size, geometry, offset, blurRadius); + } + + /// + public void Redraw(Size size, CanvasGeometry geometry) + { + Redraw(size, geometry, Offset, BlurRadius); + } + + /// + public void Redraw(Size size, CanvasGeometry geometry, Vector2 offset) + { + Redraw(size, geometry, offset, BlurRadius); + } + + /// + public void Redraw(Size size, CanvasGeometry geometry, Vector2 offset, float blurRadius) + { + // Resize the mask surface + _generator.ResizeDrawingSurface(_surfaceLock, _surface, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + + // Set the offset + Offset = offset; + + // Set the new geometry + Geometry = geometry; + + // Set the new blur radius + BlurRadius = blurRadius; + + // Redraw the mask surface + RedrawSurface(); + } + + /// + public void Resize(Size size) + { + // resize the mask surface + _generator.ResizeDrawingSurface(_surfaceLock, _surface, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + + // Redraw the mask surface + RedrawSurface(); + } + + /// + public void Dispose() + { + _surface?.Dispose(); + Geometry?.Dispose(); + if (_generator != null) + { + _generator.DeviceReplaced -= OnDeviceReplaced; + } + + _surface = null; + _generator = null; + Geometry = null; + } + + /// + /// Handles the DeviceReplaced event + /// + /// Sender + /// object + private void OnDeviceReplaced(object sender, object e) + { + // Recreate the GaussianMaskSurface + _surface = _generator.CreateDrawingSurface(_surfaceLock, Size); + + // Redraw the mask surface + RedrawSurface(); + } + + /// + /// Helper class to redraw the + /// + private void RedrawSurface() + { + _generator.RedrawGaussianMaskSurface(_surfaceLock, _surface, Size, Geometry, Offset, BlurRadius); + } + } +} \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Surface/GeometryMaskSurface.cs b/Microsoft.Toolkit.Uwp.UI.Media/Surface/GeometryMaskSurface.cs new file mode 100644 index 00000000000..e44df6a3196 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Surface/GeometryMaskSurface.cs @@ -0,0 +1,160 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Numerics; +using Microsoft.Graphics.Canvas.Geometry; +using Windows.Foundation; +using Windows.UI.Composition; + +namespace Microsoft.Toolkit.Uwp.UI.Media +{ + /// + /// Class for rendering custom shaped geometries onto so that they can be used as masks on Composition Visuals. + /// + internal sealed class GeometryMaskSurface : IGeometryMaskSurface + { + private readonly object _surfaceLock; + private ICompositionGeneratorInternal _generator; + private CompositionDrawingSurface _surface; + + /// + public ICompositionGenerator Generator => _generator; + + /// + public ICompositionSurface Surface => _surface; + + /// + public CanvasGeometry Geometry { get; private set; } + + /// + public Size Size { get; private set; } + + /// + public Vector2 Offset { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// object + /// Size of the + /// Geometry of the + /// The offset from the top left corner of the where the Geometry is rendered. + public GeometryMaskSurface(ICompositionGeneratorInternal generator, Size size, CanvasGeometry geometry, Vector2 offset) + { + _generator = generator ?? throw new ArgumentException("Generator cannot be null!", nameof(generator)); + + _generator = generator; + _surfaceLock = new object(); + Geometry = geometry; + Offset = offset; + + // Create Mask Surface + _surface = _generator.CreateDrawingSurface(_surfaceLock, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + + // Subscribe to DeviceReplaced event + _generator.DeviceReplaced += OnDeviceReplaced; + } + + /// + public void Redraw() + { + // Redraw the mask surface + RedrawSurface(); + } + + /// + public void Redraw(CanvasGeometry geometry) + { + Redraw(Size, geometry, Offset); + } + + /// + public void Redraw(CanvasGeometry geometry, Vector2 offset) + { + Redraw(Size, geometry, offset); + } + + /// + public void Redraw(Size size, CanvasGeometry geometry) + { + Redraw(size, geometry, Offset); + } + + /// + public void Redraw(Size size, CanvasGeometry geometry, Vector2 offset) + { + if (Size != size) + { + // Resize the mask surface + _generator.ResizeDrawingSurface(_surfaceLock, _surface, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + } + + // Set the new geometry + Geometry = geometry; + + // Set the offset + Offset = offset; + + // Redraw the mask surface + RedrawSurface(); + } + + /// + public void Resize(Size size) + { + // resize the mask surface + _generator.ResizeDrawingSurface(_surfaceLock, _surface, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + + // Redraw the mask surface + RedrawSurface(); + } + + /// + public void Dispose() + { + _surface?.Dispose(); + Geometry?.Dispose(); + Geometry = null; + if (_generator != null) + { + _generator.DeviceReplaced -= OnDeviceReplaced; + } + + _surface = null; + _generator = null; + } + + /// + /// Handles the DeviceReplaced event. + /// + /// Sender + /// object + private void OnDeviceReplaced(object sender, object e) + { + // Recreate the GeometryMaskSurface + _surface = _generator.CreateDrawingSurface(_surfaceLock, Size); + + // Redraw the mask surface + RedrawSurface(); + } + + /// + /// Helper class to redraw the . + /// + private void RedrawSurface() + { + _generator.RedrawGeometryMaskSurface(_surfaceLock, _surface, Size, Geometry, Offset); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Surface/GeometrySurface.cs b/Microsoft.Toolkit.Uwp.UI.Media/Surface/GeometrySurface.cs new file mode 100644 index 00000000000..bdadaffeb0e --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Surface/GeometrySurface.cs @@ -0,0 +1,776 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Microsoft.Graphics.Canvas.Brushes; +using Microsoft.Graphics.Canvas.Geometry; +using Microsoft.Toolkit.Uwp.UI.Media.Geometry; +using Windows.Foundation; +using Windows.UI; +using Windows.UI.Composition; + +namespace Microsoft.Toolkit.Uwp.UI.Media +{ + /// + /// Class for rendering custom shaped geometries onto . + /// + internal sealed class GeometrySurface : IGeometrySurface + { + private readonly object _surfaceLock; + private ICompositionGeneratorInternal _generator; + private CompositionDrawingSurface _surface; + private CanvasGeometry _geometry; + private ICanvasStroke _stroke; + private ICanvasBrush _fill; + private ICanvasBrush _backgroundBrush; + + /// + public ICompositionGenerator Generator => _generator; + + /// + public ICompositionSurface Surface => _surface; + + /// + public CanvasGeometry Geometry => _geometry; + + /// + public ICanvasStroke Stroke => _stroke; + + /// + public ICanvasBrush Fill => _fill; + + /// + public ICanvasBrush BackgroundBrush => _backgroundBrush; + + /// + public Size Size { get; private set; } + + /// + /// Initializes a new instance of the class. + /// Constructor + /// + /// object. + /// Size of the . + /// Geometry of the . + /// Stroke for the geometry. + /// Fill color of the geometry. + /// Brush to fill the background surface which is not covered by the geometry. + public GeometrySurface(ICompositionGeneratorInternal generator, Size size, CanvasGeometry geometry, ICanvasStroke stroke, Color fillColor, Color backgroundColor) + { + _generator = generator ?? throw new ArgumentException("Generator cannot be null!", nameof(generator)); + + _surfaceLock = new object(); + _geometry = geometry; + _stroke = stroke; + _fill = new CanvasSolidColorBrush(_generator.Device, fillColor); + _backgroundBrush = new CanvasSolidColorBrush(_generator.Device, backgroundColor); + + // Create GeometrySurface + _surface = _generator.CreateDrawingSurface(_surfaceLock, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + + // Subscribe to DeviceReplaced event + _generator.DeviceReplaced += OnDeviceReplaced; + } + + /// + /// Initializes a new instance of the class. + /// Constructor + /// + /// object. + /// Size of the . + /// Geometry of the . + /// Stroke for the geometry. + /// Brush to fill the geometry. + /// Brush to fill the GeometrySurface background surface which is not covered by the geometry. + public GeometrySurface(ICompositionGeneratorInternal generator, Size size, CanvasGeometry geometry, ICanvasStroke stroke, ICanvasBrush fill, ICanvasBrush backgroundBrush) + { + _generator = generator ?? throw new ArgumentException("Generator cannot be null!", nameof(generator)); + + _generator = generator; + _surfaceLock = new object(); + _geometry = geometry; + _stroke = stroke; + _fill = fill; + _backgroundBrush = backgroundBrush; + + // Create GeometrySurface + _surface = _generator.CreateDrawingSurface(_surfaceLock, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + + // Subscribe to DeviceReplaced event + _generator.DeviceReplaced += OnDeviceReplaced; + } + + /// + public void Redraw() + { + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + public void Redraw(CanvasGeometry geometry) + { + // Set the new geometry + _geometry = geometry; + + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + public void Redraw(ICanvasStroke stroke) + { + // Set the new stroke + _stroke = stroke; + + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + public void Redraw(Color fillColor) + { + // Set the fill + if (_fill is CanvasSolidColorBrush fillBrush) + { + fillBrush.Color = fillColor; + } + else + { + _fill = new CanvasSolidColorBrush(_generator.Device, fillColor); + } + + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + public void Redraw(ICanvasStroke stroke, Color fillColor) + { + // Set the new stroke + _stroke = stroke; + + // Set the fill + if (_fill is CanvasSolidColorBrush fillBrush) + { + fillBrush.Color = fillColor; + } + else + { + _fill = new CanvasSolidColorBrush(_generator.Device, fillColor); + } + + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + public void Redraw(Color fillColor, Color backgroundColor) + { + // Set the fill + if (_fill is CanvasSolidColorBrush fillBrush) + { + fillBrush.Color = fillColor; + } + else + { + _fill = new CanvasSolidColorBrush(_generator.Device, fillColor); + } + + // Set the backgroundBrush + if (_backgroundBrush is CanvasSolidColorBrush backBrush) + { + backBrush.Color = backgroundColor; + } + else + { + _backgroundBrush = new CanvasSolidColorBrush(_generator.Device, backgroundColor); + } + + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + public void Redraw(ICanvasStroke stroke, Color fillColor, Color backgroundColor) + { + // Set the new stroke + _stroke = stroke; + + // Set the fill + if (_fill is CanvasSolidColorBrush fillBrush) + { + fillBrush.Color = fillColor; + } + else + { + _fill = new CanvasSolidColorBrush(_generator.Device, fillColor); + } + + // Set the backgroundBrush + if (_backgroundBrush is CanvasSolidColorBrush backBrush) + { + backBrush.Color = backgroundColor; + } + else + { + _backgroundBrush = new CanvasSolidColorBrush(_generator.Device, backgroundColor); + } + + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + public void Redraw(ICanvasBrush fillBrush) + { + // Set the fill + _fill = fillBrush ?? new CanvasSolidColorBrush(_generator.Device, Colors.Transparent); + + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + public void Redraw(ICanvasStroke stroke, ICanvasBrush fillBrush) + { + // Set the new stroke + _stroke = stroke; + + // Set the fill + _fill = fillBrush ?? new CanvasSolidColorBrush(_generator.Device, Colors.Transparent); + + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + public void Redraw(ICanvasBrush fillBrush, ICanvasBrush backgroundBrush) + { + // Set the fill + _fill = fillBrush ?? new CanvasSolidColorBrush(_generator.Device, Colors.Transparent); + + // Set the backgroundBrush + _backgroundBrush = backgroundBrush ?? new CanvasSolidColorBrush(_generator.Device, Colors.Transparent); + + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + public void Redraw(ICanvasStroke stroke, ICanvasBrush fillBrush, ICanvasBrush backgroundBrush) + { + // Set the new stroke + _stroke = stroke; + + // Set the fill + _fill = fillBrush ?? new CanvasSolidColorBrush(_generator.Device, Colors.Transparent); + + // Set the backgroundBrush + _backgroundBrush = backgroundBrush ?? new CanvasSolidColorBrush(_generator.Device, Colors.Transparent); + + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + public void Redraw(Color fillColor, ICanvasBrush backgroundBrush) + { + // Set the fill + if (_fill is CanvasSolidColorBrush fillBrush) + { + fillBrush.Color = fillColor; + } + else + { + _fill = new CanvasSolidColorBrush(_generator.Device, fillColor); + } + + // Set the backgroundBrush + _backgroundBrush = backgroundBrush ?? new CanvasSolidColorBrush(_generator.Device, Colors.Transparent); + + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + public void Redraw(ICanvasStroke stroke, Color fillColor, ICanvasBrush backgroundBrush) + { + // Set the new stroke + _stroke = stroke; + + // Set the fill + if (_fill is CanvasSolidColorBrush fillBrush) + { + fillBrush.Color = fillColor; + } + else + { + _fill = new CanvasSolidColorBrush(_generator.Device, fillColor); + } + + // Set the backgroundBrush + _backgroundBrush = backgroundBrush ?? new CanvasSolidColorBrush(_generator.Device, Colors.Transparent); + + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + public void Redraw(ICanvasBrush fillBrush, Color backgroundColor) + { + // Set the fill + _fill = fillBrush ?? new CanvasSolidColorBrush(_generator.Device, Colors.Transparent); + + // Set the backgroundBrush + if (_backgroundBrush is CanvasSolidColorBrush backBrush) + { + backBrush.Color = backgroundColor; + } + else + { + _backgroundBrush = new CanvasSolidColorBrush(_generator.Device, backgroundColor); + } + + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + public void Redraw(ICanvasStroke stroke, ICanvasBrush fillBrush, Color backgroundColor) + { + // Set the new stroke + _stroke = stroke; + + // Set the fill + _fill = fillBrush ?? new CanvasSolidColorBrush(_generator.Device, Colors.Transparent); + + // Set the backgroundBrush + if (_backgroundBrush is CanvasSolidColorBrush backBrush) + { + backBrush.Color = backgroundColor; + } + else + { + _backgroundBrush = new CanvasSolidColorBrush(_generator.Device, backgroundColor); + } + + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + public void Redraw(Size size, CanvasGeometry geometry) + { + // Resize the GeometrySurface + _generator.ResizeDrawingSurface(_surfaceLock, _surface, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + + // Set the new geometry + _geometry = geometry; + + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + public void Redraw(Size size, CanvasGeometry geometry, ICanvasStroke stroke) + { + // Resize the GeometrySurface + _generator.ResizeDrawingSurface(_surfaceLock, _surface, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + + // Set the new geometry + _geometry = geometry; + + // Set the new stroke + _stroke = stroke; + + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + public void Redraw(Size size, CanvasGeometry geometry, Color fillColor) + { + // Resize the GeometrySurface + _generator.ResizeDrawingSurface(_surfaceLock, _surface, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + + // Set the new geometry + _geometry = geometry; + + // Set the fill + if (_fill is CanvasSolidColorBrush fillBrush) + { + fillBrush.Color = fillColor; + } + else + { + _fill = new CanvasSolidColorBrush(_generator.Device, fillColor); + } + + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + public void Redraw(Size size, CanvasGeometry geometry, ICanvasStroke stroke, Color fillColor) + { + // Resize the GeometrySurface + _generator.ResizeDrawingSurface(_surfaceLock, _surface, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + + // Set the new geometry + _geometry = geometry; + + // Set the new stroke + _stroke = stroke; + + // Set the fill + if (_fill is CanvasSolidColorBrush fillBrush) + { + fillBrush.Color = fillColor; + } + else + { + _fill = new CanvasSolidColorBrush(_generator.Device, fillColor); + } + + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + public void Redraw(Size size, CanvasGeometry geometry, Color fillColor, Color backgroundColor) + { + // Resize the GeometrySurface + _generator.ResizeDrawingSurface(_surfaceLock, _surface, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + + // Set the new geometry + _geometry = geometry; + + // Set the fill + if (_fill is CanvasSolidColorBrush fillBrush) + { + fillBrush.Color = fillColor; + } + else + { + _fill = new CanvasSolidColorBrush(_generator.Device, fillColor); + } + + // Set the backgroundBrush + if (_backgroundBrush is CanvasSolidColorBrush backBrush) + { + backBrush.Color = backgroundColor; + } + else + { + _backgroundBrush = new CanvasSolidColorBrush(_generator.Device, backgroundColor); + } + + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + public void Redraw(Size size, CanvasGeometry geometry, ICanvasStroke stroke, Color fillColor, Color backgroundColor) + { + // Resize the GeometrySurface + _generator.ResizeDrawingSurface(_surfaceLock, _surface, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + + // Set the new geometry + _geometry = geometry; + + // Set the new stroke + _stroke = stroke; + + // Set the fill + if (_fill is CanvasSolidColorBrush fillBrush) + { + fillBrush.Color = fillColor; + } + else + { + _fill = new CanvasSolidColorBrush(_generator.Device, fillColor); + } + + // Set the backgroundBrush + if (_backgroundBrush is CanvasSolidColorBrush backBrush) + { + backBrush.Color = backgroundColor; + } + else + { + _backgroundBrush = new CanvasSolidColorBrush(_generator.Device, backgroundColor); + } + + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + public void Redraw(Size size, CanvasGeometry geometry, ICanvasBrush fillBrush) + { + // Resize the GeometrySurface + _generator.ResizeDrawingSurface(_surfaceLock, _surface, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + + // Set the new geometry + _geometry = geometry; + + // Set the fill + _fill = fillBrush ?? new CanvasSolidColorBrush(_generator.Device, Colors.Transparent); + + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + public void Redraw(Size size, CanvasGeometry geometry, ICanvasBrush fillBrush, ICanvasBrush backgroundBrush) + { + _generator.ResizeDrawingSurface(_surfaceLock, _surface, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + + // Set the new geometry + _geometry = geometry; + + // Set the fill + _fill = fillBrush ?? new CanvasSolidColorBrush(_generator.Device, Colors.Transparent); + + // Set the backgroundBrush + _backgroundBrush = backgroundBrush ?? new CanvasSolidColorBrush(_generator.Device, Colors.Transparent); + + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + public void Redraw(Size size, CanvasGeometry geometry, ICanvasStroke stroke, ICanvasBrush fillBrush, ICanvasBrush backgroundBrush) + { + // Resize the GeometrySurface + _generator.ResizeDrawingSurface(_surfaceLock, _surface, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + + // Set the new geometry + _geometry = geometry; + + // Set the new stroke + _stroke = stroke; + + // Set the fill + _fill = fillBrush ?? new CanvasSolidColorBrush(_generator.Device, Colors.Transparent); + + // Set the backgroundBrush + _backgroundBrush = backgroundBrush ?? new CanvasSolidColorBrush(_generator.Device, Colors.Transparent); + + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + public void Redraw(Size size, CanvasGeometry geometry, ICanvasBrush fillBrush, Color backgroundColor) + { + _generator.ResizeDrawingSurface(_surfaceLock, _surface, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + + // Set the new geometry + _geometry = geometry; + + // Set the fill + _fill = fillBrush ?? new CanvasSolidColorBrush(_generator.Device, Colors.Transparent); + + // Set the backgroundBrush + if (_backgroundBrush is CanvasSolidColorBrush backBrush) + { + backBrush.Color = backgroundColor; + } + else + { + _backgroundBrush = new CanvasSolidColorBrush(_generator.Device, backgroundColor); + } + + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + public void Redraw(Size size, CanvasGeometry geometry, ICanvasStroke stroke, ICanvasBrush fillBrush, Color backgroundColor) + { + // Resize the GeometrySurface + _generator.ResizeDrawingSurface(_surfaceLock, _surface, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + + // Set the new geometry + _geometry = geometry; + + // Set the new stroke + _stroke = stroke; + + // Set the fill + _fill = fillBrush ?? new CanvasSolidColorBrush(_generator.Device, Colors.Transparent); + + // Set the backgroundBrush + if (_backgroundBrush is CanvasSolidColorBrush backBrush) + { + backBrush.Color = backgroundColor; + } + else + { + _backgroundBrush = new CanvasSolidColorBrush(_generator.Device, backgroundColor); + } + + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + public void Redraw(Size size, CanvasGeometry geometry, Color fillColor, ICanvasBrush backgroundBrush) + { + _generator.ResizeDrawingSurface(_surfaceLock, _surface, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + + // Set the new geometry + _geometry = geometry; + + // Set the fill + if (_fill is CanvasSolidColorBrush fillBrush) + { + fillBrush.Color = fillColor; + } + else + { + _fill = new CanvasSolidColorBrush(_generator.Device, fillColor); + } + + // Set the backgroundBrush + _backgroundBrush = backgroundBrush ?? new CanvasSolidColorBrush(_generator.Device, Colors.Transparent); + + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + public void Redraw(Size size, CanvasGeometry geometry, ICanvasStroke stroke, Color fillColor, ICanvasBrush backgroundBrush) + { + // Resize the GeometrySurface + _generator.ResizeDrawingSurface(_surfaceLock, _surface, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + + // Set the new geometry + _geometry = geometry; + + // Set the new stroke + _stroke = stroke; + + // Set the fill + if (_fill is CanvasSolidColorBrush fillBrush) + { + fillBrush.Color = fillColor; + } + else + { + _fill = new CanvasSolidColorBrush(_generator.Device, fillColor); + } + + // Set the backgroundBrush + _backgroundBrush = backgroundBrush ?? new CanvasSolidColorBrush(_generator.Device, Colors.Transparent); + + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + public void Resize(Size size) + { + // resize the GeometrySurface + _generator.ResizeDrawingSurface(_surfaceLock, _surface, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + public void Dispose() + { + _surface?.Dispose(); + _geometry?.Dispose(); + if (_generator != null) + { + _generator.DeviceReplaced -= OnDeviceReplaced; + } + + _stroke.Brush.Dispose(); + _fill.Dispose(); + _backgroundBrush.Dispose(); + + _stroke = null; + _fill = null; + _backgroundBrush = null; + _surface = null; + _generator = null; + _geometry = null; + } + + /// + /// Handles the DeviceReplaced event. + /// + /// Sender. + /// Event Arguments. + private void OnDeviceReplaced(object sender, object e) + { + // Recreate the GeometrySurface + _surface = _generator.CreateDrawingSurface(_surfaceLock, Size); + + // Redraw the GeometrySurface + RedrawSurface(); + } + + /// + /// Helper class to redraw the . + /// + private void RedrawSurface() + { + _generator.RedrawGeometrySurface(_surfaceLock, _surface, Size, _geometry, _stroke, _fill, _backgroundBrush); + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Surface/ImageMaskSurface.cs b/Microsoft.Toolkit.Uwp.UI.Media/Surface/ImageMaskSurface.cs new file mode 100644 index 00000000000..8784a2cc4b9 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Surface/ImageMaskSurface.cs @@ -0,0 +1,600 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Threading.Tasks; +using Microsoft.Graphics.Canvas; +using Microsoft.Toolkit.Uwp.UI.Media.Geometry; +using Windows.Foundation; +using Windows.UI.Composition; +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.UI.Media +{ + /// + /// Class for rendering a mask, using an Image's alpha values, onto an . + /// + internal sealed class ImageMaskSurface : IImageMaskSurface + { + private readonly object _surfaceLock; + private ICompositionGeneratorInternal _generator; + private CompositionDrawingSurface _surface; + private Uri _uri; + private CanvasBitmap _canvasBitmap; + private bool _raiseLoadCompletedEvent; + + /// + /// Event that is raised when the image has been downloaded, decoded and loaded to the underlying . + /// This event fires regardless of success or failure. + /// + public event TypedEventHandler LoadCompleted; + + /// + public ICompositionGenerator Generator => _generator; + + /// + public ICompositionSurface Surface => _surface; + + /// + public Uri Uri => _uri; + + /// + public CanvasBitmap SurfaceBitmap => _canvasBitmap; + + /// + public Size Size { get; private set; } + + /// + public ImageSurfaceOptions Options { get; private set; } + + /// + public Size DecodedPhysicalSize { get; private set; } + + /// + public Size DecodedSize { get; private set; } + + /// + public ImageSurfaceLoadStatus Status { get; private set; } + + /// + public Thickness MaskPadding { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// object. + /// Uri of the image to be loaded onto the . + /// Size of the . + /// The padding between the outer bounds and the bounds of the area where the mask, created from the loaded image's alpha values, should be rendered. + /// The image's resize, alignment options and blur radius in the allocated space. + public ImageMaskSurface(ICompositionGeneratorInternal generator, Uri uri, Size size, Thickness padding, ImageSurfaceOptions options) + { + _generator = generator ?? throw new ArgumentException("Generator cannot be null!", nameof(generator)); + + _generator = generator; + _surfaceLock = new object(); + + // Create the Surface of the IImageMaskSurface + _surface = _generator.CreateDrawingSurface(_surfaceLock, size); + Size = _surface?.Size ?? new Size(0, 0); + _uri = uri; + _raiseLoadCompletedEvent = _uri != null; + _canvasBitmap = null; + MaskPadding = padding; + + // Set the image options + Options = options; + + // Subscribe to DeviceReplaced event + _generator.DeviceReplaced += OnDeviceReplaced; + Status = ImageSurfaceLoadStatus.None; + } + + /// + /// Initializes a new instance of the class. + /// Constructor + /// + /// object. + /// The whose alpha values will be used to create the Mask. + /// Size of the . + /// The padding between the outer bounds and the bounds of the area where the mask, created from the loaded image's alpha values, should be rendered. + /// The image's resize, alignment options and blur radius in the allocated space. + public ImageMaskSurface(ICompositionGeneratorInternal generator, CanvasBitmap surfaceBitmap, Size size, Thickness padding, ImageSurfaceOptions options) + { + _generator = generator ?? throw new ArgumentException("Generator cannot be null!", nameof(generator)); + + _generator = generator; + _surfaceLock = new object(); + + // Create the Surface of the IImageMaskSurface + _surface = _generator.CreateDrawingSurface(_surfaceLock, size); + Size = _surface?.Size ?? new Size(0, 0); + _uri = null; + _raiseLoadCompletedEvent = false; + if (surfaceBitmap != null) + { + _canvasBitmap = CanvasBitmap.CreateFromBytes( + _generator.Device, + surfaceBitmap.GetPixelBytes(), + (int)surfaceBitmap.Bounds.Width, + (int)surfaceBitmap.Bounds.Height, + surfaceBitmap.Format); + } + + // Set the mask padding + MaskPadding = padding; + + // Set the image options + Options = options; + + // Subscribe to DeviceReplaced event + _generator.DeviceReplaced += OnDeviceReplaced; + } + + /// + public void Redraw() + { + // Reload the IImageMaskSurface + RedrawSurface(); + } + + /// + public void Redraw(ImageSurfaceOptions options) + { + // Set the image options + Options = options; + + // Redraw the IImageMaskSurface + RedrawSurface(); + } + + /// + public void Redraw(Thickness padding, ImageSurfaceOptions options) + { + // Set the mask padding + MaskPadding = padding; + + // Set the image options + Options = options; + + // Redraw the IImageMaskSurface + RedrawSurface(); + } + + /// + public void Redraw(Size size, ImageSurfaceOptions options) + { + // Resize if required + if (Size != size) + { + // resize the IImageMaskSurface + _generator.ResizeDrawingSurface(_surfaceLock, _surface, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + } + + // Set the image options + Options = options; + + // Redraw the IImageMaskSurface + RedrawSurface(); + } + + /// + public void Redraw(Size size, Thickness padding, ImageSurfaceOptions options) + { + // Resize if required + if (Size != size) + { + // resize the IImageMaskSurface + _generator.ResizeDrawingSurface(_surfaceLock, _surface, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + } + + // Set the mask padding + MaskPadding = padding; + + // Set the image options + Options = options; + + // Redraw the IImageMaskSurface + RedrawSurface(); + } + + /// + public void Redraw(IImageSurface imageSurface) + { + if (imageSurface != null) + { + Redraw(imageSurface.SurfaceBitmap, imageSurface.Size, MaskPadding, imageSurface.Options); + } + else + { + // Draw an empty surface + Redraw(surfaceBitmap: null, Size, MaskPadding, Options); + } + } + + /// + public void Redraw(IImageSurface imageSurface, ImageSurfaceOptions options) + { + if (imageSurface != null) + { + Redraw(imageSurface.SurfaceBitmap, imageSurface.Size, MaskPadding, options); + } + else + { + // Draw an empty surface + Redraw(surfaceBitmap: null, Size, MaskPadding, options); + } + } + + /// + public void Redraw(IImageSurface imageSurface, Size size, ImageSurfaceOptions options) + { + Redraw(imageSurface?.SurfaceBitmap, size, MaskPadding, options); + } + + /// + public void Redraw(IImageSurface imageSurface, Thickness padding) + { + if (imageSurface != null) + { + Redraw(imageSurface.SurfaceBitmap, imageSurface.Size, padding, Options); + } + else + { + // Draw an empty surface + Redraw(surfaceBitmap: null, Size, padding, Options); + } + } + + /// + public void Redraw(IImageSurface imageSurface, Thickness padding, ImageSurfaceOptions options) + { + if (imageSurface != null) + { + Redraw(imageSurface.SurfaceBitmap, imageSurface.Size, padding, options); + } + else + { + // Draw an empty surface + Redraw(surfaceBitmap: null, Size, padding, options); + } + } + + /// + public void Redraw(IImageSurface imageSurface, Size size, Thickness padding, ImageSurfaceOptions options) + { + Redraw(imageSurface?.SurfaceBitmap, size, padding, options); + } + + /// + public void Redraw(IImageMaskSurface imageMaskSurface) + { + if (imageMaskSurface != null) + { + Redraw(imageMaskSurface.SurfaceBitmap, imageMaskSurface.Size, imageMaskSurface.MaskPadding, imageMaskSurface.Options); + } + else + { + // Draw an empty surface + Redraw(surfaceBitmap: null, Size, MaskPadding, Options); + } + } + + /// + public void Redraw(IImageMaskSurface imageMaskSurface, Thickness padding) + { + if (imageMaskSurface != null) + { + Redraw(imageMaskSurface.SurfaceBitmap, imageMaskSurface.Size, padding, imageMaskSurface.Options); + } + else + { + // Draw an empty surface + Redraw(surfaceBitmap: null, Size, padding, Options); + } + } + + /// + public void Redraw(IImageMaskSurface imageMaskSurface, ImageSurfaceOptions options) + { + if (imageMaskSurface != null) + { + Redraw(imageMaskSurface.SurfaceBitmap, imageMaskSurface.Size, imageMaskSurface.MaskPadding, options); + } + else + { + // Draw an empty surface + Redraw(surfaceBitmap: null, Size, MaskPadding, options); + } + } + + /// + public void Redraw(IImageMaskSurface imageMaskSurface, Thickness padding, ImageSurfaceOptions options) + { + if (imageMaskSurface != null) + { + Redraw(imageMaskSurface.SurfaceBitmap, imageMaskSurface.Size, padding, options); + } + else + { + // Draw an empty surface + Redraw(surfaceBitmap: null, Size, padding, options); + } + } + + /// + public void Redraw(IImageMaskSurface imageMaskSurface, Size size, Thickness padding, ImageSurfaceOptions options) + { + Redraw(imageMaskSurface?.SurfaceBitmap, size, padding, options); + } + + /// + public void Redraw(CanvasBitmap surfaceBitmap) + { + Redraw(surfaceBitmap, Size, MaskPadding, Options); + } + + /// + public void Redraw(CanvasBitmap surfaceBitmap, Thickness padding) + { + Redraw(surfaceBitmap, Size, padding, Options); + } + + /// + public void Redraw(CanvasBitmap surfaceBitmap, ImageSurfaceOptions options) + { + Redraw(surfaceBitmap, Size, MaskPadding, options); + } + + /// + public void Redraw(CanvasBitmap surfaceBitmap, Size size, ImageSurfaceOptions options) + { + Redraw(surfaceBitmap, size, MaskPadding, options); + } + + /// + public void Redraw(CanvasBitmap surfaceBitmap, Thickness padding, ImageSurfaceOptions options) + { + Redraw(surfaceBitmap, Size, padding, options); + } + + /// + public void Redraw(CanvasBitmap surfaceBitmap, Size size, Thickness padding, ImageSurfaceOptions options) + { + if (_canvasBitmap != surfaceBitmap) + { + // Dispose the previous canvas bitmap resource (if any) + if (_canvasBitmap != null) + { + _canvasBitmap.Dispose(); + _canvasBitmap = null; + } + + if (surfaceBitmap != null) + { + // No need to copy again if _canvasBitmap and surfaceBitmap are same + if (_canvasBitmap != surfaceBitmap) + { + // Copy the surface bitmap onto _canvasBitmap + _canvasBitmap = CanvasBitmap.CreateFromBytes( + _generator.Device, + surfaceBitmap.GetPixelBytes(), + (int)surfaceBitmap.Bounds.Width, + (int)surfaceBitmap.Bounds.Height, + surfaceBitmap.Format); + } + } + else + { + _canvasBitmap = null; + } + } + + _uri = null; + _raiseLoadCompletedEvent = false; + + // Set the options + Options = options; + + // Resize if required + if (Size != size) + { + // resize the IImageMaskSurface + _generator.ResizeDrawingSurface(_surfaceLock, _surface, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + } + + // Set the mask padding + MaskPadding = padding; + + // Redraw the IImageMaskSurface + RedrawSurface(); + } + + /// + public Task RedrawAsync(Uri uri) + { + return RedrawAsync(uri, Size, MaskPadding, Options); + } + + /// + public Task RedrawAsync(Uri uri, ImageSurfaceOptions options) + { + return RedrawAsync(uri, Size, MaskPadding, options); + } + + /// + public Task RedrawAsync(Uri uri, Thickness padding, ImageSurfaceOptions options) + { + return RedrawAsync(uri, Size, padding, options); + } + + /// + public Task RedrawAsync(Uri uri, Size size, ImageSurfaceOptions options) + { + return RedrawAsync(uri, size, MaskPadding, options); + } + + /// + public async Task RedrawAsync(Uri uri, Size size, Thickness padding, ImageSurfaceOptions options) + { + // If the given Uri differs from the previously stored Uri or if the ImageSurface was + // directly created from a CanvasBitmap, dispose the existing canvasBitmap + if ((_uri != null && !_uri.IsEqualTo(uri)) + || (_uri == null && _canvasBitmap != null)) + { + _canvasBitmap?.Dispose(); + _canvasBitmap = null; + _raiseLoadCompletedEvent = uri != null; + } + + // Set the mask padding + MaskPadding = padding; + + // Set the image options + Options = options; + + // Resize the surface only if AutoResize option is disabled + if (Size != size) + { + // resize the IImageMaskSurface + _generator.ResizeDrawingSurface(_surfaceLock, _surface, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + } + + // Set the new Uri of the image to be loaded + _uri = uri; + + // Reload the IImageMaskSurface + await RedrawSurfaceAsync(); + } + + /// + public void Resize(Size size) + { + Resize(size, MaskPadding, Options); + } + + /// + public void Resize(Size size, ImageSurfaceOptions options) + { + Resize(size, MaskPadding, options); + } + + /// + public void Resize(Size size, Thickness padding, ImageSurfaceOptions options) + { + // Set the mask padding + MaskPadding = padding; + + // Set the image options + Options = options; + + // Resize the surface only if AutoResize option is disabled + if (Size != size) + { + // resize the IImageMaskSurface + _generator.ResizeDrawingSurface(_surfaceLock, _surface, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + } + + // Redraw the IImageMaskSurface + RedrawSurface(); + } + + /// + public void Dispose() + { + _surface?.Dispose(); + if (_generator != null) + { + _generator.DeviceReplaced -= OnDeviceReplaced; + } + + _canvasBitmap?.Dispose(); + _canvasBitmap = null; + _surface = null; + _generator = null; + _uri = null; + Options = null; + } + + /// + /// Redraws the asynchronously by loading the image from the Uri. + /// + /// + internal Task RedrawAsync() + { + // Reload the IImageMaskSurface + return RedrawSurfaceAsync(); + } + + /// + /// Handles the DeviceReplaced event + /// + /// Sender. + /// object. + private async void OnDeviceReplaced(object sender, object e) + { + // Recreate the ImageSurface + _surface = _generator.CreateDrawingSurface(_surfaceLock, Size); + + // Reload the IImageMaskSurface + await RedrawSurfaceAsync(); + } + + /// + /// Helper class to redraw the synchronously. + /// + private void RedrawSurface() + { + // Resize the surface image + _generator.RedrawImageMaskSurface(_surfaceLock, _surface, MaskPadding, Options, _canvasBitmap); + + Status = _canvasBitmap != null ? ImageSurfaceLoadStatus.Success : ImageSurfaceLoadStatus.Error; + + if (_canvasBitmap != null) + { + DecodedPhysicalSize = new Size(_canvasBitmap.SizeInPixels.Width, _canvasBitmap.SizeInPixels.Height); + DecodedSize = _canvasBitmap.Size; + } + } + + /// + /// Helper class to redraw the asynchronously. + /// + /// Task + private async Task RedrawSurfaceAsync() + { + // Cache the canvasBitmap to avoid reloading of the same image during Resize/Redraw operations + _canvasBitmap = await _generator.RedrawImageMaskSurfaceAsync(_surfaceLock, _surface, _uri, MaskPadding, Options, _canvasBitmap); + + Status = _canvasBitmap != null ? ImageSurfaceLoadStatus.Success : ImageSurfaceLoadStatus.Error; + + if (_canvasBitmap != null) + { + DecodedPhysicalSize = new Size(_canvasBitmap.SizeInPixels.Width, _canvasBitmap.SizeInPixels.Height); + DecodedSize = _canvasBitmap.Size; + } + + if (_raiseLoadCompletedEvent) + { + LoadCompleted?.Invoke(this, Status); + _raiseLoadCompletedEvent = false; + } + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Surface/ImageSurface.cs b/Microsoft.Toolkit.Uwp.UI.Media/Surface/ImageSurface.cs new file mode 100644 index 00000000000..665dbafd703 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Surface/ImageSurface.cs @@ -0,0 +1,437 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Threading.Tasks; +using Microsoft.Graphics.Canvas; +using Microsoft.Toolkit.Uwp.UI.Media.Geometry; +using Windows.Foundation; +using Windows.UI.Composition; + +namespace Microsoft.Toolkit.Uwp.UI.Media +{ + /// + /// Class for rendering an image onto a . + /// + internal sealed class ImageSurface : IImageSurface + { + private readonly object _surfaceLock; + private ICompositionGeneratorInternal _generator; + private CompositionDrawingSurface _surface; + private Uri _uri; + private CanvasBitmap _canvasBitmap; + private bool _raiseLoadCompletedEvent; + + /// + /// Event that is raised when the image has been downloaded, decoded and loaded + /// to the underlying IImageSurface. This event fires regardless of success or failure. + /// + public event TypedEventHandler LoadCompleted; + + /// + public ICompositionGenerator Generator => _generator; + + /// + public ICompositionSurface Surface => _surface; + + /// + public Uri Uri => _uri; + + /// + public Size Size { get; private set; } + + /// + public ImageSurfaceOptions Options { get; private set; } + + /// + public Size DecodedPhysicalSize { get; private set; } + + /// + public Size DecodedSize { get; private set; } + + /// + public ImageSurfaceLoadStatus Status { get; private set; } + + /// + public CanvasBitmap SurfaceBitmap => _canvasBitmap; + + /// + /// Initializes a new instance of the class. + /// Constructor + /// + /// object. + /// of the image to be loaded onto the . + /// Size of the . + /// The image's resize and alignment options in the allocated space. + public ImageSurface(ICompositionGeneratorInternal generator, Uri uri, Size size, ImageSurfaceOptions options) + { + _generator = generator ?? throw new ArgumentException("Generator cannot be null!", nameof(generator)); + + _generator = generator; + _surfaceLock = new object(); + + // Create the Surface of the IImageSurface + _surface = _generator.CreateDrawingSurface(_surfaceLock, size); + Size = _surface?.Size ?? new Size(0, 0); + _uri = uri; + _raiseLoadCompletedEvent = _uri != null; + _canvasBitmap = null; + + // Set the image options + Options = options; + + // Subscribe to DeviceReplaced event + _generator.DeviceReplaced += OnDeviceReplaced; + Status = ImageSurfaceLoadStatus.None; + } + + /// + /// Initializes a new instance of the class. + /// Constructor + /// + /// object. + /// which will be rendered on the . + /// Size of the . + /// The image's resize and alignment options in the allocated space. + internal ImageSurface(ICompositionGeneratorInternal generator, CanvasBitmap surfaceBitmap, Size size, ImageSurfaceOptions options) + { + _generator = generator ?? throw new ArgumentException("Generator cannot be null!", nameof(generator)); + + _generator = generator; + _surfaceLock = new object(); + + // Create the Surface of the IImageSurface + _surface = _generator.CreateDrawingSurface(_surfaceLock, size); + Size = _surface?.Size ?? new Size(0, 0); + _uri = null; + _raiseLoadCompletedEvent = false; + if (surfaceBitmap != null) + { + _canvasBitmap = CanvasBitmap.CreateFromBytes( + _generator.Device, + surfaceBitmap.GetPixelBytes(), + (int)surfaceBitmap.Bounds.Width, + (int)surfaceBitmap.Bounds.Height, + surfaceBitmap.Format); + } + + // Set the image options + Options = options; + + // Subscribe to DeviceReplaced event + _generator.DeviceReplaced += OnDeviceReplaced; + } + + /// + public void Redraw() + { + // Reload the IImageSurface + RedrawSurface(); + } + + /// + public void Redraw(ImageSurfaceOptions options) + { + // Set the image options + Options = options; + + // Redraw the IImageSurface + RedrawSurface(); + } + + /// + public void Redraw(Size size, ImageSurfaceOptions options) + { + // Resize the surface only if AutoResize option is disabled + if (!Options.AutoResize && Size != size) + { + // resize the IImageMaskSurface + _generator.ResizeDrawingSurface(_surfaceLock, _surface, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + } + + // Set the image options + Options = options; + + // Redraw the IImageSurface + RedrawSurface(); + } + + /// + public void Redraw(IImageSurface imageSurface) + { + if (imageSurface != null) + { + Redraw(imageSurface.SurfaceBitmap, imageSurface.Size, imageSurface.Options); + } + else + { + // Draw an empty surface + Redraw(surfaceBitmap: null); + } + } + + /// + public void Redraw(IImageSurface imageSurface, ImageSurfaceOptions options) + { + if (imageSurface != null) + { + Redraw(imageSurface.SurfaceBitmap, imageSurface.Size, options); + } + else + { + // Draw an empty surface + Redraw(surfaceBitmap: null, options); + } + } + + /// + public void Redraw(IImageSurface imageSurface, Size size, ImageSurfaceOptions options) + { + Redraw(imageSurface?.SurfaceBitmap, size, options); + } + + /// + public void Redraw(CanvasBitmap surfaceBitmap) + { + Redraw(surfaceBitmap, Size, Options); + } + + /// + public void Redraw(CanvasBitmap surfaceBitmap, ImageSurfaceOptions options) + { + // Set the image options + Options = options; + + // Redraw the IImageSurface + Redraw(surfaceBitmap, Size, Options); + } + + /// + public void Redraw(CanvasBitmap surfaceBitmap, Size size, ImageSurfaceOptions options) + { + if (_canvasBitmap != surfaceBitmap) + { + // Dispose the previous canvas bitmap resource (if any) + if (_canvasBitmap != null) + { + _canvasBitmap.Dispose(); + _canvasBitmap = null; + } + + if (surfaceBitmap != null) + { + if (_canvasBitmap != surfaceBitmap) + { + // Copy the surface bitmap onto _canvasBitmap + _canvasBitmap = CanvasBitmap.CreateFromBytes( + _generator.Device, + surfaceBitmap.GetPixelBytes(), + (int)surfaceBitmap.Bounds.Width, + (int)surfaceBitmap.Bounds.Height, + surfaceBitmap.Format); + } + } + else + { + _canvasBitmap = null; + } + } + + _uri = null; + _raiseLoadCompletedEvent = false; + + // Set the options + Options = options; + + // Resize the surface only if AutoResize option is disabled + if (!Options.AutoResize && Size != size) + { + // resize the IImageMaskSurface + _generator.ResizeDrawingSurface(_surfaceLock, _surface, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + } + + // Redraw the IImageMaskSurface + RedrawSurface(); + } + + /// + public Task RedrawAsync(Uri uri) + { + return RedrawAsync(uri, Size, Options); + } + + /// + public Task RedrawAsync(Uri uri, ImageSurfaceOptions options) + { + return RedrawAsync(uri, Size, options); + } + + /// + public Task RedrawAsync(Uri uri, Size size, ImageSurfaceOptions options) + { + // If the given Uri differs from the previously stored Uri or if the ImageSurface was + // directly created from a CanvasBitmap, dispose the existing canvasBitmap + if ((_uri != null && !_uri.IsEqualTo(uri)) + || (_uri == null && _canvasBitmap != null)) + { + _canvasBitmap?.Dispose(); + _canvasBitmap = null; + _raiseLoadCompletedEvent = uri != null; + } + + // Set the image options + Options = options; + + // Resize the surface only if AutoResize option is disabled + if (!Options.AutoResize) + { + // resize the IImageSurface + _generator.ResizeDrawingSurface(_surfaceLock, _surface, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + } + + // Set the new Uri of the image to be loaded + _uri = uri; + + // Reload the IImageSurface + return RedrawSurfaceAsync(); + } + + /// + public void Resize(Size size) + { + Resize(size, Options); + } + + /// + public void Resize(Size size, ImageSurfaceOptions options) + { + // Set the image options + Options = options; + + // Resize the surface only if AutoResize option is disabled + if (!Options.AutoResize) + { + // resize the IImageSurface + _generator.ResizeDrawingSurface(_surfaceLock, _surface, size); + + // Set the size + Size = _surface?.Size ?? new Size(0, 0); + } + + // redraw the IImageSurface + RedrawSurface(); + } + + /// + public void Dispose() + { + _surface?.Dispose(); + if (_generator != null) + { + _generator.DeviceReplaced -= OnDeviceReplaced; + } + + _canvasBitmap?.Dispose(); + _canvasBitmap = null; + _surface = null; + _generator = null; + _uri = null; + Options = null; + } + + /// + /// Redraws the asynchronously by loading the image from the Uri. + /// + /// Task + internal Task RedrawAsync() + { + // Reload the IImageSurface + return RedrawSurfaceAsync(); + } + + /// + /// Handles the DeviceReplaced event. + /// + /// Sender. + /// object. + private async void OnDeviceReplaced(object sender, object e) + { + // Recreate the ImageSurface + _surface = _generator.CreateDrawingSurface(_surfaceLock, Size); + + // Reload the IImageSurface + await RedrawSurfaceAsync(); + } + + /// + /// Helper class to redraw the synchronously. + /// + private void RedrawSurface() + { + // Resize the surface image + _generator.RedrawImageSurface(_surfaceLock, _surface, Options, _canvasBitmap); + + // If AutoResize is allowed and the image is successfully loaded into the canvasBitmap, + // then update the Size property of the surface as the surface has been resized to match the canvasBitmap size + if (Options.AutoResize) + { + // If the image is successfully loaded into the canvasBitmap, then update the Size property + // of the surface as the surface has been resized to match the canvasBitmap size + Size = _canvasBitmap?.Size ?? new Size(0, 0); + } + + Status = _canvasBitmap != null ? ImageSurfaceLoadStatus.Success : ImageSurfaceLoadStatus.Error; + + if (_canvasBitmap != null) + { + DecodedPhysicalSize = new Size(_canvasBitmap.SizeInPixels.Width, _canvasBitmap.SizeInPixels.Height); + DecodedSize = _canvasBitmap.Size; + } + } + + /// + /// Helper class to redraw the asynchronously. + /// + /// + private async Task RedrawSurfaceAsync() + { + // Cache the canvasBitmap to avoid reloading of the same image during Resize/Redraw operations + _canvasBitmap = await _generator.RedrawImageSurfaceAsync(_surfaceLock, _surface, _uri, Options, _canvasBitmap); + + // If AutoResize is allowed and the image is successfully loaded into the canvasBitmap, + // then update the Size property of the surface as the surface has been resized to match the canvasBitmap size + if (Options.AutoResize) + { + // If the image is successfully loaded into the canvasBitmap, then update the Size property + // of the surface as the surface has been resized to match the canvasBitmap size + Size = _canvasBitmap?.Size ?? new Size(0, 0); + } + + Status = _canvasBitmap != null ? ImageSurfaceLoadStatus.Success : ImageSurfaceLoadStatus.Error; + + // Get the canvasbitmap dimensions + if (_canvasBitmap != null) + { + DecodedPhysicalSize = new Size(_canvasBitmap.SizeInPixels.Width, _canvasBitmap.SizeInPixels.Height); + DecodedSize = _canvasBitmap.Size; + } + + // Raise the event + if (_raiseLoadCompletedEvent) + { + LoadCompleted?.Invoke(this, Status); + _raiseLoadCompletedEvent = false; + } + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Surface/ImageSurfaceOptions.cs b/Microsoft.Toolkit.Uwp.UI.Media/Surface/ImageSurfaceOptions.cs new file mode 100644 index 00000000000..9e28e6b165c --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Surface/ImageSurfaceOptions.cs @@ -0,0 +1,306 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Microsoft.Graphics.Canvas; +using Windows.UI; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Media; + +namespace Microsoft.Toolkit.Uwp.UI.Media +{ + /// + /// Enum to define the location of the Reflection of a Visual. + /// + public enum ReflectionLocation + { + /// + /// Reflection at bottom of visual + /// + Bottom = 0, + + /// + /// Reflection at top of visual + /// + Top = 1, + + /// + /// Reflection at left of visual + /// + Left = 2, + + /// + /// Reflection at right of visual + /// + Right = 3 + } + + /// + /// Class to define the various options that would influence the rendering of the image on the ImageSurface + /// + public class ImageSurfaceOptions : DependencyObject + { + /// + /// Event which indicates that the components of the have changed. + /// + public event EventHandler Updated; + + /// + /// Gets default ImageSurfaceOptions when AutoResize is True. + /// Uniform Stretch and Center alignment. + /// + public static ImageSurfaceOptions Default => + new ImageSurfaceOptions() + { + AutoResize = true, + Interpolation = CanvasImageInterpolation.HighQualityCubic, + Opacity = 1f, + Stretch = Stretch.Uniform, + HorizontalAlignment = AlignmentX.Center, + VerticalAlignment = AlignmentY.Center, + SurfaceBackgroundColor = Colors.Transparent, + BlurRadius = 0f + }; + + /// + /// Gets default ImageSurfaceOptions when AutoResize is False. + /// Uniform Stretch and Center alignment. + /// + public static ImageSurfaceOptions DefaultOptimized => + new ImageSurfaceOptions() + { + AutoResize = false, + Interpolation = CanvasImageInterpolation.HighQualityCubic, + Opacity = 1f, + Stretch = Stretch.Uniform, + HorizontalAlignment = AlignmentX.Center, + VerticalAlignment = AlignmentY.Center, + SurfaceBackgroundColor = Colors.Transparent, + BlurRadius = 0f + }; + + /// + /// Gets default ImageSurfaceOptions for IImageMaskSurface. + /// Uniform Stretch and Center alignment + /// + public static ImageSurfaceOptions DefaultImageMaskOptions => + new ImageSurfaceOptions() + { + AutoResize = false, + Interpolation = CanvasImageInterpolation.HighQualityCubic, + Opacity = 1f, + Stretch = Stretch.Uniform, + HorizontalAlignment = AlignmentX.Center, + VerticalAlignment = AlignmentY.Center, + SurfaceBackgroundColor = Colors.Transparent, + BlurRadius = 0f + }; + + /// + /// Creates ImageSurfaceOptions for IImageMaskSurface for the given blurRadius - + /// Uniform Stretch and Center alignment + /// + /// Radius of the Gaussian Blur to be applied on the IImageMaskSurface. + /// instance. + public static ImageSurfaceOptions GetDefaultImageMaskOptionsForBlur(float blurRadius) + { + return new ImageSurfaceOptions() + { + AutoResize = false, + Interpolation = CanvasImageInterpolation.HighQualityCubic, + Opacity = 1f, + Stretch = Stretch.Uniform, + HorizontalAlignment = AlignmentX.Center, + VerticalAlignment = AlignmentY.Center, + SurfaceBackgroundColor = Colors.Transparent, + BlurRadius = blurRadius + }; + } + + /// + /// AutoResize Dependency Property + /// + public static readonly DependencyProperty AutoResizeProperty = DependencyProperty.Register( + "AutoResize", + typeof(bool), + typeof(ImageSurfaceOptions), + new PropertyMetadata(true, OnPropertyChanged)); + + /// + /// Gets or sets a value indicating whether specifies whether the IImageSurface should resize itself automatically to match the loaded image size. + /// NOTE: This property is not used by ImageMaskSurfaceBrush or IImageMaskSurface. + /// + public bool AutoResize + { + get => (bool)GetValue(AutoResizeProperty); + set => SetValue(AutoResizeProperty, value); + } + + /// + /// Stretch Dependency Property + /// + public static readonly DependencyProperty StretchProperty = DependencyProperty.Register( + "Stretch", + typeof(Stretch), + typeof(ImageSurfaceOptions), + new PropertyMetadata(Stretch.Uniform, OnPropertyChanged)); + + /// + /// Gets or sets a value describing how image is resized to fill its allocated space. + /// NOTE: This property is taken into consideration only if AutoResize is false. + /// + public Stretch Stretch + { + get => (Stretch)GetValue(StretchProperty); + set => SetValue(StretchProperty, value); + } + + /// + /// HorizontalAlignment Dependency Property + /// + public static readonly DependencyProperty HorizontalAlignmentProperty = DependencyProperty.Register( + "HorizontalAlignment", + typeof(AlignmentX), + typeof(ImageSurfaceOptions), + new PropertyMetadata(AlignmentX.Center, OnPropertyChanged)); + + /// + /// Gets or sets a value describing how image is positioned horizontally in the IImageSurface or IImageMaskSurface. + /// NOTE: This property is taken into consideration only if AutoResize is False. + /// + public AlignmentX HorizontalAlignment + { + get => (AlignmentX)GetValue(HorizontalAlignmentProperty); + set => SetValue(HorizontalAlignmentProperty, value); + } + + /// + /// VerticalAlignment Dependency Property + /// + public static readonly DependencyProperty VerticalAlignmentProperty = DependencyProperty.Register( + "VerticalAlignment", + typeof(AlignmentY), + typeof(ImageSurfaceOptions), + new PropertyMetadata(AlignmentY.Center, OnPropertyChanged)); + + /// + /// Gets or sets a value describing how image is positioned vertically in the IImageSurface or IImageMaskSurface. + /// NOTE: This property is taken into consideration only if AutoResize is False. + /// + public AlignmentY VerticalAlignment + { + get => (AlignmentY)GetValue(VerticalAlignmentProperty); + set => SetValue(VerticalAlignmentProperty, value); + } + + /// + /// Opacity Dependency Property + /// + public static readonly DependencyProperty OpacityProperty = DependencyProperty.Register( + "Opacity", + typeof(double), + typeof(ImageSurfaceOptions), + new PropertyMetadata(1d, OnPropertyChanged)); + + /// + /// Gets or sets the the opacity of the rendered the image in an IImageSurface or the mask in an IImageMaskSurface. + /// + public double Opacity + { + get => (double)GetValue(OpacityProperty); + set => SetValue(OpacityProperty, value); + } + + /// + /// Interpolation Dependency Property + /// + public static readonly DependencyProperty InterpolationProperty = DependencyProperty.Register( + "Interpolation", + typeof(CanvasImageInterpolation), + typeof(ImageSurfaceOptions), + new PropertyMetadata(CanvasImageInterpolation.HighQualityCubic, OnPropertyChanged)); + + /// + /// Gets or sets the the interpolation used to render the image in an IImageSurface or the mask in an IImageMaskSurface. + /// + public CanvasImageInterpolation Interpolation + { + get => (CanvasImageInterpolation)GetValue(InterpolationProperty); + set => SetValue(InterpolationProperty, value); + } + + /// + /// SurfaceBackgroundColor Dependency Property + /// + public static readonly DependencyProperty SurfaceBackgroundColorProperty = DependencyProperty.Register( + "SurfaceBackgroundColor", + typeof(Color), + typeof(ImageSurfaceOptions), + new PropertyMetadata(Colors.Transparent, OnPropertyChanged)); + + /// + /// Gets or sets the color which will be used to fill the IImageSurface in an IImageSurface or the mask in an IImageMaskSurface + /// in case the image is not rendered. + /// + public Color SurfaceBackgroundColor + { + get => (Color)GetValue(SurfaceBackgroundColorProperty); + set => SetValue(SurfaceBackgroundColorProperty, value); + } + + /// + /// BlurRadius Dependency Property + /// + public static readonly DependencyProperty BlurRadiusProperty = DependencyProperty.Register( + "BlurRadius", + typeof(double), + typeof(ImageSurfaceOptions), + new PropertyMetadata(0d, OnPropertyChanged)); + + /// + /// Gets or sets the radius of the Gaussian blur to be applied to the IImageMaskSurface. + /// NOTE: This property is not used by IImageSurface. + /// + public double BlurRadius + { + get => (double)GetValue(BlurRadiusProperty); + set => SetValue(BlurRadiusProperty, value); + } + + /// + /// Method that is called whenever the dependency properties of the ImageSurfaceOptions changes. + /// + /// The object whose property has changed. + /// Event arguments. + private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var options = (ImageSurfaceOptions)d; + + options.OnUpdated(); + } + + /// + /// Raises the Updated event. + /// + private void OnUpdated() + { + Updated?.Invoke(this, null); + } + + /// + /// Initializes a new instance of the class. + /// + public ImageSurfaceOptions() + { + AutoResize = false; + Interpolation = CanvasImageInterpolation.HighQualityCubic; + Opacity = 1f; + Stretch = Stretch.Uniform; + HorizontalAlignment = AlignmentX.Center; + VerticalAlignment = AlignmentY.Center; + SurfaceBackgroundColor = Colors.Transparent; + BlurRadius = 0f; + } + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Surface/Interfaces/ICompositionGenerator.cs b/Microsoft.Toolkit.Uwp.UI.Media/Surface/Interfaces/ICompositionGenerator.cs new file mode 100644 index 00000000000..413f9ff9378 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Surface/Interfaces/ICompositionGenerator.cs @@ -0,0 +1,309 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Numerics; +using System.Threading.Tasks; +using Microsoft.Graphics.Canvas; +using Microsoft.Graphics.Canvas.Brushes; +using Microsoft.Graphics.Canvas.Geometry; +using Microsoft.Toolkit.Uwp.UI.Media.Geometry; +using Windows.Foundation; +using Windows.UI; +using Windows.UI.Composition; +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.UI.Media +{ + /// + /// Interface for the + /// + public interface ICompositionGenerator : IDisposable + { + /// + /// Device Replaced Event + /// + event EventHandler DeviceReplaced; + + /// + /// Gets the . + /// + Compositor Compositor { get; } + + /// + /// Gets the . + /// + CanvasDevice Device { get; } + + /// + /// Creates an Empty having the no size and geometry. + /// NOTE: Use this API if you want to create an Empty first + /// and change its geometry and/or size of the later. + /// + /// + IGeometryMaskSurface CreateGeometryMaskSurface(); + + /// + /// Creates a having the given size and geometry. The geometry is filled + /// with white color. The surface not covered by the geometry is transparent. + /// + /// Size of the mask. + /// Geometry of the mask. + /// + IGeometryMaskSurface CreateGeometryMaskSurface(Size size, CanvasGeometry geometry); + + /// + /// Creates a having the given size and geometry. The geometry is filled + /// with white color. The surface not covered by the geometry is transparent. + /// + /// Size of the mask. + /// Geometry of the mask. + /// The offset from the top left corner of the where the is rendered. + /// + IGeometryMaskSurface CreateGeometryMaskSurface(Size size, CanvasGeometry geometry, Vector2 offset); + + /// + /// Creates an Empty having the no size and geometry. + /// NOTE: Use this API if you want to create an Empty first and change its geometry, size and/or blurRadius of the later. + /// + /// + IGaussianMaskSurface CreateGaussianMaskSurface(); + + /// + /// Creates an having the given size and geometry. The geometry is filled + /// with white color and a Gaussian blur is applied to it. The surface not covered by the geometry is transparent. + /// + /// Size of the mask. + /// Geometry of the mask. + /// The offset from the top left corner of the where the is rendered. + /// Radius of Gaussian Blur to be applied on the . + /// + IGaussianMaskSurface CreateGaussianMaskSurface(Size size, CanvasGeometry geometry, Vector2 offset, float blurRadius); + + /// + /// Creates an Empty having the no size and geometry. + /// NOTE: Use this API if you want to create an Empty first and change its geometry and/or size, fillColor or stroke later. + /// + /// + IGeometrySurface CreateGeometrySurface(); + + /// + /// Creates an having the given size, geometry, stroke. + /// + /// Size of the . + /// Geometry to be rendered on the . + /// defining the outline for the geometry. + /// + IGeometrySurface CreateGeometrySurface(Size size, CanvasGeometry geometry, ICanvasStroke stroke); + + /// + /// Creates an having the given size, geometry, fill color. + /// + /// Size of the . + /// Geometry to be rendered on the . + /// Fill color of the geometry. + /// + IGeometrySurface CreateGeometrySurface(Size size, CanvasGeometry geometry, Color fillColor); + + /// + /// Creates an having the given size, geometry, stroke and fill color. + /// + /// Size of the . + /// Geometry to be rendered on the . + /// defining the outline for the geometry. + /// Fill color of the geometry. + /// + IGeometrySurface CreateGeometrySurface(Size size, CanvasGeometry geometry, ICanvasStroke stroke, Color fillColor); + + /// + /// Creates an having the given size, geometry, fill color and background color. + /// + /// Size of the . + /// Geometry to be rendered on the . + /// Fill color of the geometry. + /// Fill color of the background which is + /// not covered by the geometry. + /// + IGeometrySurface CreateGeometrySurface(Size size, CanvasGeometry geometry, Color fillColor, Color backgroundColor); + + /// + /// Creates an having the given size, geometry, stroke, fill color and background color. + /// + /// Size of the . + /// Geometry to be rendered on the . + /// defining the outline for the geometry. + /// Fill color of the geometry. + /// Fill color of the background which is not covered by the geometry. + /// + IGeometrySurface CreateGeometrySurface(Size size, CanvasGeometry geometry, ICanvasStroke stroke, Color fillColor, Color backgroundColor); + + /// + /// Creates an having the given size, geometry and fill brush. + /// + /// Size of the . + /// Geometry to be rendered on the . + /// The brush with which the geometry has to be filled. + /// + IGeometrySurface CreateGeometrySurface(Size size, CanvasGeometry geometry, ICanvasBrush fillBrush); + + /// + /// Creates an having the given size, geometry, stroke and fill brush. + /// + /// Size of the . + /// Geometry to be rendered on the . + /// defining the outline for the geometry. + /// The brush with which the geometry has to be filled. + /// + IGeometrySurface CreateGeometrySurface(Size size, CanvasGeometry geometry, ICanvasStroke stroke, ICanvasBrush fillBrush); + + /// + /// Creates an having the given size, geometry, fill brush and background brush. + /// + /// Size of the . + /// Geometry to be rendered on the . + /// The brush with which the geometry has to be filled. + /// The brush to fill the background which is not covered by the geometry. + /// + IGeometrySurface CreateGeometrySurface(Size size, CanvasGeometry geometry, ICanvasBrush fillBrush, ICanvasBrush backgroundBrush); + + /// + /// Creates an having the given size, geometry, stroke, fill brush and background brush. + /// + /// Size of the . + /// Geometry to be rendered on the . + /// defining the outline for the geometry. + /// The brush with which the geometry has to be filled. + /// The brush to fill the background which is not covered by the geometry. + /// + IGeometrySurface CreateGeometrySurface(Size size, CanvasGeometry geometry, ICanvasStroke stroke, ICanvasBrush fillBrush, ICanvasBrush backgroundBrush); + + /// + /// Creates an having the given size, geometry, fill brush and background color. + /// + /// Size of the . + /// Geometry to be rendered on the . + /// The brush with which the geometry has to be filled. + /// Fill color of the background which is not covered by the geometry. + /// + IGeometrySurface CreateGeometrySurface(Size size, CanvasGeometry geometry, ICanvasBrush fillBrush, Color backgroundColor); + + /// + /// Creates an having the given size, geometry, stroke, fill brush and + /// background color. + /// + /// Size of the . + /// Geometry to be rendered on the . + /// defining the outline for the geometry. + /// The brush with which the geometry has to be filled. + /// Fill color of the background which is not covered by the geometry. + /// + IGeometrySurface CreateGeometrySurface(Size size, CanvasGeometry geometry, ICanvasStroke stroke, ICanvasBrush fillBrush, Color backgroundColor); + + /// + /// Creates an having the given size, geometry, fill color and background brush. + /// + /// Size of the . + /// Geometry to be rendered on the . + /// Fill color of the geometry. + /// The brush to fill the background which is not covered by the geometry. + /// + IGeometrySurface CreateGeometrySurface(Size size, CanvasGeometry geometry, Color fillColor, ICanvasBrush backgroundBrush); + + /// + /// Creates an having the given size, geometry, stroke, fill color and background brush. + /// + /// Size of the . + /// Geometry to be rendered on the . + /// defining the outline for the geometry. + /// Fill color of the geometry. + /// The brush to fill the background which is not covered by the geometry. + /// + IGeometrySurface CreateGeometrySurface(Size size, CanvasGeometry geometry, ICanvasStroke stroke, Color fillColor, ICanvasBrush backgroundBrush); + + /// + /// Creates an having the given size onto which an image (based on the and the options) is loaded. + /// + /// Uri of the image to be loaded onto the . + /// New size of the . + /// The image's resize and alignment options in the allocated space. + /// <> + Task CreateImageSurfaceAsync(Uri uri, Size size, ImageSurfaceOptions options); + + /// + /// Creates an having the given size onto which the given image is loaded. + /// + /// Image that will be loaded onto the . + /// Size of the . + /// The image's resize and alignment options in the allocated space. + /// + IImageSurface CreateImageSurface(CanvasBitmap bitmap, Size size, ImageSurfaceOptions options); + + /// + /// Creates a copy of the given + /// + /// to copy. + /// + IImageSurface CreateImageSurface(IImageSurface imageSurface); + + /// + /// Creates a copy of the given + /// + /// to copy. + /// + IImageMaskSurface CreateImageMaskSurface(IImageMaskSurface imageMaskSurface); + + /// + /// Creates an having the given size onto which an image (based on the Uri + /// and the options) is loaded. + /// + /// The CanvasBitmap whose alpha values will be used to create the Mask. + /// Size of the . + /// The padding between the outer bounds and the bounds of the area where the mask, created from the given image's alpha values, should be rendered. + /// Radius of the Gaussian blur applied to the the mask. + /// + IImageMaskSurface CreateImageMaskSurface(CanvasBitmap surfaceBitmap, Size size, Thickness padding, float blurRadius); + + /// + /// Creates an having the given size onto which an image (based on the Uri + /// and the options) is loaded. + /// + /// The CanvasBitmap whose alpha values will be used to create the Mask. + /// Size of the . + /// The padding between the outer bounds and the bounds of the area where the mask, created from the given image's alpha values, should be rendered. + /// The image's resize, alignment options and blur radius in the allocated space. + /// + IImageMaskSurface CreateImageMaskSurface(CanvasBitmap surfaceBitmap, Size size, Thickness padding, ImageSurfaceOptions options); + + /// + /// Creates an having the given size onto which an image (based on the Uri + /// and the options) is loaded. + /// + /// The whose image's alpha values will be used to create the Mask. + /// Size of the . + /// The padding between the outer bounds and the bounds of the area where the mask, created from the given image's alpha values, should be rendered. + /// The image's resize, alignment options and blur radius in the allocated space. + /// + IImageMaskSurface CreateImageMaskSurface(IImageSurface imageSurface, Size size, Thickness padding, ImageSurfaceOptions options); + + /// + /// Creates a ImageSurface having the given size onto which an image (based on the Uri + /// and the options) is loaded. + /// + /// Uri of the image whose alpha values will be used to create the Mask. + /// Size of the . + /// The padding between the outer bounds and the bounds of the area where the mask, created from the given image's alpha values, should be rendered. + /// The image's resize, alignment options and blur radius in the allocated space. + /// <> + Task CreateImageMaskSurfaceAsync(Uri uri, Size size, Thickness padding, ImageSurfaceOptions options); + + /// + /// Creates a reflection of the given . + /// + /// whose reflection has to be created. + /// Distance of the reflection from the visual. + /// Normalized Length of the reflected visual that will be visible. + /// - Location of the reflection with respect to the Visual - Bottom, Top, Left or Right. + void CreateReflection(ContainerVisual visual, float reflectionDistance = 0f, float reflectionLength = 0.7f, ReflectionLocation location = ReflectionLocation.Bottom); + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Surface/Interfaces/ICompositionGeneratorInternal.cs b/Microsoft.Toolkit.Uwp.UI.Media/Surface/Interfaces/ICompositionGeneratorInternal.cs new file mode 100644 index 00000000000..82ce6e0c99e --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Surface/Interfaces/ICompositionGeneratorInternal.cs @@ -0,0 +1,114 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Numerics; +using System.Threading.Tasks; +using Microsoft.Graphics.Canvas; +using Microsoft.Graphics.Canvas.Brushes; +using Microsoft.Graphics.Canvas.Geometry; +using Microsoft.Toolkit.Uwp.UI.Media.Geometry; +using Windows.Foundation; +using Windows.UI.Composition; +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.UI.Media +{ + /// + /// Internal interface for the ComposiitonGenerator + /// + internal interface ICompositionGeneratorInternal : ICompositionGenerator + { + /// + /// Creates a for the given size. + /// + /// The object to lock to prevent multiple threads from accessing the surface at the same time. + /// Size of the . + /// + CompositionDrawingSurface CreateDrawingSurface(object surfaceLock, Size size); + + /// + /// Resizes the to the given size. + /// + /// The object to lock to prevent multiple threads from accessing the surface at the same time. + /// . + /// New size of the . + void ResizeDrawingSurface(object surfaceLock, CompositionDrawingSurface surface, Size size); + + /// + /// Redraws the with the given size and geometry. + /// + /// The object to lock to prevent multiple threads from accessing the surface at the same time. + /// . + /// Size of the . + /// Geometry of the . + /// The offset from the top left corner of the where the is rendered. + void RedrawGeometryMaskSurface(object surfaceLock, CompositionDrawingSurface surface, Size size, CanvasGeometry geometry, Vector2 offset); + + /// + /// Redraws the with the given size and geometry. + /// + /// The object to lock to prevent multiple threads from accessing the surface at the same time. + /// . + /// Size of the . + /// Geometry of the . + /// The offset from the top left corner of the ICompositionSurface where the is rendered. + /// Radius of Gaussian Blur to be applied on the . + void RedrawGaussianMaskSurface(object surfaceLock, CompositionDrawingSurface surface, Size size, CanvasGeometry geometry, Vector2 offset, float blurRadius); + + /// + /// Redraws the with the given size, geometry, foreground brush and background brush. + /// + /// The object to lock to prevent multiple threads from accessing the surface at the same time. + /// . + /// Size of the . + /// Geometry of the . + /// defining the outline for the geometry. + /// The brush with which the geometry has to be filled. + /// The brush with which the background has to be filled. + void RedrawGeometrySurface(object surfaceLock, CompositionDrawingSurface surface, Size size, CanvasGeometry geometry, ICanvasStroke stroke, ICanvasBrush fillBrush, ICanvasBrush backgroundBrush); + + /// + /// Resizes the ImageSurface to the given size and redraws the by rendering the canvasBitmap onto the surface. + /// + /// The object to lock to prevent multiple threads from accessing the surface at the same time. + /// . + /// Describes the image's resize and alignment options in the allocated space. + /// The on which the image is loaded. + void RedrawImageSurface(object surfaceLock, CompositionDrawingSurface surface, ImageSurfaceOptions options, CanvasBitmap canvasBitmap); + + /// + /// Resizes the with the given size and redraws the by loading image from the new Uri. + /// + /// The object to lock to prevent multiple threads from accessing the surface at the same time. + /// . + /// Uri of the image to be loaded onto the . + /// Describes the image's resize and alignment options in the allocated space. + /// The on which the image is loaded. + /// + Task RedrawImageSurfaceAsync(object surfaceLock, CompositionDrawingSurface surface, Uri uri, ImageSurfaceOptions options, CanvasBitmap canvasBitmap); + + /// + /// Resizes the to the given size and redraws the IImageMaskSurface by rendering the mask using the image's alpha values onto the surface. + /// + /// The object to lock to prevent multiple threads from accessing the surface at the same time. + /// . + /// The padding between the outer bounds and the bounds of the area where the mask, created from the loaded image's alpha values, should be rendered. + /// Describes the image's resize and alignment options and blur radius in the allocated space. + /// The image whose alpha values is used to create the . + void RedrawImageMaskSurface(object surfaceLock, CompositionDrawingSurface surface, Thickness padding, ImageSurfaceOptions options, CanvasBitmap surfaceBitmap); + + /// + /// Resizes the to the given size and redraws the by loading the image from the new and rendering the mask using the image's alpha values onto the surface. + /// + /// The object to lock to prevent multiple threads from accessing the surface at the same time. + /// . + /// Uri of the image to be loaded onto the . + /// The padding between the outer bounds and the bounds of the area where the mask, created from the loaded image's alpha values, should be rendered. + /// Describes the image's resize and alignment options and blur radius in the allocated space. + /// The on which the image is loaded. + /// The whose alpha values is used to create the . + Task RedrawImageMaskSurfaceAsync(object surfaceLock, CompositionDrawingSurface surface, Uri uri, Thickness padding, ImageSurfaceOptions options, CanvasBitmap surfaceBitmap); + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Surface/Interfaces/IGaussianMaskSurface.cs b/Microsoft.Toolkit.Uwp.UI.Media/Surface/Interfaces/IGaussianMaskSurface.cs new file mode 100644 index 00000000000..4bd955e35f4 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Surface/Interfaces/IGaussianMaskSurface.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Numerics; +using Microsoft.Graphics.Canvas.Geometry; +using Windows.Foundation; +using Windows.UI.Composition; + +namespace Microsoft.Toolkit.Uwp.UI.Media +{ + /// + /// Interface for rendering custom shaped geometries onto so that they can be used as masks on Composition Visuals. + /// These geometries have a Gaussian Blur applied to them. + /// + public interface IGaussianMaskSurface : IGeometryMaskSurface + { + /// + /// Gets radius of Gaussian Blur to be applied on the . + /// + float BlurRadius { get; } + + /// + /// Applies the given blur radius to the . + /// + /// Radius of Gaussian Blur to be applied on the . + void Redraw(float blurRadius); + + /// + /// Redraws the with the new geometry and fills it with White color after applying the Gaussian blur with given blur radius. + /// + /// New to be applied to the . + /// The offset from the top left corner of the where the is rendered. + /// Radius of Gaussian Blur to be applied on the . + void Redraw(CanvasGeometry geometry, Vector2 offset, float blurRadius); + + /// + /// Resizes the with the given size and redraws the with the new geometry and fills it with White color. + /// + /// New size of the mask + /// New to be applied to the . + /// The offset from the top left corner of the where the is rendered. + /// Radius of Gaussian Blur to be applied on the . + void Redraw(Size size, CanvasGeometry geometry, Vector2 offset, float blurRadius); + } +} \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Surface/Interfaces/IGeometryMaskSurface.cs b/Microsoft.Toolkit.Uwp.UI.Media/Surface/Interfaces/IGeometryMaskSurface.cs new file mode 100644 index 00000000000..173d8cbb042 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Surface/Interfaces/IGeometryMaskSurface.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Numerics; +using Microsoft.Graphics.Canvas.Geometry; +using Windows.Foundation; +using Windows.UI.Composition; + +namespace Microsoft.Toolkit.Uwp.UI.Media +{ + /// + /// Interface for rendering custom shaped geometries onto so that they can be used as masks on Composition Visuals. + /// + public interface IGeometryMaskSurface : IRenderSurface + { + /// + /// Gets the of the . + /// + CanvasGeometry Geometry { get; } + + /// + /// Gets the offset from the top left corner of the where the is rendered. + /// + Vector2 Offset { get; } + + /// + /// Redraws the with the new geometry + /// + /// New to be applied to the . + void Redraw(CanvasGeometry geometry); + + /// + /// Resizes the with the given size and redraws the with the new geometry and fills it with White color. + /// + /// New size of the mask. + /// New to be applied to the . + void Redraw(Size size, CanvasGeometry geometry); + + /// + /// Redraws the with the new geometry. + /// + /// New to be applied to the . + /// The offset from the top left corner of the where the is rendered. + void Redraw(CanvasGeometry geometry, Vector2 offset); + + /// + /// Resizes the with the given size and redraws the with the new geometry and fills it with White color. + /// + /// New size of the mask. + /// New to be applied to the . + /// The offset from the top left corner of the where the is rendered. + void Redraw(Size size, CanvasGeometry geometry, Vector2 offset); + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Surface/Interfaces/IGeometrySurface.cs b/Microsoft.Toolkit.Uwp.UI.Media/Surface/Interfaces/IGeometrySurface.cs new file mode 100644 index 00000000000..2e5dd7c773d --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Surface/Interfaces/IGeometrySurface.cs @@ -0,0 +1,260 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#pragma warning disable CS0419 // Ambiguous reference in cref attribute + +using Microsoft.Graphics.Canvas.Brushes; +using Microsoft.Graphics.Canvas.Geometry; +using Microsoft.Toolkit.Uwp.UI.Media.Geometry; +using Windows.Foundation; +using Windows.UI; + +namespace Microsoft.Toolkit.Uwp.UI.Media +{ + /// + /// Interface for rendering custom shaped geometries onto ICompositionSurface. + /// + public interface IGeometrySurface : IRenderSurface + { + /// + /// Gets the Surface . + /// + CanvasGeometry Geometry { get; } + + /// + /// Gets the Stroke with which the Geometry is outlined. + /// + ICanvasStroke Stroke { get; } + + /// + /// Gets the Brush with which the Geometry is filled. + /// + ICanvasBrush Fill { get; } + + /// + /// Gets the Brush with which the background is filled. + /// + ICanvasBrush BackgroundBrush { get; } + + /// + /// Redraws the with the new geometry + /// + /// New to be applied to the . + void Redraw(CanvasGeometry geometry); + + /// + /// Redraws the by outlining the existing geometry with the given . + /// + /// defining the outline for the geometry. + void Redraw(ICanvasStroke stroke); + + /// + /// Redraws the by filling the existing geometry with the fill color. + /// + /// with which the geometry is to be filled. + void Redraw(Color fillColor); + + /// + /// Redraws the by filling the existing geometry with the given fill color and outlining it with the given . + /// + /// The defining the outline for the geometry. + /// with which the geometry is to be filled. + void Redraw(ICanvasStroke stroke, Color fillColor); + + /// + /// Redraws the by filling the existing geometry with the fill color and the background with the background color. + /// + /// with which the geometry is to be filled. + /// with which the background is to be filled. + void Redraw(Color fillColor, Color backgroundColor); + + /// + /// Redraws the by outlining the existing geometry with the given , filling it with the fill color and the background with the background color. + /// + /// The defining the outline for the geometry. + /// with which the geometry is to be filled. + /// with which the background is to be filled. + void Redraw(ICanvasStroke stroke, Color fillColor, Color backgroundColor); + + /// + /// Redraws the by filling the existing geometry with the fill brush. + /// + /// Brush with which the geometry is to be filled. + void Redraw(ICanvasBrush fillBrush); + + /// + /// Redraws the by outlining the existing geometry with the given , filling it with the fill brush. + /// + /// The defining the outline for the geometry. + /// Brush with which the geometry is to be filled. + void Redraw(ICanvasStroke stroke, ICanvasBrush fillBrush); + + /// + /// Redraws the by filling the existing geometry with the fill brush and the background with the background brush. + /// + /// Brush with which the geometry is to be filled. + /// Brush with which the background is to be filled. + void Redraw(ICanvasBrush fillBrush, ICanvasBrush backgroundBrush); + + /// + /// Redraws the by outlining the existing geometry with the given , filling it with the fill brush and the background with the background brush. + /// + /// The defining the outline for the geometry. + /// Brush with which the geometry is to be filled. + /// Brush with which the background is to be filled. + void Redraw(ICanvasStroke stroke, ICanvasBrush fillBrush, ICanvasBrush backgroundBrush); + + /// + /// Redraws the by filling the existing geometry with the fill color and the background with the background brush. + /// + /// with which the geometry is to be filled. + /// Brush with which the background is to be filled. + void Redraw(Color fillColor, ICanvasBrush backgroundBrush); + + /// + /// Redraws the by outlining the existing geometry with the given , filling it with the fill color and the background with the background brush. + /// + /// The defining the outline for the geometry. + /// with which the geometry is to be filled. + /// Brush with which the background is to be filled. + void Redraw(ICanvasStroke stroke, Color fillColor, ICanvasBrush backgroundBrush); + + /// + /// Redraws the by filling the existing geometry with the fill brush and the background with the background color. + /// + /// Brush with which the geometry is to be filled. + /// with which the background is to be filled. + void Redraw(ICanvasBrush fillBrush, Color backgroundColor); + + /// + /// Redraws the by outlining the existing geometry with the given , filling it with the fill brush and the background with the background color. + /// + /// The defining the outline for the geometry. + /// Brush with which the geometry is to be filled. + /// with which the background is to be filled. + void Redraw(ICanvasStroke stroke, ICanvasBrush fillBrush, Color backgroundColor); + + /// + /// Resizes the with the given size and redraws the with the new geometry. + /// + /// New size of the . + /// New to be applied to the . + void Redraw(Size size, CanvasGeometry geometry); + + /// + /// Resizes the with the given size and redraws the with the new geometry and outlines it with the given . + /// + /// New size of the . + /// New to be applied to the . + /// The defining the outline for the geometry. + void Redraw(Size size, CanvasGeometry geometry, ICanvasStroke stroke); + + /// + /// Resizes the with the given size and redraws the with the new geometry and fills it with the fill color. + /// + /// New size of the . + /// New to be applied to the . + /// Fill color for the geometry. + void Redraw(Size size, CanvasGeometry geometry, Color fillColor); + + /// + /// Resizes the with the given size and redraws the with the new geometry, outlines it with the given and fills it with the fill color. + /// + /// New size of the . + /// New to be applied to the . + /// The defining the outline for the geometry. + /// Fill color for the geometry. + void Redraw(Size size, CanvasGeometry geometry, ICanvasStroke stroke, Color fillColor); + + /// + /// Resizes the with the given size and redraws the with the new geometry and fills it with the fill color and fills the background with the background color. + /// + /// New size of the . + /// New to be applied to the . + /// Fill color for the geometry. + /// Fill color for the background. + void Redraw(Size size, CanvasGeometry geometry, Color fillColor, Color backgroundColor); + + /// + /// Resizes the with the given size and redraws the with the new geometry, outlines it with the given and + /// fills it with the fill color and fills the background with the background color. + /// + /// New size of the . + /// New to be applied to the . + /// The defining the outline for the geometry. + /// Fill color for the geometry. + /// Fill color for the background. + void Redraw(Size size, CanvasGeometry geometry, ICanvasStroke stroke, Color fillColor, Color backgroundColor); + + /// + /// Resizes the with the given size and redraws the with the new geometry and fills it with the fill brush. + /// + /// New size of the . + /// New to be applied to the . + /// Brush to fill the geometry. + void Redraw(Size size, CanvasGeometry geometry, ICanvasBrush fillBrush); + + /// + /// Resizes the with the given size and redraws the with the new geometry and fills it with the fill brush and fills the background with the background brush. + /// + /// New size of the . + /// New to be applied to the . + /// Brush to fill the geometry. + /// Brush to fill the background. + void Redraw(Size size, CanvasGeometry geometry, ICanvasBrush fillBrush, ICanvasBrush backgroundBrush); + + /// + /// Resizes the with the given size and redraws the with the new geometry, outlines it with the given and + /// fills it with the fill brush and fills the background with the background brush. + /// + /// New size of the . + /// New to be applied to the . + /// The defining the outline for the geometry. + /// Brush to fill the geometry. + /// Brush to fill the background. + void Redraw(Size size, CanvasGeometry geometry, ICanvasStroke stroke, ICanvasBrush fillBrush, ICanvasBrush backgroundBrush); + + /// + /// Resizes the with the given size and redraws the with the new geometry and fills it with the fill brush and the background with the background color. + /// + /// New size of the . + /// New to be applied to the . + /// Brush to fill the geometry. + /// Fill color for the background. + void Redraw(Size size, CanvasGeometry geometry, ICanvasBrush fillBrush, Color backgroundColor); + + /// + /// Resizes the with the given size and redraws the with the new geometry, outlines it with the given and + /// fills it with the fill brush and the background with the background color. + /// + /// New size of the . + /// New to be applied to the . + /// The defining the outline for the geometry. + /// Brush to fill the geometry. + /// Fill color for the background. + void Redraw(Size size, CanvasGeometry geometry, ICanvasStroke stroke, ICanvasBrush fillBrush, Color backgroundColor); + + /// + /// Resizes the with the given size and redraws the with the new geometry and fills it with the fill color and the background with the background brush. + /// + /// New size of the . + /// New to be applied to the . + /// Fill color for the geometry. + /// Brush to fill the background. + void Redraw(Size size, CanvasGeometry geometry, Color fillColor, ICanvasBrush backgroundBrush); + + /// + /// Resizes the with the given size and redraws the with the new geometry, outlines it with the given and + /// fills it with the fill color and the background with the background brush. + /// + /// New size of the . + /// New to be applied to the . + /// The defining the outline for the geometry. + /// Fill color for the geometry. + /// Brush to fill the background. + void Redraw(Size size, CanvasGeometry geometry, ICanvasStroke stroke, Color fillColor, ICanvasBrush backgroundBrush); + } +} + +#pragma warning restore CS0419 // Ambiguous reference in cref attribute diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Surface/Interfaces/IImageMaskSurface.cs b/Microsoft.Toolkit.Uwp.UI.Media/Surface/Interfaces/IImageMaskSurface.cs new file mode 100644 index 00000000000..8e8d184d92e --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Surface/Interfaces/IImageMaskSurface.cs @@ -0,0 +1,151 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Threading.Tasks; +using Microsoft.Graphics.Canvas; +using Windows.Foundation; +using Windows.UI.Composition; +using Windows.UI.Xaml; + +namespace Microsoft.Toolkit.Uwp.UI.Media +{ + /// + /// Interface for rendering a mask, using an Image's alpha values, onto an . + /// + public interface IImageMaskSurface : IImageSurface + { + /// + /// Gets the padding between the outer bounds and the bounds of the area where the mask, created from the loaded image's alpha values, should be rendered. + /// + Thickness MaskPadding { get; } + + /// + /// Redraws the using the given 's alpha values with the given padding. + /// + /// whose image's alpha values are to be used to create the mask. + /// The padding between the outer bounds and the bounds of the area where the mask, created from the loaded image's alpha values, should be rendered. + void Redraw(IImageSurface imageSurface, Thickness padding); + + /// + /// Redraws the using the given 's alpha values with the given padding using the given options. + /// + /// whose image's alpha values are to be used to create the mask. + /// The padding between the outer bounds and the bounds of the area where the mask, created from the loaded image's alpha values, should be rendered. + /// Describes the image's resize, alignment and blur radius options in the allocated space. + void Redraw(IImageSurface imageSurface, Thickness padding, ImageSurfaceOptions options); + + /// + /// Resizes and redraws the using the given 's alpha values with the given padding using the given options. + /// + /// whose image's alpha values are to be used to create the mask. + /// New size of the . + /// The padding between the outer bounds and the bounds of the area where the mask, created from the loaded image's alpha values, should be rendered. + /// Describes the image's resize, alignment and blur radius options in the allocated space. + void Redraw(IImageSurface imageSurface, Size size, Thickness padding, ImageSurfaceOptions options); + + /// + /// Redraws the using the given 's alpha values with the given padding. + /// + /// Image whose alpha values are to be used to create the mask. + /// The padding between the outer bounds and the bounds of the area where the mask, created from the loaded image's alpha values, should be rendered. + void Redraw(CanvasBitmap surfaceBitmap, Thickness padding); + + /// + /// Redraws the using the given 's alpha values with the given padding using the given options. + /// + /// Image whose alpha values are to be used to create the mask. + /// The padding between the outer bounds and the bounds of the area where the mask, created from the loaded image's alpha values, should be rendered. + /// Describes the image's resize, alignment and blur radius options in the allocated space. + void Redraw(CanvasBitmap surfaceBitmap, Thickness padding, ImageSurfaceOptions options); + + /// + /// Redraws the using the alpha values of the image in the given . + /// + /// whose image's alpha values are to be used to create the mask. + void Redraw(IImageMaskSurface imageMaskSurface); + + /// + /// Redraws the using the alpha values of the image in the given with the given padding and options. + /// + /// whose image's alpha values are to be used to create the mask. + /// The padding between the outer bounds and the bounds of the area where the mask, created from the loaded image's alpha values, should be rendered. + void Redraw(IImageMaskSurface imageMaskSurface, Thickness padding); + + /// + /// Redraws the using the alpha values of the image in the given with the given options. + /// + /// whose image's alpha values are to be used to create the mask. + /// Describes the image's resize, alignment and blur radius options in the allocated space. + void Redraw(IImageMaskSurface imageMaskSurface, ImageSurfaceOptions options); + + /// + /// Redraws the using the alpha values of the image in the given using the given padding and options. + /// + /// whose image's alpha values are to be used to create the mask. + /// The padding between the outer bounds and the bounds of the area where the mask, created from the loaded image's alpha values, should be rendered. + /// Describes the image's resize, alignment and blur radius options in the allocated space. + void Redraw(IImageMaskSurface imageMaskSurface, Thickness padding, ImageSurfaceOptions options); + + /// + /// Resizes and redraws the using the alpha values of the image in the given with the given padding and options. + /// + /// whose image's alpha values are to be used to create the mask. + /// New size of the . + /// The padding between the outer bounds and the bounds of the area where the mask, created from the loaded image's alpha values, should be rendered. + /// Describes the image's resize, alignment and blur radius options in the allocated space. + void Redraw(IImageMaskSurface imageMaskSurface, Size size, Thickness padding, ImageSurfaceOptions options); + + /// + /// Resizes and redraws the using the given 's alpha values with the given padding using the given options. + /// + /// The padding between the outer bounds and the bounds of the area where the mask, created from the loaded image's alpha values, should be rendered. + /// Describes the image's resize, alignment and blur radius options in the allocated space. + void Redraw(Thickness padding, ImageSurfaceOptions options); + + /// + /// Resizes and redraws the using the given padding and image options. + /// + /// New size of the . + /// The padding between the outer bounds and the bounds of the area where the mask, created from the loaded image's alpha values, should be rendered. + /// Describes the image's resize, alignment and blur radius options in the allocated space. + void Redraw(Size size, Thickness padding, ImageSurfaceOptions options); + + /// + /// Resizes and redraws the using the given 's alpha values with the given padding using the given options. + /// + /// Image whose alpha values are to be used to create the mask. + /// New size of the . + /// The padding between the outer bounds and the bounds of the area where the mask, created from the loaded image's alpha values, should be rendered. + /// Describes the image's resize, alignment and blur radius options in the allocated space. + void Redraw(CanvasBitmap surfaceBitmap, Size size, Thickness padding, ImageSurfaceOptions options); + + /// + /// Redraws the by loading image from the new and image options. + /// + /// Uri of the image to be loaded on to the image surface. + /// The padding between the outer bounds and the bounds of the area where the mask, created from the loaded image's alpha values, should be rendered. + /// Describes the image's resize, alignment and blur radius options in the allocated space. + /// Task + Task RedrawAsync(Uri uri, Thickness padding, ImageSurfaceOptions options); + + /// + /// Resizes the with the given size and loads the image from the new Uri and uses its alpha values to redraw the IImageMaskSurface with the given padding using the given options. + /// + /// Uri of the image to be loaded onto the . + /// New size of the + /// The padding between the outer bounds and the bounds of the area where the mask, created from the loaded image's alpha values, should be rendered. + /// Describes the image's resize, alignment and blur radius options in the allocated space. + /// Task + Task RedrawAsync(Uri uri, Size size, Thickness padding, ImageSurfaceOptions options); + + /// + /// Resizes the to the new size and redraws it with the given padding using the given options. + /// + /// New size of the + /// The padding between the outer bounds and the bounds of the area where the mask, created from the loaded image's alpha values, should be rendered. + /// The image's resize, alignment and blur radius options in the allocated space. + void Resize(Size size, Thickness padding, ImageSurfaceOptions options); + } +} \ No newline at end of file diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Surface/Interfaces/IImageSurface.cs b/Microsoft.Toolkit.Uwp.UI.Media/Surface/Interfaces/IImageSurface.cs new file mode 100644 index 00000000000..0552fafc2f1 --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Surface/Interfaces/IImageSurface.cs @@ -0,0 +1,161 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Threading.Tasks; +using Microsoft.Graphics.Canvas; +using Windows.Foundation; +using Windows.UI.Composition; + +namespace Microsoft.Toolkit.Uwp.UI.Media +{ + /// + /// Enumeration to describe the status of the loading of an image on the IImageSurface. + /// + public enum ImageSurfaceLoadStatus + { + /// + /// Indicates that no image has been loaded on the . + /// + None, + + /// + /// Indicates that the image was successfully loaded on the . + /// + Success, + + /// + /// Indicates that the image could not be loaded on the . + /// + Error + } + + /// + /// Interface for rendering an image onto an + /// + public interface IImageSurface : IRenderSurface + { + /// + /// Event that is raised when the image has been downloaded, decoded and loaded to the underlying . + /// This event fires regardless of success or failure. + /// + event TypedEventHandler LoadCompleted; + + /// + /// Gets the Uri of the image to be loaded onto the . + /// + Uri Uri { get; } + + /// + /// Gets the representing the loaded image. + /// + CanvasBitmap SurfaceBitmap { get; } + + /// + /// Gets the image's resize and alignment options in the allocated space. + /// + ImageSurfaceOptions Options { get; } + + /// + /// Gets the size of the decoded image in physical pixels. + /// + Size DecodedPhysicalSize { get; } + + /// + /// Gets the size of the decoded image in device independent pixels. + /// + Size DecodedSize { get; } + + /// + /// Gets the status whether the image was loaded successfully or not. + /// + ImageSurfaceLoadStatus Status { get; } + + /// + /// Redraws the with the given image options. + /// + /// The image's resize and alignment options in the allocated space. + void Redraw(ImageSurfaceOptions options); + + /// + /// Resizes and redraws the using the given options. + /// + /// New size of the . + /// Describes the image's resize, alignment options in the allocated space. + void Redraw(Size size, ImageSurfaceOptions options); + + /// + /// Redraws the (using the image in the given imageSurface) or the (using the alpha values of image in the given imageSurface). + /// + /// IImageSurface whose image is to be loaded on the surface. + void Redraw(IImageSurface imageSurface); + + /// + /// Redraws the (using the given ) or the (using the given 's alpha values) using the given options. + /// + /// whose image is to be loaded on the surface. + /// Describes the image's resize, alignment options in the allocated space. + void Redraw(IImageSurface imageSurface, ImageSurfaceOptions options); + + /// + /// Resizes and redraws the (using the given ) or the (using the given 's alpha values) using the given options. + /// + /// whose image is to be loaded on the surface. + /// New size of the . + /// Describes the image's resize, alignment options in the allocated space. + void Redraw(IImageSurface imageSurface, Size size, ImageSurfaceOptions options); + + /// + /// Redraws the (using the given ) or the (using the given 's alpha values). + /// + /// Image to be loaded on the surface. + void Redraw(CanvasBitmap surfaceBitmap); + + /// + /// Redraws the (using the given ) or the (using the given 's alpha values) using the given options. + /// + /// Image to be loaded on the surface. + /// Describes the image's resize, alignment options in the allocated space. + void Redraw(CanvasBitmap surfaceBitmap, ImageSurfaceOptions options); + + /// + /// Resizes and redraws the (using the given ) or the (using the given 's alpha values) using the given options. + /// + /// Image to be loaded on the surface.. + /// New size of the IImageMaskSurface. + /// Describes the image's resize, alignment options in the allocated space. + void Redraw(CanvasBitmap surfaceBitmap, Size size, ImageSurfaceOptions options); + + /// + /// Redraws the or by loading image from the new . + /// + /// Uri of the image to be loaded on to the surface. + /// Task + Task RedrawAsync(Uri uri); + + /// + /// Redraws the or by loading image from the new and applying the image options. + /// + /// Uri of the image to be loaded on to the surface. + /// The image's resize and alignment options in the allocated space. + /// Task + Task RedrawAsync(Uri uri, ImageSurfaceOptions options); + + /// + /// Resizes the or with the given size and redraws it by loading image from the new Uri. + /// + /// Uri of the image to be loaded onto the . + /// New size of the + /// The image's resize and alignment options in the allocated space. + /// Task + Task RedrawAsync(Uri uri, Size size, ImageSurfaceOptions options); + + /// + /// Resizes the or to the new size with the given image options. + /// + /// New size of the . + /// The image's resize and alignment options in the allocated space. + void Resize(Size size, ImageSurfaceOptions options); + } +} diff --git a/Microsoft.Toolkit.Uwp.UI.Media/Surface/Interfaces/IRenderSurface.cs b/Microsoft.Toolkit.Uwp.UI.Media/Surface/Interfaces/IRenderSurface.cs new file mode 100644 index 00000000000..b2d2d1d277b --- /dev/null +++ b/Microsoft.Toolkit.Uwp.UI.Media/Surface/Interfaces/IRenderSurface.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Windows.Foundation; +using Windows.UI.Composition; + +namespace Microsoft.Toolkit.Uwp.UI.Media +{ + /// + /// Represents the core interface for interfaces which render onto ICompositionSurface. + /// + public interface IRenderSurface : IDisposable + { + /// + /// Gets the . + /// + ICompositionGenerator Generator { get; } + + /// + /// Gets the . + /// + ICompositionSurface Surface { get; } + + /// + /// Gets the Surface Size. + /// + Size Size { get; } + + /// + /// Redraws the surface. + /// + void Redraw(); + + /// + /// Resizes the surface to the specified size. + /// + /// New size of the surface + void Resize(Size size); + } +} diff --git a/UnitTests/UnitTests.UWP/Geometry/Test_RegexFactory.cs b/UnitTests/UnitTests.UWP/Geometry/Test_RegexFactory.cs index 69b7cb0f0b8..bba3dd22567 100644 --- a/UnitTests/UnitTests.UWP/Geometry/Test_RegexFactory.cs +++ b/UnitTests/UnitTests.UWP/Geometry/Test_RegexFactory.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core; +using Microsoft.Toolkit.Uwp.UI.Media.Geometry; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace UnitTests.UWP.Geometry