Merge branch 'master' into aleader/notif-api-consistency

This commit is contained in:
Kyaa Dost 2021-02-09 08:38:53 -08:00 коммит произвёл GitHub
Родитель 48c779b2bf 676df13c08
Коммит 8b9742062d
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
152 изменённых файлов: 9166 добавлений и 239 удалений

Просмотреть файл

@ -0,0 +1,97 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard1.4;netstandard2.0;netstandard2.1;net5.0</TargetFrameworks>
<LangVersion>9.0</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>enable</Nullable>
<Title>Windows Community Toolkit Diagnostics .NET Standard</Title>
<Description>
This package includes .NET Standard code only helpers such as:
- Guard: Helper methods to verify conditions when running code.
- ThrowHelper: Helper methods to efficiently throw exceptions.
</Description>
<PackageTags>UWP Toolkit Windows IncrementalLoadingCollection String Array extensions helpers</PackageTags>
</PropertyGroup>
<Choose>
<When Condition=" '$(TargetFramework)' == 'netstandard1.4' ">
<ItemGroup>
<!-- .NET Standard 1.4 doesn't have the Span<T> type, ValueTuple or the [Pure] attribute -->
<PackageReference Include="System.Diagnostics.Contracts" Version="4.3.0" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="System.Memory" Version="4.5.4" />
</ItemGroup>
</When>
<When Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<ItemGroup>
<!-- .NET Standard 2.0 doesn't have the Span<T> type -->
<PackageReference Include="System.Memory" Version="4.5.4" />
</ItemGroup>
</When>
<When Condition=" '$(TargetFramework)' == 'netstandard2.1' ">
<PropertyGroup>
<DefineConstants>NETSTANDARD2_1_OR_GREATER</DefineConstants>
</PropertyGroup>
<ItemGroup>
<!-- .NET Standard 2.1 doesn't have the Unsafe type -->
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="5.0.0" />
</ItemGroup>
</When>
<When Condition=" '$(TargetFramework)' == 'net5.0' ">
<PropertyGroup>
<DefineConstants>NETSTANDARD2_1_OR_GREATER</DefineConstants>
</PropertyGroup>
</When>
</Choose>
<ItemGroup>
<None Update="Generated\Guard.Comparable.Numeric.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>Guard.Comparable.Numeric.g.cs</LastGenOutput>
</None>
<None Update="Generated\Guard.Collection.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>Guard.Collection.g.cs</LastGenOutput>
</None>
<None Update="Generated\ThrowHelper.Collection.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>ThrowHelper.Collection.g.cs</LastGenOutput>
</None>
<None Update="Generated\TypeInfo.ttinclude">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>TypeInfo.g.cs</LastGenOutput>
</None>
</ItemGroup>
<!-- T4 service used by the Guard APIs -->
<ItemGroup>
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
</ItemGroup>
<ItemGroup>
<Compile Update="Generated\Guard.Comparable.Numeric.g.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Guard.Comparable.Numeric.tt</DependentUpon>
</Compile>
<Compile Update="Generated\Guard.Collection.g.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Guard.Collection.tt</DependentUpon>
</Compile>
<Compile Update="Generated\ThrowHelper.Collection.g.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>ThrowHelper.Collection.tt</DependentUpon>
</Compile>
<Compile Update="Generated\TypeInfo.g.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>TypeInfo.ttinclude</DependentUpon>
</Compile>
</ItemGroup>
</Project>

Просмотреть файл

@ -11,7 +11,6 @@ using Microsoft.Toolkit.HighPerformance.Buffers.Internals;
#endif
using Microsoft.Toolkit.HighPerformance.Enumerables;
using Microsoft.Toolkit.HighPerformance.Helpers.Internals;
using Microsoft.Toolkit.HighPerformance.Memory;
using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers;
namespace Microsoft.Toolkit.HighPerformance.Extensions

Просмотреть файл

@ -10,7 +10,6 @@ using System.Runtime.InteropServices;
using Microsoft.Toolkit.HighPerformance.Buffers.Internals;
#endif
using Microsoft.Toolkit.HighPerformance.Helpers.Internals;
using Microsoft.Toolkit.HighPerformance.Memory;
using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers;
namespace Microsoft.Toolkit.HighPerformance.Extensions

Просмотреть файл

@ -23,7 +23,14 @@ namespace Microsoft.Toolkit.HighPerformance.Extensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe byte ToByte(this bool flag)
{
return *(byte*)&flag;
// Whenever we need to take the address of an argument, we make a local copy first.
// This will be removed by the JIT anyway, but it can help produce better codegen and
// remove unwanted stack spills if the caller is using constant arguments. This is
// because taking the address of an argument can interfere with some of the flow
// analysis executed by the JIT, which can in some cases block constant propagation.
bool copy = flag;
return *(byte*)&copy;
}
/// <summary>
@ -58,7 +65,8 @@ namespace Microsoft.Toolkit.HighPerformance.Extensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe int ToBitwiseMask32(this bool flag)
{
byte rangeFlag = *(byte*)&flag;
bool copy = flag;
byte rangeFlag = *(byte*)&copy;
int
negativeFlag = rangeFlag - 1,
mask = ~negativeFlag;
@ -77,7 +85,8 @@ namespace Microsoft.Toolkit.HighPerformance.Extensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe long ToBitwiseMask64(this bool flag)
{
byte rangeFlag = *(byte*)&flag;
bool copy = flag;
byte rangeFlag = *(byte*)&copy;
long
negativeFlag = (long)rangeFlag - 1,
mask = ~negativeFlag;

Просмотреть файл

@ -7,9 +7,6 @@ using System.Diagnostics.Contracts;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if SPAN_RUNTIME_SUPPORT
using Microsoft.Toolkit.HighPerformance.Memory;
#endif
using MemoryStream = Microsoft.Toolkit.HighPerformance.Streams.MemoryStream;
namespace Microsoft.Toolkit.HighPerformance.Extensions

Просмотреть файл

@ -10,9 +10,6 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Microsoft.Toolkit.HighPerformance.Buffers.Internals;
using Microsoft.Toolkit.HighPerformance.Buffers.Internals.Interfaces;
#if SPAN_RUNTIME_SUPPORT
using Microsoft.Toolkit.HighPerformance.Memory;
#endif
using MemoryStream = Microsoft.Toolkit.HighPerformance.Streams.MemoryStream;
namespace Microsoft.Toolkit.HighPerformance.Extensions

Просмотреть файл

@ -8,9 +8,6 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Microsoft.Toolkit.HighPerformance.Enumerables;
using Microsoft.Toolkit.HighPerformance.Helpers.Internals;
#if SPAN_RUNTIME_SUPPORT
using Microsoft.Toolkit.HighPerformance.Memory;
#endif
namespace Microsoft.Toolkit.HighPerformance.Extensions
{

Просмотреть файл

@ -8,9 +8,6 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Microsoft.Toolkit.HighPerformance.Enumerables;
using Microsoft.Toolkit.HighPerformance.Helpers.Internals;
#if SPAN_RUNTIME_SUPPORT
using Microsoft.Toolkit.HighPerformance.Memory;
#endif
namespace Microsoft.Toolkit.HighPerformance.Extensions
{

Просмотреть файл

@ -209,8 +209,9 @@ namespace Microsoft.Toolkit.HighPerformance.Helpers
// and perform an OR with the resulting value of the previous
// operation. This will always guaranteed to work, thanks to the
// initial code clearing that bit before setting it again.
bool copy = flag;
uint
flag32 = *(byte*)&flag,
flag32 = *(byte*)&copy,
shift = flag32 << n,
or = and | shift;
@ -378,8 +379,9 @@ namespace Microsoft.Toolkit.HighPerformance.Helpers
ulong
bit = 1ul << n,
not = ~bit,
and = value & not,
flag64 = *(byte*)&flag,
and = value & not;
bool copy = flag;
ulong flag64 = *(byte*)&copy,
shift = flag64 << n,
or = and | shift;

Просмотреть файл

@ -5,7 +5,6 @@
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Microsoft.Toolkit.HighPerformance.Memory;
namespace Microsoft.Toolkit.HighPerformance.Helpers
{

Просмотреть файл

@ -5,7 +5,6 @@
using System;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Microsoft.Toolkit.HighPerformance.Memory;
namespace Microsoft.Toolkit.HighPerformance.Helpers
{

Просмотреть файл

@ -19,7 +19,7 @@ using static Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers;
#pragma warning disable CA2231
namespace Microsoft.Toolkit.HighPerformance.Memory
namespace Microsoft.Toolkit.HighPerformance
{
/// <summary>
/// <see cref="Memory2D{T}"/> represents a 2D region of arbitrary memory. It is to <see cref="Span2D{T}"/>
@ -894,7 +894,7 @@ namespace Microsoft.Toolkit.HighPerformance.Memory
/// <inheritdoc/>
public override string ToString()
{
return $"Microsoft.Toolkit.HighPerformance.Memory.Memory2D<{typeof(T)}>[{this.height}, {this.width}]";
return $"Microsoft.Toolkit.HighPerformance.Memory2D<{typeof(T)}>[{this.height}, {this.width}]";
}
/// <summary>

Просмотреть файл

@ -19,7 +19,7 @@ using static Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers;
#pragma warning disable CA2231
namespace Microsoft.Toolkit.HighPerformance.Memory
namespace Microsoft.Toolkit.HighPerformance
{
/// <summary>
/// A readonly version of <see cref="Memory2D{T}"/>.
@ -907,7 +907,7 @@ namespace Microsoft.Toolkit.HighPerformance.Memory
/// <inheritdoc/>
public override string ToString()
{
return $"Microsoft.Toolkit.HighPerformance.Memory.ReadOnlyMemory2D<{typeof(T)}>[{this.height}, {this.width}]";
return $"Microsoft.Toolkit.HighPerformance.ReadOnlyMemory2D<{typeof(T)}>[{this.height}, {this.width}]";
}
/// <summary>

Просмотреть файл

@ -13,7 +13,7 @@ using System.Runtime.InteropServices;
using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers;
#endif
namespace Microsoft.Toolkit.HighPerformance.Memory
namespace Microsoft.Toolkit.HighPerformance
{
/// <inheritdoc cref="ReadOnlySpan2D{T}"/>
public readonly ref partial struct ReadOnlySpan2D<T>

Просмотреть файл

@ -17,7 +17,7 @@ using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.Runti
#pragma warning disable CS0809, CA1065
namespace Microsoft.Toolkit.HighPerformance.Memory
namespace Microsoft.Toolkit.HighPerformance
{
/// <summary>
/// A readonly version of <see cref="Span2D{T}"/>.
@ -974,7 +974,7 @@ namespace Microsoft.Toolkit.HighPerformance.Memory
/// <inheritdoc/>
public override string ToString()
{
return $"Microsoft.Toolkit.HighPerformance.Memory.ReadOnlySpan2D<{typeof(T)}>[{Height}, {this.width}]";
return $"Microsoft.Toolkit.HighPerformance.ReadOnlySpan2D<{typeof(T)}>[{Height}, {this.width}]";
}
/// <summary>

Просмотреть файл

@ -13,7 +13,7 @@ using System.Runtime.InteropServices;
using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.RuntimeHelpers;
#endif
namespace Microsoft.Toolkit.HighPerformance.Memory
namespace Microsoft.Toolkit.HighPerformance
{
/// <inheritdoc cref="Span2D{T}"/>
public readonly ref partial struct Span2D<T>

Просмотреть файл

@ -17,7 +17,7 @@ using RuntimeHelpers = Microsoft.Toolkit.HighPerformance.Helpers.Internals.Runti
#pragma warning disable CS0809, CA1065
namespace Microsoft.Toolkit.HighPerformance.Memory
namespace Microsoft.Toolkit.HighPerformance
{
/// <summary>
/// <see cref="Span2D{T}"/> represents a 2D region of arbitrary memory. Like the <see cref="Span{T}"/> type,
@ -1130,7 +1130,7 @@ namespace Microsoft.Toolkit.HighPerformance.Memory
/// <inheritdoc/>
public override string ToString()
{
return $"Microsoft.Toolkit.HighPerformance.Memory.Span2D<{typeof(T)}>[{Height}, {this.width}]";
return $"Microsoft.Toolkit.HighPerformance.Span2D<{typeof(T)}>[{Height}, {this.width}]";
}
/// <summary>

Просмотреть файл

@ -35,6 +35,11 @@
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="$(ExtrasUwpMetaPackageVersion)" PrivateAssets="all" IsImplicitlyDefined="true" />
</ItemGroup>
<!-- In order to support .NET Native, we need to include an appropriate .rd.xml for UWP (remove from everything else) -->
<ItemGroup Condition="'$(TargetFramework)' != 'uap10.0.19041' and '$(TargetFramework)' != 'native'">
<EmbeddedResource Remove="Properties\Microsoft.Toolkit.Uwp.Notifications.rd.xml" />
</ItemGroup>
<ItemGroup>
<None Include="Microsoft.Toolkit.Uwp.Notifications.targets" Pack="true" PackagePath="build\native" />
</ItemGroup>

Просмотреть файл

@ -0,0 +1,13 @@
<!--
This file enable reflections for Toolkit.Notifications and all of its public/private members,
enabling the library to work in .NET Native even if developers modified their default rd.xml.
See issue https://github.com/windows-toolkit/WindowsCommunityToolkit/issues/3093 for more details.
-->
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
<Library Name="Microsoft.Toolkit.Uwp.Notifications">
<!-- TODO: We can potentially minimize the metadata needed, but further
investigation is needed to fill this in and adequately test this -->
<Assembly Name="Microsoft.Toolkit.Uwp.Notifications" Dynamic="Required All" />
</Library>
</Directives>

Просмотреть файл

@ -273,6 +273,7 @@
<Content Include="SamplePages\Eyedropper\Eyedropper.png" />
<Content Include="SamplePages\OnDevice\OnDevice.png" />
<Content Include="SamplePages\AcrylicBrush\AcrylicBrush.png" />
<Content Include="SamplePages\CanvasPathGeometry\CanvasPathGeometry.png" />
<Content Include="SamplePages\PipelineBrush\PipelineBrush.png" />
<Content Include="SamplePages\RemoteDeviceHelper\RemoteDeviceHelper.png" />
<Content Include="SamplePages\ImageCropper\ImageCropper.png" />
@ -485,6 +486,7 @@
<Compile Include="SamplePages\AutoFocusBehavior\AutoFocusBehaviorPage.xaml.cs">
<DependentUpon>AutoFocusBehaviorPage.xaml</DependentUpon>
</Compile>
<Compile Include="SamplePages\CanvasPathGeometry\GeometryStreamReader.cs" />
<Compile Include="SamplePages\ColorPicker\ColorPickerButtonPage.xaml.cs">
<DependentUpon>ColorPickerButtonPage.xaml</DependentUpon>
</Compile>
@ -494,6 +496,9 @@
<Compile Include="SamplePages\EnumValuesExtension\EnumValuesExtensionPage.xaml.cs">
<DependentUpon>EnumValuesExtensionPage.xaml</DependentUpon>
</Compile>
<Compile Include="SamplePages\CanvasPathGeometry\CanvasPathGeometryPage.xaml.cs">
<DependentUpon>CanvasPathGeometryPage.xaml</DependentUpon>
</Compile>
<Compile Include="SamplePages\FocusBehavior\FocusBehaviorPage.xaml.cs">
<DependentUpon>FocusBehaviorPage.xaml</DependentUpon>
</Compile>
@ -975,6 +980,10 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="SamplePages\CanvasPathGeometry\CanvasPathGeometryPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="SamplePages\TilesBrush\TilesBrushPage.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 4.5 KiB

Просмотреть файл

@ -0,0 +1,156 @@
<Page x:Class="Microsoft.Toolkit.Uwp.SampleApp.SamplePages.CanvasPathGeometryPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:win2d="using:Microsoft.Graphics.Canvas.UI.Xaml"
mc:Ignorable="d">
<Grid Padding="10">
<Grid.RowDefinitions>
<RowDefinition Height="32" />
<RowDefinition Height="48" />
<RowDefinition Height="0.1*" />
<RowDefinition Height="48" />
<RowDefinition Height="0.9*" />
</Grid.RowDefinitions>
<TextBlock Margin="12,3"
VerticalAlignment="Center"
Text="Samples: " />
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"
MinWidth="145" />
<ColumnDefinition Width="*"
MinWidth="145" />
<ColumnDefinition Width="*"
MinWidth="145" />
<ColumnDefinition Width="*"
MinWidth="145" />
<ColumnDefinition Width="*"
MinWidth="145" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
Width="120"
Height="36"
Margin="10,3"
HorizontalAlignment="Center"
Click="{x:Bind OnShowRoundedStarSample}"
Content="Rounded Star" />
<Button Grid.Column="1"
Width="120"
Height="36"
Margin="10,3"
HorizontalAlignment="Center"
Click="{x:Bind OnShowStarSample}"
Content="Star" />
<Button Grid.Column="2"
Width="120"
Height="36"
Margin="10,3"
HorizontalAlignment="Center"
Click="{x:Bind OnShowSpiralSample}"
Content="Spiral" />
<Button Grid.Column="3"
Width="120"
Height="36"
Margin="10,3"
HorizontalAlignment="Center"
Click="{x:Bind OnShowFlowerSample}"
Content="Flower" />
<Button Grid.Column="4"
Width="120"
Height="36"
Margin="10,3"
HorizontalAlignment="Center"
Click="{x:Bind OnShowGearSample}"
Content="Gear" />
</Grid>
<TextBox x:Name="InputData"
Grid.Row="2"
Margin="10,3"
FontFamily="Courier New"
FontSize="14"
PlaceholderText="Enter SVG/XAML Path Data and press Parse. (Or click on one of the Samples from above)"
ScrollViewer.VerticalScrollBarVisibility="Auto"
SelectionHighlightColor="#007aff"
Text="{Binding InputText, Mode=TwoWay}"
TextChanged="{x:Bind OnInputTextChanged}"
TextWrapping="Wrap" />
<Button Grid.Row="3"
Width="120"
Height="36"
Margin="10,3"
HorizontalAlignment="Left"
Click="{x:Bind OnClearCanvas}"
Content="Clear" />
<Pivot x:Name="RootPivot"
Grid.Row="4">
<PivotItem Foreground="Black"
Header="Canvas">
<ScrollViewer x:Name="RenderScroll"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<win2d:CanvasControl x:Name="RenderCanvas"
Width="{Binding ElementName=RenderScroll, Path=ActualWidth}"
Height="{Binding ElementName=RenderScroll, Path=ActualHeight}"
MinWidth="1200"
MinHeight="800"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
ClearColor="#A7A7A7"
Draw="{x:Bind OnCanvasDraw}" />
</ScrollViewer>
</PivotItem>
<PivotItem Foreground="Black"
Header="Commands">
<ScrollViewer Background="#272727"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<TextBlock x:Name="CommandsList"
Padding="10"
FontFamily="Courier New"
FontSize="16"
Foreground="White"
IsTextSelectionEnabled="True"
SelectionHighlightColor="#007aff"
TextWrapping="Wrap" />
</ScrollViewer>
</PivotItem>
</Pivot>
<StackPanel Grid.Row="4"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Orientation="Horizontal">
<TextBlock Margin="10,10,5,-10"
Text="StrokeThickness" />
<Slider x:Name="StrokeThickness"
Width="150"
Height="30"
Margin="0,4,10,4"
Background="White"
Foreground="White"
Maximum="10"
Minimum="0"
StepFrequency="0.1"
ValueChanged="{x:Bind OnStrokeThicknessChanged}" />
<TextBlock Margin="10,10,5,-10"
Text="Stroke Color: " />
<ComboBox x:Name="StrokeList"
Width="150"
Height="30"
Margin="0,4,10,4"
SelectionChanged="{x:Bind OnStrokeColorChanged}" />
<TextBlock Margin="10,10,5,-10"
Text="Fill Color: " />
<ComboBox x:Name="FillList"
Width="150"
Height="30"
Margin="0,4,10,4"
SelectionChanged="{x:Bind OnFillColorChanged}" />
</StackPanel>
</Grid>
</Page>

Просмотреть файл

@ -0,0 +1,330 @@
// 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 System.Text;
using Microsoft.Graphics.Canvas.Geometry;
using Microsoft.Graphics.Canvas.UI.Xaml;
using Microsoft.Toolkit.Uwp.UI.Extensions;
using Microsoft.Toolkit.Uwp.UI.Media.Geometry;
using Windows.System;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Media;
namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class CanvasPathGeometryPage : Page
{
private const string Sample1 =
"F0 M 656.500,400.500 C 656.500,350.637 598.572,307.493 514.292,286.708 C 493.507,202.428 450.363,144.500 400.500,144.500 C 350.637,144.500 307.493,202.428 286.708,286.708 C 202.428,307.493 144.500,350.637 144.500,400.500 C 144.500,450.363 202.428,493.507 286.708,514.292 C 307.493,598.572 350.637,656.500 400.500,656.500 C 450.363,656.500 493.507,598.572 514.292,514.292 C 598.572,493.507 656.500,450.363 656.500,400.500 ZM 581.519,219.481 C 546.261,184.222 474.793,194.676 400.500,239.574 C 326.207,194.676 254.739,184.222 219.481,219.481 C 184.222,254.739 194.676,326.207 239.574,400.500 C 194.676,474.792 184.222,546.261 219.481,581.519 C 254.739,616.778 326.207,606.324 400.500,561.426 C 474.793,606.324 546.261,616.778 581.519,581.519 C 616.778,546.261 606.324,474.792 561.426,400.500 C 606.324,326.207 616.778,254.739 581.519,219.481 ZU 112.5 112.5 570 570 36 36";
private const string Sample2 =
"F1 M 331.341,81.975 L 398.766,218.593 L 549.533,240.500 L 440.437,346.842 L 466.191,497.000 L 331.341,426.105 L 196.491,497.000 L 222.245,346.842 L 113.150,240.500 L 263.916,218.593 L 331.341,81.975 Z";
private const string Sample3 =
"F1 M 545.497,397.058 C 454.492,512.882 286.824,533.003 171.000,441.998 C 78.340,369.194 62.244,235.059 135.048,142.400 C 193.291,68.272 300.599,55.395 374.726,113.639 C 434.028,160.233 444.330,246.079 397.736,305.381 C 360.460,352.823 291.783,361.064 244.341,323.788 C 206.388,293.968 199.795,239.026 229.616,201.073 C 253.472,170.711 297.425,165.436 327.788,189.293 C 352.078,208.378 356.297,243.540 337.212,267.830 C 321.944,287.262 293.814,290.638 274.382,275.370 C 258.836,263.155 256.136,240.651 268.350,225.106 C 278.122,212.669 296.125,210.509 308.562,220.280 C 318.511,228.098 320.239,242.500 312.422,252.449";
private const string Sample4 =
"F1 M 311.717,332.110 C 285.669,332.110 264.552,310.994 264.552,284.945 C 264.552,258.897 285.669,237.781 311.717,237.781 C 337.765,237.781 358.881,258.897 358.881,284.945 C 358.881,310.994 337.765,332.110 311.717,332.110 Z M 505.712,232.846 C 634.939,203.833 411.705,171.395 371.772,213.383 C 411.705,171.395 311.872,-30.889 311.872,92.013 C 311.872,-30.889 212.038,171.395 251.972,213.383 C 212.038,171.395 -11.196,203.833 118.031,232.846 C -11.196,203.833 150.338,361.289 214.951,327.320 C 150.338,361.289 112.205,583.622 192.072,460.719 C 112.205,583.622 311.872,478.651 311.872,397.737 C 311.872,478.651 511.538,583.622 431.672,460.719 C 511.538,583.622 473.405,361.289 408.792,327.320 C 473.405,361.289 634.939,203.833 505.712,232.846 Z";
private const string Sample5 =
"F1 M 391.853,348.284 C 391.853,357.113 384.696,364.271 375.867,364.271 L 301.927,364.271 C 293.098,364.271 285.940,357.113 285.940,348.284 L 285.940,274.345 C 285.940,265.515 293.098,258.358 301.927,258.358 L 375.867,258.358 C 384.696,258.358 391.853,265.515 391.853,274.345 L 391.853,348.284 Z M 544.748,282.990 L 485.488,267.081 C 472.521,263.600 466.301,248.839 472.866,237.128 L 502.867,183.604 C 512.642,166.166 494.336,146.433 476.214,154.872 L 420.592,180.776 C 408.421,186.445 394.169,179.136 391.670,165.944 L 380.248,105.658 C 376.526,86.017 349.819,82.667 341.362,100.780 L 315.403,156.378 C 309.723,168.543 294.107,172.105 283.714,163.607 L 236.213,124.767 C 220.737,112.113 198.125,126.714 203.289,146.025 L 219.141,205.301 C 222.610,218.271 212.937,231.038 199.512,231.208 L 138.159,231.988 C 118.170,232.242 110.233,257.962 126.602,269.436 L 176.847,304.655 C 187.841,312.361 188.638,328.358 178.464,337.118 L 131.965,377.153 C 116.816,390.196 127.269,415.001 147.184,413.268 L 208.312,407.950 C 221.687,406.786 232.580,418.529 230.417,431.779 L 220.531,492.336 C 217.310,512.066 241.261,524.348 255.403,510.220 L 298.811,466.854 C 308.310,457.365 324.202,459.358 331.062,470.899 L 362.415,523.643 C 372.629,540.827 398.872,534.840 400.624,514.927 L 406.001,453.804 C 407.178,440.430 420.634,431.742 433.307,436.173 L 491.227,456.425 C 510.098,463.022 526.353,441.568 514.895,425.187 L 479.725,374.908 C 472.030,363.906 476.753,348.601 489.310,343.850 L 546.697,322.133 C 565.393,315.057 564.054,288.173 544.748,282.990 Z";
private const string ErrorString =
"F1 M 19.648,24.605 L 19.648,30.220 L 29.404,30.220 L 29.404,28.149 C 29.404,27.229 29.581,26.573 29.936,26.181 C 30.290,25.790 30.753,25.594 31.325,25.594 C 31.885,25.594 " +
"32.342,25.790 32.696,26.181 C 33.051,26.573 33.228,27.229 33.228,28.149 L 33.228,34.044 L 15.227,34.044 C 14.307,34.044 13.651,33.867 13.259,33.512 C 12.867,33.158 12.672,32.695 " +
"12.672,32.122 C 12.672,31.563 12.870,31.106 13.268,30.751 C 13.666,30.397 14.319,30.220 15.227,30.220 L 15.824,30.220 L 15.824,15.260 L 15.227,15.260 C 14.307,15.260 13.651,15.082 " +
"13.259,14.728 C 12.867,14.373 12.672,13.910 12.672,13.338 C 12.672,12.766 12.867,12.303 13.259,11.948 C 13.651,11.594 14.307,11.417 15.227,11.417 L 32.388,11.436 L 32.388,17.255 C " +
"32.388,18.163 32.214,18.813 31.866,19.205 C 31.518,19.596 31.058,19.792 30.486,19.792 C 29.914,19.792 29.451,19.600 29.096,19.214 C 28.742,18.829 28.564,18.176 28.564,17.255 L " +
"28.564,15.260 L 19.648,15.260 L 19.648,20.781 L 23.006,20.781 C 23.006,19.786 23.099,19.146 23.285,18.860 C 23.671,18.250 24.218,17.946 24.927,17.946 C 25.487,17.946 25.944,18.142 " +
"26.298,18.533 C 26.652,18.925 26.830,19.581 26.830,20.501 L 26.830,24.903 C 26.830,25.737 26.730,26.297 26.531,26.582 C 26.133,27.167 25.599,27.459 24.927,27.459 C 24.218,27.459 " +
"23.671,27.155 23.285,26.545 C 23.099,26.259 23.006,25.612 23.006,24.605 L 19.648,24.605 ZM 45.707,17.106 L 45.707,19.494 C 47.311,18.337 48.577,17.567 49.503,17.181 C 50.430,16.795 " +
"51.297,16.603 52.105,16.603 C 53.349,16.603 54.555,17.063 55.724,17.983 C 56.520,18.605 56.918,19.239 56.918,19.886 C 56.918,20.433 56.728,20.896 56.349,21.275 C 55.970,21.655 " +
"55.513,21.844 54.978,21.844 C 54.505,21.844 54.008,21.608 53.486,21.135 C 52.963,20.663 52.497,20.427 52.087,20.427 C 51.552,20.427 50.753,20.762 49.690,21.434 C 48.626,22.105 " +
"47.299,23.113 45.707,24.456 L 45.707,30.220 L 51.154,30.220 C 52.074,30.220 52.730,30.397 53.122,30.751 C 53.514,31.106 53.710,31.569 53.710,32.141 C 53.710,32.701 53.514,33.158 " +
"53.122,33.512 C 52.730,33.867 52.074,34.044 51.154,34.044 L 39.607,34.044 C 38.687,34.044 38.031,33.867 37.639,33.512 C 37.248,33.158 37.052,32.695 37.052,32.122 C 37.052,31.563 " +
"37.248,31.106 37.639,30.751 C 38.031,30.397 38.687,30.220 39.607,30.220 L 41.883,30.220 L 41.883,20.930 L 40.503,20.930 C 39.582,20.930 38.927,20.753 38.535,20.399 C 38.143,20.044 " +
"37.947,19.581 37.947,19.009 C 37.947,18.449 38.143,17.992 38.535,17.638 C 38.927,17.283 39.582,17.106 40.503,17.106 L 45.707,17.106 ZM 68.633,17.106 L 68.633,19.494 C 70.237,18.337 " +
"71.502,17.567 72.429,17.181 C 73.355,16.795 74.222,16.603 75.031,16.603 C 76.274,16.603 77.480,17.063 78.650,17.983 C 79.445,18.605 79.843,19.239 79.843,19.886 C 79.843,20.433 " +
"79.654,20.896 79.274,21.275 C 78.895,21.655 78.438,21.844 77.903,21.844 C 77.431,21.844 76.933,21.608 76.411,21.135 C 75.889,20.663 75.423,20.427 75.012,20.427 C 74.477,20.427 " +
"73.678,20.762 72.615,21.434 C 71.552,22.105 70.224,23.113 68.633,24.456 L 68.633,30.220 L 74.079,30.220 C 74.999,30.220 75.656,30.397 76.047,30.751 C 76.439,31.106 76.635,31.569 " +
"76.635,32.141 C 76.635,32.701 76.439,33.158 76.047,33.512 C 75.656,33.867 74.999,34.044 74.079,34.044 L 62.533,34.044 C 61.612,34.044 60.957,33.867 60.565,33.512 C 60.173,33.158 " +
"59.977,32.695 59.977,32.122 C 59.977,31.563 60.173,31.106 60.565,30.751 C 60.957,30.397 61.612,30.220 62.533,30.220 L 64.809,30.220 L 64.809,20.930 L 63.428,20.930 C 62.508,20.930 " +
"61.852,20.753 61.460,20.399 C 61.069,20.044 60.873,19.581 60.873,19.009 C 60.873,18.449 61.069,17.992 61.460,17.638 C 61.852,17.283 62.508,17.106 63.428,17.106 L 68.633,17.106 ZM " +
"98.460,25.911 C 98.460,24.680 98.018,23.548 97.135,22.516 C 95.929,21.123 94.343,20.427 92.379,20.427 C 90.650,20.427 89.208,20.980 88.051,22.087 C 86.895,23.194 86.316,24.474 " +
"86.316,25.929 C 86.316,27.123 86.901,28.239 88.070,29.278 C 89.239,30.316 90.675,30.835 92.379,30.835 C 94.095,30.835 95.537,30.316 96.706,29.278 C 97.875,28.239 98.460,27.117 " +
"98.460,25.911 Z M 102.284,25.892 C 102.284,27.360 101.876,28.780 101.062,30.154 C 100.247,31.529 99.035,32.623 97.425,33.438 C 95.814,34.252 94.132,34.659 92.379,34.659 C " +
"90.638,34.659 88.971,34.258 87.380,33.456 C 85.788,32.654 84.575,31.563 83.742,30.182 C 82.909,28.802 82.492,27.360 82.492,25.855 C 82.492,24.325 82.915,22.824 83.761,21.350 C " +
"84.606,19.876 85.822,18.717 87.408,17.871 C 88.993,17.026 90.650,16.603 92.379,16.603 C 94.119,16.603 95.795,17.035 97.406,17.899 C 99.016,18.763 100.232,19.926 101.053,21.387 C " +
"101.873,22.849 102.284,24.350 102.284,25.892 ZM 114.483,17.106 L 114.483,19.494 C 116.088,18.337 117.353,17.567 118.279,17.181 C 119.206,16.795 120.073,16.603 120.882,16.603 C " +
"122.125,16.603 123.331,17.063 124.500,17.983 C 125.296,18.605 125.694,19.239 125.694,19.886 C 125.694,20.433 125.504,20.896 125.125,21.275 C 124.746,21.655 124.289,21.844 " +
"123.754,21.844 C 123.282,21.844 122.784,21.608 122.262,21.135 C 121.740,20.663 121.273,20.427 120.863,20.427 C 120.328,20.427 119.529,20.762 118.466,21.434 C 117.403,22.105 " +
"116.075,23.113 114.483,24.456 L 114.483,30.220 L 119.930,30.220 C 120.850,30.220 121.506,30.397 121.898,30.751 C 122.290,31.106 122.486,31.569 122.486,32.141 C 122.486,32.701 " +
"122.290,33.158 121.898,33.512 C 121.506,33.867 120.850,34.044 119.930,34.044 L 108.384,34.044 C 107.463,34.044 106.807,33.867 106.416,33.512 C 106.024,33.158 105.828,32.695 " +
"105.828,32.122 C 105.828,31.563 106.024,31.106 106.416,30.751 C 106.807,30.397 107.463,30.220 108.384,30.220 L 110.659,30.220 L 110.659,20.930 L 109.279,20.930 C 108.359,20.930 " +
"107.703,20.753 107.311,20.399 C 106.919,20.044 106.723,19.581 106.723,19.009 C 106.723,18.449 106.919,17.992 107.311,17.638 C 107.703,17.283 108.359,17.106 109.279,17.106 L " +
"114.483,17.106 ZM 140.431,32.645 C 140.431,33.192 140.225,33.655 139.815,34.034 C 139.405,34.414 138.838,34.603 138.118,34.603 C 137.396,34.603 136.830,34.414 136.420,34.034 C " +
"136.010,33.655 135.804,33.192 135.804,32.645 C 135.804,32.110 136.006,31.653 136.411,31.274 C 136.815,30.895 137.384,30.705 138.118,30.705 C 138.851,30.705 139.420,30.891 " +
"139.824,31.264 C 140.228,31.637 140.431,32.098 140.431,32.645 Z M 141.046,13.655 L 139.983,25.183 C 139.933,25.780 139.734,26.244 139.386,26.573 C 139.038,26.903 138.603,27.067 " +
"138.080,27.067 C 137.558,27.067 137.123,26.903 136.774,26.573 C 136.426,26.244 136.227,25.780 136.178,25.183 L 135.096,13.655 C 135.046,13.071 135.021,12.685 135.021,12.499 C " +
"135.021,11.529 135.313,10.749 135.898,10.158 C 136.482,9.567 137.210,9.272 138.080,9.272 C 138.938,9.272 139.662,9.570 140.253,10.167 C 140.844,10.764 141.139,11.516 141.139,12.424 " +
"C 141.139,12.611 141.108,13.021 141.046,13.655 Z";
private const string ParseError1 = "Parameter \"(pathData matches.Count == 0)\" must be false, was true: ";
private const string ParseError2 = "Parameter \"(pathData matches.Count > 1)\" must be false, was true: ";
private const string ParseError3 = "PATH_ERR003";
private DispatcherQueueTimer _typeTimer = DispatcherQueue.GetForCurrentThread().CreateTimer();
private List<Color> _colors;
private List<string> _samples;
private string _data = string.Empty;
private StringBuilder _logger;
private float _strokeThickness;
private Color _strokeColor;
private Color _fillColor;
private bool _selectionChanged = false;
private bool _isParsing = false;
private CanvasGeometry _errorGeometry;
private GeometryStreamReader _reader;
private SolidColorBrush _commandBrush;
private SolidColorBrush _commandErrorBrush;
public string InputText { get; set; }
public CanvasPathGeometryPage()
{
this.InitializeComponent();
_reader = new GeometryStreamReader();
_logger = new StringBuilder();
_colors = new List<Color>()
{
Colors.Transparent,
Colors.Black,
Colors.White,
Colors.Crimson,
CanvasPathGeometry.CreateColor("#bf5af2"),
CanvasPathGeometry.CreateColor("#0a84ff"),
CanvasPathGeometry.CreateColor("#32d74b"),
CanvasPathGeometry.CreateColor("#ff9500"),
CanvasPathGeometry.CreateColor("#ffd60a")
};
_commandBrush = new SolidColorBrush(Colors.White);
_commandErrorBrush = new SolidColorBrush(Colors.Red);
var colorList = new List<string>()
{
"Transparent",
"Black",
"White",
"Crimson",
"Purple",
"LightBlue",
"LightGreen",
"Orange",
"Yellow"
};
this._samples = new List<string>()
{
string.Empty,
Sample1,
Sample2,
Sample3,
Sample4,
Sample5
};
var sampleList = new List<string>()
{
"None",
"Sample 1",
"Sample 2",
"Sample 3",
"Sample 4",
"Sample 5",
};
StrokeList.ItemsSource = colorList;
FillList.ItemsSource = colorList;
StrokeThickness.Value = 1;
StrokeList.SelectedIndex = 1;
FillList.SelectedIndex = 0;
_selectionChanged = false;
}
private void ParseData()
{
_data = InputData.Text;
_isParsing = true;
RenderCanvas.Invalidate();
_isParsing = false;
}
private void OnCanvasDraw(CanvasControl sender, CanvasDrawEventArgs args)
{
if (string.IsNullOrWhiteSpace(_data))
{
CommandsList.Text = string.Empty;
return;
}
this._errorGeometry ??= CanvasPathGeometry.CreateGeometry(sender, ErrorString);
_logger?.Clear();
CommandsList.Text = string.Empty;
try
{
_logger?.AppendLine("// The following commands represent the CanvasPathBuilder command(s) needed");
_logger?.AppendLine("// to create the CanvasGeometry from the specified Win2d Path Mini Language.");
var geometry = CanvasPathGeometry.CreateGeometry(sender, _data);
_reader.StartLogging();
geometry.SendPathTo(_reader);
_logger?.AppendLine(_reader.EndLogging());
CommandsList.Text = _logger?.ToString() ?? string.Empty;
args.DrawingSession.FillGeometry(geometry, _fillColor);
args.DrawingSession.DrawGeometry(geometry, _strokeColor, _strokeThickness);
RootPivot.SelectedIndex = 0;
CommandsList.Foreground = _commandBrush;
}
catch (ArgumentException argEx)
{
var message = argEx.Message;
var errorCode = message.Substring(0, 11);
var parseError = string.Empty;
if (message.StartsWith(ParseError1))
{
parseError = "Parse Error: No matching data!";
}
else if (message.StartsWith(ParseError2))
{
parseError = "Parse Error: Multiple FillRule elements present in Path Data!";
}
else if (message.StartsWith(ParseError3))
{
var tokens = message.Split('\n', StringSplitOptions.RemoveEmptyEntries);
if (tokens.Length == 3)
{
parseError = $"Parse Error at {tokens[1]}. Cannot parse '{tokens[2]}'.";
}
}
else
{
parseError = "Parsing error! Invalid input data!";
}
args.DrawingSession.FillGeometry(_errorGeometry, Colors.Black);
CommandsList.Text = parseError;
RootPivot.SelectedIndex = 1;
CommandsList.Foreground = _commandErrorBrush;
}
catch (Exception)
{
args.DrawingSession.FillGeometry(_errorGeometry, Colors.Black);
CommandsList.Text = "Parsing error! Invalid input data!";
RootPivot.SelectedIndex = 1;
CommandsList.Foreground = _commandErrorBrush;
}
}
private void OnStrokeThicknessChanged(object sender, RangeBaseValueChangedEventArgs e)
{
_strokeThickness = (float)StrokeThickness.Value;
_selectionChanged = true;
RenderCanvas.Invalidate();
}
private void OnStrokeColorChanged(object sender, SelectionChangedEventArgs e)
{
if (StrokeList.SelectedIndex > -1)
{
_strokeColor = _colors[StrokeList.SelectedIndex];
_selectionChanged = true;
}
RenderCanvas.Invalidate();
}
private void OnFillColorChanged(object sender, SelectionChangedEventArgs e)
{
if (FillList.SelectedIndex > -1)
{
_fillColor = _colors[FillList.SelectedIndex];
_selectionChanged = true;
}
RenderCanvas.Invalidate();
}
private void ShowSample(int index)
{
InputData.Text = _samples.ElementAt(index);
if (!_selectionChanged)
{
StrokeThickness.Value = 4;
StrokeList.SelectedIndex = 1;
FillList.SelectedIndex = 5;
_selectionChanged = false;
}
_data = InputData.Text;
RenderCanvas.Invalidate();
}
private void OnClearCanvas(object sender, RoutedEventArgs e)
{
InputData.Text = string.Empty;
ParseData();
}
private void OnShowRoundedStarSample(object sender, RoutedEventArgs e)
{
ShowSample(1);
}
private void OnShowStarSample(object sender, RoutedEventArgs e)
{
ShowSample(2);
}
private void OnShowSpiralSample(object sender, RoutedEventArgs e)
{
ShowSample(3);
}
private void OnShowFlowerSample(object sender, RoutedEventArgs e)
{
ShowSample(4);
}
private void OnShowGearSample(object sender, RoutedEventArgs e)
{
ShowSample(5);
}
public void OnInputTextChanged(object sender, RoutedEventArgs e)
{
// Call the ParseData method only after 0.3 seconds have elapsed since last trigger.
_typeTimer.Debounce(ParseData, TimeSpan.FromSeconds(0.3));
}
}
}

Просмотреть файл

@ -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.Numerics;
using System.Text;
using Microsoft.Graphics.Canvas.Geometry;
namespace Microsoft.Toolkit.Uwp.SampleApp.SamplePages
{
/// <summary>
/// Class to read the <see cref="CanvasGeometry"/> path data.
/// </summary>
internal class GeometryStreamReader : ICanvasPathReceiver
{
private readonly StringBuilder _cmdBuilder;
/// <summary>
/// Initializes a new instance of the <see cref="GeometryStreamReader"/> class.
/// </summary>
public GeometryStreamReader()
{
_cmdBuilder = new StringBuilder();
}
/// <summary>
/// Starts logging the data for the sample app
/// </summary>
public void StartLogging()
{
_cmdBuilder.Clear();
_cmdBuilder.AppendLine($"using (var pathBuilder = new CanvasPathBuilder(null))");
_cmdBuilder.AppendLine("{\n");
}
/// <summary>
/// Finishes reading the geometry path data and returns the data as formatted string.
/// </summary>
/// <returns><see cref="CanvasPathBuilder"/> commands to create the CanvasGeometry</returns>
public string EndLogging()
{
_cmdBuilder.AppendLine("}");
return _cmdBuilder.ToString();
}
/// <summary>
/// Starts a new figure at the specified point, with the specified figure fill option.
/// </summary>
/// <param name="point">Start point</param>
/// <param name="fill"><see cref="CanvasFigureFill"/></param>
public void BeginFigure(Vector2 point, CanvasFigureFill fill)
{
_cmdBuilder.AppendLine($"\n pathBuilder.BeginFigure(new Vector2({point.X}, {point.Y}));");
}
/// <summary>
/// Adds a single arc to the path, specified by start and end points through which an ellipse will be fitted.
/// </summary>
/// <param name="point">Start Point</param>
/// <param name="x">radiusX</param>
/// <param name="y">radiusY</param>
/// <param name="z">rotationAngle</param>
/// <param name="sweepDirection"><see cref="CanvasSweepDirection"/></param>
/// <param name="arcSize"><see cref="CanvasArcSize"/></param>
public void AddArc(Vector2 point, float x, float y, float z, CanvasSweepDirection sweepDirection, CanvasArcSize arcSize)
{
_cmdBuilder.AppendLine($" pathBuilder.AddArc(new Vector2({point.X}, {point.Y}), {x}, {y}, {z}, {sweepDirection}, {arcSize});");
}
/// <summary>
/// Adds a cubic bezier to the path. The bezier starts where the path left off, and has the specified control points and end point.
/// </summary>
/// <param name="controlPoint1">First ControlPoint</param>
/// <param name="controlPoint2">Second Control Point</param>
/// <param name="endPoint">EndPoint</param>
public void AddCubicBezier(Vector2 controlPoint1, Vector2 controlPoint2, Vector2 endPoint)
{
_cmdBuilder.AppendLine($" pathBuilder.AddCubicBezier(new Vector2({controlPoint1.X}, {controlPoint1.Y}), new Vector2({controlPoint2.X}, {controlPoint2.Y}), new Vector2({endPoint.X}, {endPoint.Y}));");
}
/// <summary>
/// Adds a line segment to the path, with the specified end point.
/// </summary>
/// <param name="endPoint">EndPoint</param>
public void AddLine(Vector2 endPoint)
{
_cmdBuilder.AppendLine($" pathBuilder.AddLine(new Vector2({endPoint.X}, {endPoint.Y}));");
}
/// <summary>
/// Adds a quadratic bezier to the path. The bezier starts where the path left off, and has the specified control point and end point.
/// </summary>
/// <param name="controlPoint">Control Point</param>
/// <param name="endPoint">EndPoint</param>
public void AddQuadraticBezier(Vector2 controlPoint, Vector2 endPoint)
{
_cmdBuilder.AppendLine($" pathBuilder.AddQuadraticBezier(new Vector2({controlPoint.X}, {controlPoint.Y}), new Vector2({endPoint.X}, {endPoint.Y}));");
}
/// <summary>
/// Specifies the method used to determine which points are inside the geometry described by this path builder, and which points are outside.
/// </summary>
/// <param name="filledRegionDetermination"><see cref="CanvasFilledRegionDetermination"/></param>
public void SetFilledRegionDetermination(CanvasFilledRegionDetermination filledRegionDetermination)
{
_cmdBuilder.AppendLine($" pathBuilder.SetFilledRegionDetermination(CanvasFilledRegionDetermination.{filledRegionDetermination});");
}
/// <summary>
/// Specifies stroke and join options to be applied to new segments added to the path builder.
/// </summary>
/// <param name="figureSegmentOptions"><see cref="CanvasFigureSegmentOptions"/></param>
public void SetSegmentOptions(CanvasFigureSegmentOptions figureSegmentOptions)
{
// Do nothing
}
/// <summary>
/// >Ends the current figure; optionally, closes it.
/// </summary>
/// <param name="figureLoop"><see cref="CanvasFigureLoop"/></param>
public void EndFigure(CanvasFigureLoop figureLoop)
{
_cmdBuilder.AppendLine($" pathBuilder.EndFigure({(figureLoop == CanvasFigureLoop.Closed ? "CanvasFigureLoop.Closed" : "CanvasFigureLoop.Open")});");
}
}
}

Просмотреть файл

@ -900,6 +900,14 @@
"Icon": "/Assets/Helpers.png",
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/parsers/MarkdownParser.md"
},
{
"Name": "Win2d Path Mini Language Parser",
"Type": "CanvasPathGeometryPage",
"Subcategory": "Parser",
"About": "CanvasPathGeometry class allows you to convert Win2d Path Mini Language string to CanvasGeometry, Brushes, CanvasStrokes or CanvasStrokeStyles.",
"Icon": "/SamplePages/CanvasPathGeometry/CanvasPathGeometry.png",
"DocumentationUrl": "https://raw.githubusercontent.com/MicrosoftDocs/WindowsCommunityToolkitDocs/master/docs/parsers/CanvasPathGeometry.md"
},
{
"Name": "LiveTile",
"Type": "LiveTilePage",

Просмотреть файл

@ -6,7 +6,6 @@
using System;
using System.Numerics;
using Microsoft.Toolkit.Diagnostics;
using Windows.UI.Composition;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
@ -605,7 +604,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
{
if (!Matrix4x4.Decompose(to, out Vector3 toScale, out Quaternion toRotation, out Vector3 toTranslation))
{
ThrowHelper.ThrowArgumentException("The destination matrix could not be decomposed");
ThrowThrowArgumentExceptionForToDecompose();
}
Vector3? fromScale = null;
@ -616,7 +615,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
{
if (!Matrix4x4.Decompose(from.GetValueOrDefault(), out Vector3 scale3, out Quaternion rotation4, out Vector3 translation3))
{
ThrowHelper.ThrowArgumentException("The initial matrix could not be decomposed");
ThrowThrowArgumentExceptionForFromDecompose();
}
fromScale = scale3;
@ -629,6 +628,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
Translation(toTranslation, fromTranslation, delay, duration, easingType, easingMode);
return this;
static void ThrowThrowArgumentExceptionForToDecompose() => throw new ArgumentException("The destination matrix could not be decomposed");
static void ThrowThrowArgumentExceptionForFromDecompose() => throw new ArgumentException("The initial matrix could not be decomposed");
}
/// <summary>

Просмотреть файл

@ -6,7 +6,6 @@ using System;
using System.Diagnostics.Contracts;
using System.Numerics;
using System.Runtime.CompilerServices;
using Microsoft.Toolkit.Diagnostics;
using Windows.Foundation;
using Windows.UI;
using Windows.UI.Composition;
@ -161,7 +160,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
easingFunction);
}
return ThrowHelper.ThrowInvalidOperationException<CompositionAnimation>("Invalid animation type");
throw new InvalidOperationException("Invalid animation type");
}
/// <inheritdoc/>
@ -216,7 +215,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
easingFunction);
}
return ThrowHelper.ThrowInvalidOperationException<Timeline>("Invalid animation type");
throw new InvalidOperationException("Invalid animation type");
}
/// <summary>
@ -229,9 +228,33 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
private TValue GetToAs<TValue>()
where TValue : unmanaged
{
T to = To;
return Unsafe.As<T, TValue>(ref to);
// We employ this (T2)(object)t1 pattern multiple times in this library to alter generics.
// This is an equivalent but safer alternative to using Unsafe.As<TFrom, TTo>(ref TFrom).
// For instance, this method will result in the following IL being emitted:
// =============================
// IL_0000: ldarg.0
// IL_0001: call instance !0 class AnimationFactory`1<!T>::get_To()
// IL_0006: box !T
// IL_000b: unbox.any !!TValue
// IL_0010: ret
// =============================
// The key point is that the JIT (and AOT compilers such as .NET Native) can recognize this
// pattern and optimize the boxing away in case the types match. This is the case whenever
// the generic arguments are value types, which due to generic types in .NET being reified
// results in a completely different generic instantiation of the same method, making the
// type arguments effectively constant values known at compile time, ie. at JIT time.
// As a result of this, the boxing is completely avoided and the value is returned directly.
// Leveraging this pattern lets us keep the same optimal codegen while avoiding the extra
// NuGet package dependency on UWP, and the more dangerous path using the Unsafe APIs.
// As an example, assuming T is float, the JIT will produce the following codegen on x64:
// =============================
// L0000: vzeroupper
// L0003: vmovss xmm0, [rcx+8]
// L0008: ret
// =============================
// We can see how the property value is loaded directly from the underlying field and
// then returned to the caller: no boxing or unwanted overhead is introduced at all.
return (TValue)(object)To;
}
/// <summary>
@ -251,7 +274,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
T from = From.GetValueOrDefault();
return Unsafe.As<T, TValue>(ref from);
return (TValue)(object)from;
}
}

Просмотреть файл

@ -9,7 +9,7 @@ using System.Runtime.CompilerServices;
namespace Microsoft.Toolkit.Uwp.UI.Animations.Builders.Helpers
{
/// <summary>
/// A small generic builder type that allows to create <see cref="ReadOnlySpan{T}"/> instances.
/// A small generic builder type that allows to create <see cref="ArraySegment{T}"/> instances.
/// </summary>
/// <typeparam name="T">The type of items to create a sequence of.</typeparam>
internal struct ListBuilder<T>
@ -56,14 +56,14 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations.Builders.Helpers
}
/// <summary>
/// Gets a <see cref="ReadOnlySpan{T}"/> instance with the current items.
/// Gets a <see cref="ArraySegment{T}"/> instance with the current items.
/// </summary>
/// <returns>A <see cref="ReadOnlySpan{T}"/> instance with the current items.</returns>
/// <returns>A <see cref="ArraySegment{T}"/> instance with the current items.</returns>
[Pure]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<T> AsSpan()
public ArraySegment<T> GetArraySegment()
{
return this.array.AsSpan(0, this.index);
return new(this.array, 0, this.index);
}
}
}

Просмотреть файл

@ -4,7 +4,6 @@
using System;
using System.Numerics;
using Microsoft.Toolkit.Diagnostics;
using Windows.UI;
using Windows.UI.Composition;
using Windows.UI.Xaml.Media.Animation;
@ -33,7 +32,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
TimeSpan? delay,
TimeSpan duration,
RepeatOption repeat,
ReadOnlySpan<TKeyFrame> keyFrames)
ArraySegment<TKeyFrame> keyFrames)
where TKeyFrame : struct, IKeyFrameInfo
{
KeyFrameAnimation animation;
@ -42,7 +41,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
{
BooleanKeyFrameAnimation boolAnimation = target.Compositor.CreateBooleanKeyFrameAnimation();
foreach (ref readonly var keyFrame in keyFrames)
foreach (var keyFrame in keyFrames)
{
if (keyFrame.TryInsertExpressionKeyFrame(boolAnimation, duration))
{
@ -58,7 +57,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
{
ScalarKeyFrameAnimation scalarAnimation = target.Compositor.CreateScalarKeyFrameAnimation();
foreach (ref readonly var keyFrame in keyFrames)
foreach (var keyFrame in keyFrames)
{
if (keyFrame.TryInsertExpressionKeyFrame(scalarAnimation, duration))
{
@ -83,7 +82,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
{
ScalarKeyFrameAnimation scalarAnimation = target.Compositor.CreateScalarKeyFrameAnimation();
foreach (ref readonly var keyFrame in keyFrames)
foreach (var keyFrame in keyFrames)
{
if (keyFrame.TryInsertExpressionKeyFrame(scalarAnimation, duration))
{
@ -108,7 +107,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
{
Vector2KeyFrameAnimation vector2Animation = target.Compositor.CreateVector2KeyFrameAnimation();
foreach (ref readonly var keyFrame in keyFrames)
foreach (var keyFrame in keyFrames)
{
if (keyFrame.TryInsertExpressionKeyFrame(vector2Animation, duration))
{
@ -133,7 +132,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
{
Vector3KeyFrameAnimation vector3Animation = target.Compositor.CreateVector3KeyFrameAnimation();
foreach (ref readonly var keyFrame in keyFrames)
foreach (var keyFrame in keyFrames)
{
if (keyFrame.TryInsertExpressionKeyFrame(vector3Animation, duration))
{
@ -158,7 +157,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
{
Vector4KeyFrameAnimation vector4Animation = target.Compositor.CreateVector4KeyFrameAnimation();
foreach (ref readonly var keyFrame in keyFrames)
foreach (var keyFrame in keyFrames)
{
if (keyFrame.TryInsertExpressionKeyFrame(vector4Animation, duration))
{
@ -183,7 +182,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
{
ColorKeyFrameAnimation colorAnimation = target.Compositor.CreateColorKeyFrameAnimation();
foreach (ref readonly var keyFrame in keyFrames)
foreach (var keyFrame in keyFrames)
{
if (keyFrame.TryInsertExpressionKeyFrame(colorAnimation, duration))
{
@ -208,7 +207,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
{
QuaternionKeyFrameAnimation quaternionAnimation = target.Compositor.CreateQuaternionKeyFrameAnimation();
foreach (ref readonly var keyFrame in keyFrames)
foreach (var keyFrame in keyFrames)
{
if (keyFrame.TryInsertExpressionKeyFrame(quaternionAnimation, duration))
{
@ -231,7 +230,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
}
else
{
return ThrowHelper.ThrowInvalidOperationException<CompositionAnimation>("Invalid animation type");
throw new InvalidOperationException("Invalid animation type");
}
animation.Duration = duration;
@ -284,7 +283,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
this.delay,
this.duration,
this.repeat,
this.keyFrames.AsSpan());
this.keyFrames.GetArraySegment());
}
}
}

Просмотреть файл

@ -45,7 +45,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
this.delay,
this.duration,
this.repeat,
this.keyFrames.AsSpan());
this.keyFrames.GetArraySegment());
}
}
}

Просмотреть файл

@ -150,7 +150,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TValue GetValueAs<TValue>()
{
return Unsafe.As<T, TValue>(ref Unsafe.AsRef(in this.value));
return (TValue)(object)this.value;
}
/// <inheritdoc/>

Просмотреть файл

@ -47,8 +47,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
// We can retrieve the total duration from the last timed keyframe, and then set
// this as the target duration and use it to normalize the keyframe progresses.
ReadOnlySpan<KeyFrameInfo> keyFrames = this.keyFrames.AsSpan();
TimeSpan duration = keyFrames[keyFrames.Length - 1].GetTimedProgress(default);
ArraySegment<KeyFrameInfo> keyFrames = this.keyFrames.GetArraySegment();
TimeSpan duration = keyFrames[keyFrames.Count - 1].GetTimedProgress(default);
return NormalizedKeyFrameAnimationBuilder<T>.GetAnimation(
targetHint,

Просмотреть файл

@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using System;
using Microsoft.Toolkit.Diagnostics;
using Windows.Foundation;
using Windows.UI;
using Windows.UI.Xaml;
@ -32,7 +31,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
TimeSpan? delay,
TimeSpan duration,
RepeatOption repeat,
ReadOnlySpan<TKeyFrame> keyFrames)
ArraySegment<TKeyFrame> keyFrames)
where TKeyFrame : struct, IKeyFrameInfo
{
Timeline animation;
@ -118,7 +117,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
}
else
{
return ThrowHelper.ThrowInvalidOperationException<Timeline>("Invalid animation type");
static Timeline ThrowInvalidOperationException() => throw new InvalidOperationException("Invalid animation type");
return ThrowInvalidOperationException();
}
animation.BeginTime = delay;
@ -163,7 +164,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
this.delay,
default,
this.repeat,
this.keyFrames.AsSpan());
this.keyFrames.GetArraySegment());
}
}
}

Просмотреть файл

@ -143,7 +143,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public TValue GetValueAs<TValue>()
{
return Unsafe.As<T, TValue>(ref Unsafe.AsRef(in this.value));
return (TValue)(object)this.value;
}
/// <inheritdoc/>

Просмотреть файл

@ -6,7 +6,6 @@ using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Numerics;
using Microsoft.Toolkit.Diagnostics;
using Windows.UI.Composition;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
@ -111,7 +110,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
Axis.X => "AnchorPoint.X",
Axis.Y => "AnchorPoint.Y",
Axis.Z => "AnchorPoint.Z",
_ => ThrowHelper.ThrowArgumentException<string>("Invalid axis")
_ => ThrowArgumentException<string>("Invalid axis")
};
/// <summary>
@ -132,7 +131,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
Axis.X => "Translation.X",
Axis.Y => "Translation.Y",
Axis.Z => "Translation.Z",
_ => ThrowHelper.ThrowArgumentException<string>("Invalid axis")
_ => ThrowArgumentException<string>("Invalid axis")
};
/// <summary>
@ -153,7 +152,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
Axis.X => "Offset.X",
Axis.Y => "Offset.Y",
Axis.Z => "Offset.Z",
_ => ThrowHelper.ThrowArgumentException<string>("Invalid axis")
_ => ThrowArgumentException<string>("Invalid axis")
};
/// <summary>
@ -174,7 +173,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
Axis.X => "Scale.X",
Axis.Y => "Scale.Y",
Axis.Z => "Scale.Z",
_ => ThrowHelper.ThrowArgumentException<string>("Invalid axis")
_ => ThrowArgumentException<string>("Invalid axis")
};
/// <summary>
@ -195,7 +194,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
Axis.X => "CenterPoint.X",
Axis.Y => "CenterPoint.Y",
Axis.Z => "CenterPoint.Z",
_ => ThrowHelper.ThrowArgumentException<string>("Invalid axis")
_ => ThrowArgumentException<string>("Invalid axis")
};
/// <summary>
@ -217,7 +216,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
Side.Bottom => nameof(InsetClip.BottomInset),
Side.Right => nameof(InsetClip.RightInset),
Side.Left => nameof(InsetClip.LeftInset),
_ => ThrowHelper.ThrowArgumentException<string>("Invalid clip side")
_ => ThrowArgumentException<string>("Invalid clip side")
};
/// <summary>
@ -238,7 +237,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
Axis.X => "Size.X",
Axis.Y => "Size.Y",
Axis.Z => "Size.Z",
_ => ThrowHelper.ThrowArgumentException<string>("Invalid axis")
_ => ThrowArgumentException<string>("Invalid axis")
};
}
@ -257,7 +256,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
{
Axis.X => nameof(CompositeTransform.TranslateX),
Axis.Y => nameof(CompositeTransform.TranslateY),
_ => ThrowHelper.ThrowArgumentException<string>("Invalid axis")
_ => ThrowArgumentException<string>("Invalid axis")
};
/// <summary>
@ -270,7 +269,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
{
Axis.X => nameof(CompositeTransform.ScaleX),
Axis.Y => nameof(CompositeTransform.ScaleY),
_ => ThrowHelper.ThrowArgumentException<string>("Invalid axis")
_ => ThrowArgumentException<string>("Invalid axis")
};
/// <summary>
@ -283,7 +282,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
{
Axis.X => nameof(CompositeTransform.CenterX),
Axis.Y => nameof(CompositeTransform.CenterY),
_ => ThrowHelper.ThrowArgumentException<string>("Invalid axis")
_ => ThrowArgumentException<string>("Invalid axis")
};
/// <summary>
@ -296,9 +295,17 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
{
Axis.X => nameof(FrameworkElement.Width),
Axis.Y => nameof(FrameworkElement.Height),
_ => ThrowHelper.ThrowArgumentException<string>("Invalid axis")
_ => ThrowArgumentException<string>("Invalid axis")
};
}
/// <summary>
/// Throws a new <see cref="ArgumentException"/> with a given message.
/// </summary>
private static T ThrowArgumentException<T>(string message)
{
throw new ArgumentException(message);
}
}
}
}

Просмотреть файл

@ -4,8 +4,8 @@
#nullable enable
using System;
using System.Diagnostics.Contracts;
using Microsoft.Toolkit.Diagnostics;
using Windows.UI.Xaml.Media.Animation;
using static Microsoft.Toolkit.Uwp.UI.Animations.AnimationExtensions;
@ -46,8 +46,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
EasingType.Quintic => new QuinticEase { EasingMode = easingMode },
EasingType.Sine => new SineEase { EasingMode = easingMode },
_ => ThrowHelper.ThrowArgumentException<EasingFunctionBase?>("Invalid easing type")
_ => ThrowArgumentException()
};
static EasingFunctionBase ThrowArgumentException() => throw new ArgumentException("Invalid easing type");
}
}
}

Просмотреть файл

@ -2,8 +2,8 @@
// 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.Diagnostics;
using Windows.UI.Composition;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
@ -66,11 +66,13 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
visual.StartAnimation($"{nameof(Visual.Offset)}.{targetAxis}", animation);
break;
default:
ThrowHelper.ThrowArgumentException("Invalid target property");
ThrowArgumentException();
break;
}
return animation;
static ExpressionAnimation ThrowArgumentException() => throw new ArgumentException("Invalid target property");
}
}
}

Просмотреть файл

@ -4,7 +4,6 @@
using System;
using System.Diagnostics.Contracts;
using Microsoft.Toolkit.Diagnostics;
using Windows.Foundation.Metadata;
using Windows.UI.Composition;
using Windows.UI.Xaml.Media.Animation;
@ -52,7 +51,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
[Pure]
public static RepeatOption Count(int count)
{
Guard.IsGreaterThanOrEqualTo(count, 0, nameof(count));
if (count < 0)
{
ThrowArgumentOutOfRangeForCount();
}
return new(count);
}
@ -76,7 +78,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
return Forever;
}
return ThrowHelper.ThrowArgumentException<RepeatOption>("Invalid input text");
return ThrowArgumentExceptionForText();
}
/// <summary>
@ -109,5 +111,21 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
return (AnimationIterationBehavior.Count, this.value);
}
/// <summary>
/// Throws a new <see cref="ArgumentOutOfRangeException"/> when the constructor is invoked with an incorrect parameter.
/// </summary>
private static void ThrowArgumentOutOfRangeForCount()
{
throw new ArgumentOutOfRangeException("The parameter \"count\" must be greater than or equal to 0.");
}
/// <summary>
/// Throws a new <see cref="ArgumentOutOfRangeException"/> when the constructor is invoked with an incorrect parameter.
/// </summary>
private static RepeatOption ThrowArgumentExceptionForText()
{
throw new ArgumentException("The input text is not valid to parse a new RepeatOption instance. It must be either a natural number or \"Forever\".");
}
}
}

Просмотреть файл

@ -2,9 +2,9 @@
// 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;
using System.Threading.Tasks;
using Microsoft.Toolkit.Diagnostics;
using Windows.UI.Xaml;
namespace Microsoft.Toolkit.Uwp.UI.Animations
@ -53,7 +53,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
/// <inheritdoc/>
public override async Task InvokeAsync(UIElement element, CancellationToken token)
{
Guard.IsNotNull(Animation, nameof(Animation));
if (Animation is null)
{
ThrowArgumentNullException();
}
await base.InvokeAsync(element, token);
@ -72,6 +75,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
{
await Animation.StartAsync(token);
}
static void ThrowArgumentNullException() => throw new ArgumentNullException(nameof(Animation));
}
}
}

Просмотреть файл

@ -2,9 +2,9 @@
// 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;
using System.Threading.Tasks;
using Microsoft.Toolkit.Diagnostics;
using Windows.UI.Xaml;
namespace Microsoft.Toolkit.Uwp.UI.Animations
@ -53,7 +53,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
/// <inheritdoc/>
public override async Task InvokeAsync(UIElement element, CancellationToken token)
{
Guard.IsNotNull(Animation, nameof(Animation));
if (Animation is null)
{
ThrowArgumentNullException();
}
await base.InvokeAsync(element, token);
@ -69,6 +72,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
{
Animation.Stop();
}
static void ThrowArgumentNullException() => throw new ArgumentNullException(nameof(Animation));
}
}
}

Просмотреть файл

@ -9,7 +9,6 @@ using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Toolkit.Diagnostics;
using Windows.UI.Xaml;
namespace Microsoft.Toolkit.Uwp.UI.Animations
@ -218,10 +217,12 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
if (ParentReference?.TryGetTarget(out parent) != true)
{
ThrowHelper.ThrowInvalidOperationException("The current animation collection isn't bound to a parent UIElement instance.");
ThrowInvalidOperationException();
}
return parent!;
static void ThrowInvalidOperationException() => throw new InvalidOperationException("The current AnimationSet object isn't bound to a parent UIElement instance.");
}
}
}

Просмотреть файл

@ -6,7 +6,6 @@
using System;
using System.Diagnostics.Contracts;
using Microsoft.Toolkit.Diagnostics;
using Windows.UI.Composition;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Hosting;
@ -85,10 +84,12 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
if (ParentReference?.TryGetTarget(out parent) != true)
{
ThrowHelper.ThrowInvalidOperationException("The current animation collection isn't bound to a parent UIElement instance.");
ThrowInvalidOperationException();
}
return parent!;
static void ThrowInvalidOperationException() => throw new InvalidOperationException("The current ImplicitAnimationSet object isn't bound to a parent UIElement instance.");
}
}
}

Просмотреть файл

@ -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.Diagnostics;
using System;
using Microsoft.Toolkit.Uwp.UI.Animations;
using Microsoft.Xaml.Interactivity;
using Windows.UI.Xaml;
@ -53,7 +53,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Behaviors
/// <inheritdoc/>
public object Execute(object sender, object parameter)
{
Guard.IsNotNull(Animation, nameof(Animation));
if (Animation is null)
{
ThrowArgumentNullException();
}
if (TargetObject is not null)
{
@ -65,6 +68,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Behaviors
}
return null!;
static void ThrowArgumentNullException() => throw new ArgumentNullException(nameof(Animation));
}
}
}

Просмотреть файл

@ -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.Diagnostics;
using System;
using Microsoft.Toolkit.Uwp.UI.Animations;
using Microsoft.Xaml.Interactivity;
using Windows.UI.Xaml;
@ -53,7 +53,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Behaviors
/// <inheritdoc/>
public object Execute(object sender, object parameter)
{
Guard.IsNotNull(Animation, nameof(Animation));
if (Animation is null)
{
ThrowArgumentNullException();
}
if (TargetObject is not null)
{
@ -65,6 +68,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Behaviors
}
return null!;
static void ThrowArgumentNullException() => throw new ArgumentNullException(nameof(Animation));
}
}
}

Просмотреть файл

@ -3,14 +3,11 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Globalization;
using Microsoft.Toolkit.Diagnostics;
using Microsoft.Toolkit.Uwp.Helpers;
using Microsoft.Toolkit.Uwp.UI.Controls.ColorPickerConverters;
using Windows.Foundation;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
@ -233,10 +230,12 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
T child = this.GetTemplateChild(childName) as T;
if ((child == null) && isRequired)
{
ThrowHelper.ThrowArgumentNullException(childName);
ThrowArgumentNullException();
}
return child;
static void ThrowArgumentNullException() => throw new ArgumentNullException(nameof(childName));
}
/// <summary>

Просмотреть файл

@ -148,6 +148,11 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
var horizontalChange = e.Delta.Translation.X;
var verticalChange = e.Delta.Translation.Y;
if (this.FlowDirection == FlowDirection.RightToLeft)
{
horizontalChange *= -1;
}
if (_resizeDirection == GridResizeDirection.Columns)
{
if (HorizontalMove(horizontalChange))

Просмотреть файл

@ -5,11 +5,8 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using Microsoft.Toolkit.Diagnostics;
using Microsoft.Toolkit.Uwp.Helpers;
namespace Microsoft.Toolkit.Uwp.UI.Controls
@ -54,9 +51,12 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
public InterspersedObservableCollection(object itemsSource)
{
Guard.IsAssignableToType<IList>(itemsSource, nameof(itemsSource));
if (!(itemsSource is IList list))
{
ThrowArgumentException();
}
ItemsSource = itemsSource as IList;
ItemsSource = list;
if (ItemsSource is INotifyCollectionChanged notifier)
{
@ -67,6 +67,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
};
notifier.CollectionChanged += weakPropertyChangedListener.OnEvent;
}
static void ThrowArgumentException() => throw new ArgumentNullException("The input items source must be assignable to the System.Collections.IList type.");
}
private void ItemsSource_CollectionChanged(object source, NotifyCollectionChangedEventArgs eventArgs)
@ -192,12 +194,20 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
/// <returns>Inner ItemsSource Index.</returns>
private int ToInnerIndex(int outerIndex)
{
#if DEBUG
Guard.IsInRange(outerIndex, 0, Count, nameof(outerIndex));
Guard.IsFalse(_interspersedObjects.ContainsKey(outerIndex), nameof(outerIndex)); // We can't map a inserted key to the original collection!
#endif
if ((uint)outerIndex >= Count)
{
ThrowArgumentOutOfRangeException();
}
if (_interspersedObjects.ContainsKey(outerIndex))
{
ThrowArgumentException();
}
return outerIndex - _interspersedObjects.Keys.Count(key => key.Value <= outerIndex);
static void ThrowArgumentOutOfRangeException() => throw new ArgumentOutOfRangeException(nameof(outerIndex));
static void ThrowArgumentException() => throw new ArgumentException("The outer index can't be inserted as a key to the original collection.");
}
/// <summary>
@ -207,9 +217,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
/// <returns>Index into the entire collection.</returns>
private int ToOuterIndex(int innerIndex)
{
#if DEBUG
Guard.IsInRange(innerIndex, 0, ItemsSource.Count, nameof(innerIndex));
#endif
if ((uint)innerIndex >= ItemsSource.Count)
{
ThrowArgumentOutOfRangeException();
}
var keys = _interspersedObjects.OrderBy(v => v.Key);
@ -226,6 +237,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
}
return innerIndex;
static void ThrowArgumentOutOfRangeException() => throw new ArgumentOutOfRangeException(nameof(innerIndex));
}
/// <summary>
@ -235,9 +248,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
/// <returns>Projected index in the entire collection.</returns>
private int ToOuterIndexAfterRemoval(int innerIndexToProject)
{
#if DEBUG
Guard.IsInRange(innerIndexToProject, 0, ItemsSource.Count + 1, nameof(innerIndexToProject));
#endif
if ((uint)innerIndexToProject >= ItemsSource.Count + 1)
{
ThrowArgumentOutOfRangeException();
}
//// TODO: Deal with bounds (0 / Count)? Or is it the same?
@ -256,6 +270,8 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
}
return innerIndexToProject;
static void ThrowArgumentOutOfRangeException() => throw new ArgumentOutOfRangeException(nameof(innerIndexToProject));
}
/// <summary>

Просмотреть файл

@ -4,7 +4,6 @@
using System.Collections;
using System.Drawing;
using Microsoft.Toolkit.Diagnostics;
namespace Microsoft.Toolkit.Uwp.UI.Controls
{
@ -28,9 +27,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
/// <param name="columns">The number of columns to track.</param>
public TakenSpotsReferenceHolder(int rows, int columns)
{
Guard.IsGreaterThanOrEqualTo(rows, 0, nameof(rows));
Guard.IsGreaterThanOrEqualTo(columns, 0, nameof(columns));
Height = rows;
Width = columns;

Просмотреть файл

@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using Microsoft.Toolkit.Diagnostics;
using Windows.Foundation;
using Windows.UI.Xaml.Controls;
@ -63,8 +62,10 @@ namespace Microsoft.Toolkit.Uwp.UI.Controls
{
Orientation.Vertical => new Rect(Position.V, Position.U, Size.V, Size.U),
Orientation.Horizontal => new Rect(Position.U, Position.V, Size.U, Size.V),
_ => ThrowHelper.ThrowNotSupportedException<Rect>("unsupported orientation"),
_ => ThrowArgumentException()
};
private static Rect ThrowArgumentException() => throw new ArgumentException("The input orientation is not valid.");
}
private struct Row

Просмотреть файл

@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using System;
using Microsoft.Toolkit.Diagnostics;
using Microsoft.Toolkit.Uwp.UI.Media;
using Windows.UI.Composition;
using Windows.UI.Xaml;
@ -50,14 +49,21 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
{
if (Target is not TEffect target)
{
return ThrowHelper.ThrowArgumentNullException<AnimationBuilder>("The target effect is null, make sure to set the Target property");
static AnimationBuilder ThrowArgumentNullException() => throw new ArgumentNullException("The target effect is null, make sure to set the Target property");
return ThrowArgumentNullException();
}
if (ExplicitTarget is not string explicitTarget)
{
return ThrowHelper.ThrowArgumentNullException<AnimationBuilder>(
"The target effect cannot be animated at this time. If you're targeting one of the " +
"built-in effects, make sure that the PipelineEffect.IsAnimatable property is set to true.");
static AnimationBuilder ThrowArgumentNullException()
{
throw new ArgumentNullException(
"The target effect cannot be animated at this time. If you're targeting one of the " +
"built-in effects, make sure that the PipelineEffect.IsAnimatable property is set to true.");
}
return ThrowArgumentNullException();
}
NormalizedKeyFrameAnimationBuilder<TKeyFrame>.Composition keyFrameBuilder = new(
@ -70,7 +76,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Animations
CompositionAnimation animation = keyFrameBuilder.GetAnimation(target.Brush!, out _);
return builder.ExternalAnimation(target.Brush, animation);
return builder.ExternalAnimation(target.Brush!, animation);
}
}
}

Просмотреть файл

@ -0,0 +1,307 @@
// 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;
using Microsoft.Graphics.Canvas.Brushes;
using Microsoft.Graphics.Canvas.Geometry;
using Windows.Foundation;
using Windows.UI;
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry
{
/// <summary>
/// Extension methods for CanvasDrawingSession.
/// </summary>
public static class CanvasDrawingSessionExtensions
{
/// <summary>
/// Draws a circle of at the given center, having the specified radius, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
/// </summary>
/// <param name="session">CanvasDrawingSession</param>
/// <param name="centerPoint">Center of the Circle</param>
/// <param name="radius">Radius of the Circle</param>
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
/// color and stroke style.</param>
public static void DrawCircle(this CanvasDrawingSession session, Vector2 centerPoint, float radius, ICanvasStroke stroke)
{
session.DrawCircle(centerPoint, radius, stroke.Brush, stroke.Width, stroke.Style);
}
/// <summary>
/// Draws a circle of at the given center, having the specified radius, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
/// </summary>
/// <param name="session">CanvasDrawingSession</param>
/// <param name="x">Offset of the Center in x axis</param>
/// <param name="y">Ordinate of the Center in the y axis</param>
/// <param name="radius">Radius of the Circle</param>
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
/// color and stroke style.</param>
public static void DrawCircle(this CanvasDrawingSession session, float x, float y, float radius, ICanvasStroke stroke)
{
session.DrawCircle(x, y, radius, stroke.Brush, stroke.Width, stroke.Style);
}
/// <summary>
/// Draws an Ellipse of at the given center, having the specified radius, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
/// </summary>
/// <param name="session">CanvasDrawingSession</param>
/// <param name="centerPoint">Center of the Circle</param>
/// <param name="radiusX">Radius in the X axis</param>
/// <param name="radiusY">Radius in the Y axis</param>
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
/// color and stroke style.</param>
public static void DrawEllipse(this CanvasDrawingSession session, Vector2 centerPoint, float radiusX, float radiusY, ICanvasStroke stroke)
{
session.DrawEllipse(centerPoint, radiusX, radiusY, stroke.Brush, stroke.Width, stroke.Style);
}
/// <summary>
/// Draws an Ellipse of at the given center, having the specified radius, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
/// </summary>
/// <param name="session">CanvasDrawingSession</param>
/// <param name="x">Offset of the Center on the x axis</param>
/// <param name="y">Offset of the Center on the y axis</param>
/// <param name="radiusX">Radius in the X axis</param>
/// <param name="radiusY">Radius in the Y axis</param>
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
/// color and stroke style.</param>
public static void DrawEllipse(this CanvasDrawingSession session, float x, float y, float radiusX, float radiusY, ICanvasStroke stroke)
{
session.DrawEllipse(x, y, radiusX, radiusY, stroke.Brush, stroke.Width, stroke.Style);
}
/// <summary>
/// Draws a geometry relative to the origin, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
/// </summary>
/// <param name="session">CanvasDrawingSession</param>
/// <param name="geometry">CanvasGeometry to render</param>
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
/// color and stroke style.</param>
public static void DrawGeometry(this CanvasDrawingSession session, CanvasGeometry geometry, ICanvasStroke stroke)
{
session.DrawGeometry(geometry, stroke.Brush, stroke.Width, stroke.Style);
}
/// <summary>
/// Draws a geometry relative to the specified position, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
/// </summary>
/// <param name="session">CanvasDrawingSession</param>
/// <param name="geometry">CanvasGeometry to render</param>
/// <param name="offset">Offset</param>
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
/// color and stroke style.</param>
public static void DrawGeometry(this CanvasDrawingSession session, CanvasGeometry geometry, Vector2 offset, ICanvasStroke stroke)
{
session.DrawGeometry(geometry, offset, stroke.Brush, stroke.Width, stroke.Style);
}
/// <summary>
/// Draws a geometry relative to the specified position, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
/// </summary>
/// <param name="session">CanvasDrawingSession</param>
/// <param name="geometry">CanvasGeometry to render</param>
/// <param name="x">Offset on the x axis</param>
/// <param name="y">Offset on the y axis</param>
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
/// color and stroke style.</param>
public static void DrawGeometry(this CanvasDrawingSession session, CanvasGeometry geometry, float x, float y, ICanvasStroke stroke)
{
session.DrawGeometry(geometry, x, y, stroke.Brush, stroke.Width, stroke.Style);
}
/// <summary>
/// Draws a line between the specified positions, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
/// </summary>
/// <param name="session">CanvasDrawingSession</param>
/// <param name="point0">Starting position of the line</param>
/// <param name="point1">Ending position of the line</param>
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
/// color and stroke style.</param>
public static void DrawLine(this CanvasDrawingSession session, Vector2 point0, Vector2 point1, ICanvasStroke stroke)
{
session.DrawLine(point0, point1, stroke.Brush, stroke.Width, stroke.Style);
}
/// <summary>
/// Draws a line between the specified positions, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
/// </summary>
/// <param name="session">CanvasDrawingSession</param>
/// <param name="x0">Offset of Starting position of the line on x-axis</param>
/// <param name="y0">Offset of Starting position of the line on y-axis</param>
/// <param name="x1">Offset of Ending position of the line on x-axis</param>
/// <param name="y1">Offset of Ending position of the line on y-axis</param>
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
/// color and stroke style.</param>
public static void DrawLine(this CanvasDrawingSession session, float x0, float y0, float x1, float y1, ICanvasStroke stroke)
{
session.DrawLine(x0, y0, x1, y1, stroke.Brush, stroke.Width, stroke.Style);
}
/// <summary>
/// Draws a Rectangle of the specified dimensions, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
/// </summary>
/// <param name="session">CanvasDrawingSession</param>
/// <param name="rect">Rectangle dimensions</param>
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
/// color and stroke style.</param>
public static void DrawRectangle(this CanvasDrawingSession session, Rect rect, ICanvasStroke stroke)
{
session.DrawRectangle(rect, stroke.Brush, stroke.Width, stroke.Style);
}
/// <summary>
/// Draws a Rectangle of the specified dimensions, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
/// </summary>
/// <param name="session">CanvasDrawingSession</param>
/// <param name="x">Offset of the top left corner of the Rectangle on the x-axis</param>
/// <param name="y">Offset of the top left corner of the Rectangle on the y-axis</param>
/// <param name="w">Width of the Rectangle</param>
/// <param name="h">Height of the Rectangle</param>
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
/// color and stroke style.</param>
public static void DrawRectangle(this CanvasDrawingSession session, float x, float y, float w, float h, ICanvasStroke stroke)
{
session.DrawRectangle(x, y, w, h, stroke.Brush, stroke.Width, stroke.Style);
}
/// <summary>
/// Draws a Rounded Rectangle of the specified dimensions, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
/// </summary>
/// <param name="session">CanvasDrawingSession</param>
/// <param name="rect">Rectangle dimensions</param>
/// <param name="radiusX">Corner Radius on the x axis</param>
/// <param name="radiusY">Corner Radius on the y axis</param>
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
/// color and stroke style.</param>
public static void DrawRoundedRectangle(this CanvasDrawingSession session, Rect rect, float radiusX, float radiusY, ICanvasStroke stroke)
{
session.DrawRoundedRectangle(rect, radiusX, radiusY, stroke.Brush, stroke.Width, stroke.Style);
}
/// <summary>
/// Draws a Rounded Rectangle of the specified dimensions, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
/// </summary>
/// <param name="session">CanvasDrawingSession</param>
/// <param name="x">Offset of the top left corner of the Rounded Rectangle on the x-axis</param>
/// <param name="y">Offset of the top left corner of the Rounded Rectangle on the y-axis</param>
/// <param name="w">Width of the Rounded Rectangle</param>
/// <param name="h">Height of the Rounded Rectangle</param>
/// <param name="radiusX">Corner Radius on the x axis</param>
/// <param name="radiusY">Corner Radius on the y axis</param>
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
/// color and stroke style.</param>
public static void DrawRoundedRectangle(this CanvasDrawingSession session, float x, float y, float w, float h, float radiusX, float radiusY, ICanvasStroke stroke)
{
session.DrawRoundedRectangle(x, y, w, h, radiusX, radiusY, stroke.Brush, stroke.Width, stroke.Style);
}
/// <summary>
/// Draws a Squircle of the specified dimensions, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
/// </summary>
/// <param name="session">CanvasDrawingSession</param>
/// <param name="x">Offset of the top left corner of the Squircle on the x-axis</param>
/// <param name="y">Offset of the top left corner of the Squircle on the y-axis</param>
/// <param name="w">Width of the Squircle</param>
/// <param name="h">Height of the Squircle</param>
/// <param name="radiusX">Corner Radius on the x axis</param>
/// <param name="radiusY">Corner Radius on the y axis</param>
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
/// color and stroke style.</param>
public static void DrawSquircle(this CanvasDrawingSession session, float x, float y, float w, float h, float radiusX, float radiusY, ICanvasStroke stroke)
{
using var geometry = CanvasPathGeometry.CreateSquircle(session.Device, x, y, w, h, radiusX, radiusY);
session.DrawGeometry(geometry, stroke);
}
/// <summary>
/// Draws a Squircle of the specified dimensions, using a CanvasStroke to define the stroke width, the stroke color and stroke style.
/// </summary>
/// <param name="session">CanvasDrawingSession</param>
/// <param name="x">Offset of the top left corner of the Squircle on the x-axis</param>
/// <param name="y">Offset of the top left corner of the Squircle on the y-axis</param>
/// <param name="w">Width of the Squircle</param>
/// <param name="h">Height of the Squircle</param>
/// <param name="radiusX">Corner Radius on the x axis</param>
/// <param name="radiusY">Corner Radius on the y axis</param>
/// <param name="offset">Offset of the Squircle from the origin.</param>
/// <param name="stroke">CanvasStroke defining the stroke width, the stroke
/// color and stroke style.</param>
public static void DrawSquircle(this CanvasDrawingSession session, float x, float y, float w, float h, float radiusX, float radiusY, Vector2 offset, ICanvasStroke stroke)
{
using var geometry = CanvasPathGeometry.CreateSquircle(session.Device, x, y, w, h, radiusX, radiusY);
session.DrawGeometry(geometry, offset, stroke);
}
/// <summary>
/// Fills a Squircle of the specified dimensions, using the given color.
/// </summary>
/// <param name="session">CanvasDrawingSession</param>
/// <param name="x">Offset of the top left corner of the Squircle on the x-axis</param>
/// <param name="y">Offset of the top left corner of the Squircle on the y-axis</param>
/// <param name="w">Width of the Squircle</param>
/// <param name="h">Height of the Squircle</param>
/// <param name="radiusX">Corner Radius on the x axis</param>
/// <param name="radiusY">Corner Radius on the y axis</param>
/// <param name="color">Color to fill the Squircle.</param>
public static void FillSquircle(this CanvasDrawingSession session, float x, float y, float w, float h, float radiusX, float radiusY, Color color)
{
using var geometry = CanvasPathGeometry.CreateSquircle(session.Device, x, y, w, h, radiusX, radiusY);
session.FillGeometry(geometry, color);
}
/// <summary>
/// Fills a Squircle of the specified dimensions, using the given brush.
/// </summary>
/// <param name="session">CanvasDrawingSession</param>
/// <param name="x">Offset of the top left corner of the Squircle on the x-axis</param>
/// <param name="y">Offset of the top left corner of the Squircle on the y-axis</param>
/// <param name="w">Width of the Squircle</param>
/// <param name="h">Height of the Squircle</param>
/// <param name="radiusX">Corner Radius on the x axis</param>
/// <param name="radiusY">Corner Radius on the y axis</param>
/// <param name="brush">Brush to fill the Squircle.</param>
public static void FillSquircle(this CanvasDrawingSession session, float x, float y, float w, float h, float radiusX, float radiusY, ICanvasBrush brush)
{
using var geometry = CanvasPathGeometry.CreateSquircle(session.Device, x, y, w, h, radiusX, radiusY);
session.FillGeometry(geometry, brush);
}
/// <summary>
/// Fills a Squircle of the specified dimensions, using the given color at specified offset.
/// </summary>
/// <param name="session">CanvasDrawingSession</param>
/// <param name="x">Offset of the top left corner of the Squircle on the x-axis</param>
/// <param name="y">Offset of the top left corner of the Squircle on the y-axis</param>
/// <param name="w">Width of the Squircle</param>
/// <param name="h">Height of the Squircle</param>
/// <param name="radiusX">Corner Radius on the x axis</param>
/// <param name="radiusY">Corner Radius on the y axis</param>
/// <param name="offset">Offset of the Squircle from the origin.</param>
/// <param name="color">Color to fill the Squircle.</param>
public static void FillSquircle(this CanvasDrawingSession session, float x, float y, float w, float h, float radiusX, float radiusY, Vector2 offset, Color color)
{
using var geometry = CanvasPathGeometry.CreateSquircle(session.Device, x, y, w, h, radiusX, radiusY);
session.FillGeometry(geometry, offset, color);
}
/// <summary>
/// Fills a Squircle of the specified dimensions, using the given brush at specified offset.
/// </summary>
/// <param name="session">CanvasDrawingSession</param>
/// <param name="x">Offset of the top left corner of the Squircle on the x-axis</param>
/// <param name="y">Offset of the top left corner of the Squircle on the y-axis</param>
/// <param name="w">Width of the Squircle</param>
/// <param name="h">Height of the Squircle</param>
/// <param name="radiusX">Corner Radius on the x axis</param>
/// <param name="radiusY">Corner Radius on the y axis</param>
/// <param name="offset">Offset of the Squircle from the origin.</param>
/// <param name="brush">Brush to fill the Squircle.</param>
public static void FillSquircle(this CanvasDrawingSession session, float x, float y, float w, float h, float radiusX, float radiusY, Vector2 offset, ICanvasBrush brush)
{
using var geometry = CanvasPathGeometry.CreateSquircle(session.Device, x, y, w, h, radiusX, radiusY);
session.FillGeometry(geometry, offset, brush);
}
}
}

Просмотреть файл

@ -0,0 +1,462 @@
// 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 System.Numerics;
using Microsoft.Graphics.Canvas.Geometry;
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core;
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry
{
/// <summary>
/// Defines extension methods for CanvasPathBuilder.
/// </summary>
public static class CanvasPathBuilderExtensions
{
private const float SquircleFactor = 1.125f;
private const float ControlPointFactor = 46f / 64f;
/// <summary>
/// Adds a line in the form of a cubic bezier. The control point of the quadratic bezier will be the endpoint of the line itself.
/// </summary>
/// <param name="pathBuilder"><see cref="CanvasPathBuilder"/></param>
/// <param name="end">Ending location of the line segment.</param>
public static void AddLineAsQuadraticBezier(this CanvasPathBuilder pathBuilder, Vector2 end)
{
pathBuilder.AddQuadraticBezier(end, end);
}
/// <summary>
/// Adds a line in the form of a cubic bezier. The two control points of the cubic bezier will be the endpoints of the line itself.
/// </summary>
/// <param name="pathBuilder"><see cref="CanvasPathBuilder"/></param>
/// <param name="start">Starting location of the line segment.</param>
/// <param name="end">Ending location of the line segment.</param>
public static void AddLineAsCubicBezier(this CanvasPathBuilder pathBuilder, Vector2 start, Vector2 end)
{
pathBuilder.AddCubicBezier(start, end, end);
}
/// <summary>
/// Adds a circle figure to the path.
/// </summary>
/// <param name="pathBuilder"><see cref="CanvasPathBuilder"/></param>
/// <param name="center">Center location of the circle.</param>
/// <param name="radius">Radius of the circle.</param>
public static void AddCircleFigure(this CanvasPathBuilder pathBuilder, Vector2 center, float radius)
{
pathBuilder.AddEllipseFigure(center.X, center.Y, radius, radius);
}
/// <summary>
/// Adds a circle figure to the path.
/// </summary>
/// <param name="pathBuilder"><see cref="CanvasPathBuilder"/></param>
/// <param name="x">X coordinate of the center location of the circle.</param>
/// <param name="y">Y coordinate of the center location of the circle.</param>
/// <param name="radius">Radius of the circle.</param>
public static void AddCircleFigure(this CanvasPathBuilder pathBuilder, float x, float y, float radius)
{
pathBuilder.AddEllipseFigure(x, y, radius, radius);
}
/// <summary>
/// Adds an ellipse figure to the path.
/// </summary>
/// <param name="pathBuilder"><see cref="CanvasPathBuilder"/></param>
/// <param name="center">Center location of the ellipse.</param>
/// <param name="radiusX">Radius of the ellipse on the X-axis.</param>
/// <param name="radiusY">Radius of the ellipse on the Y-axis.</param>
public static void AddEllipseFigure(this CanvasPathBuilder pathBuilder, Vector2 center, float radiusX, float radiusY)
{
pathBuilder.AddEllipseFigure(center.X, center.Y, radiusX, radiusY);
}
/// <summary>
/// Adds an ellipse figure to the path.
/// </summary>
/// <param name="pathBuilder"><see cref="CanvasPathBuilder"/></param>
/// <param name="x">X coordinate of the center location of the ellipse.</param>
/// <param name="y">Y coordinate of the center location of the ellipse.</param>
/// <param name="radiusX">Radius of the ellipse on the X-axis.</param>
/// <param name="radiusY">Radius of the ellipse on the Y-axis.</param>
public static void AddEllipseFigure(this CanvasPathBuilder pathBuilder, float x, float y, float radiusX, float radiusY)
{
// Sanitize the radiusX by taking the absolute value
radiusX = Math.Abs(radiusX);
// Sanitize the radiusY by taking the absolute value
radiusY = Math.Abs(radiusY);
try
{
pathBuilder.BeginFigure(x + radiusX, y);
}
catch (ArgumentException)
{
// An ArgumentException will be raised if another figure was already begun( and not ended) before calling AddEllipseFigure() method.
static void Throw() => throw new InvalidOperationException("A call to CanvasPathBuilder.AddEllipseFigure occurred, " +
"when another figure was already begun. Please call CanvasPathBuilder.EndFigure method, " +
"before calling CanvasPathBuilder.AddEllipseFigure, to end the previous figure.");
Throw();
}
// First Semi-Ellipse
pathBuilder.AddArc(new Vector2(x - radiusX, y), radiusX, radiusY, Scalar.Pi, CanvasSweepDirection.Clockwise, CanvasArcSize.Large);
// Second Semi-Ellipse
pathBuilder.AddArc(new Vector2(x + radiusX, y), radiusX, radiusY, Scalar.Pi, CanvasSweepDirection.Clockwise, CanvasArcSize.Large);
// End Figure
pathBuilder.EndFigure(CanvasFigureLoop.Closed);
}
/// <summary>
/// Adds a n-sided polygon figure to the path.
/// </summary>
/// <param name="pathBuilder"><see cref="CanvasPathBuilder"/></param>
/// <param name="numSides">Number of sides of the polygon.</param>
/// <param name="center">Center location of the polygon.</param>
/// <param name="radius">Radius of the circle circumscribing the polygon i.e. the distance
/// of each of the vertices of the polygon from the center.</param>
public static void AddPolygonFigure(this CanvasPathBuilder pathBuilder, int numSides, Vector2 center, float radius)
{
pathBuilder.AddPolygonFigure(numSides, center.X, center.Y, radius);
}
/// <summary>
/// Adds a n-sided polygon figure to the path.
/// </summary>
/// <param name="pathBuilder"><see cref="CanvasPathBuilder"/></param>
/// <param name="numSides">Number of sides of the polygon.</param>
/// <param name="x">X coordinate of the center location of the polygon.</param>
/// <param name="y">Y coordinate of the center location of the polygon.</param>
/// <param name="radius">Radius of the circle circumscribing the polygon i.e. the distance
/// of each of the vertices of the polygon from the center.</param>
public static void AddPolygonFigure(this CanvasPathBuilder pathBuilder, int numSides, float x, float y, float radius)
{
// Sanitize the radius by taking the absolute value
radius = Math.Abs(radius);
// A polygon should have at least 3 sides
if (numSides <= 2)
{
ThrowArgumentOutOfRangeException();
}
// Calculate the first vertex location based on the number of sides
var angle = Scalar.TwoPi / numSides;
var startAngle = numSides % 2 == 1 ? Scalar.PiByTwo : Scalar.PiByTwo - (angle / 2f);
var startX = x + (float)(radius * Math.Cos(startAngle));
var startY = y - (float)(radius * Math.Sin(startAngle));
try
{
pathBuilder.BeginFigure(startX, startY);
}
catch (ArgumentException)
{
// An ArgumentException will be raised if another figure was already begun( and not ended) before calling AddPolygonFigure() method.
static void Throw() => throw new InvalidOperationException("A call to CanvasPathBuilder.AddPolygonFigure occurred, " +
"when another figure was already begun. Please call CanvasPathBuilder.EndFigure method, " +
"before calling CanvasPathBuilder.AddPolygonFigure, to end the previous figure.");
Throw();
}
// Add lines to the remaining vertices
for (var i = 1; i < numSides; i++)
{
var posX = x + (float)(radius * Math.Cos(startAngle + (i * angle)));
var posY = y - (float)(radius * Math.Sin(startAngle + (i * angle)));
pathBuilder.AddLine(posX, posY);
}
// Add a line to the first vertex so that the lines join properly
pathBuilder.AddLine(startX, startY);
// End the Figure
pathBuilder.EndFigure(CanvasFigureLoop.Closed);
static void ThrowArgumentOutOfRangeException() => throw new ArgumentOutOfRangeException($"Parameter {nameof(numSides)} must be greater than 2.");
}
/// <summary>
/// Adds a Rectangle to the Path.
/// </summary>
/// <param name="pathBuilder"><see cref="CanvasPathBuilder"/></param>
/// <param name="x">X offset of the TopLeft corner of the Rectangle</param>
/// <param name="y">Y offset of the TopLeft corner of the Rectangle</param>
/// <param name="width">Width of the Rectangle</param>
/// <param name="height">Height of the Rectangle</param>
public static void AddRectangleFigure(this CanvasPathBuilder pathBuilder, float x, float y, float width, float height)
{
// Sanitize the width by taking the absolute value
width = Math.Abs(width);
// Sanitize the height by taking the absolute value
height = Math.Abs(height);
try
{
pathBuilder.BeginFigure(x, y);
}
catch (ArgumentException)
{
// An ArgumentException will be raised if another figure was already begun( and not ended) before calling AddPolygonFigure() method.
static void Throw() => throw new InvalidOperationException("A call to CanvasPathBuilder.AddRectangleFigure occurred, " +
"when another figure was already begun. Please call CanvasPathBuilder.EndFigure method, " +
"before calling CanvasPathBuilder.AddRectangleFigure, to end the previous figure.");
Throw();
}
// Top Side
pathBuilder.AddLine(x + width, y);
// Right Side
pathBuilder.AddLine(x + width, y + height);
// Bottom Side
pathBuilder.AddLine(x, y + height);
// Left Side
pathBuilder.AddLine(x, y);
// End the Figure
pathBuilder.EndFigure(CanvasFigureLoop.Closed);
}
/// <summary>
/// Adds a RoundedRectangle to the Path.
/// </summary>
/// <param name="pathBuilder"><see cref="CanvasPathBuilder"/></param>
/// <param name="x">X offset of the TopLeft corner of the RoundedRectangle</param>
/// <param name="y">Y offset of the TopLeft corner of the RoundedRectangle</param>
/// <param name="width">Width of the RoundedRectangle</param>
/// <param name="height">Height of the RoundedRectangle</param>
/// <param name="radiusX">Corner Radius on the x-axis</param>
/// <param name="radiusY">Corner Radius on the y-axis</param>
public static void AddRoundedRectangleFigure(this CanvasPathBuilder pathBuilder, float x, float y, float width, float height, float radiusX, float radiusY)
{
// Sanitize the width by taking the absolute value
width = Math.Abs(width);
// Sanitize the height by taking the absolute value
height = Math.Abs(height);
var rect = new CanvasRoundRect(x, y, width, height, radiusX, radiusY);
pathBuilder.AddRoundedRectangleFigure(ref rect, true);
}
/// <summary>
/// Adds a RoundedRectangle to the Path. (To be used internally)
/// </summary>
/// <param name="pathBuilder"><see cref="CanvasPathBuilder"/></param>
/// <param name="rect">CanvasRoundRect</param>
/// <param name="raiseException">Flag to indicate whether exception should be raised</param>
internal static void AddRoundedRectangleFigure(this CanvasPathBuilder pathBuilder, ref CanvasRoundRect rect, bool raiseException = false)
{
try
{
// Begin path
pathBuilder.BeginFigure(new Vector2(rect.LeftTopX, rect.LeftTopY));
}
catch (ArgumentException)
{
if (!raiseException)
{
return;
}
// An ArgumentException will be raised if another figure was already begun( and not ended) before calling AddPolygonFigure() method.
static void Throw() => throw new InvalidOperationException("A call to CanvasPathBuilder.AddRoundedRectangleFigure occurred, " +
"when another figure was already begun. Please call CanvasPathBuilder.EndFigure method, " +
"before calling CanvasPathBuilder.AddRoundedRectangleFigure, to end the previous figure.");
Throw();
}
// Top line
pathBuilder.AddLine(new Vector2(rect.RightTopX, rect.RightTopY));
// Upper-right corner
var radiusX = rect.TopRightX - rect.RightTopX;
var radiusY = rect.TopRightY - rect.RightTopY;
var center = new Vector2(rect.RightTopX, rect.TopRightY);
pathBuilder.AddArc(center, radiusX, radiusY, 3f * Scalar.PiByTwo, Scalar.PiByTwo);
// Right line
pathBuilder.AddLine(new Vector2(rect.BottomRightX, rect.BottomRightY));
// Lower-right corner
radiusX = rect.BottomRightX - rect.RightBottomX;
radiusY = rect.RightBottomY - rect.BottomRightY;
center = new Vector2(rect.RightBottomX, rect.BottomRightY);
pathBuilder.AddArc(center, radiusX, radiusY, 0f, Scalar.PiByTwo);
// Bottom line
pathBuilder.AddLine(new Vector2(rect.LeftBottomX, rect.LeftBottomY));
// Lower-left corner
radiusX = rect.LeftBottomX - rect.BottomLeftX;
radiusY = rect.LeftBottomY - rect.BottomLeftY;
center = new Vector2(rect.LeftBottomX, rect.BottomLeftY);
pathBuilder.AddArc(center, radiusX, radiusY, Scalar.PiByTwo, Scalar.PiByTwo);
// Left line
pathBuilder.AddLine(new Vector2(rect.TopLeftX, rect.TopLeftY));
// Upper-left corner
radiusX = rect.LeftTopX - rect.TopLeftX;
radiusY = rect.TopLeftY - rect.LeftTopY;
center = new Vector2(rect.LeftTopX, rect.TopLeftY);
pathBuilder.AddArc(center, radiusX, radiusY, 2f * Scalar.PiByTwo, Scalar.PiByTwo);
// End path
pathBuilder.EndFigure(CanvasFigureLoop.Closed);
}
/// <summary>
/// Adds a Squircle to the Path.
/// </summary>
/// <param name="pathBuilder"><see cref="CanvasPathBuilder"/></param>
/// <param name="x">X offset of the TopLeft corner of the Squircle</param>
/// <param name="y">Y offset of the TopLeft corner of the Squircle</param>
/// <param name="width">Width of the Squircle</param>
/// <param name="height">Height of the Squircle</param>
/// <param name="radiusX">Corner Radius on the x-axis</param>
/// <param name="radiusY">Corner Radius on the y-axis</param>
public static void AddSquircleFigure(this CanvasPathBuilder pathBuilder, float x, float y, float width, float height, float radiusX, float radiusY)
{
// Sanitize the width by taking the absolute value
width = Math.Abs(width);
// Sanitize the height by taking the absolute value
height = Math.Abs(height);
var rect = new CanvasRoundRect(x, y, width, height, radiusX * SquircleFactor, radiusY * SquircleFactor);
pathBuilder.AddSquircleFigure(ref rect, true);
}
/// <summary>
/// Adds a Squircle to the Path. (To be used internally)
/// </summary>
/// <param name="pathBuilder"><see cref="CanvasPathBuilder"/></param>
/// <param name="rect">CanvasRoundRect</param>
/// <param name="raiseException">Flag to indicate whether exception should be raised</param>
internal static void AddSquircleFigure(this CanvasPathBuilder pathBuilder, ref CanvasRoundRect rect, bool raiseException = false)
{
try
{
// Begin path
pathBuilder.BeginFigure(new Vector2(rect.LeftTopX, rect.LeftTopY));
}
catch (ArgumentException)
{
if (!raiseException)
{
return;
}
// An ArgumentException will be raised if another figure was already begun( and not ended) before calling AddPolygonFigure() method.
static void Throw() => throw new InvalidOperationException("A call to CanvasPathBuilder.AddSquircleFigure occurred, " +
"when another figure was already begun. Please call CanvasPathBuilder.EndFigure method, " +
"before calling CanvasPathBuilder.AddSquircleFigure, to end the previous figure.");
Throw();
}
// Top line
pathBuilder.AddLine(new Vector2(rect.RightTopX, rect.RightTopY));
// Upper-right corner
var rightTopControlPoint = new Vector2(rect.RightTopX + ((rect.TopRightX - rect.RightTopX) * ControlPointFactor), rect.RightTopY);
var topRightControlPoint = new Vector2(rect.TopRightX, rect.TopRightY - ((rect.TopRightY - rect.RightTopY) * ControlPointFactor));
// Top Right Curve
pathBuilder.AddCubicBezier(rightTopControlPoint, topRightControlPoint, new Vector2(rect.TopRightX, rect.TopRightY));
// Right line
pathBuilder.AddLine(new Vector2(rect.BottomRightX, rect.BottomRightY));
// Lower-right corner
var bottomRightControlPoint = new Vector2(rect.BottomRightX, rect.BottomRightY + ((rect.RightBottomY - rect.BottomRightY) * ControlPointFactor));
var rightBottomControlPoint = new Vector2(rect.RightBottomX + ((rect.BottomRightX - rect.RightBottomX) * ControlPointFactor), rect.RightBottomY);
// Bottom Right Curve
pathBuilder.AddCubicBezier(bottomRightControlPoint, rightBottomControlPoint, new Vector2(rect.RightBottomX, rect.RightBottomY));
// Bottom line
pathBuilder.AddLine(new Vector2(rect.LeftBottomX, rect.LeftBottomY));
// Lower-left corner
var leftBottomControlPoint = new Vector2(rect.LeftBottomX - ((rect.LeftBottomX - rect.BottomLeftX) * ControlPointFactor), rect.LeftBottomY);
var bottomLeftControlPoint = new Vector2(rect.BottomLeftX, rect.BottomLeftY + ((rect.LeftBottomY - rect.BottomLeftY) * ControlPointFactor));
// Bottom Left Curve
pathBuilder.AddCubicBezier(leftBottomControlPoint, bottomLeftControlPoint, new Vector2(rect.BottomLeftX, rect.BottomLeftY));
// Left line
pathBuilder.AddLine(new Vector2(rect.TopLeftX, rect.TopLeftY));
// Upper-left corner
var topLeftControlPoint = new Vector2(rect.TopLeftX, rect.TopLeftY - ((rect.TopLeftY - rect.LeftTopY) * ControlPointFactor));
var leftTopControlPoint = new Vector2(rect.LeftTopX - ((rect.LeftTopX - rect.TopLeftX) * ControlPointFactor), rect.LeftTopY);
// Top Left Curve
pathBuilder.AddCubicBezier(topLeftControlPoint, leftTopControlPoint, new Vector2(rect.LeftTopX, rect.LeftTopY));
// End path
pathBuilder.EndFigure(CanvasFigureLoop.Closed);
}
/// <summary>
/// Builds a path with the given collection of points.
/// </summary>
/// <param name="builder"><see cref="CanvasPathBuilder"/></param>
/// <param name="canvasFigureLoop">Specifies whether the figure is open or closed.
/// This affects the appearance of fills and strokes, as well as geometry operations.</param>
/// <param name="points">Collection of Vector2 points on the path.</param>
/// <returns><see cref="CanvasPathBuilder"/> object</returns>
public static CanvasPathBuilder BuildPathWithLines(this CanvasPathBuilder builder, CanvasFigureLoop canvasFigureLoop, IEnumerable<Vector2> points)
{
var first = true;
foreach (var point in points)
{
if (first)
{
builder.BeginFigure(point);
first = false;
}
else
{
builder.AddLine(point);
}
}
builder.EndFigure(canvasFigureLoop);
return builder;
}
/// <summary>
/// Builds a path with the given collection of points in the (x, y) pattern.
/// </summary>
/// <param name="builder"><see cref="CanvasPathBuilder"/></param>
/// <param name="canvasFigureLoop">Specifies whether the figure is open or closed.
/// This affects the appearance of fills and strokes, as well as geometry operations.</param>
/// <param name="nodes">Collection of points in the (x, y) pattern on the path.</param>
/// <returns><see cref="CanvasPathBuilder"/> object</returns>
public static CanvasPathBuilder BuildPathWithLines(this CanvasPathBuilder builder, CanvasFigureLoop canvasFigureLoop, IEnumerable<(float x, float y)> nodes)
{
var vectors = nodes.Select(n => new Vector2(n.x, n.y));
return BuildPathWithLines(builder, canvasFigureLoop, vectors);
}
}
}

Просмотреть файл

@ -0,0 +1,140 @@
// 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 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;
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry
{
/// <summary>
/// Helper Class for creating Win2d objects.
/// </summary>
public static class CanvasPathGeometry
{
/// <summary>
/// Parses the Path data string and converts it to CanvasGeometry.
/// </summary>
/// <param name="pathData">Path data</param>
/// <returns><see cref="CanvasGeometry"/></returns>
public static CanvasGeometry CreateGeometry(string pathData)
{
return CreateGeometry(null, pathData);
}
/// <summary>
/// Parses the Path data string and converts it to CanvasGeometry.
/// </summary>
/// <param name="resourceCreator"><see cref="ICanvasResourceCreator"/></param>
/// <param name="pathData">Path data</param>
/// <returns><see cref="CanvasGeometry"/></returns>
public static CanvasGeometry CreateGeometry(ICanvasResourceCreator resourceCreator, string pathData)
{
using (new CultureShield("en-US"))
{
// Get the CanvasGeometry from the path data
return CanvasGeometryParser.Parse(resourceCreator, pathData);
}
}
/// <summary>
/// Creates a Squircle geometry with the specified extents.
/// </summary>
/// <param name="resourceCreator">Resource creator</param>
/// <param name="x">X offset of the TopLeft corner of the Squircle</param>
/// <param name="y">Y offset of the TopLeft corner of the Squircle</param>
/// <param name="width">Width of the Squircle</param>
/// <param name="height">Height of the Squircle</param>
/// <param name="radiusX">Corner Radius on the x-axis</param>
/// <param name="radiusY">Corner Radius on the y-axis</param>
/// <returns><see cref="CanvasGeometry"/></returns>
public static CanvasGeometry CreateSquircle(ICanvasResourceCreator resourceCreator, float x, float y, float width, float height, float radiusX, float radiusY)
{
using var pathBuilder = new CanvasPathBuilder(resourceCreator);
pathBuilder.AddSquircleFigure(x, y, width, height, radiusX, radiusY);
return CanvasGeometry.CreatePath(pathBuilder);
}
/// <summary>
/// Parses the given Brush data string and converts it to ICanvasBrush.
/// </summary>
/// <param name="resourceCreator">ICanvasResourceCreator</param>
/// <param name="brushData">Brush data in string format</param>
/// <returns><see cref="ICanvasBrush"/></returns>
public static ICanvasBrush CreateBrush(ICanvasResourceCreator resourceCreator, string brushData)
{
using (new CultureShield("en-US"))
{
return CanvasBrushParser.Parse(resourceCreator, brushData);
}
}
/// <summary>
/// Parses the given Stroke data string and converts it to ICanvasStroke.
/// </summary>
/// <param name="resourceCreator">ICanvasResourceCreator</param>
/// <param name="strokeData">Stroke data in string format</param>
/// <returns><see cref="ICanvasStroke"/></returns>
public static ICanvasStroke CreateStroke(ICanvasResourceCreator resourceCreator, string strokeData)
{
using (new CultureShield("en-US"))
{
return CanvasStrokeParser.Parse(resourceCreator, strokeData);
}
}
/// <summary>
/// Parses the give CanvasStrokeStyle data string and converts it to CanvasStrokeStyle.
/// </summary>
/// <param name="styleData">CanvasStrokeStyle data in string format</param>
/// <returns><see cref="CanvasStrokeStyle"/> object</returns>
public static CanvasStrokeStyle CreateStrokeStyle(string styleData)
{
using (new CultureShield("en-US"))
{
return CanvasStrokeStyleParser.Parse(styleData);
}
}
/// <summary>
/// Converts the color string in Hexadecimal or HDR color format to the corresponding Color 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 &amp; A should have value in the range between 0 and 1, inclusive)
/// </summary>
/// <param name="colorString">Color string in Hexadecimal or HDR format</param>
/// <returns>Color</returns>
public static Color CreateColor(string colorString)
{
using (new CultureShield("en-US"))
{
return ColorParser.Parse(colorString);
}
}
/// <summary>
/// Converts a Vector4 High Dynamic Range Color to Color 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.
/// </summary>
/// <param name="hdrColor">High Dynamic Range Color</param>
/// <returns>Color</returns>
public static Color CreateColor(Vector4 hdrColor)
{
using (new CultureShield("en-US"))
{
return ColorParser.Parse(hdrColor);
}
}
}
}

Просмотреть файл

@ -0,0 +1,112 @@
// 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;
using Microsoft.Graphics.Canvas.Brushes;
using Microsoft.Graphics.Canvas.Geometry;
using Windows.UI;
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry
{
/// <summary>
/// Class to represent the Stroke which can be used to render an outline on a <see cref="CanvasGeometry"/>
/// </summary>
public sealed class CanvasStroke : ICanvasStroke
{
/// <summary>
/// Gets or sets the brush with which the stroke will be rendered
/// </summary>
public ICanvasBrush Brush { get; set; }
/// <summary>
/// Gets or sets the width of the <see cref="CanvasStroke"/>
/// </summary>
public float Width { get; set; }
/// <summary>
/// Gets or sets the Style of the <see cref="CanvasStroke"/>
/// </summary>
public CanvasStrokeStyle Style { get; set; }
/// <summary>
/// Gets or sets the Transform matrix of the <see cref="CanvasStroke"/> brush.
/// </summary>
public Matrix3x2 Transform
{
get => GetTransform();
set => SetTransform(value);
}
/// <summary>
/// Initializes a new instance of the <see cref="CanvasStroke"/> class.
/// </summary>
/// <param name="brush">The brush with which the <see cref="CanvasStroke"/> will be rendered</param>
/// <param name="strokeWidth">Width of the <see cref="CanvasStroke"/></param>
public CanvasStroke(ICanvasBrush brush, float strokeWidth = 1f)
: this(brush, strokeWidth, new CanvasStrokeStyle())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="CanvasStroke"/> class.
/// </summary>
/// <param name="brush">The brush with which the <see cref="CanvasStroke"/> will be rendered</param>
/// <param name="strokeWidth">Width of the <see cref="CanvasStroke"/></param>
/// <param name="strokeStyle">Style of the <see cref="CanvasStroke"/></param>
public CanvasStroke(ICanvasBrush brush, float strokeWidth, CanvasStrokeStyle strokeStyle)
{
Brush = brush;
Width = strokeWidth;
Style = strokeStyle;
}
/// <summary>
/// Initializes a new instance of the <see cref="CanvasStroke"/> class.
/// </summary>
/// <param name="device">ICanvasResourceCreator</param>
/// <param name="strokeColor">Color of the <see cref="CanvasStroke"/></param>
/// <param name="strokeWidth">Width of the <see cref="CanvasStroke"/></param>
public CanvasStroke(ICanvasResourceCreator device, Color strokeColor, float strokeWidth = 1f)
: this(device, strokeColor, strokeWidth, new CanvasStrokeStyle())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="CanvasStroke"/> class.
/// </summary>
/// <param name="device">ICanvasResourceCreator</param>
/// <param name="strokeColor">Color of the <see cref="CanvasStroke"/></param>
/// <param name="strokeWidth">Width of the <see cref="CanvasStroke"/></param>
/// <param name="strokeStyle">Style of the <see cref="CanvasStroke"/></param>
public CanvasStroke(ICanvasResourceCreator device, Color strokeColor, float strokeWidth, CanvasStrokeStyle strokeStyle)
{
Brush = new CanvasSolidColorBrush(device, strokeColor);
Width = strokeWidth;
Style = strokeStyle;
}
/// <summary>
/// Sets the <see cref="CanvasStroke"/>'s Transform.
/// </summary>
/// <param name="value">Transform matrix to set</param>
private void SetTransform(Matrix3x2 value)
{
if (Brush != null)
{
Brush.Transform = value;
}
}
/// <summary>
/// Gets the <see cref="CanvasStroke"/>'s Transform. If stroke is null, then returns Matrix3x2.Identity.
/// </summary>
/// <returns>Transform matrix of the <see cref="CanvasStroke"/></returns>
private Matrix3x2 GetTransform()
{
return Brush?.Transform ?? Matrix3x2.Identity;
}
}
}

Просмотреть файл

@ -0,0 +1,96 @@
// 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.Composition;
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry
{
/// <summary>
/// Extension methods for compositor to support Win2d Path Mini Language.
/// </summary>
public static class CompositorGeometryExtensions
{
/// <summary>
/// Creates a <see cref="CompositionPath"/> based on the specified path data.
/// </summary>
/// <param name="compositor"><see cref="Compositor"/></param>
/// <param name="pathData">Path data (Win2d Path Mini Language) in string format.</param>
/// <returns><see cref="CompositionPath"/></returns>
public static CompositionPath CreatePath(this Compositor compositor, string pathData)
{
// Create CanvasGeometry
var geometry = CanvasPathGeometry.CreateGeometry(pathData);
// Create CompositionPath
return new CompositionPath(geometry);
}
/// <summary>
/// Creates a <see cref="CompositionPathGeometry"/> based on the given path data.
/// </summary>
/// <param name="compositor"><see cref="Compositor"/></param>
/// <param name="pathData">Path data (Win2d Path Mini Language) in string format.</param>
/// <returns><see cref="CompositionPathGeometry"/></returns>
public static CompositionPathGeometry CreatePathGeometry(this Compositor compositor, string pathData)
{
// Create CanvasGeometry
var geometry = CanvasPathGeometry.CreateGeometry(pathData);
// Create CompositionPathGeometry
return compositor.CreatePathGeometry(new CompositionPath(geometry));
}
/// <summary>
/// Creates a <see cref="CompositionSpriteShape"/> based on the specified path data.
/// </summary>
/// <param name="compositor"><see cref="Compositor"/></param>
/// <param name="pathData">Path data (Win2d Path Mini Language) in string format.</param>
/// <returns><see cref="CompositionSpriteShape"/></returns>
public static CompositionSpriteShape CreateSpriteShape(this Compositor compositor, string pathData)
{
// Create CanvasGeometry
var geometry = CanvasPathGeometry.CreateGeometry(pathData);
// Create CompositionPathGeometry
var pathGeometry = compositor.CreatePathGeometry(new CompositionPath(geometry));
// Create CompositionSpriteShape
return compositor.CreateSpriteShape(pathGeometry);
}
/// <summary>
/// Creates a <see cref="CompositionGeometricClip"/> from the specified <see cref="CanvasGeometry"/>.
/// </summary>
/// <param name="compositor"><see cref="Compositor"/></param>
/// <param name="geometry"><see cref="CanvasGeometry"/></param>
/// <returns>CompositionGeometricClip</returns>
public static CompositionGeometricClip CreateGeometricClip(this Compositor compositor, CanvasGeometry geometry)
{
// Create the CompositionPath
var path = new CompositionPath(geometry);
// Create the CompositionPathGeometry
var pathGeometry = compositor.CreatePathGeometry(path);
// Create the CompositionGeometricClip
return compositor.CreateGeometricClip(pathGeometry);
}
/// <summary>
/// Parses the specified path data and converts it to <see cref="CompositionGeometricClip"/>.
/// </summary>
/// <param name="compositor"><see cref="Compositor"/></param>
/// <param name="pathData">Path data (Win2d Path Mini Language) in string format.</param>
/// <returns><see cref="CompositionGeometricClip"/></returns>
public static CompositionGeometricClip CreateGeometricClip(this Compositor compositor, string pathData)
{
// Create the CanvasGeometry from the path data
var geometry = CanvasPathGeometry.CreateGeometry(pathData);
// Create the CompositionGeometricClip
return compositor.CreateGeometricClip(geometry);
}
}
}

Просмотреть файл

@ -0,0 +1,280 @@
// 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;
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core
{
/// <summary>
/// Structure which encapsulates the details of each of the core points of the path of the rounded rectangle which is calculated based on
/// either the given (Size, CornerRadius, BorderThickness and Padding) or (Size, RadiusX and RadiusY).
/// </summary>
internal struct CanvasRoundRect
{
private const float Factor = 0.5f;
private readonly float _leftTopWidth;
private readonly float _topLeftHeight;
private readonly float _topRightHeight;
private readonly float _rightTopWidth;
private readonly float _rightBottomWidth;
private readonly float _bottomRightHeight;
private readonly float _bottomLeftHeight;
private readonly float _leftBottomWidth;
// This is the location of the properties within the Rect
// |--LeftTop----------------------RightTop--|
// | |
// TopLeft TopRight
// | |
// | |
// | |
// | |
// | |
// | |
// BottomLeft BottomRight
// | |
// |--LeftBottom----------------RightBottom--|
internal float LeftTopX { get; private set; }
internal float LeftTopY { get; private set; }
internal float TopLeftX { get; private set; }
internal float TopLeftY { get; private set; }
internal float TopRightX { get; private set; }
internal float TopRightY { get; private set; }
internal float RightTopX { get; private set; }
internal float RightTopY { get; private set; }
internal float RightBottomX { get; private set; }
internal float RightBottomY { get; private set; }
internal float BottomRightX { get; private set; }
internal float BottomRightY { get; private set; }
internal float BottomLeftX { get; private set; }
internal float BottomLeftY { get; private set; }
internal float LeftBottomX { get; private set; }
internal float LeftBottomY { get; private set; }
internal float Width { get; }
internal float Height { get; }
/// <summary>
/// Initializes a new instance of the <see cref="CanvasRoundRect"/> struct.
/// </summary>
/// <param name="origin">Origin of the Rect (absolute location of Top Left corner)</param>
/// <param name="size">Size of the Rect</param>
/// <param name="cornerRadius">CornerRadius</param>
/// <param name="borderThickness">BorderThickness</param>
/// <param name="padding">Padding</param>
/// <param name="isOuterBorder">Flag to indicate whether outer or inner border needs
/// to be calculated</param>
internal CanvasRoundRect(Vector2 origin, Vector2 size, Vector4 cornerRadius, Vector4 borderThickness, Vector4 padding, bool isOuterBorder)
: this()
{
Width = Math.Max(0f, size.X);
Height = Math.Max(0f, size.Y);
var left = Factor * (borderThickness.X + padding.X);
var top = Factor * (borderThickness.Y + padding.Y);
var right = Factor * (borderThickness.Z + padding.Z);
var bottom = Factor * (borderThickness.W + padding.W);
if (isOuterBorder)
{
// Top Left corner radius
if (cornerRadius.X.IsZero())
{
_leftTopWidth = _topLeftHeight = 0f;
}
else
{
_leftTopWidth = cornerRadius.X + left;
_topLeftHeight = cornerRadius.X + top;
}
// Top Right corner radius
if (cornerRadius.Y.IsZero())
{
_topRightHeight = _rightTopWidth = 0f;
}
else
{
_topRightHeight = cornerRadius.Y + top;
_rightTopWidth = cornerRadius.Y + right;
}
// Bottom Right corner radius
if (cornerRadius.Z.IsZero())
{
_rightBottomWidth = _bottomRightHeight = 0f;
}
else
{
_rightBottomWidth = cornerRadius.Z + right;
_bottomRightHeight = cornerRadius.Z + bottom;
}
// Bottom Left corner radius
if (cornerRadius.W.IsZero())
{
_bottomLeftHeight = _leftBottomWidth = 0f;
}
else
{
_bottomLeftHeight = cornerRadius.W + bottom;
_leftBottomWidth = cornerRadius.W + left;
}
}
else
{
_leftTopWidth = Math.Max(0f, cornerRadius.X - left);
_topLeftHeight = Math.Max(0f, cornerRadius.X - top);
_topRightHeight = Math.Max(0f, cornerRadius.Y - top);
_rightTopWidth = Math.Max(0f, cornerRadius.Y - right);
_rightBottomWidth = Math.Max(0f, cornerRadius.Z - right);
_bottomRightHeight = Math.Max(0f, cornerRadius.Z - bottom);
_bottomLeftHeight = Math.Max(0f, cornerRadius.W - bottom);
_leftBottomWidth = Math.Max(0f, cornerRadius.W - left);
}
// Calculate the anchor points
ComputeCoordinates(origin.X, origin.Y);
}
/// <summary>
/// Initializes a new instance of the <see cref="CanvasRoundRect"/> struct.
/// </summary>
/// <param name="origin">Top Left corner of the Rounded Rectangle</param>
/// <param name="size">Dimensions of the Rounded Rectangle</param>
/// <param name="radiusX">Radius of the corners on the x-axis</param>
/// <param name="radiusY">Radius of the corners on the y-axis</param>
internal CanvasRoundRect(Vector2 origin, Vector2 size, float radiusX, float radiusY)
: this(origin.X, origin.Y, size.X, size.Y, radiusX, radiusY)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="CanvasRoundRect"/> struct.
/// </summary>
/// <param name="x">X offset of the Top Left corner of the Rounded Rectangle</param>
/// <param name="y">Y offset of the Top Left corner of the Rounded Rectangle</param>
/// <param name="width">Width of the Rounded Rectangle.</param>
/// <param name="height">Height of the Rounded Rectangle.</param>
/// <param name="radiusX">Radius of the corners on the x-axis</param>
/// <param name="radiusY">Radius of the corners on the y-axis</param>
internal CanvasRoundRect(float x, float y, float width, float height, float radiusX, float radiusY)
: this()
{
Width = Math.Max(0f, width);
Height = Math.Max(0f, height);
// Sanitize the radii by taking the absolute value
radiusX = Math.Min(Math.Abs(radiusX), width / 2f);
radiusY = Math.Min(Math.Abs(radiusY), height / 2);
_leftTopWidth = radiusX;
_rightTopWidth = radiusX;
_rightBottomWidth = radiusX;
_leftBottomWidth = radiusX;
_topLeftHeight = radiusY;
_topRightHeight = radiusY;
_bottomRightHeight = radiusY;
_bottomLeftHeight = radiusY;
ComputeCoordinates(x, y);
}
/// <summary>
/// Computes the coordinates of the crucial points on the CanvasRoundRect
/// </summary>
/// <param name="originX">X coordinate of the origin.</param>
/// <param name="originY">Y coordinate of the origin.</param>
private void ComputeCoordinates(float originX, float originY)
{
// compute the coordinates of the key points
var leftTopX = _leftTopWidth;
var leftTopY = 0f;
var rightTopX = Width - _rightTopWidth;
var rightTopY = 0f;
var topRightX = Width;
var topRightY = _topRightHeight;
var bottomRightX = Width;
var bottomRightY = Height - _bottomRightHeight;
var rightBottomX = Width - _rightBottomWidth;
var rightBottomY = Height;
var leftBottomX = _leftBottomWidth;
var leftBottomY = Height;
var bottomLeftX = 0f;
var bottomLeftY = Height - _bottomLeftHeight;
var topLeftX = 0f;
var topLeftY = _topLeftHeight;
// check anchors for overlap and resolve by partitioning corners according to
// the percentage of each one.
// top edge
if (leftTopX > rightTopX)
{
var v = _leftTopWidth / (_leftTopWidth + _rightTopWidth) * Width;
leftTopX = v;
rightTopX = v;
}
// right edge
if (topRightY > bottomRightY)
{
var v = _topRightHeight / (_topRightHeight + _bottomRightHeight) * Height;
topRightY = v;
bottomRightY = v;
}
// bottom edge
if (leftBottomX > rightBottomX)
{
var v = _leftBottomWidth / (_leftBottomWidth + _rightBottomWidth) * Width;
rightBottomX = v;
leftBottomX = v;
}
// left edge
if (topLeftY > bottomLeftY)
{
var v = _topLeftHeight / (_topLeftHeight + _bottomLeftHeight) * Height;
bottomLeftY = v;
topLeftY = v;
}
// Apply origin translation
LeftTopX = leftTopX + originX;
LeftTopY = leftTopY + originY;
RightTopX = rightTopX + originX;
RightTopY = rightTopY + originY;
TopRightX = topRightX + originX;
TopRightY = topRightY + originY;
BottomRightX = bottomRightX + originX;
BottomRightY = bottomRightY + originY;
RightBottomX = rightBottomX + originX;
RightBottomY = rightBottomY + originY;
LeftBottomX = leftBottomX + originX;
LeftBottomY = leftBottomY + originY;
BottomLeftX = bottomLeftX + originX;
BottomLeftY = bottomLeftY + originY;
TopLeftX = topLeftX + originX;
TopLeftY = topLeftY + originY;
}
}
}

Просмотреть файл

@ -0,0 +1,59 @@
// 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.
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core
{
/// <summary>
/// Enum for the various PathFigures.
/// </summary>
internal enum PathFigureType
{
FillRule,
PathFigure,
EllipseFigure,
PolygonFigure,
RectangleFigure,
RoundedRectangleFigure
}
/// <summary>
/// Enum for the various PathElements.
/// </summary>
internal enum PathElementType
{
MoveTo,
Line,
HorizontalLine,
VerticalLine,
QuadraticBezier,
SmoothQuadraticBezier,
CubicBezier,
SmoothCubicBezier,
Arc,
ClosePath
}
/// <summary>
/// Enum for the various types of Brushes.
/// </summary>
internal enum BrushType
{
SolidColor,
LinearGradient,
RadialGradient,
LinearGradientHdr,
RadialGradientHdr
}
/// <summary>
/// Enum for the various types of GradientStop attributes.
/// </summary>
internal enum GradientStopAttributeType
{
Main,
Additional,
MainHdr,
AdditionalHdr
}
}

Просмотреть файл

@ -0,0 +1,157 @@
// 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.Text.RegularExpressions;
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path;
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core
{
/// <summary>
/// Factory class to instantiate various PathElements.
/// </summary>
internal static class PathElementFactory
{
/// <summary>
/// Creates a default Path Element for the given PathFigureType.
/// </summary>
/// <param name="figureType">PathFigureType</param>
/// <returns>ICanvasPathElement</returns>
internal static ICanvasPathElement CreateDefaultPathElement(PathFigureType figureType)
{
if (figureType == PathFigureType.FillRule)
{
return new FillRuleElement();
}
static ICanvasPathElement Throw() => throw new ArgumentException("Creation of Only Default FillRuleElement is supported.");
return Throw();
}
/// <summary>
/// Creates a default Path Element for the given PathElementType.
/// </summary>
/// <param name="elementType">PathElementType</param>
/// <returns>ICanvasPathElement</returns>
internal static ICanvasPathElement CreateDefaultPathElement(PathElementType elementType)
{
if (elementType == PathElementType.ClosePath)
{
return new ClosePathElement();
}
static ICanvasPathElement Throw() => throw new ArgumentException("Creation of Only Default ClosePathElement is supported.");
return Throw();
}
/// <summary>
/// Instantiates a PathElement based on the PathFigureType.
/// </summary>
/// <param name="figureType">PathFigureType</param>
/// <param name="match">Match object</param>
/// <param name="index">Index of the path element in the Path data</param>
/// <returns>ICanvasPathElement</returns>
internal static ICanvasPathElement CreatePathFigure(PathFigureType figureType, Match match, int index)
{
var element = CreatePathElement(figureType);
element?.Initialize(match, index);
return element;
}
/// <summary>
/// Instantiates a PathElement based on the PathFigureType.
/// </summary>
/// <param name="figureType">PathFigureType</param>
/// <param name="capture">Capture object</param>
/// <param name="index">Index of the capture</param>
/// <param name="isRelative">Indicates whether the coordinates are absolute or relative</param>
/// <returns>ICanvasPathElement</returns>
internal static ICanvasPathElement CreateAdditionalPathFigure(PathFigureType figureType, Capture capture, int index, bool isRelative)
{
var element = CreatePathElement(figureType);
element?.InitializeAdditional(capture, index, isRelative);
return element;
}
/// <summary>
/// Instantiates a PathElement based on the PathElementType.
/// </summary>
/// <param name="elementType">PathElementType</param>
/// <param name="match">Match object</param>
/// <param name="index">Index of the path element in the Path data</param>
/// <returns>ICanvasPathElement</returns>
internal static ICanvasPathElement CreatePathElement(PathElementType elementType, Match match, int index)
{
var element = CreatePathElement(elementType);
element?.Initialize(match, index);
return element;
}
/// <summary>
/// Instantiates a PathElement based on the PathElementType.
/// </summary>
/// <param name="elementType">PathElementType</param>
/// <param name="capture">Capture object</param>
/// <param name="index">Index of the capture</param>
/// <param name="isRelative">Indicates whether the coordinates are absolute or relative</param>
/// <returns>ICanvasPathElement</returns>
internal static ICanvasPathElement CreateAdditionalPathElement(PathElementType elementType, Capture capture, int index, bool isRelative)
{
// Additional attributes in MoveTo Command must be converted
// to Line commands
if (elementType == PathElementType.MoveTo)
{
elementType = PathElementType.Line;
}
var element = CreatePathElement(elementType);
element?.InitializeAdditional(capture, index, isRelative);
return element;
}
/// <summary>
/// Instantiates a PathElement based on the PathFigureType.
/// </summary>
/// <param name="figureType">PathFigureType</param>
/// <returns>ICanvasPathElement</returns>
private static ICanvasPathElement CreatePathElement(PathFigureType figureType)
{
return figureType switch
{
PathFigureType.FillRule => new FillRuleElement(),
PathFigureType.PathFigure => new CanvasPathFigure(),
PathFigureType.EllipseFigure => new CanvasEllipseFigure(),
PathFigureType.PolygonFigure => new CanvasPolygonFigure(),
PathFigureType.RectangleFigure => new CanvasRectangleFigure(),
PathFigureType.RoundedRectangleFigure => new CanvasRoundRectangleFigure(),
_ => throw new ArgumentOutOfRangeException(nameof(figureType), figureType, "Invalid PathFigureType!")
};
}
/// <summary>
/// Instantiates a PathElement based on the PathElementType.
/// </summary>
/// <param name="elementType">PathElementType</param>
/// <returns>ICanvasPathElement</returns>
private static ICanvasPathElement CreatePathElement(PathElementType elementType)
{
return elementType switch
{
PathElementType.MoveTo => new MoveToElement(),
PathElementType.Line => new LineElement(),
PathElementType.HorizontalLine => new HorizontalLineElement(),
PathElementType.VerticalLine => new VerticalLineElement(),
PathElementType.QuadraticBezier => new QuadraticBezierElement(),
PathElementType.SmoothQuadraticBezier => new SmoothQuadraticBezierElement(),
PathElementType.CubicBezier => new CubicBezierElement(),
PathElementType.SmoothCubicBezier => new SmoothCubicBezierElement(),
PathElementType.Arc => new ArcElement(),
PathElementType.ClosePath => new ClosePathElement(),
_ => throw new ArgumentOutOfRangeException(nameof(elementType), elementType, "Invalid PathElementType!")
};
}
}
}

Просмотреть файл

@ -0,0 +1,602 @@
// 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.Runtime.CompilerServices;
using System.Text.RegularExpressions;
[assembly: InternalsVisibleTo("UnitTests.UWP")]
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core
{
/// <summary>
/// Contains all the Regular Expressions which are used for parsing the Win2d Path Mini Language.
/// </summary>
internal static class RegexFactory
{
// Whitespace
private const string Spacer = @"\s*";
// Whitespace or comma
private const string SpaceOrComma = @"(?:\s+|\s*,\s*)";
// Whitespace or comma or a minus/plus sign (look ahead)
private const string Sep = @"(?:\s+|\s*,\s*|(?=[-+.]))";
// Whitespace or comma or a '#' sign (look ahead)
private const string ColorSep = @"(?:\s+|\s*,\s*|(?=[#]))";
// Positive Integer
private const string Integer = @"[+-]?[0-9]+";
// Positive Integer
private const string PositiveInteger = @"[+]?[0-9]+";
// Floating point number
private const string Float = @"(?:[-+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?)";
// Positive Floating point number
private const string PositiveFloat = @"(?:[+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?)";
// Floating point number between 0 and 1, inclusive
// private const string Float01 = @"(?:(?<!\d*[1-9]\.?0*)(?:(?:0+(?:\.\d+)?)|(?:\.\d+)|(?:1(?!\.0*[1-9]+)(?:\.0+)?)))";
private const string Float01 = @"(?:" +
@"(?:(?<!\d*[1-9]\.?0*)(?:(?:0+(?:\.\d+)?)|(?:1(?!\.0*[1-9]+)(?:\.0+)?)))|" +
@"(?:(?<!\d*[1-9])(?:\.\d+))|" +
@"(?:(?<=(?:\.\d+))(?:\.\d+))" +
@")";
// Hexadecimal characters
private const string Hex = "(?:[a-f]|[A-F]|[0-9])";
// Position
private static readonly string Pos = $"{Float}{Sep}{Float}";
// MoveTo
private static readonly string MoveTo = $"(?<MoveTo>[Mm]{Spacer}{Pos}(?:{Sep}{Pos})*{Spacer})";
// Line
private static readonly string Line = $"(?<Line>[Ll]{Spacer}{Pos}(?:{Sep}{Pos})*{Spacer})";
// Horizontal Line
private static readonly string HorizontalLine = $"(?<HorizontalLine>[Hh]{Spacer}{Float}(?:{Sep}{Float})*{Spacer})";
// Vertical Line
private static readonly string VerticalLine = $"(?<VerticalLine>[Vv]{Spacer}{Float}(?:{Sep}{Float})*{Spacer})";
// Quadratic Bezier
private static readonly string QuadraticBezier = $"(?<QuadraticBezier>[Qq]{Spacer}{Pos}{Sep}{Pos}(?:{Sep}{Pos}{Sep}{Pos})*{Spacer})";
// Smooth Quadratic Bezier
private static readonly string SmoothQuadraticBezier = $"(?<SmoothQuadraticBezier>[Tt]{Spacer}{Pos}(?:{Sep}{Pos})*{Spacer})";
// Cubic Bezier
private static readonly string CubicBezier = $"(?<CubicBezier>[Cc]{Spacer}{Pos}{Sep}{Pos}{Sep}{Pos}(?:{Sep}{Pos}{Sep}{Pos}{Sep}{Pos})*{Spacer})";
// Smooth Cubic Bezier
private static readonly string SmoothCubicBezier = $"(?<SmoothCubicBezier>[Ss]{Spacer}{Pos}{Sep}{Pos}(?:{Sep}{Pos}{Sep}{Pos})*{Spacer})";
// Arc
private static readonly string Arc = $"(?<Arc>[Aa]{Spacer}{Float}{Sep}{Float}{Sep}{Float}{SpaceOrComma}[01]{SpaceOrComma}[01]{Sep}{Pos}" +
$"(?:{Sep}{Float}{Sep}{Float}{Sep}{Float}{SpaceOrComma}[01]{SpaceOrComma}[01]{Sep}{Pos})*{Spacer})";
// Close Path
private static readonly string ClosePath = $"(?<ClosePath>[Zz]{Spacer})";
// CanvasPathFigure
private static readonly string CanvasPathFigureRegexString =
$"{MoveTo}" + // M x,y
"(" +
$"{Line}+|" + // L x,y
$"{HorizontalLine}+|" + // H x
$"{VerticalLine}+|" + // V y
$"{QuadraticBezier}+|" + // Q x1,y1 x,y
$"{SmoothQuadraticBezier}+|" + // T x,y
$"{CubicBezier}+|" + // C x1,y1 x2,y2 x,y
$"{SmoothCubicBezier}+|" + // S x2,y2 x,y
$"{Arc}+|" + // A radX, radY, angle, isLargeArc, sweepDirection, x, y
")+" +
$"{ClosePath}?"; // Close Path (Optional)
// Fill Rule
private static readonly string FillRule = $"{Spacer}(?<FillRule>[Ff]{Spacer}[01])";
// PathFigure
private static readonly string PathFigure = $"{Spacer}(?<PathFigure>{CanvasPathFigureRegexString})";
// Ellipse Figure
private static readonly string EllipseFigure = $"{Spacer}(?<EllipseFigure>[Oo]{Spacer}{Float}{Sep}{Float}{Sep}{Pos}" +
$"(?:{Sep}{Float}{Sep}{Float}{Sep}{Pos})*)";
// Polygon Figure
private static readonly string PolygonFigure = $"{Spacer}(?<PolygonFigure>[Pp]{Spacer}{Integer}{Sep}{Float}{Sep}{Pos}" +
$"(?:{Sep}{Integer}{Sep}{Float}{Sep}{Pos})*)";
// Rectangle Figure
private static readonly string RectangleFigure = $"{Spacer}(?<RectangleFigure>[Rr]{Spacer}{Pos}{Sep}{Float}{Sep}{Float}" +
$"(?:{Sep}{Pos}{Sep}{Float}{Sep}{Float})*)";
// Rounded Rectangle Figure
private static readonly string RoundedRectangleFigure = $"{Spacer}(?<RoundedRectangleFigure>[Uu]{Spacer}{Pos}{Sep}{Float}{Sep}{Float}{Sep}{Float}{Sep}{Float}" +
$"(?:{Sep}{Pos}{Sep}{Float}{Sep}{Float}{Sep}{Float}{Sep}{Float})*)";
// CanvasGeometry
private static readonly string CanvasGeometryRegexString =
$"{FillRule}?" + // F0 or F1
"(" +
$"{PathFigure}+|" + // Path Figure
$"{EllipseFigure}+|" + // O radX, radY, centerX, centerY
$"{PolygonFigure}+|" + // P numSides, radius, centerX, centerY
$"{RectangleFigure}+|" + // R x, y, width, height
$"{RoundedRectangleFigure}+" + // U x, y, width, height, radiusX, radiusY
")+";
// MoveTo
private static readonly string MoveToAttributes = $"(?<X>{Float}){Sep}(?<Y>{Float})";
private static readonly string MoveToRegexString = $"{Spacer}(?<Main>(?<Command>[Mm]){Spacer}{MoveToAttributes})" +
$"(?<Additional>{Sep}{Pos})*";
// Line
private static readonly string LineAttributes = $"(?<X>{Float}){Sep}(?<Y>{Float})";
private static readonly string LineRegexString = $"{Spacer}(?<Main>(?<Command>[Ll]){Spacer}{LineAttributes})" +
$"(?<Additional>{Sep}{Pos})*";
// Horizontal Line
private static readonly string HorizontalLineAttributes = $"(?<X>{Float})";
private static readonly string HorizontalLineRegexString = $"{Spacer}(?<Main>(?<Command>[Hh]){Spacer}{HorizontalLineAttributes})" +
$"(?<Additional>{Sep}{Float})*";
// Vertical Line
private static readonly string VerticalLineAttributes = $"(?<Y>{Float})";
private static readonly string VerticalLineRegexString = $"{Spacer}(?<Main>(?<Command>[Vv]){Spacer}{VerticalLineAttributes})" +
$"(?<Additional>{Sep}{Float})*";
// Quadratic Bezier
private static readonly string QuadraticBezierAttributes = $"(?<X1>{Float}){Sep}(?<Y1>{Float}){Sep}(?<X>{Float}){Sep}(?<Y>{Float})";
private static readonly string QuadraticBezierRegexString = $"{Spacer}(?<Main>(?<Command>[Qq]){Spacer}{QuadraticBezierAttributes})" +
$"(?<Additional>{Sep}{Pos}{Sep}{Pos})*";
// Smooth Quadratic Bezier
private static readonly string SmoothQuadraticBezierAttributes = $"(?<X>{Float}){Sep}(?<Y>{Float})";
private static readonly string SmoothQuadraticBezierRegexString = $"{Spacer}(?<Main>(?<Command>[Tt]){Spacer}{SmoothQuadraticBezierAttributes})" +
$"(?<Additional>{Sep}{Pos})*";
// Cubic Bezier
private static readonly string CubicBezierAttributes = $"(?<X1>{Float}){Sep}(?<Y1>{Float}){Sep}(?<X2>{Float}){Sep}(?<Y2>{Float}){Sep}" +
$"(?<X>{Float}){Sep}(?<Y>{Float})";
private static readonly string CubicBezierRegexString = $"{Spacer}(?<Main>(?<Command>[Cc]){Spacer}{CubicBezierAttributes})" +
$"(?<Additional>{Sep}{Pos}{Sep}{Pos}{Sep}{Pos})*";
// Smooth Cubic Bezier
private static readonly string SmoothCubicBezierAttributes = $"(?<X2>{Float}){Sep}(?<Y2>{Float}){Sep}(?<X>{Float}){Sep}(?<Y>{Float})";
private static readonly string SmoothCubicBezierRegexString = $"{Spacer}(?<Main>(?<Command>[Ss]){Spacer}{SmoothCubicBezierAttributes})" +
$"(?<Additional>{Sep}{Pos}{Sep}{Pos})*";
// Arc
private static readonly string ArcAttributes = $"(?<RadiusX>{Float}){Sep}(?<RadiusY>{Float}){Sep}(?<Angle>{Float}){SpaceOrComma}" +
$"(?<IsLargeArc>[01]){SpaceOrComma}(?<SweepDirection>[01]){Sep}(?<X>{Float}){Sep}(?<Y>{Float})";
private static readonly string ArcRegexString = $"{Spacer}(?<Main>(?<Command>[Aa]){Spacer}{ArcAttributes})" +
$"(?<Additional>{Sep}{Float}{Sep}{Float}{Sep}{Float}{SpaceOrComma}[01]{SpaceOrComma}[01]{Sep}{Pos})*";
// Close Path
private static readonly string ClosePathRegexString = $"{Spacer}(?<Main>(?<Command>[Zz])){Spacer}";
// Fill Rule
private static readonly string FillRuleRegexString = $"{Spacer}(?<Main>(?<Command>[Ff]){Spacer}(?<FillValue>[01]))";
// Path Figure
private static readonly string PathFigureRegexString = $"{Spacer}(?<Main>{PathFigure})";
// Ellipse Figure
private static readonly string EllipseFigureAttributes = $"(?<RadiusX>{Float}){Sep}(?<RadiusY>{Float}){Sep}" +
$"(?<X>{Float}){Sep}(?<Y>{Float})";
private static readonly string EllipseFigureRegexString = $"{Spacer}(?<Main>(?<Command>[Oo]){Spacer}{EllipseFigureAttributes})" +
$"(?<Additional>{Sep}{Float}{Sep}{Float}{Sep}{Pos})*";
// Polygon Figure
private static readonly string PolygonFigureAttributes = $"(?<Sides>{Integer}){Sep}(?<Radius>{Float}){Sep}(?<X>{Float}){Sep}(?<Y>{Float})";
private static readonly string PolygonFigureRegexString = $"{Spacer}(?<Main>(?<Command>[Pp]){Spacer}{PolygonFigureAttributes})" +
$"(?<Additional>{Sep}{Integer}{Sep}{Float}{Sep}{Pos})*";
// Rectangle Figure
private static readonly string RectangleFigureAttributes = $"(?<X>{Float}){Sep}(?<Y>{Float}){Sep}(?<Width>{Float}){Sep}(?<Height>{Float})";
private static readonly string RectangleFigureRegexString = $"{Spacer}(?<Main>(?<Command>[Rr]){Spacer}{RectangleFigureAttributes})" +
$"(?<Additional>{Sep}{Pos}{Sep}{Float}{Sep}{Float})*";
// Rectangle Figure
private static readonly string RoundedRectangleFigureAttributes = $"(?<X>{Float}){Sep}(?<Y>{Float}){Sep}(?<Width>{Float}){Sep}(?<Height>{Float})" +
$"{Sep}(?<RadiusX>{Float}){Sep}(?<RadiusY>{Float})";
private static readonly string RoundedRectangleFigureRegexString = $"{Spacer}(?<Main>(?<Command>[Uu]){Spacer}{RoundedRectangleFigureAttributes})" +
$"(?<Additional>{Sep}{Pos}{Sep}{Float}{Sep}{Float}{Sep}{Float}{Sep}{Float})*";
// ARGB Color
private static readonly string HexColor = $"(?:#?(?:{Hex}{{2}})?{Hex}{{6}})";
// Alpha
private static readonly string Alpha = $"(?<Alpha>{Hex}{{2}})";
// Red
private static readonly string Red = $"(?<Red>{Hex}{{2}})";
// Green
private static readonly string Green = $"(?<Green>{Hex}{{2}})";
// Blue
private static readonly string Blue = $"(?<Blue>{Hex}{{2}})";
// Hexadecimal Color
private static readonly string RgbColor = $"(?<RgbColor>{HexColor})";
// HDR Color (Vector4 in which each component has a value between 0 and 1, inclusive)
private static readonly string HdrColor = $"(?<HdrColor>{Float01}{Sep}{Float01}{Sep}{Float01}{Sep}{Float01})";
// Hexadecimal Color Attributes
private static readonly string RgbColorAttributes = $"(?<RgbColor>#{{0,1}}{Alpha}{{0,1}}{Red}{Green}{Blue})";
// HDR Color Attributes (Vector4 in which each component has a value between 0 and 1, inclusive)
private static readonly string HdrColorAttributes = $"(?<HdrColor>(?<X>{Float01}){Sep}(?<Y>{Float01}){Sep}(?<Z>{Float01}){Sep}(?<W>{Float01}))";
private static readonly string ColorRegexString = $"(?:{RgbColorAttributes}|{HdrColorAttributes})";
// Start Point
private static readonly string StartPoint = $"(?<StartPoint>[Mm]{Spacer}{Pos}{Spacer})";
// End Point
private static readonly string EndPoint = $"(?<EndPoint>[Zz]{Spacer}{Pos}{Spacer})";
// Opacity
private static readonly string Opacity = $"(?<Opacity>[Oo]{Spacer}{Float01}{Spacer})";
// Alpha Mode
private static readonly string AlphaMode = $"(?<AlphaMode>[Aa]{Spacer}[012]{Spacer})";
// Buffer Precision
private static readonly string BufferPrecision = $"(?<BufferPrecision>[Bb]{Spacer}[01234]{Spacer})";
// Edge Behavior
private static readonly string EdgeBehavior = $"(?<EdgeBehavior>[Ee]{Spacer}[012]{Spacer})";
// PreInterpolation Color Space
private static readonly string PreColorSpace = $"(?<PreColorSpace>[Pp]{Spacer}[012]{Spacer})";
// PostInterpolation Color Space
private static readonly string PostColorSpace = $"(?<PostColorSpace>[Rr]{Spacer}[012]{Spacer})";
// Radius in X-axis
private static readonly string RadiusX = $"(?<RadiusX>{Float})";
// Radius in Y-axis
private static readonly string RadiusY = $"(?<RadiusY>{Float})";
// Center location on X-axis
private static readonly string CenterX = $"(?<CenterX>{Float})";
// Center location on Y-axis
private static readonly string CenterY = $"(?<CenterY>{Float})";
// Origin Offset
private static readonly string OriginOffset = $"(?<OriginOffset>[Ff]{Spacer}{Pos}{Spacer})";
// GradientStops
private static readonly string GradientStops = $"(?<GradientStops>[Ss]{Spacer}{Float01}{ColorSep}{HexColor}(?:{Sep}{Float01}{ColorSep}{HexColor})*{Spacer})";
// GradientStopHdrs
private static readonly string GradientStopHdrs = $"(?<GradientStops>[Ss]{Spacer}{Float01}{Sep}{HdrColor}(?:{Sep}{Float01}{Sep}{HdrColor})*{Spacer})";
// Solid Color Brush
private static readonly string SolidColorBrush = $"(?<SolidColorBrush>[Ss][Cc]{Spacer}(?:{RgbColor}|{HdrColor}){Spacer}{Opacity}?)";
// LinearGradient
private static readonly string LinearGradient = $"(?<LinearGradient>[Ll][Gg]{Spacer}{StartPoint}{EndPoint}" +
$"{Opacity}?{AlphaMode}?{BufferPrecision}?{EdgeBehavior}?{PreColorSpace}?{PostColorSpace}?" +
$"{GradientStops}+{Spacer})";
// RadialGradient
private static readonly string RadialGradient = $"(?<RadialGradient>[Rr][Gg]{Spacer}{RadiusX}{Sep}{RadiusY}{Sep}{CenterX}{Sep}{CenterY}{Spacer}" +
$"{Opacity}?{AlphaMode}?{BufferPrecision}?{EdgeBehavior}?{OriginOffset}?{PreColorSpace}?{PostColorSpace}?" +
$"{GradientStops}+{Spacer})";
// LinearGradientHdr
private static readonly string LinearGradientHdr = $"(?<LinearGradientHdr>[Ll][Hh]{Spacer}{StartPoint}{EndPoint}" +
$"{Opacity}?{AlphaMode}?{BufferPrecision}?{EdgeBehavior}?{PreColorSpace}?{PostColorSpace}?" +
$"{GradientStopHdrs}+{Spacer})";
// RadialGradientHdr
private static readonly string RadialGradientHdr = $"(?<RadialGradientHdr>[Rr][Hh]{Spacer}{RadiusX}{Sep}{RadiusY}{Sep}{CenterX}{Sep}{CenterY}{Spacer}" +
$"{Opacity}?{AlphaMode}?{BufferPrecision}?{EdgeBehavior}?{OriginOffset}?{PreColorSpace}?{PostColorSpace}?" +
$"{GradientStopHdrs}+{Spacer})";
// Regex for the CanvasBrush
private static readonly string CanvasBrushRegexString = $"(?<CanvasBrush>{SolidColorBrush}|{LinearGradient}|{RadialGradient}|{LinearGradientHdr}|{RadialGradientHdr})";
// Start Point
private static readonly string StartPointAttr = $"(?:[Mm]{Spacer}(?<StartX>{Float}){Sep}(?<StartY>{Float}){Spacer})";
// End Point
private static readonly string EndPointAttr = $"(?:[Zz]{Spacer}(?<EndX>{Float}){Sep}(?<EndY>{Float}){Spacer})";
// Opacity
private static readonly string OpacityAttr = $"(?:[Oo]{Spacer}(?<Opacity>{Float01}){Spacer})";
// Alpha Mode
private static readonly string AlphaModeAttr = $"(?:[Aa]{Spacer}(?<AlphaMode>[012]){Spacer})";
// Buffer Precision
private static readonly string BufferPrecisionAttr = $"(?:[Bb]{Spacer}(?<BufferPrecision>[01234]){Spacer})";
// Edge Behavior
private static readonly string EdgeBehaviorAttr = $"(?:[Ee]{Spacer}(?<EdgeBehavior>[012]){Spacer})";
// PreInterpolation Color Space
private static readonly string PreColorSpaceAttr = $"(?:[Pp]{Spacer}(?<PreColorSpace>[012]){Spacer})";
// PostInterpolation Color Space
private static readonly string PostColorSpaceAttr = $"(?:[Rr]{Spacer}(?<PostColorSpace>[012]){Spacer})";
// Origin Offset
private static readonly string OriginOffsetAttr = $"(?<OriginOffset>[Ff]{Spacer}(?<OffsetX>{Float}){Sep}(?<OffsetY>{Float}){Spacer})";
// GradientStop Attributes
private static readonly string GradientStopAttributes = $"(?<Position>{Float01}){ColorSep}{RgbColorAttributes}";
private static readonly string GradientStopMainAttributes = $"(?<Main>[Ss]{Spacer}{GradientStopAttributes})";
private static readonly string GradientStopRegexString = $"(?<GradientStops>{GradientStopMainAttributes}" + $"(?<Additional>{Sep}{Float01}{ColorSep}{HexColor})*{Spacer})";
// GradientStopHdr Attributes
private static readonly string GradientStopHdrAttributes = $"(?<Position>{Float01}){Sep}{HdrColorAttributes}";
private static readonly string GradientStopHdrMainAttributes = $"(?<Main>(?<Command>[Ss]){Spacer}{GradientStopHdrAttributes})";
private static readonly string GradientStopHdrRegexString = $"(?<GradientStops>{GradientStopHdrMainAttributes}" + $"(?<Additional>{Sep}{Float01}{Sep}{HdrColor})*{Spacer})";
// Regex for SolidColorBrush Attributes
private static readonly string SolidColorBrushRegexString = $"(?:[Ss][Cc]{Spacer}(?:{RgbColorAttributes}|{HdrColorAttributes}){Spacer}{OpacityAttr}?)";
// Regex for LinearGradient Attributes
private static readonly string LinearGradientRegexString = $"[Ll][Gg]{Spacer}{StartPointAttr}{EndPointAttr}" +
$"{OpacityAttr}?{AlphaModeAttr}?{BufferPrecisionAttr}?" +
$"{EdgeBehaviorAttr}?{PreColorSpaceAttr}?{PostColorSpaceAttr}?" +
$"{GradientStops}+{Spacer}";
// Regex for RadialGradient Attributes
private static readonly string RadialGradientRegexString = $"[Rr][Gg]{Spacer}{RadiusX}{Sep}{RadiusY}{Sep}{CenterX}{Sep}{CenterY}{Spacer}" +
$"{OpacityAttr}?{AlphaModeAttr}?{BufferPrecisionAttr}?{EdgeBehaviorAttr}?" +
$"{OriginOffsetAttr}?{PreColorSpaceAttr}?{PostColorSpaceAttr}?" +
$"{GradientStops}+{Spacer}";
// Regex for LinearGradientHdr Attributes
private static readonly string LinearGradientHdrRegexString = $"[Ll][Hh]{Spacer}{StartPointAttr}{EndPointAttr}" +
$"{OpacityAttr}?{AlphaModeAttr}?{BufferPrecisionAttr}?" +
$"{EdgeBehaviorAttr}?{PreColorSpaceAttr}?{PostColorSpaceAttr}?" +
$"{GradientStopHdrs}+{Spacer}";
// Regex for RadialGradientHdr Attributes
private static readonly string RadialGradientHdrRegexString = $"[Rr][Hh]{Spacer}{RadiusX}{Sep}{RadiusY}{Sep}{CenterX}{Sep}{CenterY}{Spacer}" +
$"{OpacityAttr}?{AlphaModeAttr}?{BufferPrecisionAttr}?{EdgeBehaviorAttr}?" +
$"{OriginOffsetAttr}?{PreColorSpaceAttr}?{PostColorSpaceAttr}?" +
$"{GradientStopHdrs}+{Spacer}";
// CanvasStrokeStyle attributes
private static readonly string DashStyle = $"(?:[Dd][Ss]{Spacer}(?<DashStyle>[01234]){Spacer})";
private static readonly string LineJoin = $"(?:[Ll][Jj]{Spacer}(?<LineJoin>[0123]){Spacer})";
private static readonly string MiterLimit = $"(?:[Mm][Ll]{Spacer}(?<MiterLimit>{Float}){Spacer})";
private static readonly string DashOffset = $"(?:[Dd][Oo]{Spacer}(?<DashOffset>{Float}){Spacer})";
private static readonly string StartCap = $"(?:[Ss][Cc]{Spacer}(?<StartCap>[0123]){Spacer})";
private static readonly string EndCap = $"(?:[Ee][Cc]{Spacer}(?<EndCap>[0123]){Spacer})";
private static readonly string DashCap = $"(?:[Dd][Cc]{Spacer}(?<DashCap>[0123]){Spacer})";
private static readonly string TransformBehavior = $"(?:[Tt][Bb]{Spacer}(?<TransformBehavior>[012]){Spacer})";
private static readonly string CustomDashAttribute = $"(?<DashSize>{Float}){Sep}(?<SpaceSize>{Float})";
private static readonly string CustomDashStyle = $"(?<CustomDashStyle>[Cc][Dd][Ss]{Spacer}(?<Main>{CustomDashAttribute})" + $"(?<Additional>{Sep}{Float}{Sep}{Float})*{Spacer})";
// CanvasStrokeStyle Regex
private static readonly string CanvasStrokeStyleRegexString = $"(?<CanvasStrokeStyle>[Cc][Ss][Ss]{Spacer}{DashStyle}?{LineJoin}?{MiterLimit}?{DashOffset}?" +
$"{StartCap}?{EndCap}?{DashCap}?{TransformBehavior}?{CustomDashStyle}?)";
// CanvasStroke Regex
private static readonly string CanvasStrokeRegexString = $"(?<CanvasStroke>[Ss][Tt]{Spacer}" +
$"(?<StrokeWidth>{Float}){Spacer}" +
$"{CanvasBrushRegexString}{Spacer}" +
$"{CanvasStrokeStyleRegexString}?)";
private static readonly Dictionary<PathFigureType, Regex> PathFigureRegexes;
private static readonly Dictionary<PathFigureType, Regex> PathFigureAttributeRegexes;
private static readonly Dictionary<PathElementType, Regex> PathElementRegexes;
private static readonly Dictionary<PathElementType, Regex> PathElementAttributeRegexes;
private static readonly Dictionary<BrushType, Regex> BrushRegexes;
private static readonly Dictionary<GradientStopAttributeType, Regex> GradientStopAttributeRegexes;
/// <summary>
/// Gets the Regex to perform validation of Path data.
/// </summary>
public static Regex ValidationRegex { get; }
/// <summary>
/// Gets the Regex for parsing the CanvasGeometry string.
/// </summary>
public static Regex CanvasGeometryRegex { get; }
/// <summary>
/// Gets the Regex for parsing Hexadecimal Color string.
/// </summary>
public static Regex ColorRegex { get; }
/// <summary>
/// Gets the Regex for parsing the ICanvasBrush string.
/// </summary>
public static Regex CanvasBrushRegex { get; }
/// <summary>
/// Gets the Regex for parsing the GradientStop string.
/// </summary>
public static Regex GradientStopRegex { get; }
/// <summary>
/// Gets the Regex for parsing the GradientStopHdr string.
/// </summary>
public static Regex GradientStopHdrRegex { get; }
/// <summary>
/// Gets the Regex for parsing the CanvasStrokeStyle string.
/// </summary>
public static Regex CanvasStrokeStyleRegex { get; }
/// <summary>
/// Gets the Regex for parsing the CanvasStroke string.
/// </summary>
public static Regex CanvasStrokeRegex { get; }
/// <summary>
/// Gets the Regex for parsing the CustomDashStyle attributes.
/// </summary>
public static Regex CustomDashAttributeRegex { get; }
/// <summary>
/// Initializes static members of the <see cref="RegexFactory"/> class.
/// </summary>
static RegexFactory()
{
PathFigureRegexes = new Dictionary<PathFigureType, Regex>
{
[PathFigureType.FillRule] = new Regex(FillRuleRegexString, RegexOptions.Compiled),
[PathFigureType.PathFigure] = new Regex(PathFigureRegexString, RegexOptions.Compiled),
[PathFigureType.EllipseFigure] = new Regex(EllipseFigureRegexString, RegexOptions.Compiled),
[PathFigureType.PolygonFigure] = new Regex(PolygonFigureRegexString, RegexOptions.Compiled),
[PathFigureType.RectangleFigure] = new Regex(RectangleFigureRegexString, RegexOptions.Compiled),
[PathFigureType.RoundedRectangleFigure] = new Regex(RoundedRectangleFigureRegexString, RegexOptions.Compiled)
};
PathFigureAttributeRegexes = new Dictionary<PathFigureType, Regex>
{
// Not Applicable for FillRuleElement
[PathFigureType.FillRule] = null,
// Not Applicable for CanvasPathFigure
[PathFigureType.PathFigure] = null,
[PathFigureType.EllipseFigure] = new Regex($"{Sep}{EllipseFigureAttributes}", RegexOptions.Compiled),
[PathFigureType.PolygonFigure] = new Regex($"{Sep}{PolygonFigureAttributes}", RegexOptions.Compiled),
[PathFigureType.RectangleFigure] = new Regex($"{Sep}{RectangleFigureAttributes}", RegexOptions.Compiled),
[PathFigureType.RoundedRectangleFigure] = new Regex($"{Sep}{RoundedRectangleFigureAttributes}", RegexOptions.Compiled)
};
PathElementRegexes = new Dictionary<PathElementType, Regex>
{
[PathElementType.MoveTo] = new Regex(MoveToRegexString, RegexOptions.Compiled),
[PathElementType.Line] = new Regex(LineRegexString, RegexOptions.Compiled),
[PathElementType.HorizontalLine] = new Regex(HorizontalLineRegexString, RegexOptions.Compiled),
[PathElementType.VerticalLine] = new Regex(VerticalLineRegexString, RegexOptions.Compiled),
[PathElementType.QuadraticBezier] = new Regex(QuadraticBezierRegexString, RegexOptions.Compiled),
[PathElementType.SmoothQuadraticBezier] = new Regex(SmoothQuadraticBezierRegexString, RegexOptions.Compiled),
[PathElementType.CubicBezier] = new Regex(CubicBezierRegexString, RegexOptions.Compiled),
[PathElementType.SmoothCubicBezier] = new Regex(SmoothCubicBezierRegexString, RegexOptions.Compiled),
[PathElementType.Arc] = new Regex(ArcRegexString, RegexOptions.Compiled),
[PathElementType.ClosePath] = new Regex(ClosePathRegexString, RegexOptions.Compiled)
};
PathElementAttributeRegexes = new Dictionary<PathElementType, Regex>
{
[PathElementType.MoveTo] = new Regex($"{Sep}{MoveToAttributes}", RegexOptions.Compiled),
[PathElementType.Line] = new Regex($"{Sep}{LineAttributes}", RegexOptions.Compiled),
[PathElementType.HorizontalLine] = new Regex($"{Sep}{HorizontalLineAttributes}", RegexOptions.Compiled),
[PathElementType.VerticalLine] = new Regex($"{Sep}{VerticalLineAttributes}", RegexOptions.Compiled),
[PathElementType.QuadraticBezier] = new Regex($"{Sep}{QuadraticBezierAttributes}", RegexOptions.Compiled),
[PathElementType.SmoothQuadraticBezier] = new Regex($"{Sep}{SmoothQuadraticBezierAttributes}", RegexOptions.Compiled),
[PathElementType.CubicBezier] = new Regex($"{Sep}{CubicBezierAttributes}", RegexOptions.Compiled),
[PathElementType.SmoothCubicBezier] = new Regex($"{Sep}{SmoothCubicBezierAttributes}", RegexOptions.Compiled),
[PathElementType.Arc] = new Regex($"{Sep}{ArcAttributes}", RegexOptions.Compiled),
// Not Applicable for ClosePathElement as it has no attributes
[PathElementType.ClosePath] = null
};
BrushRegexes = new Dictionary<BrushType, Regex>
{
[BrushType.SolidColor] = new Regex(SolidColorBrushRegexString, RegexOptions.Compiled),
[BrushType.LinearGradient] = new Regex(LinearGradientRegexString, RegexOptions.Compiled),
[BrushType.LinearGradientHdr] = new Regex(LinearGradientHdrRegexString, RegexOptions.Compiled),
[BrushType.RadialGradient] = new Regex(RadialGradientRegexString, RegexOptions.Compiled),
[BrushType.RadialGradientHdr] = new Regex(RadialGradientHdrRegexString, RegexOptions.Compiled)
};
GradientStopAttributeRegexes = new Dictionary<GradientStopAttributeType, Regex>
{
[GradientStopAttributeType.Main] = new Regex(GradientStopMainAttributes, RegexOptions.Compiled),
[GradientStopAttributeType.Additional] = new Regex($"{Sep}{GradientStopAttributes}", RegexOptions.Compiled),
[GradientStopAttributeType.MainHdr] = new Regex(GradientStopHdrMainAttributes, RegexOptions.Compiled),
[GradientStopAttributeType.AdditionalHdr] = new Regex($"{Sep}{GradientStopHdrAttributes}", RegexOptions.Compiled)
};
ValidationRegex = new Regex(@"\s+");
CanvasGeometryRegex = new Regex(CanvasGeometryRegexString, RegexOptions.Compiled);
ColorRegex = new Regex(ColorRegexString, RegexOptions.Compiled);
CanvasBrushRegex = new Regex(CanvasBrushRegexString, RegexOptions.Compiled);
GradientStopRegex = new Regex(GradientStopRegexString, RegexOptions.Compiled);
GradientStopHdrRegex = new Regex(GradientStopHdrRegexString, RegexOptions.Compiled);
CanvasStrokeStyleRegex = new Regex(CanvasStrokeStyleRegexString, RegexOptions.Compiled);
CanvasStrokeRegex = new Regex(CanvasStrokeRegexString, RegexOptions.Compiled);
CustomDashAttributeRegex = new Regex($"{Sep}{CustomDashAttribute}", RegexOptions.Compiled);
}
/// <summary>
/// Get the Regex for the given PathFigureType
/// </summary>
/// <param name="figureType">PathFigureType</param>
/// <returns>Regex</returns>
internal static Regex GetRegex(PathFigureType figureType)
{
return PathFigureRegexes[figureType];
}
/// <summary>
/// Get the Regex for the given PathElementType
/// </summary>
/// <param name="elementType">PathElementType</param>
/// <returns>Regex</returns>
internal static Regex GetRegex(PathElementType elementType)
{
return PathElementRegexes[elementType];
}
/// <summary>
/// Get the Regex for extracting attributes of the given PathFigureType
/// </summary>
/// <param name="figureType">PathFigureType</param>
/// <returns>Regex</returns>
internal static Regex GetAttributesRegex(PathFigureType figureType)
{
return PathFigureAttributeRegexes[figureType];
}
/// <summary>
/// Get the Regex for extracting attributes of the given PathElementType
/// </summary>
/// <param name="elementType">PathElementType</param>
/// <returns>Regex</returns>
internal static Regex GetAttributesRegex(PathElementType elementType)
{
return PathElementAttributeRegexes[elementType];
}
/// <summary>
/// Gets the Regex for extracting the attributes of the given BrushType
/// </summary>
/// <param name="brushType">BrushType</param>
/// <returns>Regex</returns>
internal static Regex GetAttributesRegex(BrushType brushType)
{
return BrushRegexes[brushType];
}
/// <summary>
/// Gets the Regex for extracting the attributes of the given
/// GradientStopAttributeType
/// </summary>
/// <param name="gsAttrType">GradientStopAttributeType</param>
/// <returns>Regex</returns>
internal static Regex GetAttributesRegex(GradientStopAttributeType gsAttrType)
{
return GradientStopAttributeRegexes[gsAttrType];
}
}
}

Просмотреть файл

@ -0,0 +1,59 @@
// 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.Globalization;
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry
{
/// <summary>
/// Class which can be used to encapsulate code statement(s) so that they are executed in a specific culture.
/// <para />
/// Usage example:
/// <para />
/// The following code block will be executed using the French culture.
/// <para />
/// using (new CultureShield("fr-FR"))
/// <para />
/// {
/// <para />
/// ...
/// <para />
/// }
/// </summary>
internal readonly ref struct CultureShield
{
private readonly CultureInfo _prevCulture;
/// <summary>
/// Initializes a new instance of the <see cref="CultureShield"/> struct so that the encapsulated code statement(s) can be executed using the specified culture.
/// <para />
/// Usage example:
/// <para />
/// The following code block will be executed using the French culture.
/// <para />
/// using (new CultureShield("fr-FR"))
/// <para />
/// {
/// <para />
/// ...
/// <para />
/// }
/// </summary>
/// <param name="culture">The culture in which the encapsulated code statement(s) are to be executed.</param>
internal CultureShield(string culture)
{
_prevCulture = CultureInfo.CurrentCulture;
CultureInfo.CurrentCulture = new CultureInfo(culture);
}
/// <summary>
/// Disposes the CultureShield object.
/// </summary>
public void Dispose()
{
CultureInfo.CurrentCulture = _prevCulture;
}
}
}

Просмотреть файл

@ -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 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
{
/// <summary>
/// Abstract base class for all Brush Elements
/// </summary>
internal abstract class AbstractCanvasBrushElement : ICanvasBrushElement
{
#pragma warning disable SA1401 // Fields should be private
protected float _opacity;
#pragma warning restore SA1401 // Fields should be private
/// <summary>
/// Gets or sets the Brush data defining the Brush Element
/// </summary>
public string Data { get; protected set; }
/// <summary>
/// Gets or sets the number of non-whitespace characters in
/// the Brush Data
/// </summary>
public int ValidationCount { get; protected set; }
/// <summary>
/// Initializes the Brush Element with the given Capture
/// </summary>
/// <param name="capture">Capture object</param>
public virtual void Initialize(Capture capture)
{
Data = capture.Value;
var regex = GetAttributesRegex();
var match = regex.Match(Data);
if (!match.Success)
{
return;
}
GetAttributes(match);
// Get the number of non-whitespace characters in the data
ValidationCount = RegexFactory.ValidationRegex.Replace(Data, string.Empty).Length;
}
/// <summary>
/// Creates the ICanvasBrush from the parsed data
/// </summary>
/// <param name="resourceCreator">ICanvasResourceCreator object</param>
/// <returns>ICanvasBrush</returns>
public abstract ICanvasBrush CreateBrush(ICanvasResourceCreator resourceCreator);
/// <summary>
/// Gets the Regex for extracting Brush Element Attributes
/// </summary>
/// <returns>Regex</returns>
protected abstract Regex GetAttributesRegex();
/// <summary>
/// Gets the Brush Element Attributes from the Match
/// </summary>
/// <param name="match">Match object</param>
protected abstract void GetAttributes(Match match);
}
}

Просмотреть файл

@ -0,0 +1,40 @@
// 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.Text.RegularExpressions;
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Brushes;
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Brush
{
/// <summary>
/// Interface for a Brush Element
/// </summary>
internal interface ICanvasBrushElement
{
/// <summary>
/// Gets the Brush data defining the Brush Element
/// </summary>
string Data { get; }
/// <summary>
/// Gets the number of non-whitespace characters in
/// the Brush Data
/// </summary>
int ValidationCount { get; }
/// <summary>
/// Initializes the Brush Element with the given Capture
/// </summary>
/// <param name="capture">Capture object</param>
void Initialize(Capture capture);
/// <summary>
/// Creates the ICanvasBrush from the parsed data
/// </summary>
/// <param name="resourceCreator">ICanvasResourceCreator object</param>
/// <returns>ICanvasBrush</returns>
ICanvasBrush CreateBrush(ICanvasResourceCreator resourceCreator);
}
}

Просмотреть файл

@ -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 System.Collections.Generic;
using System.Linq;
using System.Numerics;
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
{
/// <summary>
/// Represents a CanvasLinearGradientBrush with GradientStops
/// </summary>
internal sealed class LinearGradientBrushElement : AbstractCanvasBrushElement
{
private Vector2 _startPoint;
private Vector2 _endPoint;
private CanvasAlphaMode _alphaMode;
private CanvasBufferPrecision _bufferPrecision;
private CanvasEdgeBehavior _edgeBehavior;
private CanvasColorSpace _preInterpolationColorSpace;
private CanvasColorSpace _postInterpolationColorSpace;
private List<CanvasGradientStop> _gradientStops;
/// <summary>
/// Initializes a new instance of the <see cref="LinearGradientBrushElement"/> class.
/// </summary>
/// <param name="capture">Capture object</param>
public LinearGradientBrushElement(Capture capture)
{
// Set the default values
_startPoint = Vector2.Zero;
_endPoint = Vector2.Zero;
_opacity = 1f;
_alphaMode = (CanvasAlphaMode)0;
_bufferPrecision = (CanvasBufferPrecision)0;
_edgeBehavior = (CanvasEdgeBehavior)0;
// Default ColorSpace is sRGB
_preInterpolationColorSpace = CanvasColorSpace.Srgb;
_postInterpolationColorSpace = CanvasColorSpace.Srgb;
_gradientStops = new List<CanvasGradientStop>();
// Initialize
Initialize(capture);
}
/// <summary>
/// Creates the CanvasLinearGradientBrush from the parsed data
/// </summary>
/// <param name="resourceCreator">ICanvasResourceCreator object</param>
/// <returns>An instance of <see cref="ICanvasBrush"/></returns>
public override ICanvasBrush CreateBrush(ICanvasResourceCreator resourceCreator)
{
var brush = new CanvasLinearGradientBrush(
resourceCreator,
_gradientStops.ToArray(),
_edgeBehavior,
_alphaMode,
_preInterpolationColorSpace,
_postInterpolationColorSpace,
_bufferPrecision)
{
StartPoint = _startPoint,
EndPoint = _endPoint,
Opacity = _opacity
};
return brush;
}
/// <summary>
/// Gets the Regex for extracting Brush Element Attributes
/// </summary>
/// <returns>Regex</returns>
protected override Regex GetAttributesRegex()
{
return RegexFactory.GetAttributesRegex(BrushType.LinearGradient);
}
/// <summary>
/// Gets the Brush Element Attributes from the Match
/// </summary>
/// <param name="match">Match object</param>
protected override void GetAttributes(Match match)
{
// Start Point
float.TryParse(match.Groups["StartX"].Value, out var startX);
float.TryParse(match.Groups["StartY"].Value, out var startY);
_startPoint = new Vector2(startX, startY);
// End Point
float.TryParse(match.Groups["EndX"].Value, out var endX);
float.TryParse(match.Groups["EndY"].Value, out var endY);
_endPoint = new Vector2(endX, endY);
// Opacity (optional)
var group = match.Groups["Opacity"];
if (group.Success)
{
float.TryParse(group.Value, out _opacity);
}
// Alpha Mode (optional)
group = match.Groups["AlphaMode"];
if (group.Success)
{
Enum.TryParse(group.Value, out _alphaMode);
}
// Buffer Precision (optional)
group = match.Groups["BufferPrecision"];
if (group.Success)
{
Enum.TryParse(group.Value, out _bufferPrecision);
}
// Edge Behavior (optional)
group = match.Groups["EdgeBehavior"];
if (group.Success)
{
Enum.TryParse(group.Value, out _edgeBehavior);
}
// Pre Interpolation ColorSpace (optional)
group = match.Groups["PreColorSpace"];
if (group.Success)
{
Enum.TryParse(group.Value, out _preInterpolationColorSpace);
}
// Post Interpolation ColorSpace (optional)
group = match.Groups["PostColorSpace"];
if (group.Success)
{
Enum.TryParse(group.Value, out _postInterpolationColorSpace);
}
// GradientStops
group = match.Groups["GradientStops"];
if (group.Success)
{
_gradientStops.Clear();
foreach (Capture capture in group.Captures)
{
var gradientMatch = RegexFactory.GradientStopRegex.Match(capture.Value);
if (!gradientMatch.Success)
{
continue;
}
float position;
Color color;
// Main Attributes
var main = gradientMatch.Groups["Main"];
if (main.Success)
{
var mainMatch = RegexFactory.GetAttributesRegex(GradientStopAttributeType.Main).Match(main.Value);
float.TryParse(mainMatch.Groups["Position"].Value, out position);
color = ColorParser.Parse(mainMatch);
_gradientStops.Add(new CanvasGradientStop()
{
Color = color,
Position = position
});
}
// Additional Attributes
var additional = gradientMatch.Groups["Additional"];
if (!additional.Success)
{
continue;
}
foreach (Capture addCapture in additional.Captures)
{
var addMatch = RegexFactory.GetAttributesRegex(GradientStopAttributeType.Additional).Match(addCapture.Value);
float.TryParse(addMatch.Groups["Position"].Value, out position);
color = ColorParser.Parse(addMatch);
_gradientStops.Add(new CanvasGradientStop()
{
Color = color,
Position = position
});
}
}
// Sort the stops based on their position
if (_gradientStops.Any())
{
_gradientStops = _gradientStops.OrderBy(g => g.Position).ToList();
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,212 @@
// 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 System.Numerics;
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
{
/// <summary>
/// Represents a CanvasLinearGradientBrush with GradientStopHdrs
/// </summary>
internal sealed class LinearGradientHdrBrushElement : AbstractCanvasBrushElement
{
private Vector2 _startPoint;
private Vector2 _endPoint;
private CanvasAlphaMode _alphaMode;
private CanvasBufferPrecision _bufferPrecision;
private CanvasEdgeBehavior _edgeBehavior;
private CanvasColorSpace _preInterpolationColorSpace;
private CanvasColorSpace _postInterpolationColorSpace;
private List<CanvasGradientStopHdr> _gradientStopHdrs;
/// <summary>
/// Initializes a new instance of the <see cref="LinearGradientHdrBrushElement"/> class.
/// </summary>
/// <param name="capture">Capture object</param>
public LinearGradientHdrBrushElement(Capture capture)
{
// Set the default values
_startPoint = Vector2.Zero;
_endPoint = Vector2.Zero;
_opacity = 1f;
_alphaMode = (CanvasAlphaMode)0;
_bufferPrecision = (CanvasBufferPrecision)0;
_edgeBehavior = (CanvasEdgeBehavior)0;
// Default ColorSpace is sRGB
_preInterpolationColorSpace = CanvasColorSpace.Srgb;
_postInterpolationColorSpace = CanvasColorSpace.Srgb;
_gradientStopHdrs = new List<CanvasGradientStopHdr>();
// Initialize
Initialize(capture);
}
/// <summary>
/// Creates the CanvasLinearGradientBrush from the parsed data
/// </summary>
/// <param name="resourceCreator">ICanvasResourceCreator object</param>
/// <returns>Instance of <see cref="ICanvasBrush"/></returns>
public override ICanvasBrush CreateBrush(ICanvasResourceCreator resourceCreator)
{
var brush = CanvasLinearGradientBrush.CreateHdr(
resourceCreator,
_gradientStopHdrs.ToArray(),
_edgeBehavior,
_alphaMode,
_preInterpolationColorSpace,
_postInterpolationColorSpace,
_bufferPrecision);
brush.StartPoint = _startPoint;
brush.EndPoint = _endPoint;
brush.Opacity = _opacity;
return brush;
}
/// <summary>
/// Gets the Regex for extracting Brush Element Attributes
/// </summary>
/// <returns>Regex</returns>
protected override Regex GetAttributesRegex()
{
return RegexFactory.GetAttributesRegex(BrushType.LinearGradientHdr);
}
/// <summary>
/// Gets the Brush Element Attributes from the Match
/// </summary>
/// <param name="match">Match object</param>
protected override void GetAttributes(Match match)
{
// Start Point
float.TryParse(match.Groups["StartX"].Value, out var startX);
float.TryParse(match.Groups["StartY"].Value, out var startY);
_startPoint = new Vector2(startX, startY);
// End Point
float.TryParse(match.Groups["EndX"].Value, out var endX);
float.TryParse(match.Groups["EndY"].Value, out var endY);
_endPoint = new Vector2(endX, endY);
// Opacity (optional)
var group = match.Groups["Opacity"];
if (group.Success)
{
float.TryParse(group.Value, out _opacity);
}
// Alpha Mode (optional)
group = match.Groups["AlphaMode"];
if (group.Success)
{
Enum.TryParse(group.Value, out _alphaMode);
}
// Buffer Precision (optional)
group = match.Groups["BufferPrecision"];
if (group.Success)
{
Enum.TryParse(group.Value, out _bufferPrecision);
}
// Edge Behavior (optional)
group = match.Groups["EdgeBehavior"];
if (group.Success)
{
Enum.TryParse(group.Value, out _edgeBehavior);
}
// Pre Interpolation ColorSpace (optional)
group = match.Groups["PreColorSpace"];
if (group.Success)
{
Enum.TryParse(group.Value, out _preInterpolationColorSpace);
}
// Post Interpolation ColorSpace (optional)
group = match.Groups["PostColorSpace"];
if (group.Success)
{
Enum.TryParse(group.Value, out _postInterpolationColorSpace);
}
// GradientStopHdrs
group = match.Groups["GradientStops"];
if (group.Success)
{
_gradientStopHdrs.Clear();
foreach (Capture capture in group.Captures)
{
var gradientMatch = RegexFactory.GradientStopHdrRegex.Match(capture.Value);
if (!gradientMatch.Success)
{
continue;
}
// Main Attributes
var main = gradientMatch.Groups["Main"];
if (main.Success)
{
var mainMatch = RegexFactory.GetAttributesRegex(GradientStopAttributeType.MainHdr)
.Match(main.Value);
float.TryParse(mainMatch.Groups["Position"].Value, out float position);
float.TryParse(mainMatch.Groups["X"].Value, out float x);
float.TryParse(mainMatch.Groups["Y"].Value, out float y);
float.TryParse(mainMatch.Groups["Z"].Value, out float z);
float.TryParse(mainMatch.Groups["W"].Value, out float w);
var color = new Vector4(x, y, z, w);
_gradientStopHdrs.Add(new CanvasGradientStopHdr()
{
Color = color,
Position = position
});
}
// Additional Attributes
var additional = gradientMatch.Groups["Additional"];
if (!additional.Success)
{
continue;
}
foreach (Capture addCapture in additional.Captures)
{
var addMatch = RegexFactory.GetAttributesRegex(GradientStopAttributeType.AdditionalHdr)
.Match(addCapture.Value);
float.TryParse(addMatch.Groups["Position"].Value, out float position);
float.TryParse(addMatch.Groups["X"].Value, out float x);
float.TryParse(addMatch.Groups["Y"].Value, out float y);
float.TryParse(addMatch.Groups["Z"].Value, out float z);
float.TryParse(addMatch.Groups["W"].Value, out float w);
var color = new Vector4(x, y, z, w);
_gradientStopHdrs.Add(new CanvasGradientStopHdr()
{
Color = color,
Position = position
});
}
}
// Sort the stops based on their position
if (_gradientStopHdrs.Any())
{
_gradientStopHdrs = _gradientStopHdrs.OrderBy(g => g.Position).ToList();
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,230 @@
// 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 System.Numerics;
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
{
/// <summary>
/// Represents a CanvasRadialGradientBrush with GradientStops
/// </summary>
internal sealed class RadialGradientBrushElement : AbstractCanvasBrushElement
{
private float _radiusX;
private float _radiusY;
private Vector2 _center;
private Vector2 _originOffset;
private CanvasAlphaMode _alphaMode;
private CanvasBufferPrecision _bufferPrecision;
private CanvasEdgeBehavior _edgeBehavior;
private CanvasColorSpace _preInterpolationColorSpace;
private CanvasColorSpace _postInterpolationColorSpace;
private List<CanvasGradientStop> _gradientStops;
/// <summary>
/// Initializes a new instance of the <see cref="RadialGradientBrushElement"/> class.
/// </summary>
/// <param name="capture">Capture object</param>
public RadialGradientBrushElement(Capture capture)
{
// Set the default values
_radiusX = 0f;
_radiusY = 0f;
_center = Vector2.Zero;
_originOffset = Vector2.Zero;
_opacity = 1f;
_alphaMode = (CanvasAlphaMode)0;
_bufferPrecision = (CanvasBufferPrecision)0;
_edgeBehavior = (CanvasEdgeBehavior)0;
// Default ColorSpace is sRGB
_preInterpolationColorSpace = CanvasColorSpace.Srgb;
_postInterpolationColorSpace = CanvasColorSpace.Srgb;
_gradientStops = new List<CanvasGradientStop>();
// Initialize
Initialize(capture);
}
/// <summary>
/// Creates the CanvasLinearGradientBrush from the parsed data
/// </summary>
/// <param name="resourceCreator">ICanvasResourceCreator object</param>
/// <returns>Instance of <see cref="ICanvasBrush"/></returns>
public override ICanvasBrush CreateBrush(ICanvasResourceCreator resourceCreator)
{
var brush = new CanvasRadialGradientBrush(
resourceCreator,
_gradientStops.ToArray(),
_edgeBehavior,
_alphaMode,
_preInterpolationColorSpace,
_postInterpolationColorSpace,
_bufferPrecision)
{
RadiusX = _radiusX,
RadiusY = _radiusY,
Center = _center,
OriginOffset = _originOffset,
Opacity = _opacity
};
return brush;
}
/// <summary>
/// Gets the Regex for extracting Brush Element Attributes
/// </summary>
/// <returns>Regex</returns>
protected override Regex GetAttributesRegex()
{
return RegexFactory.GetAttributesRegex(BrushType.RadialGradient);
}
/// <summary>
/// Gets the Brush Element Attributes from the Match
/// </summary>
/// <param name="match">Match object</param>
protected override void GetAttributes(Match match)
{
// RadiusX
float.TryParse(match.Groups["RadiusX"].Value, out _radiusX);
// Sanitize by taking the absolute value
_radiusX = Math.Abs(_radiusX);
// RadiusY
float.TryParse(match.Groups["RadiusY"].Value, out _radiusY);
// Sanitize by taking the absolute value
_radiusY = Math.Abs(_radiusY);
// CenterX
float.TryParse(match.Groups["CenterX"].Value, out var centerX);
// CenterY
float.TryParse(match.Groups["CenterY"].Value, out var centerY);
_center = new Vector2(centerX, centerY);
// Opacity (optional)
var group = match.Groups["Opacity"];
if (group.Success)
{
float.TryParse(group.Value, out _opacity);
}
// Origin Offset (optional)
group = match.Groups["OriginOffset"];
if (group.Success)
{
float.TryParse(match.Groups["OffsetX"].Value, out var offsetX);
float.TryParse(match.Groups["OffsetY"].Value, out var offsetY);
_originOffset = new Vector2(offsetX, offsetY);
}
// Alpha Mode (optional)
group = match.Groups["AlphaMode"];
if (group.Success)
{
Enum.TryParse(group.Value, out _alphaMode);
}
// Buffer Precision (optional)
group = match.Groups["BufferPrecision"];
if (group.Success)
{
Enum.TryParse(group.Value, out _bufferPrecision);
}
// Edge Behavior (optional)
group = match.Groups["EdgeBehavior"];
if (group.Success)
{
Enum.TryParse(group.Value, out _edgeBehavior);
}
// Pre Interpolation ColorSpace (optional)
group = match.Groups["PreColorSpace"];
if (group.Success)
{
Enum.TryParse(group.Value, out _preInterpolationColorSpace);
}
// Post Interpolation ColorSpace (optional)
group = match.Groups["PostColorSpace"];
if (group.Success)
{
Enum.TryParse(group.Value, out _postInterpolationColorSpace);
}
// Gradient Stops
group = match.Groups["GradientStops"];
if (group.Success)
{
_gradientStops.Clear();
foreach (Capture capture in group.Captures)
{
var gradientMatch = RegexFactory.GradientStopRegex.Match(capture.Value);
if (!gradientMatch.Success)
{
continue;
}
float position;
Color color;
// Main Attributes
var main = gradientMatch.Groups["Main"];
if (main.Success)
{
var mainMatch = RegexFactory.GetAttributesRegex(GradientStopAttributeType.Main).Match(main.Value);
float.TryParse(mainMatch.Groups["Position"].Value, out position);
color = ColorParser.Parse(mainMatch);
_gradientStops.Add(new CanvasGradientStop()
{
Color = color,
Position = position
});
}
// Additional Attributes
var additional = gradientMatch.Groups["Additional"];
if (!additional.Success)
{
continue;
}
foreach (Capture addCapture in additional.Captures)
{
var addMatch = RegexFactory.GetAttributesRegex(GradientStopAttributeType.Additional).Match(addCapture.Value);
float.TryParse(addMatch.Groups["Position"].Value, out position);
color = ColorParser.Parse(addMatch);
_gradientStops.Add(new CanvasGradientStop()
{
Color = color,
Position = position
});
}
}
// Sort the stops based on their position
if (_gradientStops.Any())
{
_gradientStops = _gradientStops.OrderBy(g => g.Position).ToList();
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,237 @@
// 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 System.Numerics;
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
{
/// <summary>
/// Represents a CanvasRadialGradientBrush with GradientStopHdrs
/// </summary>
internal sealed class RadialGradientHdrBrushElement : AbstractCanvasBrushElement
{
private float _radiusX;
private float _radiusY;
private Vector2 _center;
private Vector2 _originOffset;
private CanvasAlphaMode _alphaMode;
private CanvasBufferPrecision _bufferPrecision;
private CanvasEdgeBehavior _edgeBehavior;
private CanvasColorSpace _preInterpolationColorSpace;
private CanvasColorSpace _postInterpolationColorSpace;
private List<CanvasGradientStopHdr> _gradientStopHdrs;
/// <summary>
/// Initializes a new instance of the <see cref="RadialGradientHdrBrushElement"/> class.
/// </summary>
/// <param name="capture">Capture object</param>
public RadialGradientHdrBrushElement(Capture capture)
{
// Set the default values
_radiusX = 0f;
_radiusY = 0f;
_center = Vector2.Zero;
_originOffset = Vector2.Zero;
_opacity = 1f;
_alphaMode = (CanvasAlphaMode)0;
_bufferPrecision = (CanvasBufferPrecision)0;
_edgeBehavior = (CanvasEdgeBehavior)0;
// Default ColorSpace is sRGB
_preInterpolationColorSpace = CanvasColorSpace.Srgb;
_postInterpolationColorSpace = CanvasColorSpace.Srgb;
_gradientStopHdrs = new List<CanvasGradientStopHdr>();
// Initialize
Initialize(capture);
}
/// <summary>
/// Creates the CanvasLinearGradientBrush from the parsed data
/// </summary>
/// <param name="resourceCreator">ICanvasResourceCreator object</param>
/// <returns>Instance of <see cref="ICanvasBrush"/></returns>
public override ICanvasBrush CreateBrush(ICanvasResourceCreator resourceCreator)
{
var brush = CanvasRadialGradientBrush.CreateHdr(
resourceCreator,
_gradientStopHdrs.ToArray(),
_edgeBehavior,
_alphaMode,
_preInterpolationColorSpace,
_postInterpolationColorSpace,
_bufferPrecision);
brush.RadiusX = _radiusX;
brush.RadiusY = _radiusY;
brush.Center = _center;
brush.OriginOffset = _originOffset;
brush.Opacity = _opacity;
return brush;
}
/// <summary>
/// Gets the Regex for extracting Brush Element Attributes
/// </summary>
/// <returns>Regex</returns>
protected override Regex GetAttributesRegex()
{
return RegexFactory.GetAttributesRegex(BrushType.RadialGradientHdr);
}
/// <summary>
/// Gets the Brush Element Attributes from the Match
/// </summary>
/// <param name="match">Match object</param>
protected override void GetAttributes(Match match)
{
// RadiusX
float.TryParse(match.Groups["RadiusX"].Value, out _radiusX);
// Sanitize by taking the absolute value
_radiusX = Math.Abs(_radiusX);
// RadiusY
float.TryParse(match.Groups["RadiusY"].Value, out _radiusY);
// Sanitize by taking the absolute value
_radiusY = Math.Abs(_radiusY);
// CenterX
float.TryParse(match.Groups["CenterX"].Value, out var centerX);
// CenterY
float.TryParse(match.Groups["CenterY"].Value, out var centerY);
_center = new Vector2(centerX, centerY);
// Opacity (optional)
var group = match.Groups["Opacity"];
if (group.Success)
{
float.TryParse(group.Value, out _opacity);
}
// Origin Offset (optional)
group = match.Groups["OriginOffset"];
if (group.Success)
{
float.TryParse(match.Groups["OffsetX"].Value, out var offsetX);
float.TryParse(match.Groups["OffsetY"].Value, out var offsetY);
_originOffset = new Vector2(offsetX, offsetY);
}
// Alpha Mode (optional)
group = match.Groups["AlphaMode"];
if (group.Success)
{
Enum.TryParse(group.Value, out _alphaMode);
}
// Buffer Precision (optional)
group = match.Groups["BufferPrecision"];
if (group.Success)
{
Enum.TryParse(group.Value, out _bufferPrecision);
}
// Edge Behavior (optional)
group = match.Groups["EdgeBehavior"];
if (group.Success)
{
Enum.TryParse(group.Value, out _edgeBehavior);
}
// Pre Interpolation ColorSpace (optional)
group = match.Groups["PreColorSpace"];
if (group.Success)
{
Enum.TryParse(group.Value, out _preInterpolationColorSpace);
}
// Post Interpolation ColorSpace (optional)
group = match.Groups["PostColorSpace"];
if (group.Success)
{
Enum.TryParse(group.Value, out _postInterpolationColorSpace);
}
// GradientStopHdrs
group = match.Groups["GradientStops"];
if (group.Success)
{
_gradientStopHdrs.Clear();
foreach (Capture capture in group.Captures)
{
var gradientMatch = RegexFactory.GradientStopHdrRegex.Match(capture.Value);
if (!gradientMatch.Success)
{
continue;
}
float position;
float x = 0, y = 0, z = 0, w = 0;
var main = gradientMatch.Groups["Main"];
if (main.Success)
{
var mainMatch = RegexFactory.GetAttributesRegex(GradientStopAttributeType.MainHdr)
.Match(main.Value);
float.TryParse(mainMatch.Groups["Position"].Value, out position);
float.TryParse(mainMatch.Groups["X"].Value, out x);
float.TryParse(mainMatch.Groups["Y"].Value, out y);
float.TryParse(mainMatch.Groups["Z"].Value, out z);
float.TryParse(mainMatch.Groups["W"].Value, out w);
var color = new Vector4(x, y, z, w);
_gradientStopHdrs.Add(new CanvasGradientStopHdr()
{
Color = color,
Position = position
});
}
var additional = gradientMatch.Groups["Additional"];
if (!additional.Success)
{
continue;
}
foreach (Capture addCapture in additional.Captures)
{
var addMatch = RegexFactory.GetAttributesRegex(GradientStopAttributeType.AdditionalHdr)
.Match(addCapture.Value);
float.TryParse(addMatch.Groups["Position"].Value, out position);
float.TryParse(addMatch.Groups["X"].Value, out x);
float.TryParse(addMatch.Groups["Y"].Value, out y);
float.TryParse(addMatch.Groups["Z"].Value, out z);
float.TryParse(addMatch.Groups["W"].Value, out w);
var color = new Vector4(x, y, z, w);
_gradientStopHdrs.Add(new CanvasGradientStopHdr()
{
Color = color,
Position = position
});
}
}
// Sort the stops based on their position
if (_gradientStopHdrs.Any())
{
_gradientStopHdrs = _gradientStopHdrs.OrderBy(g => g.Position).ToList();
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,73 @@
// 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.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
{
/// <summary>
/// Represents a CanvasSolidColorBrush
/// </summary>
internal sealed class SolidColorBrushElement : AbstractCanvasBrushElement
{
private Color _color;
/// <summary>
/// Initializes a new instance of the <see cref="SolidColorBrushElement"/> class.
/// </summary>
/// <param name="capture">Capture object</param>
public SolidColorBrushElement(Capture capture)
{
// Set the default values
_color = Colors.Transparent;
_opacity = 1f;
Initialize(capture);
}
/// <summary>
/// Creates the ICanvasBrush from the parsed data
/// </summary>
/// <param name="resourceCreator">ICanvasResourceCreator object</param>
/// <returns>Instance of <see cref="ICanvasBrush"/></returns>
public override ICanvasBrush CreateBrush(ICanvasResourceCreator resourceCreator)
{
return new CanvasSolidColorBrush(resourceCreator, _color)
{
Opacity = _opacity
};
}
/// <summary>
/// Gets the Regex for extracting Brush Element Attributes
/// </summary>
/// <returns>Regex</returns>
protected override Regex GetAttributesRegex()
{
return RegexFactory.GetAttributesRegex(BrushType.SolidColor);
}
/// <summary>
/// Gets the Brush Element Attributes from the Match
/// </summary>
/// <param name="match">Match object</param>
protected override void GetAttributes(Match match)
{
// Parse the Color
_color = ColorParser.Parse(match);
// Opacity (optional)
var group = match.Groups["Opacity"];
if (group.Success)
{
float.TryParse(group.Value, out _opacity);
}
}
}
}

Просмотреть файл

@ -0,0 +1,107 @@
// 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 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
{
/// <summary>
/// Abstract base class for all Path Elements
/// </summary>
internal abstract class AbstractPathElement : ICanvasPathElement
{
/// <summary>
/// Gets or sets index of the Path Element in the Path Data
/// </summary>
public int Index { get; protected set; } = -1;
/// <summary>
/// Gets or sets path data defining the Path Element
/// </summary>
public string Data { get; protected set; } = string.Empty;
/// <summary>
/// Gets or sets number of non-whitespace characters in
/// the Path Element Data
/// </summary>
public int ValidationCount { get; protected set; } = 0;
/// <summary>
/// Gets or sets a value indicating whether the path element contains
/// absolute or relative coordinates.
/// </summary>
public bool IsRelative { get; protected set; } = false;
/// <summary>
/// Initializes the Path Element with the given Match
/// </summary>
/// <param name="match">Match object</param>
/// <param name="index">Index within the match</param>
public virtual void Initialize(Match match, int index)
{
var main = match.Groups["Main"];
Index = index;
Data = main.Value;
var command = match.Groups["Command"].Value[0];
IsRelative = char.IsLower(command);
// Get the Path Element attributes
GetAttributes(match);
// Get the number of non-whitespace characters in the data
ValidationCount = RegexFactory.ValidationRegex.Replace(main.Value, string.Empty).Length;
}
/// <summary>
/// Initializes the Path Element with the given Capture
/// </summary>
/// <param name="capture">Capture object</param>
/// <param name="index">Index of the Path Element in the Path data.</param>
/// <param name="isRelative">Indicates whether the Path Element coordinates are
/// absolute or relative</param>
public virtual void InitializeAdditional(Capture capture, int index, bool isRelative)
{
Index = index;
Data = capture.Value;
IsRelative = isRelative;
var match = GetAttributesRegex().Match(Data);
if (match.Captures.Count != 1)
{
return;
}
// Get the Path Element attributes
GetAttributes(match);
// Get the number of non-whitespace characters in the data
ValidationCount = RegexFactory.ValidationRegex.Replace(Data, string.Empty).Length;
}
/// <summary>
/// Adds the Path Element to the Path.
/// </summary>
/// <param name="pathBuilder">CanvasPathBuilder object</param>
/// <param name="currentPoint">The last active location in the Path before adding
/// the Path Element</param>
/// <param name="lastElement">The previous PathElement in the Path.</param>
/// <returns>The latest location in the Path after adding the Path Element</returns>
public abstract Vector2 CreatePath(CanvasPathBuilder pathBuilder, Vector2 currentPoint, ref ICanvasPathElement lastElement);
/// <summary>
/// Get the Regex for extracting Path Element Attributes
/// </summary>
/// <returns>Instance of <see cref="Regex"/></returns>
protected abstract Regex GetAttributesRegex();
/// <summary>
/// Gets the Path Element Attributes from the Match
/// </summary>
/// <param name="match">Match object</param>
protected abstract void GetAttributes(Match match);
}
}

Просмотреть файл

@ -0,0 +1,101 @@
// 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.Text.RegularExpressions;
using Microsoft.Graphics.Canvas.Geometry;
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core;
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path
{
/// <summary>
/// Class representing the Arc Element in a Path Geometry
/// </summary>
internal class ArcElement : AbstractPathElement
{
private float _radiusX;
private float _radiusY;
private float _angle;
private CanvasArcSize _arcSize;
private CanvasSweepDirection _sweepDirection;
private float _x;
private float _y;
/// <summary>
/// Initializes a new instance of the <see cref="ArcElement"/> class.
/// </summary>
public ArcElement()
{
_radiusX = _radiusY = _angle = 0;
_arcSize = CanvasArcSize.Small;
_sweepDirection = CanvasSweepDirection.Clockwise;
_sweepDirection = 0;
_x = _y = 0;
}
/// <summary>
/// Adds the Path Element to the Path.
/// </summary>
/// <param name="pathBuilder">CanvasPathBuilder object</param>
/// <param name="currentPoint">The last active location in the Path before adding
/// the Path Element</param>
/// <param name="lastElement">The previous PathElement in the Path.</param>
/// <returns>The latest location in the Path after adding the Path Element</returns>
public override Vector2 CreatePath(CanvasPathBuilder pathBuilder, Vector2 currentPoint, ref ICanvasPathElement lastElement)
{
// Calculate coordinates
var point = new Vector2(_x, _y);
if (IsRelative)
{
point += currentPoint;
}
// Execute command
pathBuilder.AddArc(point, _radiusX, _radiusY, _angle, _sweepDirection, _arcSize);
// Set Last Element
lastElement = this;
// Return current point
return point;
}
/// <summary>
/// Get the Regex for extracting Path Element Attributes
/// </summary>
/// <returns>Instance of <see cref="Regex"/></returns>
protected override Regex GetAttributesRegex()
{
return RegexFactory.GetAttributesRegex(PathElementType.Arc);
}
/// <summary>
/// Gets the Path Element Attributes from the Match
/// </summary>
/// <param name="match">Match object</param>
protected override void GetAttributes(Match match)
{
float.TryParse(match.Groups["RadiusX"].Value, out _radiusX);
// Sanitize by taking only the absolute value
_radiusX = Math.Abs(_radiusX);
float.TryParse(match.Groups["RadiusY"].Value, out _radiusY);
// Sanitize by taking only the absolute value
_radiusY = Math.Abs(_radiusY);
// Angle is provided in degrees
float.TryParse(match.Groups["Angle"].Value, out _angle);
// Convert angle to radians as CanvasPathBuilder.AddArc() method
// requires the angle to be in radians
_angle *= Scalar.DegreesToRadians;
Enum.TryParse(match.Groups["IsLargeArc"].Value, out _arcSize);
Enum.TryParse(match.Groups["SweepDirection"].Value, out _sweepDirection);
float.TryParse(match.Groups["X"].Value, out _x);
float.TryParse(match.Groups["Y"].Value, out _y);
}
}
}

Просмотреть файл

@ -0,0 +1,83 @@
// 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.Text.RegularExpressions;
using Microsoft.Graphics.Canvas.Geometry;
using Microsoft.Toolkit.Uwp.UI.Media.Geometry.Core;
namespace Microsoft.Toolkit.Uwp.UI.Media.Geometry.Elements.Path
{
/// <summary>
/// Class representing the Ellipse Figure in a Path Geometry
/// </summary>
internal class CanvasEllipseFigure : AbstractPathElement
{
private float _radiusX;
private float _radiusY;
private float _x;
private float _y;
/// <summary>
/// Initializes a new instance of the <see cref="CanvasEllipseFigure"/> class.
/// </summary>
public CanvasEllipseFigure()
{
_radiusX = _radiusY = _x = _y = 0;
}
/// <summary>
/// Adds the Path Element to the Path.
/// </summary>
/// <param name="pathBuilder">CanvasPathBuilder object</param>
/// <param name="currentPoint">The last active location in the Path before adding
/// the EllipseFigure</param>
/// <param name="lastElement">The previous PathElement in the Path.</param>
/// <returns>The latest location in the Path after adding the EllipseFigure</returns>
public override Vector2 CreatePath(CanvasPathBuilder pathBuilder, Vector2 currentPoint, ref ICanvasPathElement lastElement)
{
// Calculate coordinates
var center = new Vector2(_x, _y);
if (IsRelative)
{
center += currentPoint;
}
// Execute command
pathBuilder.AddEllipseFigure(center.X, center.Y, _radiusX, _radiusY);
// No need to update the lastElement or currentPoint here as we are creating
// a separate closed figure here. So current point will not change.
return currentPoint;
}
/// <summary>
/// Get the Regex for extracting Path Element Attributes
/// </summary>
/// <returns>Instance of <see cref="Regex"/></returns>
protected override Regex GetAttributesRegex()
{
return RegexFactory.GetAttributesRegex(PathFigureType.EllipseFigure);
}
/// <summary>
/// Gets the Path Element Attributes from the Match
/// </summary>
/// <param name="match">Match object</param>
protected override void GetAttributes(Match match)
{
float.TryParse(match.Groups["RadiusX"].Value, out _radiusX);
// Sanitize by taking the absolute value
_radiusX = Math.Abs(_radiusX);
float.TryParse(match.Groups["RadiusY"].Value, out _radiusY);
// Sanitize by taking the absolute value
_radiusY = Math.Abs(_radiusY);
float.TryParse(match.Groups["X"].Value, out _x);
float.TryParse(match.Groups["Y"].Value, out _y);
}
}
}

Просмотреть файл

@ -0,0 +1,136 @@
// 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 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
{
/// <summary>
/// Class which contains a collection of ICanvasPathElements
/// which can be used to create CanvasGeometry.
/// </summary>
internal class CanvasPathFigure : AbstractPathElement
{
// Collection of Path Elements
private List<ICanvasPathElement> _elements;
public CanvasPathFigure()
{
_elements = new List<ICanvasPathElement>();
ValidationCount = 0;
}
/// <summary>
/// Initializes the Path Element with the given Match
/// </summary>
/// <param name="match">Match object</param>
/// <param name="index">Index of the path element in the Path data.</param>
public override void Initialize(Match match, int index)
{
Index = index;
var main = match.Groups["Main"];
Data = main.Value;
var elements = new List<ICanvasPathElement>();
foreach (PathElementType type in Enum.GetValues(typeof(PathElementType)))
{
foreach (Capture elementCapture in match.Groups[type.ToString()].Captures)
{
var elementRootIndex = elementCapture.Index;
var regex = RegexFactory.GetRegex(type);
var elementMatch = regex.Match(elementCapture.Value);
var isRelative = false;
// Process the 'Main' Group which contains the Path Command and
// corresponding attributes
if (elementMatch.Groups["Main"].Captures.Count == 1)
{
var figure = PathElementFactory.CreatePathElement(type, elementMatch, elementRootIndex);
elements.Add(figure);
isRelative = figure.IsRelative;
}
// Process the 'Additional' Group which contains just the attributes
elements.AddRange(from Capture capture in elementMatch.Groups["Additional"].Captures
select PathElementFactory.CreateAdditionalPathElement(type, capture, elementRootIndex + capture.Index, isRelative));
}
}
// Sort the path elements based on their index value
_elements.AddRange(elements.OrderBy(e => e.Index));
if (_elements.Count <= 0)
{
return;
}
// Check if the last path element in the figure is an ClosePathElement
// which would indicate that the path needs to be closed. Otherwise,
// add a default ClosePathElement at the end to indicate that the path
// is not closed.
var lastElement = _elements.ElementAt(_elements.Count - 1);
if ((lastElement as ClosePathElement) == null)
{
_elements.Add(PathElementFactory.CreateDefaultPathElement(PathElementType.ClosePath));
}
// Validation Count will be the cumulative sum of the validation count
// of child elements of the PathFigure
ValidationCount = _elements.Sum(x => x.ValidationCount);
}
/// <summary>
/// Initializes the Path Element with the given Capture
/// </summary>
/// <param name="capture">Capture object</param>
/// <param name="index">Index of the Path Element in the Path data.</param>
/// <param name="isRelative">Indicates whether the Path Element coordinates are
/// absolute or relative</param>
public override void InitializeAdditional(Capture capture, int index, bool isRelative)
{
// Do nothing as this scenario is not valid for CanvasPathFigure
}
/// <summary>
/// Adds the Path Element to the Path.
/// </summary>
/// <param name="pathBuilder">CanvasPathBuilder object</param>
/// <param name="currentPoint">The last active location in the Path before adding
/// the Path Element</param>
/// <param name="lastElement">The previous PathElement in the Path.</param>
/// <returns>The latest location in the Path after adding the Path Element</returns>
public override Vector2 CreatePath(CanvasPathBuilder pathBuilder, Vector2 currentPoint, ref ICanvasPathElement lastElement)
{
foreach (var pathElement in _elements)
{
currentPoint = pathElement.CreatePath(pathBuilder, currentPoint, ref lastElement);
}
return currentPoint;
}
/// <summary>
/// Get the Regex for extracting Path Element Attributes
/// </summary>
/// <returns>Instance of <see cref="Regex"/></returns>
protected override Regex GetAttributesRegex()
{
return null;
}
/// <summary>
/// Gets the Path Element Attributes from the Match
/// </summary>
/// <param name="match">Match object</param>
protected override void GetAttributes(Match match)
{
// Do nothing
}
}
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше