From 740bc4886c258243d2ef48bc0dafed81df819aec Mon Sep 17 00:00:00 2001 From: Kalachik Roman Date: Mon, 8 Jan 2024 22:33:02 +0300 Subject: [PATCH 01/24] SVG CSS support --- .gitmodules | 3 +- externals/SVG | 2 +- src/Avalonia.Svg.Skia/Svg.cs | 37 +++++++++++++++++-- src/Avalonia.Svg.Skia/SvgImage.cs | 22 ++++++++++++ src/Avalonia.Svg.Skia/SvgImageExtension.cs | 32 ++++++++++++++--- src/Avalonia.Svg.Skia/SvgSource.cs | 20 ++++++++--- src/Avalonia.Svg/Svg.cs | 3 +- src/Avalonia.Svg/SvgSource.cs | 9 ++--- src/Svg.Model/SvgExtensions.IO.cs | 12 +++---- src/Svg.Model/SvgParameters.cs | 12 +++++++ src/Svg.Skia/SKSvg.Model.cs | 41 +++++++++++++++++++--- 11 files changed, 164 insertions(+), 29 deletions(-) create mode 100644 src/Svg.Model/SvgParameters.cs diff --git a/.gitmodules b/.gitmodules index bfb843304..f3714fb2c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,7 @@ [submodule "externals/SVG"] path = externals/SVG - url = https://github.com/wieslawsoltes/SVG.git + url = https://github.com/RomanKalachik/SVG.git + branch = master [submodule "externals/resvg"] path = externals/resvg url = https://github.com/wieslawsoltes/resvg.git diff --git a/externals/SVG b/externals/SVG index c21c914c5..5528cee1c 160000 --- a/externals/SVG +++ b/externals/SVG @@ -1 +1 @@ -Subproject commit c21c914c560b285fdead795fa37fd4e5264094a3 +Subproject commit 5528cee1c04c55b75f7b2c1159266e6664f74dec diff --git a/src/Avalonia.Svg.Skia/Svg.cs b/src/Avalonia.Svg.Skia/Svg.cs index 210599eaa..5dc003d12 100644 --- a/src/Avalonia.Svg.Skia/Svg.cs +++ b/src/Avalonia.Svg.Skia/Svg.cs @@ -5,6 +5,7 @@ using Avalonia.Media; using Avalonia.Metadata; using ShimSkiaSharp; +using Svg.Model; using Svg.Skia; namespace Avalonia.Svg.Skia; @@ -19,6 +20,18 @@ public class Svg : Control private bool _enableCache; private Dictionary? _cache; + /// + /// Defines CSS for global icnos theming + /// + public static readonly AttachedProperty CSSProperty = + AvaloniaProperty.RegisterAttached("CSS", inherits: true); + + /// + /// Defines CSS for control states like Hover, Pressed, Enabled + /// + public static readonly AttachedProperty CSSCurrentProperty = + AvaloniaProperty.RegisterAttached("CSSCurrent", inherits: true); + /// /// Defines the property. /// @@ -63,6 +76,13 @@ public string? Path set => SetValue(PathProperty, value); } + public static string? GetCSS(AvaloniaObject element) => element.GetValue(CSSProperty); + public static void SetCSS(AvaloniaObject element, string? value) => element.SetValue(CSSProperty, value); + + public static string? GetCSSCurrent(AvaloniaObject element) => element.GetValue(CSSCurrentProperty); + public static void SetCSSCurrent(AvaloniaObject element, string? value) => element.SetValue(CSSCurrentProperty, value); + + /// /// Gets or sets the Svg source. /// @@ -118,6 +138,17 @@ static Svg() { AffectsRender(PathProperty, SourceProperty, StretchProperty, StretchDirectionProperty); AffectsMeasure(PathProperty, SourceProperty, StretchProperty, StretchDirectionProperty); + + CSSProperty.Changed.AddClassHandler(OnCSSPropertyAttachedPropertyChanged); + CSSCurrentProperty.Changed.AddClassHandler(OnCSSPropertyAttachedPropertyChanged); + + } + + private static void OnCSSPropertyAttachedPropertyChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e) + { + var control = d as Control; + if (control != null) + control.InvalidateVisual(); } /// @@ -212,10 +243,10 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang { base.OnPropertyChanged(change); - if (change.Property == PathProperty) + if (change.Property == PathProperty || change.Property == CSSProperty || change.Property == CSSCurrentProperty) { var path = change.GetNewValue(); - LoadFromPath(path); + LoadFromPath(path, new SvgParameters() { CSS = SvgSource.CombineCSS(GetCSS(this), GetCSSCurrent(this))}); InvalidateVisual(); } @@ -240,7 +271,7 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang } } - private void LoadFromPath(string? path, Dictionary? entities = null) + private void LoadFromPath(string? path, SvgParameters? entities = null) { if (path is null) { diff --git a/src/Avalonia.Svg.Skia/SvgImage.cs b/src/Avalonia.Svg.Skia/SvgImage.cs index ad26666b1..31a80b552 100644 --- a/src/Avalonia.Svg.Skia/SvgImage.cs +++ b/src/Avalonia.Svg.Skia/SvgImage.cs @@ -1,5 +1,6 @@ using Avalonia.Media; using Avalonia.Metadata; +using Svg.Model; namespace Avalonia.Svg.Skia; @@ -13,6 +14,12 @@ public class SvgImage : AvaloniaObject, IImage /// public static readonly StyledProperty SourceProperty = AvaloniaProperty.Register(nameof(Source)); + + public static readonly StyledProperty CSSProperty = + AvaloniaProperty.Register(nameof(CSS)); + + public static readonly StyledProperty CSSCurrentProperty = + AvaloniaProperty.Register(nameof(CSSCurrent)); /// /// Gets or sets the content. @@ -23,6 +30,18 @@ public SvgSource? Source get => GetValue(SourceProperty); set => SetValue(SourceProperty, value); } + + public string CSS + { + get => GetValue(CSSProperty); + set => SetValue(CSSProperty, value); + } + + public string CSSCurrent + { + get => GetValue(CSSCurrentProperty); + set => SetValue(CSSCurrentProperty, value); + } /// public Size Size => @@ -32,6 +51,9 @@ public SvgSource? Source void IImage.Draw(DrawingContext context, Rect sourceRect, Rect destRect) { var source = Source; + var css = SvgSource.CombineCSS(CSS, CSSCurrent); + if (source?.Entities?.CSS != css) + source?.ReLoad(new SvgParameters() { CSS = css }); if (source?.Picture is null) { return; diff --git a/src/Avalonia.Svg.Skia/SvgImageExtension.cs b/src/Avalonia.Svg.Skia/SvgImageExtension.cs index 2921bcb15..6ecdd1657 100644 --- a/src/Avalonia.Svg.Skia/SvgImageExtension.cs +++ b/src/Avalonia.Svg.Skia/SvgImageExtension.cs @@ -2,6 +2,7 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; using Avalonia.Media; +using Svg.Model; namespace Avalonia.Svg.Skia; @@ -27,16 +28,37 @@ public override object ProvideValue(IServiceProvider serviceProvider) var path = Path; var context = (IUriContext)serviceProvider.GetService(typeof(IUriContext))!; var baseUri = context.BaseUri; - var source = SvgSource.Load(path, baseUri); var target = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget))!; + var targetControl = target.TargetObject as Control; + var iimage = ProvideValue(path, baseUri, targetControl); if (target.TargetProperty is AvaloniaProperty property) { if (property.PropertyType == typeof(IImage)) { - return new SvgImage { Source = source }; + return iimage; } - return new Image { Source = new SvgImage { Source = source } }; + return new Image { Source = iimage }; } - return new SvgImage { Source = source }; + return iimage; } -} \ No newline at end of file + + public static IImage ProvideValue(string path, Uri baseUri, Control targetControl) + { + var css = targetControl != null ? Svg.GetCSS(targetControl) : null; + var cssCurrent = targetControl != null ? Svg.GetCSSCurrent(targetControl) : null; + var source = SvgSource.Load(path, baseUri, new SvgParameters() { CSS = SvgSource.CombineCSS(css, cssCurrent) }); + return CreateSvgImage(source, targetControl); + } + + public static SvgImage CreateSvgImage(SvgSource source, Control? targetControl) + { + var result = new SvgImage(); + result.Source = source; + if (targetControl != null) + { + result.Bind(SvgImage.CSSProperty, targetControl.GetObservable(Svg.CSSProperty).ToBinding()); + result.Bind(SvgImage.CSSCurrentProperty, targetControl.GetObservable(Svg.CSSCurrentProperty).ToBinding()); + } + return result; + } +} diff --git a/src/Avalonia.Svg.Skia/SvgSource.cs b/src/Avalonia.Svg.Skia/SvgSource.cs index 706341550..1694a2d50 100644 --- a/src/Avalonia.Svg.Skia/SvgSource.cs +++ b/src/Avalonia.Svg.Skia/SvgSource.cs @@ -5,6 +5,7 @@ using System.IO; using System.Net.Http; using Svg; +using Svg.Model; using Svg.Skia; namespace Avalonia.Svg.Skia; @@ -15,6 +16,13 @@ namespace Avalonia.Svg.Skia; [TypeConverter(typeof(SvgSourceTypeConverter))] public class SvgSource : SKSvg { + public static bool ThrowExceptionIfResourceMissing { get; set; } = false; + public static T? ResourceMissing(string path) where T : SKSvg, new() + { + if (ThrowExceptionIfResourceMissing) + throw new ArgumentException($"Missing resource: {path}"); + return default; + } /// t /// Loads svg source from file or resource. /// @@ -22,7 +30,7 @@ public class SvgSource : SKSvg /// The base uri. /// The svg entities. /// The svg source. - public static T? Load(string path, Uri? baseUri, Dictionary? entities = null) where T : SKSvg, new() + public static T? Load(string path, Uri? baseUri, SvgParameters? entities = null) where T : SKSvg, new() { if (File.Exists(path)) { @@ -50,7 +58,7 @@ public class SvgSource : SKSvg Debug.WriteLine(e.ToString()); } - return default; + return ResourceMissing(path); } var uri = path.StartsWith("/") ? new Uri(path, UriKind.Relative) : new Uri(path, UriKind.RelativeOrAbsolute); @@ -65,13 +73,17 @@ public class SvgSource : SKSvg var stream = Platform.AssetLoader.Open(uri, baseUri); if (stream is null) { - return default; + return ResourceMissing(path); } var source = new T(); source.Load(stream, entities); return source; } } + public static string CombineCSS(string? css, string? cssCurrent) + { + return $"{css} {cssCurrent}"; // The last entry has higher prioriry, so cssCurrent can overrride styles + } /// /// Loads svg source from svg source. @@ -91,7 +103,7 @@ public class SvgSource : SKSvg /// The svg stream. /// The svg entities. /// The svg source. - public static T? LoadFromStream(Stream stream, Dictionary? entities = null) where T : SKSvg, new() + public static T? LoadFromStream(Stream stream, SvgParameters? entities = null) where T : SKSvg, new() { var skSvg = new T(); skSvg.Load(stream, entities); diff --git a/src/Avalonia.Svg/Svg.cs b/src/Avalonia.Svg/Svg.cs index d7cc6f034..a802b8278 100644 --- a/src/Avalonia.Svg/Svg.cs +++ b/src/Avalonia.Svg/Svg.cs @@ -4,6 +4,7 @@ using Avalonia.Media; using Avalonia.Metadata; using ShimSkiaSharp; +using Svg.Model; namespace Avalonia.Svg; @@ -198,7 +199,7 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang } } - private void LoadFromPath(string? path, Dictionary? entities = null) + private void LoadFromPath(string? path, SvgParameters? entities = null) { _picture = default; _avaloniaPicture?.Dispose(); diff --git a/src/Avalonia.Svg/SvgSource.cs b/src/Avalonia.Svg/SvgSource.cs index be1f66b74..b55c070c9 100644 --- a/src/Avalonia.Svg/SvgSource.cs +++ b/src/Avalonia.Svg/SvgSource.cs @@ -6,6 +6,7 @@ using System.Net.Http; using Avalonia.Platform; using ShimSkiaSharp; +using Svg.Model; using SM = Svg.Model; using SP = Svg.Model; @@ -28,7 +29,7 @@ public class SvgSource /// The base uri. /// The svg entities. /// The svg picture. - public static SKPicture? LoadPicture(string path, Uri? baseUri, Dictionary? entities = null) + public static SKPicture? LoadPicture(string path, Uri? baseUri, SvgParameters? entities = null) { if (File.Exists(path)) { @@ -82,7 +83,7 @@ public class SvgSource /// The base uri. /// The svg entities. /// The svg source. - public static SvgSource Load(string path, Uri? baseUri, Dictionary? entities = null) + public static SvgSource Load(string path, Uri? baseUri, SvgParameters? entities = null) { return new() { Picture = LoadPicture(path, baseUri, entities) }; } @@ -93,7 +94,7 @@ public static SvgSource Load(string path, Uri? baseUri, DictionaryThe svg stream. /// The svg entities. /// The svg picture. - public static SKPicture? LoadPicture(Stream stream, Dictionary? entities = null) + public static SKPicture? LoadPicture(Stream stream, SvgParameters? entities = null) { var document = SM.SvgExtensions.Open(stream, entities); return document is { } ? SM.SvgExtensions.ToModel(document, s_assetLoader, out _, out _) : default; @@ -105,7 +106,7 @@ public static SvgSource Load(string path, Uri? baseUri, DictionaryThe svg stream. /// The svg entities. /// The svg source. - public static SvgSource Load(Stream stream, Dictionary? entities = null) + public static SvgSource Load(Stream stream, SvgParameters? entities = null) { return new() { Picture = LoadPicture(stream, entities) }; } diff --git a/src/Svg.Model/SvgExtensions.IO.cs b/src/Svg.Model/SvgExtensions.IO.cs index 2bdc9d800..6cf8283cf 100644 --- a/src/Svg.Model/SvgExtensions.IO.cs +++ b/src/Svg.Model/SvgExtensions.IO.cs @@ -273,12 +273,12 @@ internal static SvgDocument LoadSvgz(System.IO.Stream stream, Uri baseUri) return picture; } - public static SvgDocument? OpenSvg(string path, Dictionary? entities = null) + public static SvgDocument? OpenSvg(string path, SvgParameters? entities = null) { - return SvgDocument.Open(path, entities); + return SvgDocument.Open(path, entities?.ParserEntities, entities?.CSS); } - public static SvgDocument? OpenSvgz(string path, Dictionary? entities = null) + public static SvgDocument? OpenSvgz(string path, SvgParameters? entities = null) { using var fileStream = System.IO.File.OpenRead(path); using var gzipStream = new GZipStream(fileStream, CompressionMode.Decompress); @@ -290,7 +290,7 @@ internal static SvgDocument LoadSvgz(System.IO.Stream stream, Uri baseUri) return Open(memoryStream, entities); } - public static SvgDocument? Open(string path, Dictionary? entities = null) + public static SvgDocument? Open(string path, SvgParameters? entities = null) { var extension = System.IO.Path.GetExtension(path); return extension.ToLower() switch @@ -301,9 +301,9 @@ internal static SvgDocument LoadSvgz(System.IO.Stream stream, Uri baseUri) }; } - public static SvgDocument? Open(System.IO.Stream stream, Dictionary? entities = null) + public static SvgDocument? Open(System.IO.Stream stream, SvgParameters? entities = null) { - return SvgDocument.Open(stream, entities); + return SvgDocument.Open(stream, entities?.ParserEntities, entities?.CSS); } public static SvgDocument? FromSvg(string svg) diff --git a/src/Svg.Model/SvgParameters.cs b/src/Svg.Model/SvgParameters.cs new file mode 100644 index 000000000..c15e0cee9 --- /dev/null +++ b/src/Svg.Model/SvgParameters.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; + +namespace Svg.Model +{ + public class SvgParameters + { + public Dictionary? ParserEntities { get; set; } + public string? CSS { get; set; } + + } +} diff --git a/src/Svg.Skia/SKSvg.Model.cs b/src/Svg.Skia/SKSvg.Model.cs index 115c19a22..84ed3be27 100644 --- a/src/Svg.Skia/SKSvg.Model.cs +++ b/src/Svg.Skia/SKSvg.Model.cs @@ -4,12 +4,13 @@ using System.Xml; using Svg.Model.Drawables; using ShimSkiaSharp; +using System.IO; namespace Svg.Skia; public class SKSvg : IDisposable { - public static SKSvg CreateFromStream(System.IO.Stream stream, Dictionary? entities = null) + public static SKSvg CreateFromStream(System.IO.Stream stream, SvgParameters? entities = null) { var skSvg = new SKSvg(); skSvg.Load(stream, entities); @@ -18,7 +19,7 @@ public static SKSvg CreateFromStream(System.IO.Stream stream, Dictionary CreateFromStream(stream, null); - public static SKSvg CreateFromFile(string path, Dictionary? entities = null) + public static SKSvg CreateFromFile(string path, SvgParameters? entities = null) { var skSvg = new SKSvg(); skSvg.Load(path, entities); @@ -89,6 +90,11 @@ public static void Draw(SkiaSharp.SKCanvas skCanvas, string path, SkiaModel skia public SkiaSharp.SKPicture? Picture { get; private set; } + internal string Path { get; set; } + + public SvgParameters? Entities { get; set; } + + System.IO.Stream? Stream { get; set; } public SKSvg() { Settings = new SKSvgSettings(); @@ -96,9 +102,18 @@ public SKSvg() AssetLoader = new SkiaAssetLoader(SkiaModel); } - public SkiaSharp.SKPicture? Load(System.IO.Stream stream, Dictionary? entities = null) + public SkiaSharp.SKPicture? Load(System.IO.Stream stream, SvgParameters? entities = null) { Reset(); + if (Stream != stream) + { + Stream?.Dispose(); + Stream = new MemoryStream(); + stream.CopyTo(Stream); + } + Path = null; + Entities = entities; + Stream.Position = 0; var svgDocument = SvgExtensions.Open(stream, entities); if (svgDocument is { }) { @@ -109,12 +124,28 @@ public SKSvg() } return null; } + public SkiaSharp.SKPicture? ReLoad(SvgParameters? entities) + { + Reset(); + Entities = entities; + if (Stream == null) + return Load(Path, entities); + else + { + Stream.Position = 0; + return Load(Stream, entities); + } + } public SkiaSharp.SKPicture? Load(System.IO.Stream stream) => Load(stream, null); - public SkiaSharp.SKPicture? Load(string path, Dictionary? entities = null) + public SkiaSharp.SKPicture? Load(string path, SvgParameters? entities = null) { Reset(); + Path = path; + Entities = entities; + Stream?.Dispose(); + Stream = null; var svgDocument = SvgExtensions.Open(path, entities); if (svgDocument is { }) { @@ -194,10 +225,12 @@ private void Reset() Drawable = null; Picture?.Dispose(); Picture = null; + } public void Dispose() { Reset(); + Stream?.Dispose(); } } From efa1a83f88134a988b4ca33d7dcaa438e0fdef7c Mon Sep 17 00:00:00 2001 From: Kalachik Roman Date: Mon, 8 Jan 2024 22:47:27 +0300 Subject: [PATCH 02/24] fixed typo --- src/Svg.Skia/SKSvg.Model.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Svg.Skia/SKSvg.Model.cs b/src/Svg.Skia/SKSvg.Model.cs index 84ed3be27..e69c74147 100644 --- a/src/Svg.Skia/SKSvg.Model.cs +++ b/src/Svg.Skia/SKSvg.Model.cs @@ -114,7 +114,7 @@ public SKSvg() Path = null; Entities = entities; Stream.Position = 0; - var svgDocument = SvgExtensions.Open(stream, entities); + var svgDocument = SvgExtensions.Open(Stream, entities); if (svgDocument is { }) { Model = SvgExtensions.ToModel(svgDocument, AssetLoader, out var drawable, out _); From 6e93bdcea63723418bb45453a3628ceb9f055882 Mon Sep 17 00:00:00 2001 From: Kalachik Roman Date: Mon, 8 Jan 2024 23:16:16 +0300 Subject: [PATCH 03/24] added example --- Svg.Skia.sln | 43 +- samples/AvaloniaSvgCSSSample/App.axaml | 9 + samples/AvaloniaSvgCSSSample/App.axaml.cs | 23 + .../AvaloniaSvgCSSSample/Assets/__tiger.svg | 1978 +++++++++++++++++ .../AvaloniaSvgCSSSample.csproj | 24 + samples/AvaloniaSvgCSSSample/MainWindow.axaml | 32 + .../AvaloniaSvgCSSSample/MainWindow.axaml.cs | 25 + samples/AvaloniaSvgCSSSample/Program.cs | 24 + 8 files changed, 2140 insertions(+), 18 deletions(-) create mode 100644 samples/AvaloniaSvgCSSSample/App.axaml create mode 100644 samples/AvaloniaSvgCSSSample/App.axaml.cs create mode 100644 samples/AvaloniaSvgCSSSample/Assets/__tiger.svg create mode 100644 samples/AvaloniaSvgCSSSample/AvaloniaSvgCSSSample.csproj create mode 100644 samples/AvaloniaSvgCSSSample/MainWindow.axaml create mode 100644 samples/AvaloniaSvgCSSSample/MainWindow.axaml.cs create mode 100644 samples/AvaloniaSvgCSSSample/Program.cs diff --git a/Svg.Skia.sln b/Svg.Skia.sln index 046bc01a6..6097a4235 100644 --- a/Svg.Skia.sln +++ b/Svg.Skia.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29102.190 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.34330.188 MinimumVisualStudioVersion = 15.0.26124.0 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Svg.Skia", "src\Svg.Skia\Svg.Skia.csproj", "{3E4E8B64-FF8F-4B07-AE82-81E5FC2DDE59}" EndProject @@ -10,11 +10,11 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{32B4A27D-6FC0-498C-9AD8-5510ACF2C4A1}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig + azure-pipelines.yml = azure-pipelines.yml build.ps1 = build.ps1 build.sh = build.sh global.json = global.json build\svg.skia.public.snk = build\svg.skia.public.snk - azure-pipelines.yml = azure-pipelines.yml EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{8F2FA341-A87B-4BF8-986F-95A123073091}" @@ -45,6 +45,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "props", "props", "{5BFEF4F8 build\Avalonia.props = build\Avalonia.props build\Avalonia.ReactiveUI.props = build\Avalonia.ReactiveUI.props build\Avalonia.Skia.props = build\Avalonia.Skia.props + build\Avalonia.Themes.Fluent.props = build\Avalonia.Themes.Fluent.props + build\Avalonia.Web.props = build\Avalonia.Web.props build\Base.props = build\Base.props build\HarfBuzzSharp.NativeAssets.Linux.props = build\HarfBuzzSharp.NativeAssets.Linux.props build\Newtonsoft.Json.props = build\Newtonsoft.Json.props @@ -54,12 +56,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "props", "props", "{5BFEF4F8 build\SkiaSharp.HarfBuzz.props = build\SkiaSharp.HarfBuzz.props build\SkiaSharp.Linux.props = build\SkiaSharp.Linux.props build\SkiaSharp.props = build\SkiaSharp.props + build\SourceLink.props = build\SourceLink.props build\Svg.props = build\Svg.props build\System.CommandLine.props = build\System.CommandLine.props build\XUnit.props = build\XUnit.props - build\SourceLink.props = build\SourceLink.props - build\Avalonia.Themes.Fluent.props = build\Avalonia.Themes.Fluent.props - build\Avalonia.Web.props = build\Avalonia.Web.props EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{7863AE7D-FF68-45BF-BA68-6FA0E5604CB7}" @@ -84,32 +84,34 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.Skia", "s EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Svg.Generators", "externals\SVG\Generators\Svg.Generators.csproj", "{AF8AEF5B-0664-4106-9BD3-389758F39B12}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApp", "samples\TestApp\TestApp.csproj", "{B4BC7C90-09C3-46CB-B8B1-0450CC7EAAB0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestApp", "samples\TestApp\TestApp.csproj", "{B4BC7C90-09C3-46CB-B8B1-0450CC7EAAB0}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "externals", "externals", "{C5FFCF4B-86DC-453E-8006-44EE9EEFEE39}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShimSkiaSharp", "src\ShimSkiaSharp\ShimSkiaSharp.csproj", "{6D2786A1-F110-4448-9CBF-D3CC9D803F31}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ShimSkiaSharp", "src\ShimSkiaSharp\ShimSkiaSharp.csproj", "{6D2786A1-F110-4448-9CBF-D3CC9D803F31}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AvaloniaControlsSample", "samples\AvaloniaControlsSample\AvaloniaControlsSample.csproj", "{BE25FC07-9A8C-4494-A6AE-F2561CF89010}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AvaloniaControlsSample", "samples\AvaloniaControlsSample\AvaloniaControlsSample.csproj", "{BE25FC07-9A8C-4494-A6AE-F2561CF89010}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "github", "github", "{380E7565-C6FF-45C1-A683-E4E1FC744DCC}" ProjectSection(SolutionItems) = preProject .github\workflows\build.yml = .github\workflows\build.yml EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "svgc", "samples\svgc\svgc.csproj", "{6B758C64-5BDA-4842-B0F5-A124D65D83F9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "svgc", "samples\svgc\svgc.csproj", "{6B758C64-5BDA-4842-B0F5-A124D65D83F9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Svg.SourceGenerator.Skia.Sample", "samples\Svg.SourceGenerator.Skia.Sample\Svg.SourceGenerator.Skia.Sample.csproj", "{89FD53A6-8DE6-4733-AF87-EF8660C8EBD3}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Svg.SourceGenerator.Skia.Sample", "samples\Svg.SourceGenerator.Skia.Sample\Svg.SourceGenerator.Skia.Sample.csproj", "{89FD53A6-8DE6-4733-AF87-EF8660C8EBD3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Svg.Skia.Converter", "samples\Svg.Skia.Converter\Svg.Skia.Converter.csproj", "{68F9524C-BA9F-451D-881D-6192EF2FAAAA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Svg.Skia.Converter", "samples\Svg.Skia.Converter\Svg.Skia.Converter.csproj", "{68F9524C-BA9F-451D-881D-6192EF2FAAAA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AvaloniaSvgSample", "samples\AvaloniaSvgSample\AvaloniaSvgSample.csproj", "{C3BCD2D5-DFC2-43C1-922D-2E76E6AEF122}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AvaloniaSvgSample", "samples\AvaloniaSvgSample\AvaloniaSvgSample.csproj", "{C3BCD2D5-DFC2-43C1-922D-2E76E6AEF122}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AvaloniaSKPictureImageSample", "samples\AvaloniaSKPictureImageSample\AvaloniaSKPictureImageSample.csproj", "{5A3BC87E-F3F6-4F89-B412-1324CCA2C32B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AvaloniaSKPictureImageSample", "samples\AvaloniaSKPictureImageSample\AvaloniaSKPictureImageSample.csproj", "{5A3BC87E-F3F6-4F89-B412-1324CCA2C32B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Svg.SourceGenerator.Skia", "src\Svg.SourceGenerator.Skia\Svg.SourceGenerator.Skia.csproj", "{3049C672-8A3F-4FE4-9973-515B8323B546}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Svg.SourceGenerator.Skia", "src\Svg.SourceGenerator.Skia\Svg.SourceGenerator.Skia.csproj", "{3049C672-8A3F-4FE4-9973-515B8323B546}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Svg", "src\Avalonia.Svg\Avalonia.Svg.csproj", "{B742F260-0EC6-4805-AE9F-987818CE3CF4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Svg", "src\Avalonia.Svg\Avalonia.Svg.csproj", "{B742F260-0EC6-4805-AE9F-987818CE3CF4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AvaloniaSvgCSSSample", "samples\AvaloniaSvgCSSSample\AvaloniaSvgCSSSample.csproj", "{8A938DC2-1634-4387-BAB3-69F871D54FB5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -201,6 +203,10 @@ Global {B742F260-0EC6-4805-AE9F-987818CE3CF4}.Debug|Any CPU.Build.0 = Debug|Any CPU {B742F260-0EC6-4805-AE9F-987818CE3CF4}.Release|Any CPU.ActiveCfg = Release|Any CPU {B742F260-0EC6-4805-AE9F-987818CE3CF4}.Release|Any CPU.Build.0 = Release|Any CPU + {8A938DC2-1634-4387-BAB3-69F871D54FB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8A938DC2-1634-4387-BAB3-69F871D54FB5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8A938DC2-1634-4387-BAB3-69F871D54FB5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8A938DC2-1634-4387-BAB3-69F871D54FB5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -214,13 +220,13 @@ Global {1BD5FA09-D543-4315-99A6-81E9DD8746EC} = {7863AE7D-FF68-45BF-BA68-6FA0E5604CB7} {8BAAB509-6073-4D68-9F16-EA28986839B1} = {4C42912C-9F8C-43D9-A4B5-4427F7EC8F18} {81724F00-B7C3-4E25-B473-C7433BABDC81} = {B65D5B3A-77BE-4AFF-B502-A136B9C932F8} + {CFA46E73-0050-4C57-85CE-6C5868A2483C} = {C5FFCF4B-86DC-453E-8006-44EE9EEFEE39} {D4467DCA-494D-4C32-9525-4A9713221A53} = {7863AE7D-FF68-45BF-BA68-6FA0E5604CB7} {4C970B2C-6C96-445B-B80B-4EFBF803FD5F} = {4C42912C-9F8C-43D9-A4B5-4427F7EC8F18} {29F59C87-EAE6-4DD3-8666-B79BFAF6B34D} = {4C42912C-9F8C-43D9-A4B5-4427F7EC8F18} {223B7A5A-E263-4D40-9A6E-FE31EAE92F45} = {4C42912C-9F8C-43D9-A4B5-4427F7EC8F18} - {B4BC7C90-09C3-46CB-B8B1-0450CC7EAAB0} = {B65D5B3A-77BE-4AFF-B502-A136B9C932F8} - {CFA46E73-0050-4C57-85CE-6C5868A2483C} = {C5FFCF4B-86DC-453E-8006-44EE9EEFEE39} {AF8AEF5B-0664-4106-9BD3-389758F39B12} = {C5FFCF4B-86DC-453E-8006-44EE9EEFEE39} + {B4BC7C90-09C3-46CB-B8B1-0450CC7EAAB0} = {B65D5B3A-77BE-4AFF-B502-A136B9C932F8} {6D2786A1-F110-4448-9CBF-D3CC9D803F31} = {4C42912C-9F8C-43D9-A4B5-4427F7EC8F18} {BE25FC07-9A8C-4494-A6AE-F2561CF89010} = {B65D5B3A-77BE-4AFF-B502-A136B9C932F8} {380E7565-C6FF-45C1-A683-E4E1FC744DCC} = {32B4A27D-6FC0-498C-9AD8-5510ACF2C4A1} @@ -231,6 +237,7 @@ Global {5A3BC87E-F3F6-4F89-B412-1324CCA2C32B} = {B65D5B3A-77BE-4AFF-B502-A136B9C932F8} {3049C672-8A3F-4FE4-9973-515B8323B546} = {4C42912C-9F8C-43D9-A4B5-4427F7EC8F18} {B742F260-0EC6-4805-AE9F-987818CE3CF4} = {4C42912C-9F8C-43D9-A4B5-4427F7EC8F18} + {8A938DC2-1634-4387-BAB3-69F871D54FB5} = {B65D5B3A-77BE-4AFF-B502-A136B9C932F8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {12D5E557-A27B-4FB2-83A3-4AC75B04B22C} diff --git a/samples/AvaloniaSvgCSSSample/App.axaml b/samples/AvaloniaSvgCSSSample/App.axaml new file mode 100644 index 000000000..352b0dda0 --- /dev/null +++ b/samples/AvaloniaSvgCSSSample/App.axaml @@ -0,0 +1,9 @@ + + + + + diff --git a/samples/AvaloniaSvgCSSSample/App.axaml.cs b/samples/AvaloniaSvgCSSSample/App.axaml.cs new file mode 100644 index 000000000..6ac8e676b --- /dev/null +++ b/samples/AvaloniaSvgCSSSample/App.axaml.cs @@ -0,0 +1,23 @@ +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; + +namespace AvaloniaSvgCSSSample; + +public class App : Application +{ + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + } + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + desktop.MainWindow = new MainWindow(); + } + + base.OnFrameworkInitializationCompleted(); + } +} \ No newline at end of file diff --git a/samples/AvaloniaSvgCSSSample/Assets/__tiger.svg b/samples/AvaloniaSvgCSSSample/Assets/__tiger.svg new file mode 100644 index 000000000..11abcd061 --- /dev/null +++ b/samples/AvaloniaSvgCSSSample/Assets/__tiger.svg @@ -0,0 +1,1978 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaSvgCSSSample/AvaloniaSvgCSSSample.csproj b/samples/AvaloniaSvgCSSSample/AvaloniaSvgCSSSample.csproj new file mode 100644 index 000000000..acd68a662 --- /dev/null +++ b/samples/AvaloniaSvgCSSSample/AvaloniaSvgCSSSample.csproj @@ -0,0 +1,24 @@ + + + + WinExe + net8.0 + latest + False + disable + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaSvgCSSSample/MainWindow.axaml b/samples/AvaloniaSvgCSSSample/MainWindow.axaml new file mode 100644 index 000000000..45980ce11 --- /dev/null +++ b/samples/AvaloniaSvgCSSSample/MainWindow.axaml @@ -0,0 +1,32 @@ + + + + Hover mouse on a tiger to apply SVG palette for hover state + + + + + diff --git a/samples/AvaloniaSvgCSSSample/MainWindow.axaml.cs b/samples/AvaloniaSvgCSSSample/MainWindow.axaml.cs new file mode 100644 index 000000000..98339095b --- /dev/null +++ b/samples/AvaloniaSvgCSSSample/MainWindow.axaml.cs @@ -0,0 +1,25 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Media; +using System; +using System.Linq; + +namespace AvaloniaSvgCSSSample; + +public partial class MainWindow : Window +{ + public MainWindow() + { + InitializeComponent(); +#if DEBUG + this.AttachDevTools(); +#endif + ApplyButton.AddHandler(Button.ClickEvent, OnApply); + } + + private void OnApply(object sender, EventArgs e) + { + this.SetValue(Avalonia.Svg.Skia.Svg.CSSProperty, ".Black { fill: #AAAAFF; }"); + } +} diff --git a/samples/AvaloniaSvgCSSSample/Program.cs b/samples/AvaloniaSvgCSSSample/Program.cs new file mode 100644 index 000000000..e36ac4979 --- /dev/null +++ b/samples/AvaloniaSvgCSSSample/Program.cs @@ -0,0 +1,24 @@ +using System; +using Avalonia; +using Avalonia.Svg.Skia; + +namespace AvaloniaSvgCSSSample; + +internal class Program +{ + [STAThread] + public static void Main(string[] args) => BuildAvaloniaApp() + .StartWithClassicDesktopLifetime(args); + + public static AppBuilder BuildAvaloniaApp() + { + GC.KeepAlive(typeof(SvgImageExtension).Assembly); + GC.KeepAlive(typeof(Avalonia.Svg.Skia.Svg).Assembly); + return AppBuilder.Configure() + .UsePlatformDetect() + .With(new X11PlatformOptions + { + }) + .LogToTrace(); + } +} From 79a7a92c210ddb1d5605da982c65b83fbbb3498d Mon Sep 17 00:00:00 2001 From: Kalachik Roman Date: Thu, 11 Jan 2024 17:51:10 +0300 Subject: [PATCH 04/24] merge master --- .gitmodules | 3 +- externals/SVG | 2 +- src/Avalonia.Svg.Skia/Svg.cs | 37 +++++++++++++++++-- src/Avalonia.Svg.Skia/SvgImage.cs | 22 ++++++++++++ src/Avalonia.Svg.Skia/SvgImageExtension.cs | 32 ++++++++++++++--- src/Avalonia.Svg.Skia/SvgSource.cs | 20 ++++++++--- src/Avalonia.Svg/Svg.cs | 3 +- src/Avalonia.Svg/SvgSource.cs | 9 ++--- src/Svg.Model/SvgExtensions.IO.cs | 12 +++---- src/Svg.Model/SvgParameters.cs | 12 +++++++ src/Svg.Skia/SKSvg.Model.cs | 41 +++++++++++++++++++--- 11 files changed, 164 insertions(+), 29 deletions(-) create mode 100644 src/Svg.Model/SvgParameters.cs diff --git a/.gitmodules b/.gitmodules index bfb843304..f3714fb2c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,7 @@ [submodule "externals/SVG"] path = externals/SVG - url = https://github.com/wieslawsoltes/SVG.git + url = https://github.com/RomanKalachik/SVG.git + branch = master [submodule "externals/resvg"] path = externals/resvg url = https://github.com/wieslawsoltes/resvg.git diff --git a/externals/SVG b/externals/SVG index e0bfdc386..5528cee1c 160000 --- a/externals/SVG +++ b/externals/SVG @@ -1 +1 @@ -Subproject commit e0bfdc3868bae7b5048f78a8f7d1d7fd82afdc05 +Subproject commit 5528cee1c04c55b75f7b2c1159266e6664f74dec diff --git a/src/Avalonia.Svg.Skia/Svg.cs b/src/Avalonia.Svg.Skia/Svg.cs index 210599eaa..5dc003d12 100644 --- a/src/Avalonia.Svg.Skia/Svg.cs +++ b/src/Avalonia.Svg.Skia/Svg.cs @@ -5,6 +5,7 @@ using Avalonia.Media; using Avalonia.Metadata; using ShimSkiaSharp; +using Svg.Model; using Svg.Skia; namespace Avalonia.Svg.Skia; @@ -19,6 +20,18 @@ public class Svg : Control private bool _enableCache; private Dictionary? _cache; + /// + /// Defines CSS for global icnos theming + /// + public static readonly AttachedProperty CSSProperty = + AvaloniaProperty.RegisterAttached("CSS", inherits: true); + + /// + /// Defines CSS for control states like Hover, Pressed, Enabled + /// + public static readonly AttachedProperty CSSCurrentProperty = + AvaloniaProperty.RegisterAttached("CSSCurrent", inherits: true); + /// /// Defines the property. /// @@ -63,6 +76,13 @@ public string? Path set => SetValue(PathProperty, value); } + public static string? GetCSS(AvaloniaObject element) => element.GetValue(CSSProperty); + public static void SetCSS(AvaloniaObject element, string? value) => element.SetValue(CSSProperty, value); + + public static string? GetCSSCurrent(AvaloniaObject element) => element.GetValue(CSSCurrentProperty); + public static void SetCSSCurrent(AvaloniaObject element, string? value) => element.SetValue(CSSCurrentProperty, value); + + /// /// Gets or sets the Svg source. /// @@ -118,6 +138,17 @@ static Svg() { AffectsRender(PathProperty, SourceProperty, StretchProperty, StretchDirectionProperty); AffectsMeasure(PathProperty, SourceProperty, StretchProperty, StretchDirectionProperty); + + CSSProperty.Changed.AddClassHandler(OnCSSPropertyAttachedPropertyChanged); + CSSCurrentProperty.Changed.AddClassHandler(OnCSSPropertyAttachedPropertyChanged); + + } + + private static void OnCSSPropertyAttachedPropertyChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e) + { + var control = d as Control; + if (control != null) + control.InvalidateVisual(); } /// @@ -212,10 +243,10 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang { base.OnPropertyChanged(change); - if (change.Property == PathProperty) + if (change.Property == PathProperty || change.Property == CSSProperty || change.Property == CSSCurrentProperty) { var path = change.GetNewValue(); - LoadFromPath(path); + LoadFromPath(path, new SvgParameters() { CSS = SvgSource.CombineCSS(GetCSS(this), GetCSSCurrent(this))}); InvalidateVisual(); } @@ -240,7 +271,7 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang } } - private void LoadFromPath(string? path, Dictionary? entities = null) + private void LoadFromPath(string? path, SvgParameters? entities = null) { if (path is null) { diff --git a/src/Avalonia.Svg.Skia/SvgImage.cs b/src/Avalonia.Svg.Skia/SvgImage.cs index ad26666b1..31a80b552 100644 --- a/src/Avalonia.Svg.Skia/SvgImage.cs +++ b/src/Avalonia.Svg.Skia/SvgImage.cs @@ -1,5 +1,6 @@ using Avalonia.Media; using Avalonia.Metadata; +using Svg.Model; namespace Avalonia.Svg.Skia; @@ -13,6 +14,12 @@ public class SvgImage : AvaloniaObject, IImage /// public static readonly StyledProperty SourceProperty = AvaloniaProperty.Register(nameof(Source)); + + public static readonly StyledProperty CSSProperty = + AvaloniaProperty.Register(nameof(CSS)); + + public static readonly StyledProperty CSSCurrentProperty = + AvaloniaProperty.Register(nameof(CSSCurrent)); /// /// Gets or sets the content. @@ -23,6 +30,18 @@ public SvgSource? Source get => GetValue(SourceProperty); set => SetValue(SourceProperty, value); } + + public string CSS + { + get => GetValue(CSSProperty); + set => SetValue(CSSProperty, value); + } + + public string CSSCurrent + { + get => GetValue(CSSCurrentProperty); + set => SetValue(CSSCurrentProperty, value); + } /// public Size Size => @@ -32,6 +51,9 @@ public SvgSource? Source void IImage.Draw(DrawingContext context, Rect sourceRect, Rect destRect) { var source = Source; + var css = SvgSource.CombineCSS(CSS, CSSCurrent); + if (source?.Entities?.CSS != css) + source?.ReLoad(new SvgParameters() { CSS = css }); if (source?.Picture is null) { return; diff --git a/src/Avalonia.Svg.Skia/SvgImageExtension.cs b/src/Avalonia.Svg.Skia/SvgImageExtension.cs index 2921bcb15..6ecdd1657 100644 --- a/src/Avalonia.Svg.Skia/SvgImageExtension.cs +++ b/src/Avalonia.Svg.Skia/SvgImageExtension.cs @@ -2,6 +2,7 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; using Avalonia.Media; +using Svg.Model; namespace Avalonia.Svg.Skia; @@ -27,16 +28,37 @@ public override object ProvideValue(IServiceProvider serviceProvider) var path = Path; var context = (IUriContext)serviceProvider.GetService(typeof(IUriContext))!; var baseUri = context.BaseUri; - var source = SvgSource.Load(path, baseUri); var target = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget))!; + var targetControl = target.TargetObject as Control; + var iimage = ProvideValue(path, baseUri, targetControl); if (target.TargetProperty is AvaloniaProperty property) { if (property.PropertyType == typeof(IImage)) { - return new SvgImage { Source = source }; + return iimage; } - return new Image { Source = new SvgImage { Source = source } }; + return new Image { Source = iimage }; } - return new SvgImage { Source = source }; + return iimage; } -} \ No newline at end of file + + public static IImage ProvideValue(string path, Uri baseUri, Control targetControl) + { + var css = targetControl != null ? Svg.GetCSS(targetControl) : null; + var cssCurrent = targetControl != null ? Svg.GetCSSCurrent(targetControl) : null; + var source = SvgSource.Load(path, baseUri, new SvgParameters() { CSS = SvgSource.CombineCSS(css, cssCurrent) }); + return CreateSvgImage(source, targetControl); + } + + public static SvgImage CreateSvgImage(SvgSource source, Control? targetControl) + { + var result = new SvgImage(); + result.Source = source; + if (targetControl != null) + { + result.Bind(SvgImage.CSSProperty, targetControl.GetObservable(Svg.CSSProperty).ToBinding()); + result.Bind(SvgImage.CSSCurrentProperty, targetControl.GetObservable(Svg.CSSCurrentProperty).ToBinding()); + } + return result; + } +} diff --git a/src/Avalonia.Svg.Skia/SvgSource.cs b/src/Avalonia.Svg.Skia/SvgSource.cs index 706341550..1694a2d50 100644 --- a/src/Avalonia.Svg.Skia/SvgSource.cs +++ b/src/Avalonia.Svg.Skia/SvgSource.cs @@ -5,6 +5,7 @@ using System.IO; using System.Net.Http; using Svg; +using Svg.Model; using Svg.Skia; namespace Avalonia.Svg.Skia; @@ -15,6 +16,13 @@ namespace Avalonia.Svg.Skia; [TypeConverter(typeof(SvgSourceTypeConverter))] public class SvgSource : SKSvg { + public static bool ThrowExceptionIfResourceMissing { get; set; } = false; + public static T? ResourceMissing(string path) where T : SKSvg, new() + { + if (ThrowExceptionIfResourceMissing) + throw new ArgumentException($"Missing resource: {path}"); + return default; + } /// t /// Loads svg source from file or resource. /// @@ -22,7 +30,7 @@ public class SvgSource : SKSvg /// The base uri. /// The svg entities. /// The svg source. - public static T? Load(string path, Uri? baseUri, Dictionary? entities = null) where T : SKSvg, new() + public static T? Load(string path, Uri? baseUri, SvgParameters? entities = null) where T : SKSvg, new() { if (File.Exists(path)) { @@ -50,7 +58,7 @@ public class SvgSource : SKSvg Debug.WriteLine(e.ToString()); } - return default; + return ResourceMissing(path); } var uri = path.StartsWith("/") ? new Uri(path, UriKind.Relative) : new Uri(path, UriKind.RelativeOrAbsolute); @@ -65,13 +73,17 @@ public class SvgSource : SKSvg var stream = Platform.AssetLoader.Open(uri, baseUri); if (stream is null) { - return default; + return ResourceMissing(path); } var source = new T(); source.Load(stream, entities); return source; } } + public static string CombineCSS(string? css, string? cssCurrent) + { + return $"{css} {cssCurrent}"; // The last entry has higher prioriry, so cssCurrent can overrride styles + } /// /// Loads svg source from svg source. @@ -91,7 +103,7 @@ public class SvgSource : SKSvg /// The svg stream. /// The svg entities. /// The svg source. - public static T? LoadFromStream(Stream stream, Dictionary? entities = null) where T : SKSvg, new() + public static T? LoadFromStream(Stream stream, SvgParameters? entities = null) where T : SKSvg, new() { var skSvg = new T(); skSvg.Load(stream, entities); diff --git a/src/Avalonia.Svg/Svg.cs b/src/Avalonia.Svg/Svg.cs index d7cc6f034..a802b8278 100644 --- a/src/Avalonia.Svg/Svg.cs +++ b/src/Avalonia.Svg/Svg.cs @@ -4,6 +4,7 @@ using Avalonia.Media; using Avalonia.Metadata; using ShimSkiaSharp; +using Svg.Model; namespace Avalonia.Svg; @@ -198,7 +199,7 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang } } - private void LoadFromPath(string? path, Dictionary? entities = null) + private void LoadFromPath(string? path, SvgParameters? entities = null) { _picture = default; _avaloniaPicture?.Dispose(); diff --git a/src/Avalonia.Svg/SvgSource.cs b/src/Avalonia.Svg/SvgSource.cs index be1f66b74..b55c070c9 100644 --- a/src/Avalonia.Svg/SvgSource.cs +++ b/src/Avalonia.Svg/SvgSource.cs @@ -6,6 +6,7 @@ using System.Net.Http; using Avalonia.Platform; using ShimSkiaSharp; +using Svg.Model; using SM = Svg.Model; using SP = Svg.Model; @@ -28,7 +29,7 @@ public class SvgSource /// The base uri. /// The svg entities. /// The svg picture. - public static SKPicture? LoadPicture(string path, Uri? baseUri, Dictionary? entities = null) + public static SKPicture? LoadPicture(string path, Uri? baseUri, SvgParameters? entities = null) { if (File.Exists(path)) { @@ -82,7 +83,7 @@ public class SvgSource /// The base uri. /// The svg entities. /// The svg source. - public static SvgSource Load(string path, Uri? baseUri, Dictionary? entities = null) + public static SvgSource Load(string path, Uri? baseUri, SvgParameters? entities = null) { return new() { Picture = LoadPicture(path, baseUri, entities) }; } @@ -93,7 +94,7 @@ public static SvgSource Load(string path, Uri? baseUri, DictionaryThe svg stream. /// The svg entities. /// The svg picture. - public static SKPicture? LoadPicture(Stream stream, Dictionary? entities = null) + public static SKPicture? LoadPicture(Stream stream, SvgParameters? entities = null) { var document = SM.SvgExtensions.Open(stream, entities); return document is { } ? SM.SvgExtensions.ToModel(document, s_assetLoader, out _, out _) : default; @@ -105,7 +106,7 @@ public static SvgSource Load(string path, Uri? baseUri, DictionaryThe svg stream. /// The svg entities. /// The svg source. - public static SvgSource Load(Stream stream, Dictionary? entities = null) + public static SvgSource Load(Stream stream, SvgParameters? entities = null) { return new() { Picture = LoadPicture(stream, entities) }; } diff --git a/src/Svg.Model/SvgExtensions.IO.cs b/src/Svg.Model/SvgExtensions.IO.cs index 2bdc9d800..6cf8283cf 100644 --- a/src/Svg.Model/SvgExtensions.IO.cs +++ b/src/Svg.Model/SvgExtensions.IO.cs @@ -273,12 +273,12 @@ internal static SvgDocument LoadSvgz(System.IO.Stream stream, Uri baseUri) return picture; } - public static SvgDocument? OpenSvg(string path, Dictionary? entities = null) + public static SvgDocument? OpenSvg(string path, SvgParameters? entities = null) { - return SvgDocument.Open(path, entities); + return SvgDocument.Open(path, entities?.ParserEntities, entities?.CSS); } - public static SvgDocument? OpenSvgz(string path, Dictionary? entities = null) + public static SvgDocument? OpenSvgz(string path, SvgParameters? entities = null) { using var fileStream = System.IO.File.OpenRead(path); using var gzipStream = new GZipStream(fileStream, CompressionMode.Decompress); @@ -290,7 +290,7 @@ internal static SvgDocument LoadSvgz(System.IO.Stream stream, Uri baseUri) return Open(memoryStream, entities); } - public static SvgDocument? Open(string path, Dictionary? entities = null) + public static SvgDocument? Open(string path, SvgParameters? entities = null) { var extension = System.IO.Path.GetExtension(path); return extension.ToLower() switch @@ -301,9 +301,9 @@ internal static SvgDocument LoadSvgz(System.IO.Stream stream, Uri baseUri) }; } - public static SvgDocument? Open(System.IO.Stream stream, Dictionary? entities = null) + public static SvgDocument? Open(System.IO.Stream stream, SvgParameters? entities = null) { - return SvgDocument.Open(stream, entities); + return SvgDocument.Open(stream, entities?.ParserEntities, entities?.CSS); } public static SvgDocument? FromSvg(string svg) diff --git a/src/Svg.Model/SvgParameters.cs b/src/Svg.Model/SvgParameters.cs new file mode 100644 index 000000000..c15e0cee9 --- /dev/null +++ b/src/Svg.Model/SvgParameters.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; + +namespace Svg.Model +{ + public class SvgParameters + { + public Dictionary? ParserEntities { get; set; } + public string? CSS { get; set; } + + } +} diff --git a/src/Svg.Skia/SKSvg.Model.cs b/src/Svg.Skia/SKSvg.Model.cs index 115c19a22..84ed3be27 100644 --- a/src/Svg.Skia/SKSvg.Model.cs +++ b/src/Svg.Skia/SKSvg.Model.cs @@ -4,12 +4,13 @@ using System.Xml; using Svg.Model.Drawables; using ShimSkiaSharp; +using System.IO; namespace Svg.Skia; public class SKSvg : IDisposable { - public static SKSvg CreateFromStream(System.IO.Stream stream, Dictionary? entities = null) + public static SKSvg CreateFromStream(System.IO.Stream stream, SvgParameters? entities = null) { var skSvg = new SKSvg(); skSvg.Load(stream, entities); @@ -18,7 +19,7 @@ public static SKSvg CreateFromStream(System.IO.Stream stream, Dictionary CreateFromStream(stream, null); - public static SKSvg CreateFromFile(string path, Dictionary? entities = null) + public static SKSvg CreateFromFile(string path, SvgParameters? entities = null) { var skSvg = new SKSvg(); skSvg.Load(path, entities); @@ -89,6 +90,11 @@ public static void Draw(SkiaSharp.SKCanvas skCanvas, string path, SkiaModel skia public SkiaSharp.SKPicture? Picture { get; private set; } + internal string Path { get; set; } + + public SvgParameters? Entities { get; set; } + + System.IO.Stream? Stream { get; set; } public SKSvg() { Settings = new SKSvgSettings(); @@ -96,9 +102,18 @@ public SKSvg() AssetLoader = new SkiaAssetLoader(SkiaModel); } - public SkiaSharp.SKPicture? Load(System.IO.Stream stream, Dictionary? entities = null) + public SkiaSharp.SKPicture? Load(System.IO.Stream stream, SvgParameters? entities = null) { Reset(); + if (Stream != stream) + { + Stream?.Dispose(); + Stream = new MemoryStream(); + stream.CopyTo(Stream); + } + Path = null; + Entities = entities; + Stream.Position = 0; var svgDocument = SvgExtensions.Open(stream, entities); if (svgDocument is { }) { @@ -109,12 +124,28 @@ public SKSvg() } return null; } + public SkiaSharp.SKPicture? ReLoad(SvgParameters? entities) + { + Reset(); + Entities = entities; + if (Stream == null) + return Load(Path, entities); + else + { + Stream.Position = 0; + return Load(Stream, entities); + } + } public SkiaSharp.SKPicture? Load(System.IO.Stream stream) => Load(stream, null); - public SkiaSharp.SKPicture? Load(string path, Dictionary? entities = null) + public SkiaSharp.SKPicture? Load(string path, SvgParameters? entities = null) { Reset(); + Path = path; + Entities = entities; + Stream?.Dispose(); + Stream = null; var svgDocument = SvgExtensions.Open(path, entities); if (svgDocument is { }) { @@ -194,10 +225,12 @@ private void Reset() Drawable = null; Picture?.Dispose(); Picture = null; + } public void Dispose() { Reset(); + Stream?.Dispose(); } } From 51b3d348da1ebd99b9394e1d5f4ceddcef188236 Mon Sep 17 00:00:00 2001 From: Kalachik Roman Date: Mon, 8 Jan 2024 23:16:16 +0300 Subject: [PATCH 05/24] added example --- Svg.Skia.sln | 43 +- samples/AvaloniaSvgCSSSample/App.axaml | 9 + samples/AvaloniaSvgCSSSample/App.axaml.cs | 23 + .../AvaloniaSvgCSSSample/Assets/__tiger.svg | 1978 +++++++++++++++++ .../AvaloniaSvgCSSSample.csproj | 24 + samples/AvaloniaSvgCSSSample/MainWindow.axaml | 32 + .../AvaloniaSvgCSSSample/MainWindow.axaml.cs | 25 + samples/AvaloniaSvgCSSSample/Program.cs | 24 + 8 files changed, 2140 insertions(+), 18 deletions(-) create mode 100644 samples/AvaloniaSvgCSSSample/App.axaml create mode 100644 samples/AvaloniaSvgCSSSample/App.axaml.cs create mode 100644 samples/AvaloniaSvgCSSSample/Assets/__tiger.svg create mode 100644 samples/AvaloniaSvgCSSSample/AvaloniaSvgCSSSample.csproj create mode 100644 samples/AvaloniaSvgCSSSample/MainWindow.axaml create mode 100644 samples/AvaloniaSvgCSSSample/MainWindow.axaml.cs create mode 100644 samples/AvaloniaSvgCSSSample/Program.cs diff --git a/Svg.Skia.sln b/Svg.Skia.sln index 046bc01a6..6097a4235 100644 --- a/Svg.Skia.sln +++ b/Svg.Skia.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29102.190 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.34330.188 MinimumVisualStudioVersion = 15.0.26124.0 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Svg.Skia", "src\Svg.Skia\Svg.Skia.csproj", "{3E4E8B64-FF8F-4B07-AE82-81E5FC2DDE59}" EndProject @@ -10,11 +10,11 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{32B4A27D-6FC0-498C-9AD8-5510ACF2C4A1}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig + azure-pipelines.yml = azure-pipelines.yml build.ps1 = build.ps1 build.sh = build.sh global.json = global.json build\svg.skia.public.snk = build\svg.skia.public.snk - azure-pipelines.yml = azure-pipelines.yml EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{8F2FA341-A87B-4BF8-986F-95A123073091}" @@ -45,6 +45,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "props", "props", "{5BFEF4F8 build\Avalonia.props = build\Avalonia.props build\Avalonia.ReactiveUI.props = build\Avalonia.ReactiveUI.props build\Avalonia.Skia.props = build\Avalonia.Skia.props + build\Avalonia.Themes.Fluent.props = build\Avalonia.Themes.Fluent.props + build\Avalonia.Web.props = build\Avalonia.Web.props build\Base.props = build\Base.props build\HarfBuzzSharp.NativeAssets.Linux.props = build\HarfBuzzSharp.NativeAssets.Linux.props build\Newtonsoft.Json.props = build\Newtonsoft.Json.props @@ -54,12 +56,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "props", "props", "{5BFEF4F8 build\SkiaSharp.HarfBuzz.props = build\SkiaSharp.HarfBuzz.props build\SkiaSharp.Linux.props = build\SkiaSharp.Linux.props build\SkiaSharp.props = build\SkiaSharp.props + build\SourceLink.props = build\SourceLink.props build\Svg.props = build\Svg.props build\System.CommandLine.props = build\System.CommandLine.props build\XUnit.props = build\XUnit.props - build\SourceLink.props = build\SourceLink.props - build\Avalonia.Themes.Fluent.props = build\Avalonia.Themes.Fluent.props - build\Avalonia.Web.props = build\Avalonia.Web.props EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{7863AE7D-FF68-45BF-BA68-6FA0E5604CB7}" @@ -84,32 +84,34 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.Skia", "s EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Svg.Generators", "externals\SVG\Generators\Svg.Generators.csproj", "{AF8AEF5B-0664-4106-9BD3-389758F39B12}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApp", "samples\TestApp\TestApp.csproj", "{B4BC7C90-09C3-46CB-B8B1-0450CC7EAAB0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestApp", "samples\TestApp\TestApp.csproj", "{B4BC7C90-09C3-46CB-B8B1-0450CC7EAAB0}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "externals", "externals", "{C5FFCF4B-86DC-453E-8006-44EE9EEFEE39}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShimSkiaSharp", "src\ShimSkiaSharp\ShimSkiaSharp.csproj", "{6D2786A1-F110-4448-9CBF-D3CC9D803F31}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ShimSkiaSharp", "src\ShimSkiaSharp\ShimSkiaSharp.csproj", "{6D2786A1-F110-4448-9CBF-D3CC9D803F31}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AvaloniaControlsSample", "samples\AvaloniaControlsSample\AvaloniaControlsSample.csproj", "{BE25FC07-9A8C-4494-A6AE-F2561CF89010}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AvaloniaControlsSample", "samples\AvaloniaControlsSample\AvaloniaControlsSample.csproj", "{BE25FC07-9A8C-4494-A6AE-F2561CF89010}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "github", "github", "{380E7565-C6FF-45C1-A683-E4E1FC744DCC}" ProjectSection(SolutionItems) = preProject .github\workflows\build.yml = .github\workflows\build.yml EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "svgc", "samples\svgc\svgc.csproj", "{6B758C64-5BDA-4842-B0F5-A124D65D83F9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "svgc", "samples\svgc\svgc.csproj", "{6B758C64-5BDA-4842-B0F5-A124D65D83F9}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Svg.SourceGenerator.Skia.Sample", "samples\Svg.SourceGenerator.Skia.Sample\Svg.SourceGenerator.Skia.Sample.csproj", "{89FD53A6-8DE6-4733-AF87-EF8660C8EBD3}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Svg.SourceGenerator.Skia.Sample", "samples\Svg.SourceGenerator.Skia.Sample\Svg.SourceGenerator.Skia.Sample.csproj", "{89FD53A6-8DE6-4733-AF87-EF8660C8EBD3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Svg.Skia.Converter", "samples\Svg.Skia.Converter\Svg.Skia.Converter.csproj", "{68F9524C-BA9F-451D-881D-6192EF2FAAAA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Svg.Skia.Converter", "samples\Svg.Skia.Converter\Svg.Skia.Converter.csproj", "{68F9524C-BA9F-451D-881D-6192EF2FAAAA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AvaloniaSvgSample", "samples\AvaloniaSvgSample\AvaloniaSvgSample.csproj", "{C3BCD2D5-DFC2-43C1-922D-2E76E6AEF122}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AvaloniaSvgSample", "samples\AvaloniaSvgSample\AvaloniaSvgSample.csproj", "{C3BCD2D5-DFC2-43C1-922D-2E76E6AEF122}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AvaloniaSKPictureImageSample", "samples\AvaloniaSKPictureImageSample\AvaloniaSKPictureImageSample.csproj", "{5A3BC87E-F3F6-4F89-B412-1324CCA2C32B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AvaloniaSKPictureImageSample", "samples\AvaloniaSKPictureImageSample\AvaloniaSKPictureImageSample.csproj", "{5A3BC87E-F3F6-4F89-B412-1324CCA2C32B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Svg.SourceGenerator.Skia", "src\Svg.SourceGenerator.Skia\Svg.SourceGenerator.Skia.csproj", "{3049C672-8A3F-4FE4-9973-515B8323B546}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Svg.SourceGenerator.Skia", "src\Svg.SourceGenerator.Skia\Svg.SourceGenerator.Skia.csproj", "{3049C672-8A3F-4FE4-9973-515B8323B546}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Svg", "src\Avalonia.Svg\Avalonia.Svg.csproj", "{B742F260-0EC6-4805-AE9F-987818CE3CF4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Svg", "src\Avalonia.Svg\Avalonia.Svg.csproj", "{B742F260-0EC6-4805-AE9F-987818CE3CF4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AvaloniaSvgCSSSample", "samples\AvaloniaSvgCSSSample\AvaloniaSvgCSSSample.csproj", "{8A938DC2-1634-4387-BAB3-69F871D54FB5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -201,6 +203,10 @@ Global {B742F260-0EC6-4805-AE9F-987818CE3CF4}.Debug|Any CPU.Build.0 = Debug|Any CPU {B742F260-0EC6-4805-AE9F-987818CE3CF4}.Release|Any CPU.ActiveCfg = Release|Any CPU {B742F260-0EC6-4805-AE9F-987818CE3CF4}.Release|Any CPU.Build.0 = Release|Any CPU + {8A938DC2-1634-4387-BAB3-69F871D54FB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8A938DC2-1634-4387-BAB3-69F871D54FB5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8A938DC2-1634-4387-BAB3-69F871D54FB5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8A938DC2-1634-4387-BAB3-69F871D54FB5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -214,13 +220,13 @@ Global {1BD5FA09-D543-4315-99A6-81E9DD8746EC} = {7863AE7D-FF68-45BF-BA68-6FA0E5604CB7} {8BAAB509-6073-4D68-9F16-EA28986839B1} = {4C42912C-9F8C-43D9-A4B5-4427F7EC8F18} {81724F00-B7C3-4E25-B473-C7433BABDC81} = {B65D5B3A-77BE-4AFF-B502-A136B9C932F8} + {CFA46E73-0050-4C57-85CE-6C5868A2483C} = {C5FFCF4B-86DC-453E-8006-44EE9EEFEE39} {D4467DCA-494D-4C32-9525-4A9713221A53} = {7863AE7D-FF68-45BF-BA68-6FA0E5604CB7} {4C970B2C-6C96-445B-B80B-4EFBF803FD5F} = {4C42912C-9F8C-43D9-A4B5-4427F7EC8F18} {29F59C87-EAE6-4DD3-8666-B79BFAF6B34D} = {4C42912C-9F8C-43D9-A4B5-4427F7EC8F18} {223B7A5A-E263-4D40-9A6E-FE31EAE92F45} = {4C42912C-9F8C-43D9-A4B5-4427F7EC8F18} - {B4BC7C90-09C3-46CB-B8B1-0450CC7EAAB0} = {B65D5B3A-77BE-4AFF-B502-A136B9C932F8} - {CFA46E73-0050-4C57-85CE-6C5868A2483C} = {C5FFCF4B-86DC-453E-8006-44EE9EEFEE39} {AF8AEF5B-0664-4106-9BD3-389758F39B12} = {C5FFCF4B-86DC-453E-8006-44EE9EEFEE39} + {B4BC7C90-09C3-46CB-B8B1-0450CC7EAAB0} = {B65D5B3A-77BE-4AFF-B502-A136B9C932F8} {6D2786A1-F110-4448-9CBF-D3CC9D803F31} = {4C42912C-9F8C-43D9-A4B5-4427F7EC8F18} {BE25FC07-9A8C-4494-A6AE-F2561CF89010} = {B65D5B3A-77BE-4AFF-B502-A136B9C932F8} {380E7565-C6FF-45C1-A683-E4E1FC744DCC} = {32B4A27D-6FC0-498C-9AD8-5510ACF2C4A1} @@ -231,6 +237,7 @@ Global {5A3BC87E-F3F6-4F89-B412-1324CCA2C32B} = {B65D5B3A-77BE-4AFF-B502-A136B9C932F8} {3049C672-8A3F-4FE4-9973-515B8323B546} = {4C42912C-9F8C-43D9-A4B5-4427F7EC8F18} {B742F260-0EC6-4805-AE9F-987818CE3CF4} = {4C42912C-9F8C-43D9-A4B5-4427F7EC8F18} + {8A938DC2-1634-4387-BAB3-69F871D54FB5} = {B65D5B3A-77BE-4AFF-B502-A136B9C932F8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {12D5E557-A27B-4FB2-83A3-4AC75B04B22C} diff --git a/samples/AvaloniaSvgCSSSample/App.axaml b/samples/AvaloniaSvgCSSSample/App.axaml new file mode 100644 index 000000000..352b0dda0 --- /dev/null +++ b/samples/AvaloniaSvgCSSSample/App.axaml @@ -0,0 +1,9 @@ + + + + + diff --git a/samples/AvaloniaSvgCSSSample/App.axaml.cs b/samples/AvaloniaSvgCSSSample/App.axaml.cs new file mode 100644 index 000000000..6ac8e676b --- /dev/null +++ b/samples/AvaloniaSvgCSSSample/App.axaml.cs @@ -0,0 +1,23 @@ +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; + +namespace AvaloniaSvgCSSSample; + +public class App : Application +{ + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + } + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + desktop.MainWindow = new MainWindow(); + } + + base.OnFrameworkInitializationCompleted(); + } +} \ No newline at end of file diff --git a/samples/AvaloniaSvgCSSSample/Assets/__tiger.svg b/samples/AvaloniaSvgCSSSample/Assets/__tiger.svg new file mode 100644 index 000000000..11abcd061 --- /dev/null +++ b/samples/AvaloniaSvgCSSSample/Assets/__tiger.svg @@ -0,0 +1,1978 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaSvgCSSSample/AvaloniaSvgCSSSample.csproj b/samples/AvaloniaSvgCSSSample/AvaloniaSvgCSSSample.csproj new file mode 100644 index 000000000..acd68a662 --- /dev/null +++ b/samples/AvaloniaSvgCSSSample/AvaloniaSvgCSSSample.csproj @@ -0,0 +1,24 @@ + + + + WinExe + net8.0 + latest + False + disable + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaSvgCSSSample/MainWindow.axaml b/samples/AvaloniaSvgCSSSample/MainWindow.axaml new file mode 100644 index 000000000..45980ce11 --- /dev/null +++ b/samples/AvaloniaSvgCSSSample/MainWindow.axaml @@ -0,0 +1,32 @@ + + + + Hover mouse on a tiger to apply SVG palette for hover state + + + + + diff --git a/samples/AvaloniaSvgCSSSample/MainWindow.axaml.cs b/samples/AvaloniaSvgCSSSample/MainWindow.axaml.cs new file mode 100644 index 000000000..98339095b --- /dev/null +++ b/samples/AvaloniaSvgCSSSample/MainWindow.axaml.cs @@ -0,0 +1,25 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Media; +using System; +using System.Linq; + +namespace AvaloniaSvgCSSSample; + +public partial class MainWindow : Window +{ + public MainWindow() + { + InitializeComponent(); +#if DEBUG + this.AttachDevTools(); +#endif + ApplyButton.AddHandler(Button.ClickEvent, OnApply); + } + + private void OnApply(object sender, EventArgs e) + { + this.SetValue(Avalonia.Svg.Skia.Svg.CSSProperty, ".Black { fill: #AAAAFF; }"); + } +} diff --git a/samples/AvaloniaSvgCSSSample/Program.cs b/samples/AvaloniaSvgCSSSample/Program.cs new file mode 100644 index 000000000..e36ac4979 --- /dev/null +++ b/samples/AvaloniaSvgCSSSample/Program.cs @@ -0,0 +1,24 @@ +using System; +using Avalonia; +using Avalonia.Svg.Skia; + +namespace AvaloniaSvgCSSSample; + +internal class Program +{ + [STAThread] + public static void Main(string[] args) => BuildAvaloniaApp() + .StartWithClassicDesktopLifetime(args); + + public static AppBuilder BuildAvaloniaApp() + { + GC.KeepAlive(typeof(SvgImageExtension).Assembly); + GC.KeepAlive(typeof(Avalonia.Svg.Skia.Svg).Assembly); + return AppBuilder.Configure() + .UsePlatformDetect() + .With(new X11PlatformOptions + { + }) + .LogToTrace(); + } +} From f513cbff6f492581c800aee5895d86c15f37ffe7 Mon Sep 17 00:00:00 2001 From: Kalachik Roman Date: Mon, 8 Jan 2024 22:47:27 +0300 Subject: [PATCH 06/24] fixed typo --- src/Svg.Skia/SKSvg.Model.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Svg.Skia/SKSvg.Model.cs b/src/Svg.Skia/SKSvg.Model.cs index 84ed3be27..e69c74147 100644 --- a/src/Svg.Skia/SKSvg.Model.cs +++ b/src/Svg.Skia/SKSvg.Model.cs @@ -114,7 +114,7 @@ public SKSvg() Path = null; Entities = entities; Stream.Position = 0; - var svgDocument = SvgExtensions.Open(stream, entities); + var svgDocument = SvgExtensions.Open(Stream, entities); if (svgDocument is { }) { Model = SvgExtensions.ToModel(svgDocument, AssetLoader, out var drawable, out _); From a46c89418753669c479f0b8b4a661a121146f489 Mon Sep 17 00:00:00 2001 From: Kalachik Roman Date: Thu, 11 Jan 2024 18:05:09 +0300 Subject: [PATCH 07/24] updated git modules --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index f3714fb2c..e566f7907 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "externals/SVG"] path = externals/SVG - url = https://github.com/RomanKalachik/SVG.git + url = https://github.com/wieslawsoltes/SVG.git branch = master [submodule "externals/resvg"] path = externals/resvg From 170c9d4336294c5615c60fed194790f7301018e4 Mon Sep 17 00:00:00 2001 From: Kalachik Roman Date: Thu, 11 Jan 2024 18:32:49 +0300 Subject: [PATCH 08/24] updated git modules --- .gitmodules | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index e566f7907..bfb843304 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,6 @@ [submodule "externals/SVG"] path = externals/SVG url = https://github.com/wieslawsoltes/SVG.git - branch = master [submodule "externals/resvg"] path = externals/resvg url = https://github.com/wieslawsoltes/resvg.git From b2fdd78a415b7b09209331af30bf7799debf5b64 Mon Sep 17 00:00:00 2001 From: Kalachik Roman Date: Thu, 11 Jan 2024 18:44:11 +0300 Subject: [PATCH 09/24] updated git modules --- externals/SVG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/SVG b/externals/SVG index 5528cee1c..2e765f404 160000 --- a/externals/SVG +++ b/externals/SVG @@ -1 +1 @@ -Subproject commit 5528cee1c04c55b75f7b2c1159266e6664f74dec +Subproject commit 2e765f404ba0992e8b17c4081f011b668d3aa8d4 From ae648ab34b9b619b3a8d5430fce5bf888aaf0a9e Mon Sep 17 00:00:00 2001 From: Kalachik Roman Date: Thu, 11 Jan 2024 18:47:10 +0300 Subject: [PATCH 10/24] Revert "updated git modules" This reverts commit b2fdd78a415b7b09209331af30bf7799debf5b64. --- externals/SVG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/SVG b/externals/SVG index 2e765f404..5528cee1c 160000 --- a/externals/SVG +++ b/externals/SVG @@ -1 +1 @@ -Subproject commit 2e765f404ba0992e8b17c4081f011b668d3aa8d4 +Subproject commit 5528cee1c04c55b75f7b2c1159266e6664f74dec From b691875ac3e91cb7e4d051fd7edef4a95a8a4529 Mon Sep 17 00:00:00 2001 From: Kalachik Roman Date: Thu, 11 Jan 2024 18:51:41 +0300 Subject: [PATCH 11/24] net8 support --- externals/SVG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/SVG b/externals/SVG index 5528cee1c..2e765f404 160000 --- a/externals/SVG +++ b/externals/SVG @@ -1 +1 @@ -Subproject commit 5528cee1c04c55b75f7b2c1159266e6664f74dec +Subproject commit 2e765f404ba0992e8b17c4081f011b668d3aa8d4 From a91a9602f5d9cfec04c33bd413a655d24261c0aa Mon Sep 17 00:00:00 2001 From: Kalachik Roman Date: Thu, 11 Jan 2024 18:52:35 +0300 Subject: [PATCH 12/24] net8 support --- externals/SVG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/SVG b/externals/SVG index 2e765f404..1d73aece6 160000 --- a/externals/SVG +++ b/externals/SVG @@ -1 +1 @@ -Subproject commit 2e765f404ba0992e8b17c4081f011b668d3aa8d4 +Subproject commit 1d73aece6719a7d0eda0c1cee12d7a4c568e7e38 From e0934f73468cb78778f3cbd9236e7f9c1707b108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Fri, 26 Jan 2024 13:13:14 +0100 Subject: [PATCH 13/24] Fix --- samples/AvaloniaSvgCSSSample/MainWindow.axaml | 63 ++++++++------- .../AvaloniaSvgCSSSample/MainWindow.axaml.cs | 2 +- src/Avalonia.Svg.Skia/Svg.cs | 80 ++++++++++++++----- src/Avalonia.Svg.Skia/SvgImage.cs | 46 +++++++---- src/Avalonia.Svg.Skia/SvgImageExtension.cs | 56 +++++++++---- src/Avalonia.Svg.Skia/SvgSource.cs | 45 ++++++----- src/Avalonia.Svg/SvgSource.cs | 30 +++---- src/Svg.Model/SvgExtensions.IO.cs | 20 ++--- src/Svg.Model/SvgParameters.cs | 14 ++-- src/Svg.Skia/SKSvg.Model.cs | 45 ++++++----- 10 files changed, 240 insertions(+), 161 deletions(-) diff --git a/samples/AvaloniaSvgCSSSample/MainWindow.axaml b/samples/AvaloniaSvgCSSSample/MainWindow.axaml index 45980ce11..0600bfbbf 100644 --- a/samples/AvaloniaSvgCSSSample/MainWindow.axaml +++ b/samples/AvaloniaSvgCSSSample/MainWindow.axaml @@ -1,32 +1,33 @@ - - - - Hover mouse on a tiger to apply SVG palette for hover state - - - - + + + + Hover mouse on a tiger to apply SVG palette for hover state + + + + + + diff --git a/samples/AvaloniaSvgCSSSample/MainWindow.axaml.cs b/samples/AvaloniaSvgCSSSample/MainWindow.axaml.cs index 98339095b..cefd0dc84 100644 --- a/samples/AvaloniaSvgCSSSample/MainWindow.axaml.cs +++ b/samples/AvaloniaSvgCSSSample/MainWindow.axaml.cs @@ -20,6 +20,6 @@ public MainWindow() private void OnApply(object sender, EventArgs e) { - this.SetValue(Avalonia.Svg.Skia.Svg.CSSProperty, ".Black { fill: #AAAAFF; }"); + this.SetValue(Avalonia.Svg.Skia.Svg.StyleProperty, ".Black { fill: #AAAAFF; }"); } } diff --git a/src/Avalonia.Svg.Skia/Svg.cs b/src/Avalonia.Svg.Skia/Svg.cs index 5dc003d12..9e38f53ca 100644 --- a/src/Avalonia.Svg.Skia/Svg.cs +++ b/src/Avalonia.Svg.Skia/Svg.cs @@ -21,16 +21,16 @@ public class Svg : Control private Dictionary? _cache; /// - /// Defines CSS for global icnos theming + /// Defines the property. /// - public static readonly AttachedProperty CSSProperty = - AvaloniaProperty.RegisterAttached("CSS", inherits: true); + public static readonly AttachedProperty StyleProperty = + AvaloniaProperty.RegisterAttached("Style", inherits: true); /// - /// Defines CSS for control states like Hover, Pressed, Enabled + /// Defines the property. /// - public static readonly AttachedProperty CSSCurrentProperty = - AvaloniaProperty.RegisterAttached("CSSCurrent", inherits: true); + public static readonly AttachedProperty CurrentStyleProperty = + AvaloniaProperty.RegisterAttached("CurrentStyle", inherits: true); /// /// Defines the property. @@ -76,13 +76,6 @@ public string? Path set => SetValue(PathProperty, value); } - public static string? GetCSS(AvaloniaObject element) => element.GetValue(CSSProperty); - public static void SetCSS(AvaloniaObject element, string? value) => element.SetValue(CSSProperty, value); - - public static string? GetCSSCurrent(AvaloniaObject element) => element.GetValue(CSSCurrentProperty); - public static void SetCSSCurrent(AvaloniaObject element, string? value) => element.SetValue(CSSCurrentProperty, value); - - /// /// Gets or sets the Svg source. /// @@ -139,16 +132,36 @@ static Svg() AffectsRender(PathProperty, SourceProperty, StretchProperty, StretchDirectionProperty); AffectsMeasure(PathProperty, SourceProperty, StretchProperty, StretchDirectionProperty); - CSSProperty.Changed.AddClassHandler(OnCSSPropertyAttachedPropertyChanged); - CSSCurrentProperty.Changed.AddClassHandler(OnCSSPropertyAttachedPropertyChanged); + StyleProperty.Changed.AddClassHandler(OnStylePropertyAttachedPropertyChanged); + CurrentStyleProperty.Changed.AddClassHandler(OnStylePropertyAttachedPropertyChanged); + } + + public static string? GetStyle(AvaloniaObject element) + { + return element.GetValue(StyleProperty); + } + + public static void SetStyle(AvaloniaObject element, string? value) + { + element.SetValue(StyleProperty, value); + } + + public static string? GetCurrentStyle(AvaloniaObject element) + { + return element.GetValue(CurrentStyleProperty); + } + public static void SetCurrentStyle(AvaloniaObject element, string? value) + { + element.SetValue(CurrentStyleProperty, value); } - private static void OnCSSPropertyAttachedPropertyChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e) + private static void OnStylePropertyAttachedPropertyChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e) { - var control = d as Control; - if (control != null) + if (d is Control control) + { control.InvalidateVisual(); + } } /// @@ -243,10 +256,33 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang { base.OnPropertyChanged(change); - if (change.Property == PathProperty || change.Property == CSSProperty || change.Property == CSSCurrentProperty) + if (change.Property == PathProperty) { var path = change.GetNewValue(); - LoadFromPath(path, new SvgParameters() { CSS = SvgSource.CombineCSS(GetCSS(this), GetCSSCurrent(this))}); + var style = GetStyle(this); + var currentStyle = GetCurrentStyle(this); + var parameters = new SvgParameters(null, string.Concat(style, ' ', currentStyle)); + LoadFromPath(path, parameters); + InvalidateVisual(); + } + + if (change.Property == StyleProperty) + { + var path = Path; + var style = change.GetNewValue(); + var currentStyle = GetCurrentStyle(this); + var parameters = new SvgParameters(null, string.Concat(style, ' ', currentStyle)); + LoadFromPath(path, parameters); + InvalidateVisual(); + } + + if (change.Property == CurrentStyleProperty) + { + var path = Path; + var style = GetStyle(this); + var currentStyle = change.GetNewValue(); + var parameters = new SvgParameters(null, string.Concat(style, ' ', currentStyle)); + LoadFromPath(path, parameters); InvalidateVisual(); } @@ -271,7 +307,7 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang } } - private void LoadFromPath(string? path, SvgParameters? entities = null) + private void LoadFromPath(string? path, SvgParameters? parameters = null) { if (path is null) { @@ -295,7 +331,7 @@ private void LoadFromPath(string? path, SvgParameters? entities = null) try { - _svg = SvgSource.Load(path, _baseUri, entities); + _svg = SvgSource.Load(path, _baseUri, parameters); if (_enableCache && _cache is { } && _svg is { }) { diff --git a/src/Avalonia.Svg.Skia/SvgImage.cs b/src/Avalonia.Svg.Skia/SvgImage.cs index 31a80b552..2bbc326a1 100644 --- a/src/Avalonia.Svg.Skia/SvgImage.cs +++ b/src/Avalonia.Svg.Skia/SvgImage.cs @@ -14,12 +14,18 @@ public class SvgImage : AvaloniaObject, IImage /// public static readonly StyledProperty SourceProperty = AvaloniaProperty.Register(nameof(Source)); - - public static readonly StyledProperty CSSProperty = - AvaloniaProperty.Register(nameof(CSS)); - public static readonly StyledProperty CSSCurrentProperty = - AvaloniaProperty.Register(nameof(CSSCurrent)); + /// + /// Defines the property. + /// + public static readonly StyledProperty StyleProperty = + AvaloniaProperty.Register(nameof(Style)); + + /// + /// Defines the property. + /// + public static readonly StyledProperty CurrentStyleProperty = + AvaloniaProperty.Register(nameof(CurrentStyle)); /// /// Gets or sets the content. @@ -30,17 +36,23 @@ public SvgSource? Source get => GetValue(SourceProperty); set => SetValue(SourceProperty, value); } - - public string CSS + + /// + /// Gets or sets the style. + /// + public string? Style { - get => GetValue(CSSProperty); - set => SetValue(CSSProperty, value); + get => GetValue(StyleProperty); + set => SetValue(StyleProperty, value); } - public string CSSCurrent + /// + /// Gets or sets the current style. + /// + public string? CurrentStyle { - get => GetValue(CSSCurrentProperty); - set => SetValue(CSSCurrentProperty, value); + get => GetValue(CurrentStyleProperty); + set => SetValue(CurrentStyleProperty, value); } /// @@ -51,9 +63,13 @@ public string CSSCurrent void IImage.Draw(DrawingContext context, Rect sourceRect, Rect destRect) { var source = Source; - var css = SvgSource.CombineCSS(CSS, CSSCurrent); - if (source?.Entities?.CSS != css) - source?.ReLoad(new SvgParameters() { CSS = css }); + + var style = string.Concat(Style, ' ', CurrentStyle); + if (source?.Parameters?.Style != style) + { + source?.ReLoad(new SvgParameters(null, style)); + } + if (source?.Picture is null) { return; diff --git a/src/Avalonia.Svg.Skia/SvgImageExtension.cs b/src/Avalonia.Svg.Skia/SvgImageExtension.cs index 6ecdd1657..0a827be85 100644 --- a/src/Avalonia.Svg.Skia/SvgImageExtension.cs +++ b/src/Avalonia.Svg.Skia/SvgImageExtension.cs @@ -12,7 +12,7 @@ namespace Avalonia.Svg.Skia; public class SvgImageExtension : MarkupExtension { /// - /// Initialises a new instance of an . + /// Initialises a new instance of an . /// /// The resource or file path public SvgImageExtension(string path) => Path = path; @@ -30,35 +30,59 @@ public override object ProvideValue(IServiceProvider serviceProvider) var baseUri = context.BaseUri; var target = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget))!; var targetControl = target.TargetObject as Control; - var iimage = ProvideValue(path, baseUri, targetControl); + var image = CreateImage(path, baseUri, targetControl); if (target.TargetProperty is AvaloniaProperty property) { if (property.PropertyType == typeof(IImage)) { - return iimage; + return image; } - return new Image { Source = iimage }; + return new Image { Source = image }; } - return iimage; + return image; } - public static IImage ProvideValue(string path, Uri baseUri, Control targetControl) + private static IImage CreateImage(string path, Uri baseUri, Control? targetControl) { - var css = targetControl != null ? Svg.GetCSS(targetControl) : null; - var cssCurrent = targetControl != null ? Svg.GetCSSCurrent(targetControl) : null; - var source = SvgSource.Load(path, baseUri, new SvgParameters() { CSS = SvgSource.CombineCSS(css, cssCurrent) }); - return CreateSvgImage(source, targetControl); + if (targetControl is not null) + { + var style = Svg.GetStyle(targetControl); + var currentStyle = Svg.GetCurrentStyle(targetControl); + var source = SvgSource.Load( + path, + baseUri, + new SvgParameters(null, string.Concat(style, ' ', currentStyle))); + + return CreateSvgImage(source, targetControl); + } + else + { + var source = SvgSource.Load( + path, + baseUri); + + return CreateSvgImage(source, targetControl); + } } - public static SvgImage CreateSvgImage(SvgSource source, Control? targetControl) + private static SvgImage CreateSvgImage(SvgSource? source, Control? targetControl) { - var result = new SvgImage(); - result.Source = source; - if (targetControl != null) + var result = new SvgImage { - result.Bind(SvgImage.CSSProperty, targetControl.GetObservable(Svg.CSSProperty).ToBinding()); - result.Bind(SvgImage.CSSCurrentProperty, targetControl.GetObservable(Svg.CSSCurrentProperty).ToBinding()); + Source = source + }; + + if (targetControl == null) + { + return result; } + + var styleBinding = targetControl.GetObservable(Svg.StyleProperty).ToBinding(); + var currentStyleBinding = targetControl.GetObservable(Svg.CurrentStyleProperty).ToBinding(); + + result.Bind(SvgImage.StyleProperty, styleBinding); + result.Bind(SvgImage.CurrentStyleProperty, currentStyleBinding); + return result; } } diff --git a/src/Avalonia.Svg.Skia/SvgSource.cs b/src/Avalonia.Svg.Skia/SvgSource.cs index 1694a2d50..442796da8 100644 --- a/src/Avalonia.Svg.Skia/SvgSource.cs +++ b/src/Avalonia.Svg.Skia/SvgSource.cs @@ -16,26 +16,24 @@ namespace Avalonia.Svg.Skia; [TypeConverter(typeof(SvgSourceTypeConverter))] public class SvgSource : SKSvg { - public static bool ThrowExceptionIfResourceMissing { get; set; } = false; - public static T? ResourceMissing(string path) where T : SKSvg, new() - { - if (ThrowExceptionIfResourceMissing) - throw new ArgumentException($"Missing resource: {path}"); - return default; - } + /// + /// Enable throw exception on missing resource. + /// + public static bool EnableThrowOnMissingResource { get; set; } + /// t /// Loads svg source from file or resource. /// /// The path to file or resource. /// The base uri. - /// The svg entities. + /// The svg parameters. /// The svg source. - public static T? Load(string path, Uri? baseUri, SvgParameters? entities = null) where T : SKSvg, new() + public static T? Load(string path, Uri? baseUri, SvgParameters? parameters = null) where T : SKSvg, new() { if (File.Exists(path)) { var source = new T(); - source.Load(path, entities); + source.Load(path, parameters); return source; } @@ -48,7 +46,7 @@ public class SvgSource : SKSvg { var stream = response.Content.ReadAsStreamAsync().Result; var source = new T(); - source.Load(stream, entities); + source.Load(stream, parameters); return source; } } @@ -58,14 +56,14 @@ public class SvgSource : SKSvg Debug.WriteLine(e.ToString()); } - return ResourceMissing(path); + return ThrowOnMissingResource(path); } var uri = path.StartsWith("/") ? new Uri(path, UriKind.Relative) : new Uri(path, UriKind.RelativeOrAbsolute); if (uri.IsAbsoluteUri && uri.IsFile) { var source = new T(); - source.Load(uri.LocalPath, entities); + source.Load(uri.LocalPath, parameters); return source; } else @@ -73,17 +71,13 @@ public class SvgSource : SKSvg var stream = Platform.AssetLoader.Open(uri, baseUri); if (stream is null) { - return ResourceMissing(path); + return ThrowOnMissingResource(path); } var source = new T(); - source.Load(stream, entities); + source.Load(stream, parameters); return source; } } - public static string CombineCSS(string? css, string? cssCurrent) - { - return $"{css} {cssCurrent}"; // The last entry has higher prioriry, so cssCurrent can overrride styles - } /// /// Loads svg source from svg source. @@ -101,12 +95,12 @@ public static string CombineCSS(string? css, string? cssCurrent) /// Loads svg source from stream. /// /// The svg stream. - /// The svg entities. + /// The svg parameters. /// The svg source. - public static T? LoadFromStream(Stream stream, SvgParameters? entities = null) where T : SKSvg, new() + public static T? LoadFromStream(Stream stream, SvgParameters? parameters = null) where T : SKSvg, new() { var skSvg = new T(); - skSvg.Load(stream, entities); + skSvg.Load(stream, parameters); return skSvg; } @@ -121,4 +115,11 @@ public static string CombineCSS(string? css, string? cssCurrent) skSvg.FromSvgDocument(document); return skSvg; } + + private static T? ThrowOnMissingResource(string path) where T : SKSvg, new() + { + return EnableThrowOnMissingResource + ? throw new ArgumentException($"Invalid resource path: {path}") + : default; + } } diff --git a/src/Avalonia.Svg/SvgSource.cs b/src/Avalonia.Svg/SvgSource.cs index b55c070c9..77c46db5f 100644 --- a/src/Avalonia.Svg/SvgSource.cs +++ b/src/Avalonia.Svg/SvgSource.cs @@ -27,13 +27,13 @@ public class SvgSource /// /// The path to file or resource. /// The base uri. - /// The svg entities. + /// The svg parameters. /// The svg picture. - public static SKPicture? LoadPicture(string path, Uri? baseUri, SvgParameters? entities = null) + public static SKPicture? LoadPicture(string path, Uri? baseUri, SvgParameters? parameters = null) { if (File.Exists(path)) { - var document = SM.SvgExtensions.Open(path, entities); + var document = SM.SvgExtensions.Open(path, parameters); return document is { } ? SM.SvgExtensions.ToModel(document, s_assetLoader, out _, out _) : default; } @@ -45,7 +45,7 @@ public class SvgSource if (response.IsSuccessStatusCode) { var stream = response.Content.ReadAsStreamAsync().Result; - var document = SM.SvgExtensions.Open(stream, entities); + var document = SM.SvgExtensions.Open(stream, parameters); return document is { } ? SM.SvgExtensions.ToModel(document, s_assetLoader, out _, out _) : default; } } @@ -61,7 +61,7 @@ public class SvgSource var uri = path.StartsWith("/") ? new Uri(path, UriKind.Relative) : new Uri(path, UriKind.RelativeOrAbsolute); if (uri.IsAbsoluteUri && uri.IsFile) { - var document = SM.SvgExtensions.Open(uri.LocalPath, entities); + var document = SM.SvgExtensions.Open(uri.LocalPath, parameters); return document is { } ? SM.SvgExtensions.ToModel(document, s_assetLoader, out _, out _) : default; } else @@ -71,7 +71,7 @@ public class SvgSource { return default; } - var document = SM.SvgExtensions.Open(stream, entities); + var document = SM.SvgExtensions.Open(stream, parameters); return document is { } ? SM.SvgExtensions.ToModel(document, s_assetLoader, out _, out _) : default; } } @@ -81,22 +81,22 @@ public class SvgSource /// /// The path to file or resource. /// The base uri. - /// The svg entities. + /// The svg parameters. /// The svg source. - public static SvgSource Load(string path, Uri? baseUri, SvgParameters? entities = null) + public static SvgSource Load(string path, Uri? baseUri, SvgParameters? parameters = null) { - return new() { Picture = LoadPicture(path, baseUri, entities) }; + return new() { Picture = LoadPicture(path, baseUri, parameters) }; } /// /// Loads svg picture from stream. /// /// The svg stream. - /// The svg entities. + /// The svg parameters. /// The svg picture. - public static SKPicture? LoadPicture(Stream stream, SvgParameters? entities = null) + public static SKPicture? LoadPicture(Stream stream, SvgParameters? parameters = null) { - var document = SM.SvgExtensions.Open(stream, entities); + var document = SM.SvgExtensions.Open(stream, parameters); return document is { } ? SM.SvgExtensions.ToModel(document, s_assetLoader, out _, out _) : default; } @@ -104,11 +104,11 @@ public static SvgSource Load(string path, Uri? baseUri, SvgParameters? entities /// Loads svg source from stream. /// /// The svg stream. - /// The svg entities. + /// The svg parameters. /// The svg source. - public static SvgSource Load(Stream stream, SvgParameters? entities = null) + public static SvgSource Load(Stream stream, SvgParameters? parameters = null) { - return new() { Picture = LoadPicture(stream, entities) }; + return new() { Picture = LoadPicture(stream, parameters) }; } /// diff --git a/src/Svg.Model/SvgExtensions.IO.cs b/src/Svg.Model/SvgExtensions.IO.cs index 6cf8283cf..c1e39eea9 100644 --- a/src/Svg.Model/SvgExtensions.IO.cs +++ b/src/Svg.Model/SvgExtensions.IO.cs @@ -273,12 +273,12 @@ internal static SvgDocument LoadSvgz(System.IO.Stream stream, Uri baseUri) return picture; } - public static SvgDocument? OpenSvg(string path, SvgParameters? entities = null) + public static SvgDocument? OpenSvg(string path, SvgParameters? parameters = null) { - return SvgDocument.Open(path, entities?.ParserEntities, entities?.CSS); + return SvgDocument.Open(path, new SvgOptions(parameters?.Entities, parameters?.Style)); } - public static SvgDocument? OpenSvgz(string path, SvgParameters? entities = null) + public static SvgDocument? OpenSvgz(string path, SvgParameters? parameters = null) { using var fileStream = System.IO.File.OpenRead(path); using var gzipStream = new GZipStream(fileStream, CompressionMode.Decompress); @@ -287,23 +287,23 @@ internal static SvgDocument LoadSvgz(System.IO.Stream stream, Uri baseUri) gzipStream.CopyTo(memoryStream); memoryStream.Position = 0; - return Open(memoryStream, entities); + return Open(memoryStream, parameters); } - public static SvgDocument? Open(string path, SvgParameters? entities = null) + public static SvgDocument? Open(string path, SvgParameters? parameters = null) { var extension = System.IO.Path.GetExtension(path); return extension.ToLower() switch { - ".svg" => OpenSvg(path, entities), - ".svgz" => OpenSvgz(path, entities), - _ => OpenSvg(path, entities), + ".svg" => OpenSvg(path, parameters), + ".svgz" => OpenSvgz(path, parameters), + _ => OpenSvg(path, parameters), }; } - public static SvgDocument? Open(System.IO.Stream stream, SvgParameters? entities = null) + public static SvgDocument? Open(System.IO.Stream stream, SvgParameters? parameters = null) { - return SvgDocument.Open(stream, entities?.ParserEntities, entities?.CSS); + return SvgDocument.Open(stream, new SvgOptions(parameters?.Entities, parameters?.Style)); } public static SvgDocument? FromSvg(string svg) diff --git a/src/Svg.Model/SvgParameters.cs b/src/Svg.Model/SvgParameters.cs index c15e0cee9..74f67b5be 100644 --- a/src/Svg.Model/SvgParameters.cs +++ b/src/Svg.Model/SvgParameters.cs @@ -1,12 +1,10 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; -namespace Svg.Model +namespace Svg.Model; + +public class SvgParameters(Dictionary? entities, string? style) { - public class SvgParameters - { - public Dictionary? ParserEntities { get; set; } - public string? CSS { get; set; } + public Dictionary? Entities { get; } = entities; - } + public string? Style { get; } = style; } diff --git a/src/Svg.Skia/SKSvg.Model.cs b/src/Svg.Skia/SKSvg.Model.cs index e69c74147..82b44846d 100644 --- a/src/Svg.Skia/SKSvg.Model.cs +++ b/src/Svg.Skia/SKSvg.Model.cs @@ -4,25 +4,24 @@ using System.Xml; using Svg.Model.Drawables; using ShimSkiaSharp; -using System.IO; namespace Svg.Skia; public class SKSvg : IDisposable { - public static SKSvg CreateFromStream(System.IO.Stream stream, SvgParameters? entities = null) + public static SKSvg CreateFromStream(System.IO.Stream stream, SvgParameters? parameters = null) { var skSvg = new SKSvg(); - skSvg.Load(stream, entities); + skSvg.Load(stream, parameters); return skSvg; } public static SKSvg CreateFromStream(System.IO.Stream stream) => CreateFromStream(stream, null); - public static SKSvg CreateFromFile(string path, SvgParameters? entities = null) + public static SKSvg CreateFromFile(string path, SvgParameters? parameters = null) { var skSvg = new SKSvg(); - skSvg.Load(path, entities); + skSvg.Load(path, parameters); return skSvg; } @@ -90,11 +89,12 @@ public static void Draw(SkiaSharp.SKCanvas skCanvas, string path, SkiaModel skia public SkiaSharp.SKPicture? Picture { get; private set; } - internal string Path { get; set; } + public SvgParameters? Parameters { get; set; } - public SvgParameters? Entities { get; set; } + private string? Path { get; set; } + + private System.IO.Stream? Stream { get; set; } - System.IO.Stream? Stream { get; set; } public SKSvg() { Settings = new SKSvgSettings(); @@ -102,19 +102,19 @@ public SKSvg() AssetLoader = new SkiaAssetLoader(SkiaModel); } - public SkiaSharp.SKPicture? Load(System.IO.Stream stream, SvgParameters? entities = null) + public SkiaSharp.SKPicture? Load(System.IO.Stream stream, SvgParameters? parameters = null) { Reset(); if (Stream != stream) { Stream?.Dispose(); - Stream = new MemoryStream(); + Stream = new System.IO.MemoryStream(); stream.CopyTo(Stream); } Path = null; - Entities = entities; + Parameters = parameters; Stream.Position = 0; - var svgDocument = SvgExtensions.Open(Stream, entities); + var svgDocument = SvgExtensions.Open(Stream, parameters); if (svgDocument is { }) { Model = SvgExtensions.ToModel(svgDocument, AssetLoader, out var drawable, out _); @@ -124,29 +124,32 @@ public SKSvg() } return null; } - public SkiaSharp.SKPicture? ReLoad(SvgParameters? entities) + + public SkiaSharp.SKPicture? ReLoad(SvgParameters? parameters) { Reset(); - Entities = entities; + + Parameters = parameters; + if (Stream == null) - return Load(Path, entities); - else { - Stream.Position = 0; - return Load(Stream, entities); + return Load(Path, parameters); } + + Stream.Position = 0; + return Load(Stream, parameters); } public SkiaSharp.SKPicture? Load(System.IO.Stream stream) => Load(stream, null); - public SkiaSharp.SKPicture? Load(string path, SvgParameters? entities = null) + public SkiaSharp.SKPicture? Load(string path, SvgParameters? parameters = null) { Reset(); Path = path; - Entities = entities; + Parameters = parameters; Stream?.Dispose(); Stream = null; - var svgDocument = SvgExtensions.Open(path, entities); + var svgDocument = SvgExtensions.Open(path, parameters); if (svgDocument is { }) { Model = SvgExtensions.ToModel(svgDocument, AssetLoader, out var drawable, out _); From 82f603c4c2ece6764e87f1b49e94ab992617f075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Fri, 26 Jan 2024 19:13:33 +0100 Subject: [PATCH 14/24] Invert if --- src/Avalonia.Svg.Skia/SvgImageExtension.cs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Svg.Skia/SvgImageExtension.cs b/src/Avalonia.Svg.Skia/SvgImageExtension.cs index 0a827be85..8f7c19755 100644 --- a/src/Avalonia.Svg.Skia/SvgImageExtension.cs +++ b/src/Avalonia.Svg.Skia/SvgImageExtension.cs @@ -31,15 +31,18 @@ public override object ProvideValue(IServiceProvider serviceProvider) var target = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget))!; var targetControl = target.TargetObject as Control; var image = CreateImage(path, baseUri, targetControl); - if (target.TargetProperty is AvaloniaProperty property) + + if (target.TargetProperty is not AvaloniaProperty property) { - if (property.PropertyType == typeof(IImage)) - { - return image; - } - return new Image { Source = image }; + return image; } - return image; + + if (property.PropertyType == typeof(IImage)) + { + return image; + } + + return new Image { Source = image }; } private static IImage CreateImage(string path, Uri baseUri, Control? targetControl) From cc86ed8b7526bf733c363dc89680460fdd73ffd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Fri, 26 Jan 2024 19:21:49 +0100 Subject: [PATCH 15/24] Rename --- Svg.Skia.sln | 2 +- .../App.axaml | 4 ++-- .../App.axaml.cs | 4 ++-- .../Assets/__tiger.svg | 0 .../AvaloniaSvgSkiaStylingSample.csproj} | 1 + .../MainWindow.axaml | 2 +- .../MainWindow.axaml.cs | 2 +- .../Program.cs | 2 +- 8 files changed, 9 insertions(+), 8 deletions(-) rename samples/{AvaloniaSvgCSSSample => AvaloniaSvgSkiaStylingSample}/App.axaml (70%) rename samples/{AvaloniaSvgCSSSample => AvaloniaSvgSkiaStylingSample}/App.axaml.cs (92%) rename samples/{AvaloniaSvgCSSSample => AvaloniaSvgSkiaStylingSample}/Assets/__tiger.svg (100%) rename samples/{AvaloniaSvgCSSSample/AvaloniaSvgCSSSample.csproj => AvaloniaSvgSkiaStylingSample/AvaloniaSvgSkiaStylingSample.csproj} (91%) rename samples/{AvaloniaSvgCSSSample => AvaloniaSvgSkiaStylingSample}/MainWindow.axaml (95%) rename samples/{AvaloniaSvgCSSSample => AvaloniaSvgSkiaStylingSample}/MainWindow.axaml.cs (92%) rename samples/{AvaloniaSvgCSSSample => AvaloniaSvgSkiaStylingSample}/Program.cs (93%) diff --git a/Svg.Skia.sln b/Svg.Skia.sln index 6097a4235..40b73ff86 100644 --- a/Svg.Skia.sln +++ b/Svg.Skia.sln @@ -111,7 +111,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Svg.SourceGenerator.Skia", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Svg", "src\Avalonia.Svg\Avalonia.Svg.csproj", "{B742F260-0EC6-4805-AE9F-987818CE3CF4}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AvaloniaSvgCSSSample", "samples\AvaloniaSvgCSSSample\AvaloniaSvgCSSSample.csproj", "{8A938DC2-1634-4387-BAB3-69F871D54FB5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AvaloniaSvgSkiaStylingSample", "samples\AvaloniaSvgSkiaStylingSample\AvaloniaSvgSkiaStylingSample.csproj", "{8A938DC2-1634-4387-BAB3-69F871D54FB5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/samples/AvaloniaSvgCSSSample/App.axaml b/samples/AvaloniaSvgSkiaStylingSample/App.axaml similarity index 70% rename from samples/AvaloniaSvgCSSSample/App.axaml rename to samples/AvaloniaSvgSkiaStylingSample/App.axaml index 352b0dda0..4761ac7b2 100644 --- a/samples/AvaloniaSvgCSSSample/App.axaml +++ b/samples/AvaloniaSvgSkiaStylingSample/App.axaml @@ -1,7 +1,7 @@  diff --git a/samples/AvaloniaSvgCSSSample/App.axaml.cs b/samples/AvaloniaSvgSkiaStylingSample/App.axaml.cs similarity index 92% rename from samples/AvaloniaSvgCSSSample/App.axaml.cs rename to samples/AvaloniaSvgSkiaStylingSample/App.axaml.cs index 6ac8e676b..0c33b33fe 100644 --- a/samples/AvaloniaSvgCSSSample/App.axaml.cs +++ b/samples/AvaloniaSvgSkiaStylingSample/App.axaml.cs @@ -2,7 +2,7 @@ using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; -namespace AvaloniaSvgCSSSample; +namespace AvaloniaSvgSkiaStylingSample; public class App : Application { @@ -20,4 +20,4 @@ public override void OnFrameworkInitializationCompleted() base.OnFrameworkInitializationCompleted(); } -} \ No newline at end of file +} diff --git a/samples/AvaloniaSvgCSSSample/Assets/__tiger.svg b/samples/AvaloniaSvgSkiaStylingSample/Assets/__tiger.svg similarity index 100% rename from samples/AvaloniaSvgCSSSample/Assets/__tiger.svg rename to samples/AvaloniaSvgSkiaStylingSample/Assets/__tiger.svg diff --git a/samples/AvaloniaSvgCSSSample/AvaloniaSvgCSSSample.csproj b/samples/AvaloniaSvgSkiaStylingSample/AvaloniaSvgSkiaStylingSample.csproj similarity index 91% rename from samples/AvaloniaSvgCSSSample/AvaloniaSvgCSSSample.csproj rename to samples/AvaloniaSvgSkiaStylingSample/AvaloniaSvgSkiaStylingSample.csproj index acd68a662..471073975 100644 --- a/samples/AvaloniaSvgCSSSample/AvaloniaSvgCSSSample.csproj +++ b/samples/AvaloniaSvgSkiaStylingSample/AvaloniaSvgSkiaStylingSample.csproj @@ -6,6 +6,7 @@ latest False disable + AvaloniaSvgSkiaStylingSample diff --git a/samples/AvaloniaSvgCSSSample/MainWindow.axaml b/samples/AvaloniaSvgSkiaStylingSample/MainWindow.axaml similarity index 95% rename from samples/AvaloniaSvgCSSSample/MainWindow.axaml rename to samples/AvaloniaSvgSkiaStylingSample/MainWindow.axaml index 0600bfbbf..56f24dcf5 100644 --- a/samples/AvaloniaSvgCSSSample/MainWindow.axaml +++ b/samples/AvaloniaSvgSkiaStylingSample/MainWindow.axaml @@ -1,4 +1,4 @@ - Date: Fri, 26 Jan 2024 19:22:00 +0100 Subject: [PATCH 16/24] Move reload invalidation --- src/Avalonia.Svg.Skia/SvgImage.cs | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Svg.Skia/SvgImage.cs b/src/Avalonia.Svg.Skia/SvgImage.cs index 2bbc326a1..4c26370fc 100644 --- a/src/Avalonia.Svg.Skia/SvgImage.cs +++ b/src/Avalonia.Svg.Skia/SvgImage.cs @@ -64,12 +64,6 @@ void IImage.Draw(DrawingContext context, Rect sourceRect, Rect destRect) { var source = Source; - var style = string.Concat(Style, ' ', CurrentStyle); - if (source?.Parameters?.Style != style) - { - source?.ReLoad(new SvgParameters(null, style)); - } - if (source?.Picture is null) { return; @@ -101,9 +95,30 @@ void IImage.Draw(DrawingContext context, Rect sourceRect, Rect destRect) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { base.OnPropertyChanged(change); + if (change.Property == SourceProperty) { // TODO: Invalidate IImage } + + if (change.Property == StyleProperty) + { + var style = string.Concat(Style, ' ', CurrentStyle); + + if (Source?.Parameters?.Style != style) + { + Source?.ReLoad(new SvgParameters(null, style)); + } + } + + if (change.Property == CurrentStyleProperty) + { + var style = string.Concat(Style, ' ', CurrentStyle); + + if (Source?.Parameters?.Style != style) + { + Source?.ReLoad(new SvgParameters(null, style)); + } + } } } From bb357407150e505c7337826be9de34d3c5124fb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Fri, 26 Jan 2024 19:32:02 +0100 Subject: [PATCH 17/24] Refactor --- src/Avalonia.Svg.Skia/SvgImage.cs | 8 +-- src/Svg.Skia/SKSvg.Model.cs | 93 +++++++++++++++++-------------- 2 files changed, 56 insertions(+), 45 deletions(-) diff --git a/src/Avalonia.Svg.Skia/SvgImage.cs b/src/Avalonia.Svg.Skia/SvgImage.cs index 4c26370fc..d683f637f 100644 --- a/src/Avalonia.Svg.Skia/SvgImage.cs +++ b/src/Avalonia.Svg.Skia/SvgImage.cs @@ -103,9 +103,9 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang if (change.Property == StyleProperty) { - var style = string.Concat(Style, ' ', CurrentStyle); + var style = string.Concat(change.GetNewValue(), ' ', CurrentStyle); - if (Source?.Parameters?.Style != style) + if (Source?.Style != style) { Source?.ReLoad(new SvgParameters(null, style)); } @@ -113,9 +113,9 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang if (change.Property == CurrentStyleProperty) { - var style = string.Concat(Style, ' ', CurrentStyle); + var style = string.Concat(Style, ' ', change.GetNewValue()); - if (Source?.Parameters?.Style != style) + if (Source?.Style != style) { Source?.ReLoad(new SvgParameters(null, style)); } diff --git a/src/Svg.Skia/SKSvg.Model.cs b/src/Svg.Skia/SKSvg.Model.cs index 82b44846d..e7d8bf8b6 100644 --- a/src/Svg.Skia/SKSvg.Model.cs +++ b/src/Svg.Skia/SKSvg.Model.cs @@ -77,6 +77,10 @@ public static void Draw(SkiaSharp.SKCanvas skCanvas, string path, SkiaModel skia } } + private SvgParameters? _originalParameters; + private string? _originalPath; + private System.IO.Stream? _originalStream; + public SKSvgSettings Settings { get; } public IAssetLoader AssetLoader { get; } @@ -89,11 +93,9 @@ public static void Draw(SkiaSharp.SKCanvas skCanvas, string path, SkiaModel skia public SkiaSharp.SKPicture? Picture { get; private set; } - public SvgParameters? Parameters { get; set; } - - private string? Path { get; set; } + public string? Style => _originalParameters?.Style; - private System.IO.Stream? Stream { get; set; } + public Dictionary? Entities => _originalParameters?.Entities; public SKSvg() { @@ -105,39 +107,29 @@ public SKSvg() public SkiaSharp.SKPicture? Load(System.IO.Stream stream, SvgParameters? parameters = null) { Reset(); - if (Stream != stream) - { - Stream?.Dispose(); - Stream = new System.IO.MemoryStream(); - stream.CopyTo(Stream); - } - Path = null; - Parameters = parameters; - Stream.Position = 0; - var svgDocument = SvgExtensions.Open(Stream, parameters); - if (svgDocument is { }) + + if (_originalStream != stream) { - Model = SvgExtensions.ToModel(svgDocument, AssetLoader, out var drawable, out _); - Drawable = drawable; - Picture = SkiaModel.ToSKPicture(Model); - return Picture; + _originalStream?.Dispose(); + _originalStream = new System.IO.MemoryStream(); + stream.CopyTo(_originalStream); } - return null; - } - public SkiaSharp.SKPicture? ReLoad(SvgParameters? parameters) - { - Reset(); + _originalPath = null; + _originalParameters = parameters; + _originalStream.Position = 0; - Parameters = parameters; - - if (Stream == null) + var svgDocument = SvgExtensions.Open(_originalStream, parameters); + if (svgDocument is null) { - return Load(Path, parameters); + return null; } - Stream.Position = 0; - return Load(Stream, parameters); + Model = SvgExtensions.ToModel(svgDocument, AssetLoader, out var drawable, out _); + Drawable = drawable; + Picture = SkiaModel.ToSKPicture(Model); + + return Picture; } public SkiaSharp.SKPicture? Load(System.IO.Stream stream) => Load(stream, null); @@ -145,19 +137,22 @@ public SKSvg() public SkiaSharp.SKPicture? Load(string path, SvgParameters? parameters = null) { Reset(); - Path = path; - Parameters = parameters; - Stream?.Dispose(); - Stream = null; + + _originalPath = path; + _originalStream?.Dispose(); + _originalStream = null; + var svgDocument = SvgExtensions.Open(path, parameters); - if (svgDocument is { }) + if (svgDocument is null) { - Model = SvgExtensions.ToModel(svgDocument, AssetLoader, out var drawable, out _); - Drawable = drawable; - Picture = SkiaModel.ToSKPicture(Model); - return Picture; + return null; } - return null; + + Model = SvgExtensions.ToModel(svgDocument, AssetLoader, out var drawable, out _); + Drawable = drawable; + Picture = SkiaModel.ToSKPicture(Model); + + return Picture; } public SkiaSharp.SKPicture? Load(string path) => Load(path, null); @@ -176,6 +171,22 @@ public SKSvg() return null; } + public SkiaSharp.SKPicture? ReLoad(SvgParameters? parameters) + { + Reset(); + + _originalParameters = parameters; + + if (_originalStream == null) + { + return Load(_originalPath, parameters); + } + + _originalStream.Position = 0; + + return Load(_originalStream, parameters); + } + public SkiaSharp.SKPicture? FromSvg(string svg) { Reset(); @@ -234,6 +245,6 @@ private void Reset() public void Dispose() { Reset(); - Stream?.Dispose(); + _originalStream?.Dispose(); } } From 2e09906c17acf454086925b9d755d18b22df6b18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Fri, 26 Jan 2024 19:42:05 +0100 Subject: [PATCH 18/24] Update sample --- .../MainWindow.axaml | 64 ++++++++++++++----- .../MainWindow.axaml.cs | 12 +++- 2 files changed, 56 insertions(+), 20 deletions(-) diff --git a/samples/AvaloniaSvgSkiaStylingSample/MainWindow.axaml b/samples/AvaloniaSvgSkiaStylingSample/MainWindow.axaml index 56f24dcf5..0fc9b3be7 100644 --- a/samples/AvaloniaSvgSkiaStylingSample/MainWindow.axaml +++ b/samples/AvaloniaSvgSkiaStylingSample/MainWindow.axaml @@ -11,23 +11,53 @@ UseLayoutRounding="True" WindowStartupLocation="CenterScreen" mc:Ignorable="d"> - - - Hover mouse on a tiger to apply SVG palette for hover state - - - - - - + + + + + + + + + + + + + + + + + + + diff --git a/samples/AvaloniaSvgSkiaStylingSample/MainWindow.axaml.cs b/samples/AvaloniaSvgSkiaStylingSample/MainWindow.axaml.cs index ea3ae4bff..69cc93014 100644 --- a/samples/AvaloniaSvgSkiaStylingSample/MainWindow.axaml.cs +++ b/samples/AvaloniaSvgSkiaStylingSample/MainWindow.axaml.cs @@ -15,11 +15,17 @@ public MainWindow() #if DEBUG this.AttachDevTools(); #endif - ApplyButton.AddHandler(Button.ClickEvent, OnApply); + ApplySvgStyleButton.Click += ApplySvgStyleButtonClick; + ApplySvgImageStyleButton.Click += ApplySvgImageStyleButtonClick; } - private void OnApply(object sender, EventArgs e) + private void ApplySvgStyleButtonClick(object sender, EventArgs e) { - this.SetValue(Avalonia.Svg.Skia.Svg.StyleProperty, ".Black { fill: #AAAAFF; }"); + SvgControl.SetCurrentValue(Avalonia.Svg.Skia.Svg.StyleProperty, ".Black { fill: #AAAAFF; }"); + } + + private void ApplySvgImageStyleButtonClick(object sender, EventArgs e) + { + SvgImageButton.SetCurrentValue(Avalonia.Svg.Skia.Svg.StyleProperty, ".Black { fill: #AAAAFF; }"); } } From f7f2f5b32afcf2d478bcf2981364b72d3dc40bda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Fri, 26 Jan 2024 19:51:23 +0100 Subject: [PATCH 19/24] Update W3CTestSuiteTests.cs --- tests/Svg.Skia.UnitTests/W3CTestSuiteTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Svg.Skia.UnitTests/W3CTestSuiteTests.cs b/tests/Svg.Skia.UnitTests/W3CTestSuiteTests.cs index 4a2d45d0d..5dca2ac1e 100644 --- a/tests/Svg.Skia.UnitTests/W3CTestSuiteTests.cs +++ b/tests/Svg.Skia.UnitTests/W3CTestSuiteTests.cs @@ -724,7 +724,7 @@ private void TestImpl(string name, double errorThreshold, float scaleX = 1.0f, f [InlineData("coords-transformattr-05-f", 0.022)] public void coords_transformattr(string name, double errorThreshold) => TestImpl(name, errorThreshold); - [WindowsAndOSXTheory] + [WindowsAndOSXTheory(Skip = "Missing svg files")] [InlineData("paths-data-01-t", 0.074)] [InlineData("paths-data-02-t", 0.086)] [InlineData("paths-data-03-f", 0.076)] @@ -746,7 +746,7 @@ private void TestImpl(string name, double errorThreshold, float scaleX = 1.0f, f [InlineData("paths-data-20-f", 0.063)] public void paths_data(string name, double errorThreshold) => TestImpl(name, errorThreshold); - [WindowsTheory] + [WindowsTheory(Skip = "Missing svg files")] [InlineData("__AJ_Digital_Camera", 0.027)] [InlineData("__Telefunken_FuBK_test_pattern", 0.022)] [InlineData("__tiger", 0.055)] From 2daaa02a6653068803215b3429bc78f5223eb11f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Fri, 26 Jan 2024 20:01:22 +0100 Subject: [PATCH 20/24] Use readonly record struct --- src/Svg.Model/IsExternalInit.cs | 5 +++++ src/Svg.Model/SvgParameters.cs | 7 +------ 2 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 src/Svg.Model/IsExternalInit.cs diff --git a/src/Svg.Model/IsExternalInit.cs b/src/Svg.Model/IsExternalInit.cs new file mode 100644 index 000000000..64f0748e4 --- /dev/null +++ b/src/Svg.Model/IsExternalInit.cs @@ -0,0 +1,5 @@ +#if NET461 || NETSTANDARD +// ReSharper disable once CheckNamespace +namespace System.Runtime.CompilerServices; +internal static class IsExternalInit {} +#endif diff --git a/src/Svg.Model/SvgParameters.cs b/src/Svg.Model/SvgParameters.cs index 74f67b5be..673bb4ea7 100644 --- a/src/Svg.Model/SvgParameters.cs +++ b/src/Svg.Model/SvgParameters.cs @@ -2,9 +2,4 @@ namespace Svg.Model; -public class SvgParameters(Dictionary? entities, string? style) -{ - public Dictionary? Entities { get; } = entities; - - public string? Style { get; } = style; -} +public readonly record struct SvgParameters(Dictionary? Entities, string? Style); From 29833b208af57f89fb4d34332995198cd54a0c72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Fri, 26 Jan 2024 20:16:00 +0100 Subject: [PATCH 21/24] Rename --- .../MainWindow.axaml | 4 +- .../MainWindow.axaml.cs | 4 +- src/Avalonia.Svg.Skia/Svg.cs | 56 +++++++++---------- src/Avalonia.Svg.Skia/SvgImage.cs | 40 ++++++------- src/Avalonia.Svg.Skia/SvgImageExtension.cs | 14 ++--- src/Svg.Model/SvgExtensions.IO.cs | 4 +- src/Svg.Model/SvgParameters.cs | 2 +- src/Svg.Skia/SKSvg.Model.cs | 2 +- 8 files changed, 63 insertions(+), 63 deletions(-) diff --git a/samples/AvaloniaSvgSkiaStylingSample/MainWindow.axaml b/samples/AvaloniaSvgSkiaStylingSample/MainWindow.axaml index 0fc9b3be7..ddbfa9687 100644 --- a/samples/AvaloniaSvgSkiaStylingSample/MainWindow.axaml +++ b/samples/AvaloniaSvgSkiaStylingSample/MainWindow.axaml @@ -25,7 +25,7 @@ VerticalAlignment="Center"> @@ -51,7 +51,7 @@ Name="SvgImageButton"> diff --git a/samples/AvaloniaSvgSkiaStylingSample/MainWindow.axaml.cs b/samples/AvaloniaSvgSkiaStylingSample/MainWindow.axaml.cs index 69cc93014..a6580d4b3 100644 --- a/samples/AvaloniaSvgSkiaStylingSample/MainWindow.axaml.cs +++ b/samples/AvaloniaSvgSkiaStylingSample/MainWindow.axaml.cs @@ -21,11 +21,11 @@ public MainWindow() private void ApplySvgStyleButtonClick(object sender, EventArgs e) { - SvgControl.SetCurrentValue(Avalonia.Svg.Skia.Svg.StyleProperty, ".Black { fill: #AAAAFF; }"); + SvgControl.SetCurrentValue(Avalonia.Svg.Skia.Svg.CssProperty, ".Black { fill: #AAAAFF; }"); } private void ApplySvgImageStyleButtonClick(object sender, EventArgs e) { - SvgImageButton.SetCurrentValue(Avalonia.Svg.Skia.Svg.StyleProperty, ".Black { fill: #AAAAFF; }"); + SvgImageButton.SetCurrentValue(Avalonia.Svg.Skia.Svg.CssProperty, ".Black { fill: #AAAAFF; }"); } } diff --git a/src/Avalonia.Svg.Skia/Svg.cs b/src/Avalonia.Svg.Skia/Svg.cs index 9e38f53ca..b6409c106 100644 --- a/src/Avalonia.Svg.Skia/Svg.cs +++ b/src/Avalonia.Svg.Skia/Svg.cs @@ -21,16 +21,16 @@ public class Svg : Control private Dictionary? _cache; /// - /// Defines the property. + /// Defines the property. /// - public static readonly AttachedProperty StyleProperty = - AvaloniaProperty.RegisterAttached("Style", inherits: true); + public static readonly AttachedProperty CssProperty = + AvaloniaProperty.RegisterAttached("Css", inherits: true); /// - /// Defines the property. + /// Defines the property. /// - public static readonly AttachedProperty CurrentStyleProperty = - AvaloniaProperty.RegisterAttached("CurrentStyle", inherits: true); + public static readonly AttachedProperty CurrentCssProperty = + AvaloniaProperty.RegisterAttached("CurrentCss", inherits: true); /// /// Defines the property. @@ -132,31 +132,31 @@ static Svg() AffectsRender(PathProperty, SourceProperty, StretchProperty, StretchDirectionProperty); AffectsMeasure(PathProperty, SourceProperty, StretchProperty, StretchDirectionProperty); - StyleProperty.Changed.AddClassHandler(OnStylePropertyAttachedPropertyChanged); - CurrentStyleProperty.Changed.AddClassHandler(OnStylePropertyAttachedPropertyChanged); + CssProperty.Changed.AddClassHandler(OnCssPropertyAttachedPropertyChanged); + CurrentCssProperty.Changed.AddClassHandler(OnCssPropertyAttachedPropertyChanged); } - public static string? GetStyle(AvaloniaObject element) + public static string? GetCss(AvaloniaObject element) { - return element.GetValue(StyleProperty); + return element.GetValue(CssProperty); } - public static void SetStyle(AvaloniaObject element, string? value) + public static void SetCss(AvaloniaObject element, string? value) { - element.SetValue(StyleProperty, value); + element.SetValue(CssProperty, value); } - public static string? GetCurrentStyle(AvaloniaObject element) + public static string? GetCurrentCss(AvaloniaObject element) { - return element.GetValue(CurrentStyleProperty); + return element.GetValue(CurrentCssProperty); } - public static void SetCurrentStyle(AvaloniaObject element, string? value) + public static void SetCurrentCss(AvaloniaObject element, string? value) { - element.SetValue(CurrentStyleProperty, value); + element.SetValue(CurrentCssProperty, value); } - private static void OnStylePropertyAttachedPropertyChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e) + private static void OnCssPropertyAttachedPropertyChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e) { if (d is Control control) { @@ -259,29 +259,29 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang if (change.Property == PathProperty) { var path = change.GetNewValue(); - var style = GetStyle(this); - var currentStyle = GetCurrentStyle(this); - var parameters = new SvgParameters(null, string.Concat(style, ' ', currentStyle)); + var css = GetCss(this); + var currentCss = GetCurrentCss(this); + var parameters = new SvgParameters(null, string.Concat(css, ' ', currentCss)); LoadFromPath(path, parameters); InvalidateVisual(); } - if (change.Property == StyleProperty) + if (change.Property == CssProperty) { var path = Path; - var style = change.GetNewValue(); - var currentStyle = GetCurrentStyle(this); - var parameters = new SvgParameters(null, string.Concat(style, ' ', currentStyle)); + var css = change.GetNewValue(); + var currentCss = GetCurrentCss(this); + var parameters = new SvgParameters(null, string.Concat(css, ' ', currentCss)); LoadFromPath(path, parameters); InvalidateVisual(); } - if (change.Property == CurrentStyleProperty) + if (change.Property == CurrentCssProperty) { var path = Path; - var style = GetStyle(this); - var currentStyle = change.GetNewValue(); - var parameters = new SvgParameters(null, string.Concat(style, ' ', currentStyle)); + var css = GetCss(this); + var currentCss = change.GetNewValue(); + var parameters = new SvgParameters(null, string.Concat(css, ' ', currentCss)); LoadFromPath(path, parameters); InvalidateVisual(); } diff --git a/src/Avalonia.Svg.Skia/SvgImage.cs b/src/Avalonia.Svg.Skia/SvgImage.cs index d683f637f..30c1cded3 100644 --- a/src/Avalonia.Svg.Skia/SvgImage.cs +++ b/src/Avalonia.Svg.Skia/SvgImage.cs @@ -16,16 +16,16 @@ public class SvgImage : AvaloniaObject, IImage AvaloniaProperty.Register(nameof(Source)); /// - /// Defines the property. + /// Defines the property. /// - public static readonly StyledProperty StyleProperty = - AvaloniaProperty.Register(nameof(Style)); + public static readonly StyledProperty CssProperty = + AvaloniaProperty.Register(nameof(Css)); /// - /// Defines the property. + /// Defines the property. /// - public static readonly StyledProperty CurrentStyleProperty = - AvaloniaProperty.Register(nameof(CurrentStyle)); + public static readonly StyledProperty CurrentCssProperty = + AvaloniaProperty.Register(nameof(CurrentCss)); /// /// Gets or sets the content. @@ -40,19 +40,19 @@ public SvgSource? Source /// /// Gets or sets the style. /// - public string? Style + public string? Css { - get => GetValue(StyleProperty); - set => SetValue(StyleProperty, value); + get => GetValue(CssProperty); + set => SetValue(CssProperty, value); } /// /// Gets or sets the current style. /// - public string? CurrentStyle + public string? CurrentCss { - get => GetValue(CurrentStyleProperty); - set => SetValue(CurrentStyleProperty, value); + get => GetValue(CurrentCssProperty); + set => SetValue(CurrentCssProperty, value); } /// @@ -101,23 +101,23 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang // TODO: Invalidate IImage } - if (change.Property == StyleProperty) + if (change.Property == CssProperty) { - var style = string.Concat(change.GetNewValue(), ' ', CurrentStyle); + var css = string.Concat(change.GetNewValue(), ' ', CurrentCss); - if (Source?.Style != style) + if (Source?.Css != css) { - Source?.ReLoad(new SvgParameters(null, style)); + Source?.ReLoad(new SvgParameters(null, css)); } } - if (change.Property == CurrentStyleProperty) + if (change.Property == CurrentCssProperty) { - var style = string.Concat(Style, ' ', change.GetNewValue()); + var css = string.Concat(Css, ' ', change.GetNewValue()); - if (Source?.Style != style) + if (Source?.Css != css) { - Source?.ReLoad(new SvgParameters(null, style)); + Source?.ReLoad(new SvgParameters(null, css)); } } } diff --git a/src/Avalonia.Svg.Skia/SvgImageExtension.cs b/src/Avalonia.Svg.Skia/SvgImageExtension.cs index 8f7c19755..90ebace78 100644 --- a/src/Avalonia.Svg.Skia/SvgImageExtension.cs +++ b/src/Avalonia.Svg.Skia/SvgImageExtension.cs @@ -49,12 +49,12 @@ private static IImage CreateImage(string path, Uri baseUri, Control? targetContr { if (targetControl is not null) { - var style = Svg.GetStyle(targetControl); - var currentStyle = Svg.GetCurrentStyle(targetControl); + var css = Svg.GetCss(targetControl); + var currentCss = Svg.GetCurrentCss(targetControl); var source = SvgSource.Load( path, baseUri, - new SvgParameters(null, string.Concat(style, ' ', currentStyle))); + new SvgParameters(null, string.Concat(css, ' ', currentCss))); return CreateSvgImage(source, targetControl); } @@ -80,11 +80,11 @@ private static SvgImage CreateSvgImage(SvgSource? source, Control? targetControl return result; } - var styleBinding = targetControl.GetObservable(Svg.StyleProperty).ToBinding(); - var currentStyleBinding = targetControl.GetObservable(Svg.CurrentStyleProperty).ToBinding(); + var styleBinding = targetControl.GetObservable(Svg.CssProperty).ToBinding(); + var currentStyleBinding = targetControl.GetObservable(Svg.CurrentCssProperty).ToBinding(); - result.Bind(SvgImage.StyleProperty, styleBinding); - result.Bind(SvgImage.CurrentStyleProperty, currentStyleBinding); + result.Bind(SvgImage.CssProperty, styleBinding); + result.Bind(SvgImage.CurrentCssProperty, currentStyleBinding); return result; } diff --git a/src/Svg.Model/SvgExtensions.IO.cs b/src/Svg.Model/SvgExtensions.IO.cs index c1e39eea9..02c238d5b 100644 --- a/src/Svg.Model/SvgExtensions.IO.cs +++ b/src/Svg.Model/SvgExtensions.IO.cs @@ -275,7 +275,7 @@ internal static SvgDocument LoadSvgz(System.IO.Stream stream, Uri baseUri) public static SvgDocument? OpenSvg(string path, SvgParameters? parameters = null) { - return SvgDocument.Open(path, new SvgOptions(parameters?.Entities, parameters?.Style)); + return SvgDocument.Open(path, new SvgOptions(parameters?.Entities, parameters?.Css)); } public static SvgDocument? OpenSvgz(string path, SvgParameters? parameters = null) @@ -303,7 +303,7 @@ internal static SvgDocument LoadSvgz(System.IO.Stream stream, Uri baseUri) public static SvgDocument? Open(System.IO.Stream stream, SvgParameters? parameters = null) { - return SvgDocument.Open(stream, new SvgOptions(parameters?.Entities, parameters?.Style)); + return SvgDocument.Open(stream, new SvgOptions(parameters?.Entities, parameters?.Css)); } public static SvgDocument? FromSvg(string svg) diff --git a/src/Svg.Model/SvgParameters.cs b/src/Svg.Model/SvgParameters.cs index 673bb4ea7..0942f92cb 100644 --- a/src/Svg.Model/SvgParameters.cs +++ b/src/Svg.Model/SvgParameters.cs @@ -2,4 +2,4 @@ namespace Svg.Model; -public readonly record struct SvgParameters(Dictionary? Entities, string? Style); +public readonly record struct SvgParameters(Dictionary? Entities, string? Css); diff --git a/src/Svg.Skia/SKSvg.Model.cs b/src/Svg.Skia/SKSvg.Model.cs index e7d8bf8b6..16aa3a861 100644 --- a/src/Svg.Skia/SKSvg.Model.cs +++ b/src/Svg.Skia/SKSvg.Model.cs @@ -93,7 +93,7 @@ public static void Draw(SkiaSharp.SKCanvas skCanvas, string path, SkiaModel skia public SkiaSharp.SKPicture? Picture { get; private set; } - public string? Style => _originalParameters?.Style; + public string? Css => _originalParameters?.Css; public Dictionary? Entities => _originalParameters?.Entities; From 8a16457e3b08950ca1e89078b95a11aeebb56cf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Fri, 26 Jan 2024 20:25:37 +0100 Subject: [PATCH 22/24] Update Svg.cs --- src/Avalonia.Svg.Skia/Svg.cs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Avalonia.Svg.Skia/Svg.cs b/src/Avalonia.Svg.Skia/Svg.cs index b6409c106..1a455e8e9 100644 --- a/src/Avalonia.Svg.Skia/Svg.cs +++ b/src/Avalonia.Svg.Skia/Svg.cs @@ -20,18 +20,6 @@ public class Svg : Control private bool _enableCache; private Dictionary? _cache; - /// - /// Defines the property. - /// - public static readonly AttachedProperty CssProperty = - AvaloniaProperty.RegisterAttached("Css", inherits: true); - - /// - /// Defines the property. - /// - public static readonly AttachedProperty CurrentCssProperty = - AvaloniaProperty.RegisterAttached("CurrentCss", inherits: true); - /// /// Defines the property. /// @@ -66,6 +54,18 @@ public class Svg : Control o => o.EnableCache, (o, v) => o.EnableCache = v); + /// + /// Defines the Css property. + /// + public static readonly AttachedProperty CssProperty = + AvaloniaProperty.RegisterAttached("Css", inherits: true); + + /// + /// Defines the CurrentCss property. + /// + public static readonly AttachedProperty CurrentCssProperty = + AvaloniaProperty.RegisterAttached("CurrentCss", inherits: true); + /// /// Gets or sets the Svg path. /// From f2d9273ff900d14e6507ccdec64b0fd8e42928dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Fri, 26 Jan 2024 20:26:31 +0100 Subject: [PATCH 23/24] Update Svg.cs --- src/Avalonia.Svg/Svg.cs | 72 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Svg/Svg.cs b/src/Avalonia.Svg/Svg.cs index a802b8278..04e6ad28a 100644 --- a/src/Avalonia.Svg/Svg.cs +++ b/src/Avalonia.Svg/Svg.cs @@ -80,6 +80,18 @@ public StretchDirection StretchDirection set { SetValue(StretchDirectionProperty, value); } } + /// + /// Defines the Css property. + /// + public static readonly AttachedProperty CssProperty = + AvaloniaProperty.RegisterAttached("Css", inherits: true); + + /// + /// Defines the CurrentCss property. + /// + public static readonly AttachedProperty CurrentCssProperty = + AvaloniaProperty.RegisterAttached("CurrentCss", inherits: true); + /// /// Gets svg model. /// @@ -89,6 +101,37 @@ static Svg() { AffectsRender(PathProperty, SourceProperty, StretchProperty, StretchDirectionProperty); AffectsMeasure(PathProperty, SourceProperty, StretchProperty, StretchDirectionProperty); + + CssProperty.Changed.AddClassHandler(OnCssPropertyAttachedPropertyChanged); + CurrentCssProperty.Changed.AddClassHandler(OnCssPropertyAttachedPropertyChanged); + } + + public static string? GetCss(AvaloniaObject element) + { + return element.GetValue(CssProperty); + } + + public static void SetCss(AvaloniaObject element, string? value) + { + element.SetValue(CssProperty, value); + } + + public static string? GetCurrentCss(AvaloniaObject element) + { + return element.GetValue(CurrentCssProperty); + } + + public static void SetCurrentCss(AvaloniaObject element, string? value) + { + element.SetValue(CurrentCssProperty, value); + } + + private static void OnCssPropertyAttachedPropertyChanged(AvaloniaObject d, AvaloniaPropertyChangedEventArgs e) + { + if (d is Control control) + { + control.InvalidateVisual(); + } } /// @@ -187,7 +230,30 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang if (change.Property == PathProperty) { var path = change.GetNewValue(); - LoadFromPath(path); + var css = GetCss(this); + var currentCss = GetCurrentCss(this); + var parameters = new SvgParameters(null, string.Concat(css, ' ', currentCss)); + LoadFromPath(path, parameters); + InvalidateVisual(); + } + + if (change.Property == CssProperty) + { + var path = Path; + var css = change.GetNewValue(); + var currentCss = GetCurrentCss(this); + var parameters = new SvgParameters(null, string.Concat(css, ' ', currentCss)); + LoadFromPath(path, parameters); + InvalidateVisual(); + } + + if (change.Property == CurrentCssProperty) + { + var path = Path; + var css = GetCss(this); + var currentCss = change.GetNewValue(); + var parameters = new SvgParameters(null, string.Concat(css, ' ', currentCss)); + LoadFromPath(path, parameters); InvalidateVisual(); } @@ -199,14 +265,14 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang } } - private void LoadFromPath(string? path, SvgParameters? entities = null) + private void LoadFromPath(string? path, SvgParameters? parameters = null) { _picture = default; _avaloniaPicture?.Dispose(); if (path is not null) { - _picture = SvgSource.LoadPicture(path, _baseUri, entities); + _picture = SvgSource.LoadPicture(path, _baseUri, parameters); if (_picture is { }) { _avaloniaPicture = AvaloniaPicture.Record(_picture); From c87fd109ad0b64b460806eb95e225763ee73e163 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wies=C5=82aw=20S=CC=8Colte=CC=81s?= Date: Fri, 26 Jan 2024 20:26:35 +0100 Subject: [PATCH 24/24] Update MainWindow.axaml --- samples/AvaloniaSvgSample/MainWindow.axaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/samples/AvaloniaSvgSample/MainWindow.axaml b/samples/AvaloniaSvgSample/MainWindow.axaml index fd75e5771..c58229cee 100644 --- a/samples/AvaloniaSvgSample/MainWindow.axaml +++ b/samples/AvaloniaSvgSample/MainWindow.axaml @@ -28,6 +28,16 @@ + + + + + +