From ee9d89b9680fe7e147be08d67758083f238e7d65 Mon Sep 17 00:00:00 2001 From: Konstantin S Date: Mon, 13 Mar 2023 02:56:54 +0400 Subject: [PATCH] feat: Added GeneratedIconSource istead GeneratedIcon class. #48. --- README.md | 34 ++-- .../Views/NotificationView.xaml.cs | 4 +- .../InlineContextMenuWindow.xaml | 2 +- src/libs/H.NotifyIcon.Shared/GeneratedIcon.cs | 190 ------------------ .../GeneratedIconSource.System.Drawing.cs | 16 +- .../GeneratedIconSource.Wpf.cs | 4 +- .../GeneratedIconSource.cs | 42 ++++ src/libs/H.NotifyIcon.Shared/GlobalUsings.cs | 2 + .../TaskbarIcon.GeneratedIcon.cs | 22 -- .../TaskbarIcon.IconSource.cs | 21 ++ .../TaskbarIcon.Properties.cs | 32 --- src/libs/H.NotifyIcon.Shared/TaskbarIcon.cs | 1 - .../Utilities/ImageExtensions.WinRT.cs | 64 ++++++ .../Utilities/ImageExtensions.Wpf.cs | 87 ++++++++ .../Utilities/ImageExtensions.cs | 105 +++------- .../System.Drawing/StreamExtensions.cs | 4 +- 16 files changed, 281 insertions(+), 349 deletions(-) delete mode 100644 src/libs/H.NotifyIcon.Shared/GeneratedIcon.cs delete mode 100644 src/libs/H.NotifyIcon.Shared/TaskbarIcon.GeneratedIcon.cs create mode 100644 src/libs/H.NotifyIcon.Shared/TaskbarIcon.IconSource.cs create mode 100644 src/libs/H.NotifyIcon.Shared/Utilities/ImageExtensions.WinRT.cs create mode 100644 src/libs/H.NotifyIcon.Shared/Utilities/ImageExtensions.Wpf.cs diff --git a/README.md b/README.md index c64aa5b..bac2fb7 100644 --- a/README.md +++ b/README.md @@ -72,12 +72,12 @@ TaskbarIcon.ForceCreate(bool enablesEfficiencyMode = true) // default value Example 1: image ```xml - - + - + ``` Example 2: image @@ -85,21 +85,21 @@ Example 2: image - - + - + ``` Example 3: image ```xml - - + - + ``` Example 4: image ```xml - - + - + - - + + - - - + + + ``` diff --git a/src/apps/H.NotifyIcon.Apps.WinUI/Views/NotificationView.xaml.cs b/src/apps/H.NotifyIcon.Apps.WinUI/Views/NotificationView.xaml.cs index 0654d57..03587e2 100644 --- a/src/apps/H.NotifyIcon.Apps.WinUI/Views/NotificationView.xaml.cs +++ b/src/apps/H.NotifyIcon.Apps.WinUI/Views/NotificationView.xaml.cs @@ -24,9 +24,9 @@ private void ShowNotificationButton_Click(object sender, RoutedEventArgs e) "Error" => NotificationIcon.Error, _ => NotificationIcon.None, }, - customIcon: selectedIcon switch + customIconHandle: selectedIcon switch { - "Custom" => TrayIcon.Icon, + "Custom" => TrayIcon.Icon?.Handle, _ => null, }, //largeIcon: LargeIconCheckBox.IsChecked ?? false, diff --git a/src/apps/H.NotifyIcon.Apps.Wpf/Tutorials/04 - ContextMenus/InlineContextMenuWindow.xaml b/src/apps/H.NotifyIcon.Apps.Wpf/Tutorials/04 - ContextMenus/InlineContextMenuWindow.xaml index b4bee61..7400802 100644 --- a/src/apps/H.NotifyIcon.Apps.Wpf/Tutorials/04 - ContextMenus/InlineContextMenuWindow.xaml +++ b/src/apps/H.NotifyIcon.Apps.Wpf/Tutorials/04 - ContextMenus/InlineContextMenuWindow.xaml @@ -18,7 +18,7 @@ diff --git a/src/libs/H.NotifyIcon.Shared/GeneratedIcon.cs b/src/libs/H.NotifyIcon.Shared/GeneratedIcon.cs deleted file mode 100644 index 73c2b32..0000000 --- a/src/libs/H.NotifyIcon.Shared/GeneratedIcon.cs +++ /dev/null @@ -1,190 +0,0 @@ -namespace H.NotifyIcon; - -/// -/// -/// -[DependencyProperty("Text", DefaultValue = "", OnChanged = nameof(Refresh), - Description = "Defines generated icon text.", Category = Category, Localizability = Localizability.Text)] -[DependencyProperty("Margin", OnChanged = nameof(Refresh), - Description = "Defines generated icon margin.", Category = Category)] -[DependencyProperty("BackgroundType", OnChanged = nameof(Refresh), - Description = "Defines generated icon background type.", Category = Category)] -[DependencyProperty("TextMargin", OnChanged = nameof(Refresh), - Description = "Defines generated icon text margin.", Category = Category)] -[DependencyProperty("CornerRadius", OnChanged = nameof(Refresh), - Description = "Defines generated icon corner radius.", Category = Category)] -[DependencyProperty("Foreground", OnChanged = nameof(Refresh), -#if HAS_WPF - DefaultValueExpression = "Brushes.Black", -#else - DefaultValueExpression = "new SolidColorBrush(Colors.Black)", -#endif - Description = "Defines generated icon foreground.", Category = Category)] -[DependencyProperty("Background", OnChanged = nameof(Refresh), - Description = "Defines generated icon background.", Category = Category)] -[DependencyProperty("FontFamily", OnChanged = nameof(Refresh), -#if HAS_WPF - DefaultValueExpression = "SystemFonts.IconFontFamily", -#else - DefaultValueExpression = "new FontFamily(\"Segoe UI\")", -#endif - Description = "Defines generated icon font family.", Category = Category, Localizability = Localizability.Font)] -[DependencyProperty("FontStyle", OnChanged = nameof(Refresh), -#if HAS_WPF - DefaultValueExpression = "SystemFonts.IconFontStyle", -#else - DefaultValue = FontStyle.Normal, -#endif - Description = "Defines generated icon font style.", Category = Category)] -[DependencyProperty("FontWeight", OnChanged = nameof(Refresh), -#if HAS_WPF - DefaultValueExpression = "SystemFonts.IconFontWeight", -#else - DefaultValueExpression = "FontWeights.Normal", -#endif - Description = "Defines generated icon font weight.", Category = Category)] -[DependencyProperty("FontStretch", OnChanged = nameof(Refresh), -#if HAS_WPF - DefaultValueExpression = "FontStretches.Normal", -#else - DefaultValue = FontStretch.Normal, -#endif - Description = "Defines generated icon font stretch.", Category = Category)] -[DependencyProperty("FontSize", OnChanged = nameof(Refresh), -#if HAS_WPF - DefaultValueExpression = "4 * SystemFonts.IconFontSize", - TypeConverter = typeof(FontSizeConverter), -#else - DefaultValue = 4 * 12.0, -#endif - Description = "Defines generated icon font size.", Category = Category, - Localizability = Localizability.None)] -[DependencyProperty("Size", OnChanged = nameof(Refresh), DefaultValue = 128, - Description = "Defines generated icon size.", Category = Category)] -[DependencyProperty("BorderThickness", OnChanged = nameof(Refresh), - Description = "Defines generated icon border thickness.", Category = Category)] -[DependencyProperty("BorderBrush", OnChanged = nameof(Refresh), -#if HAS_WPF - DefaultValueExpression = "Brushes.Black", -#else - DefaultValueExpression = "new SolidColorBrush(Colors.Black)", -#endif - Description = "Defines generated icon border brush.", Category = Category)] -[DependencyProperty("Icon", ClsCompliant = false, - Description = "Defines generated icon. Use this for dynamically generated content.", Category = Category)] -#if HAS_WINUI || HAS_UNO -[CLSCompliant(false)] -#endif -public sealed partial class GeneratedIcon : DependencyObject, IDisposable -{ - #region Constants - - /// - /// Category name that is set on designer properties. - /// - public const string Category = "NotifyIcon"; - - #endregion - - #region Properties - - internal TaskbarIcon? TaskbarIcon { get; set; } - - partial void OnIconChanged(Icon? oldValue, Icon? newValue) - { - oldValue?.Dispose(); - - if (TaskbarIcon == null) - { - return; - } - - TaskbarIcon.UpdateIcon(newValue); - } - - #endregion - - #region Methods - - internal void Refresh() - { - if (TaskbarIcon == null) - { - return; - } - -#if HAS_SYSTEM_DRAWING - var size = Size; - using var fontFamily = - FontFamily?.ToSystemDrawingFontFamily() ?? - new System.Drawing.FontFamily(string.Empty); - using var font = new System.Drawing.Font( - fontFamily, - (float)FontSize, - FontStyle.ToSystemDrawingFontStyle(FontWeight)); - using var baseImage = TaskbarIcon.Icon?.ToBitmap(); - using var pen = BorderBrush.ToSystemDrawingPen(BorderThickness); - using var backgroundBrush = Background.ToSystemDrawingBrush(); - using var foregroundBrush = Foreground.ToSystemDrawingBrush(); - - using var bitmap = IconGenerator.Generate( - backgroundBrush: backgroundBrush, - foregroundBrush: foregroundBrush, - pen: BorderThickness > 0.01F - ? pen - : null, - backgroundType: BackgroundType, - cornerRadius: (float)CornerRadius.TopLeft, - rectangle: Margin == default - ? null - : Margin.ToSystemDrawingRectangleF(width: size, height: size), - text: Text, - font: font, - textRectangle: TextMargin.ToSystemDrawingRectangleF(width: size, height: size), - baseImage: baseImage, - size: size); - - Icon = Icon.FromHandle(bitmap.GetHicon()); -#elif HAS_SKIA_SHARP - var size = Size; - // using var fontFamily = - // FontFamily?.ToSystemDrawingFontFamily() ?? - // new System.Drawing.FontFamily(string.Empty); - // using var font = new SkiaSharp.SKFont( - // fontFamily, - // (float)FontSize, - // FontStyle.ToSkiaSharpFontStyle(FontWeight)); - //using var baseImage = TaskbarIcon.Icon?.ToBitmap(); - using var pen = BorderBrush.ToSkiaSharpPaint(BorderThickness); - using var backgroundBrush = Background.ToSkiaSharpPaint(); - using var foregroundBrush = Foreground.ToSkiaSharpPaint(); - - Icon = IconGenerator.Generate( - backgroundBrush: backgroundBrush, - foregroundBrush: foregroundBrush, - pen: BorderThickness > 0.01F - ? pen - : null, - backgroundType: BackgroundType, - cornerRadius: (float)CornerRadius.TopLeft, - rectangle: Margin == default - ? null - : Margin.ToSkiaSharpRectangle(width: size, height: size), - text: Text, - //font: font, - textRectangle: TextMargin.ToSkiaSharpRectangle(width: size, height: size), - //baseImage: baseImage, - size: size); -#endif - } - - /// - /// - /// - public void Dispose() - { - Icon?.Dispose(); - } - - #endregion -} diff --git a/src/libs/H.NotifyIcon.Shared/GeneratedIconSource.System.Drawing.cs b/src/libs/H.NotifyIcon.Shared/GeneratedIconSource.System.Drawing.cs index 79086f0..e345154 100644 --- a/src/libs/H.NotifyIcon.Shared/GeneratedIconSource.System.Drawing.cs +++ b/src/libs/H.NotifyIcon.Shared/GeneratedIconSource.System.Drawing.cs @@ -2,7 +2,7 @@ public sealed partial class GeneratedIconSource : BitmapSource { - internal System.Drawing.Bitmap Generate() + internal Bitmap Generate(Bitmap? backgroundBitmap = null) { var size = Size; using var fontFamily = @@ -12,8 +12,7 @@ internal System.Drawing.Bitmap Generate() fontFamily, (float)FontSize, FontStyle.ToSystemDrawingFontStyle(FontWeight)); - //using var baseImageStream = Background?.ToStream(); - //using var baseImage = baseImageStream?.ToBitmap(); + using var baseImage = backgroundBitmap ?? BackgroundSource?.ToBitmap(); using var pen = BorderBrush.ToSystemDrawingPen(BorderThickness); using var backgroundBrush = Background.ToSystemDrawingBrush(); using var foregroundBrush = Foreground.ToSystemDrawingBrush(); @@ -32,7 +31,16 @@ internal System.Drawing.Bitmap Generate() text: Text, font: font, textRectangle: TextMargin.ToSystemDrawingRectangleF(width: size, height: size), - baseImage: null, + baseImage: baseImage, size: size); } + + internal async Task GenerateAsync(CancellationToken cancellationToken = default) + { + using var baseImage = BackgroundSource == null + ? null + : await BackgroundSource.ToBitmapAsync(cancellationToken).ConfigureAwait(true); + + return Generate(baseImage); + } } diff --git a/src/libs/H.NotifyIcon.Shared/GeneratedIconSource.Wpf.cs b/src/libs/H.NotifyIcon.Shared/GeneratedIconSource.Wpf.cs index 54c0906..598d38c 100644 --- a/src/libs/H.NotifyIcon.Shared/GeneratedIconSource.Wpf.cs +++ b/src/libs/H.NotifyIcon.Shared/GeneratedIconSource.Wpf.cs @@ -1,5 +1,4 @@ -#if HAS_WPF -namespace H.NotifyIcon; +namespace H.NotifyIcon; /// /// @@ -34,4 +33,3 @@ protected override Freezable CreateInstanceCore() stride: bits.Stride); } } -#endif diff --git a/src/libs/H.NotifyIcon.Shared/GeneratedIconSource.cs b/src/libs/H.NotifyIcon.Shared/GeneratedIconSource.cs index 2104562..6a71aeb 100644 --- a/src/libs/H.NotifyIcon.Shared/GeneratedIconSource.cs +++ b/src/libs/H.NotifyIcon.Shared/GeneratedIconSource.cs @@ -70,6 +70,8 @@ DefaultValueExpression = "new SolidColorBrush(Colors.Black)", #endif Description = "Defines generated icon border brush.", Category = Category)] +[DependencyProperty("BackgroundSource", + Description = "Resolves an image source and uses this as background.", Category = Category)] #if HAS_WINUI || HAS_UNO [CLSCompliant(false)] #endif @@ -86,6 +88,46 @@ public sealed partial class GeneratedIconSource : BitmapSource #region Methods + /// + /// + /// + /// + public Bitmap ToBitmap() + { + return Generate(); + } + + /// + /// + /// + /// + public Icon ToIcon() + { + using var bitmap = Generate(); + + return Icon.FromHandle(bitmap.GetHicon()); + } + + /// + /// + /// + /// + public async Task ToBitmapAsync(CancellationToken cancellationToken = default) + { + return await GenerateAsync(cancellationToken).ConfigureAwait(true); + } + + /// + /// + /// + /// + public async Task ToIconAsync(CancellationToken cancellationToken = default) + { + using var bitmap = await GenerateAsync(cancellationToken).ConfigureAwait(true); + + return Icon.FromHandle(bitmap.GetHicon()); + } + internal void Refresh() { #if HAS_WPF diff --git a/src/libs/H.NotifyIcon.Shared/GlobalUsings.cs b/src/libs/H.NotifyIcon.Shared/GlobalUsings.cs index 5d781f4..bc83c16 100644 --- a/src/libs/H.NotifyIcon.Shared/GlobalUsings.cs +++ b/src/libs/H.NotifyIcon.Shared/GlobalUsings.cs @@ -59,6 +59,8 @@ #endif #if HAS_SYSTEM_DRAWING global using Icon = System.Drawing.Icon; +global using Bitmap = System.Drawing.Bitmap; #elif HAS_SKIA_SHARP global using Icon = SkiaSharp.SKBitmap; +global using Bitmap = SkiaSharp.SKBitmap; #endif diff --git a/src/libs/H.NotifyIcon.Shared/TaskbarIcon.GeneratedIcon.cs b/src/libs/H.NotifyIcon.Shared/TaskbarIcon.GeneratedIcon.cs deleted file mode 100644 index 48ce298..0000000 --- a/src/libs/H.NotifyIcon.Shared/TaskbarIcon.GeneratedIcon.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace H.NotifyIcon; - -[DependencyProperty("GeneratedIcon", - Description = "Defines generated icon.", Category = CategoryName)] -public partial class TaskbarIcon -{ - #region Properties - - partial void OnGeneratedIconChanged( - GeneratedIcon? oldValue, - GeneratedIcon? newValue) - { - oldValue?.Dispose(); - if (newValue != null) - { - newValue.TaskbarIcon = this; - newValue.Refresh(); - } - } - - #endregion -} diff --git a/src/libs/H.NotifyIcon.Shared/TaskbarIcon.IconSource.cs b/src/libs/H.NotifyIcon.Shared/TaskbarIcon.IconSource.cs new file mode 100644 index 0000000..fd7bd37 --- /dev/null +++ b/src/libs/H.NotifyIcon.Shared/TaskbarIcon.IconSource.cs @@ -0,0 +1,21 @@ +namespace H.NotifyIcon; + +[DependencyProperty("IconSource", + Description = "Resolves an image source and updates the Icon property accordingly.", Category = CategoryName)] +public partial class TaskbarIcon +{ + async partial void OnIconSourceChanged(ImageSource? oldValue, ImageSource? newValue) + { + if (newValue == null) + { + Icon = null; + return; + } + +#if MACOS + TrayIcon.Icon = NSImage.FromStream(stream); +#else + Icon = await newValue.ToIconAsync().ConfigureAwait(true); +#endif + } +} diff --git a/src/libs/H.NotifyIcon.Shared/TaskbarIcon.Properties.cs b/src/libs/H.NotifyIcon.Shared/TaskbarIcon.Properties.cs index df47ea3..905b047 100644 --- a/src/libs/H.NotifyIcon.Shared/TaskbarIcon.Properties.cs +++ b/src/libs/H.NotifyIcon.Shared/TaskbarIcon.Properties.cs @@ -8,8 +8,6 @@ #endif [DependencyProperty("Icon", ClsCompliant = false, Description = "Gets or sets the icon to be displayed. Use this for dynamically generated System.Drawing.Icons.", Category = CategoryName)] -[DependencyProperty("IconSource", - Description = "Resolves an image source and updates the Icon property accordingly.", Category = CategoryName)] public partial class TaskbarIcon { #region Constants @@ -56,36 +54,6 @@ public void UpdateIcon(Icon? value) TrayIcon.UpdateIcon((nint?)value?.Handle ?? 0); #endif } - -#if HAS_WPF - partial void OnIconSourceChanged(ImageSource? oldValue, ImageSource? newValue) -#else - async partial void OnIconSourceChanged(ImageSource? oldValue, ImageSource? newValue) -#endif - { - if (newValue == null) - { - Icon = null; - GeneratedIcon?.Refresh(); - return; - } - -#if HAS_SYSTEM_DRAWING -#if HAS_WPF - using var stream = newValue.ToStream(); -#else - using var stream = await newValue.ToStreamAsync().ConfigureAwait(true); -#endif - -#if MACOS - TrayIcon.Icon = NSImage.FromStream(stream); -#else - Icon = stream.ToSmallIcon(); -#endif -#endif - - GeneratedIcon?.Refresh(); - } #endregion diff --git a/src/libs/H.NotifyIcon.Shared/TaskbarIcon.cs b/src/libs/H.NotifyIcon.Shared/TaskbarIcon.cs index d8d6c38..9667385 100644 --- a/src/libs/H.NotifyIcon.Shared/TaskbarIcon.cs +++ b/src/libs/H.NotifyIcon.Shared/TaskbarIcon.cs @@ -260,7 +260,6 @@ protected virtual void Dispose(bool disposing) TrayIcon.Dispose(); Icon?.Dispose(); - GeneratedIcon?.Dispose(); } } diff --git a/src/libs/H.NotifyIcon.Shared/Utilities/ImageExtensions.WinRT.cs b/src/libs/H.NotifyIcon.Shared/Utilities/ImageExtensions.WinRT.cs new file mode 100644 index 0000000..a62e2e0 --- /dev/null +++ b/src/libs/H.NotifyIcon.Shared/Utilities/ImageExtensions.WinRT.cs @@ -0,0 +1,64 @@ +namespace H.NotifyIcon; + +internal static partial class ImageExtensions +{ + internal static Stream ToStream(this Uri uri) + { + var prefix = uri.Scheme switch + { + "ms-appx" or "ms-appx-web" => AppContext.BaseDirectory, + _ => string.Empty, + }; + // additional schemes, like ms-appdata could be added here + // see: https://learn.microsoft.com/en-us/windows/uwp/app-resources/uri-schemes + var absolutePath = $"{prefix}{uri.LocalPath}"; + + return File.OpenRead(absolutePath); + } + + internal static async Task ToStreamAsync(this Uri uri, CancellationToken cancellationToken = default) + { +#if IS_PACKABLE + if (Interop.DesktopBridgeHelpers.IsRunningAsUwp()) + { + var file = await StorageFile.GetFileFromApplicationUriAsync(uri); + + return await file.OpenStreamForReadAsync().ConfigureAwait(true); + } +#endif + + return uri.ToStream(); + } + + public static Stream ToStream(this ImageSource imageSource) + { + switch (imageSource) + { + case BitmapImage bitmapImage: + { + var uri = bitmapImage.UriSource; + + return uri.ToStream(); + } + + default: + throw new NotImplementedException($"ImageSource type: {imageSource.GetType()} is not supported"); + } + } + + public static async Task ToStreamAsync(this ImageSource imageSource, CancellationToken cancellationToken = default) + { + switch(imageSource) + { + case BitmapImage bitmapImage: + { + var uri = bitmapImage.UriSource; + + return await uri.ToStreamAsync(cancellationToken).ConfigureAwait(true); + } + + default: + throw new NotImplementedException($"ImageSource type: {imageSource.GetType()} is not supported"); + } + } +} diff --git a/src/libs/H.NotifyIcon.Shared/Utilities/ImageExtensions.Wpf.cs b/src/libs/H.NotifyIcon.Shared/Utilities/ImageExtensions.Wpf.cs new file mode 100644 index 0000000..894a248 --- /dev/null +++ b/src/libs/H.NotifyIcon.Shared/Utilities/ImageExtensions.Wpf.cs @@ -0,0 +1,87 @@ +using System.Globalization; + +namespace H.NotifyIcon; + +internal static partial class ImageExtensions +{ + internal static Stream ToStream(this Uri uri) + { + if (uri.Scheme == Uri.UriSchemeFile) + { + return File.OpenRead(uri.LocalPath); + } + + var streamInfo = + Application.GetResourceStream(uri) ?? + throw new ArgumentException($"Uri: {uri} is not resolved."); + + return streamInfo.Stream; + } + + internal static Task ToStreamAsync(this Uri uri, CancellationToken cancellationToken = default) + { + return Task.FromResult(uri.ToStream()); + } + + public static Stream ToStream(this ImageSource imageSource) + { + switch (imageSource) + { + case BitmapImage bitmapImage: + { + var uri = bitmapImage.UriSource; + + return uri.ToStream(); + } + case BitmapFrame frame: + { + var uri = new Uri(frame.ToString(CultureInfo.InvariantCulture)); + + return uri.ToStream(); + } + + default: + throw new NotImplementedException($"ImageSource type: {imageSource.GetType()} is not supported"); + } + } + + public static async Task ToStreamAsync(this ImageSource imageSource, CancellationToken cancellationToken = default) + { + switch(imageSource) + { + case BitmapImage bitmapImage: + { + var uri = bitmapImage.UriSource; + + return await uri.ToStreamAsync(cancellationToken).ConfigureAwait(true); + } + case BitmapFrame frame: + { + var uri = new Uri(frame.ToString(CultureInfo.InvariantCulture)); + + return await uri.ToStreamAsync(cancellationToken).ConfigureAwait(true); + } + + default: + throw new NotImplementedException($"ImageSource type: {imageSource.GetType()} is not supported"); + } + } + + public static Stream ToStream(this BitmapFrame frame) + { + var encoder = new PngBitmapEncoder(); + encoder.Frames.Add(frame); + + using var stream = new MemoryStream(); + encoder.Save(stream); + + var iconBytes = stream.ToArray().ConvertPngToIco(); + + return new MemoryStream(iconBytes); + } + + public static Stream ToStream(this BitmapSource bitmap) + { + return BitmapFrame.Create(bitmap).ToStream(); + } +} diff --git a/src/libs/H.NotifyIcon.Shared/Utilities/ImageExtensions.cs b/src/libs/H.NotifyIcon.Shared/Utilities/ImageExtensions.cs index d334363..a9d66ab 100644 --- a/src/libs/H.NotifyIcon.Shared/Utilities/ImageExtensions.cs +++ b/src/libs/H.NotifyIcon.Shared/Utilities/ImageExtensions.cs @@ -1,97 +1,52 @@ -using System.Globalization; +namespace H.NotifyIcon; -namespace H.NotifyIcon; - -internal static class ImageExtensions +internal static partial class ImageExtensions { -#if HAS_WPF - internal static Stream ToStream(this Uri uri) -#else - internal static async Task ToStreamAsync(this Uri uri) -#endif + public static Bitmap ToBitmap(this ImageSource imageSource) { -#if HAS_WPF - if (uri.Scheme == Uri.UriSchemeFile) + if (imageSource is GeneratedIconSource generatedIconSource) { - return File.OpenRead(uri.LocalPath); + return generatedIconSource.ToBitmap(); } - var streamInfo = - Application.GetResourceStream(uri) ?? - throw new ArgumentException($"Uri: {uri} is not resolved."); - return streamInfo.Stream; -#else -#if IS_PACKABLE - if (Interop.DesktopBridgeHelpers.IsRunningAsUwp()) { - var file = await StorageFile.GetFileFromApplicationUriAsync(uri); - return await file.OpenStreamForReadAsync().ConfigureAwait(true); - } else { -#endif - string prefix = ""; - if (uri.Scheme == "ms-appx" || uri.Scheme == "ms-appx-web") { - prefix = AppContext.BaseDirectory; - } - // additional schemes, like ms-appdata could be added here - // see: https://learn.microsoft.com/en-us/windows/uwp/app-resources/uri-schemes - var absolutePath = $"{prefix}{uri.LocalPath}"; - return File.OpenRead(absolutePath); -#if IS_PACKABLE - } -#endif -#endif + using var stream = imageSource.ToStream(); + + return stream.ToBitmap(); } -#if HAS_WPF - public static Stream ToStream(this ImageSource imageSource) -#else - public static async Task ToStreamAsync(this ImageSource imageSource) -#endif + public static Icon ToIcon(this ImageSource imageSource) { - switch(imageSource) + if (imageSource is GeneratedIconSource generatedIconSource) { - case BitmapImage bitmapImage: - { - var uri = bitmapImage.UriSource; - -#if HAS_WPF - return uri.ToStream(); -#else - return await uri.ToStreamAsync().ConfigureAwait(true); -#endif - } -#if HAS_WPF - case BitmapFrame frame: - { - var uri = new Uri(frame.ToString(CultureInfo.InvariantCulture)); + return generatedIconSource.ToIcon(); + } - return uri.ToStream(); - } -#endif + using var stream = imageSource.ToStream(); - default: - throw new NotImplementedException($"ImageSource type: {imageSource.GetType()} is not supported"); - } + return stream.ToSmallIcon(); } - -#if HAS_WPF - public static Stream ToStream(this BitmapFrame frame) + public static async Task ToBitmapAsync(this ImageSource imageSource, CancellationToken cancellationToken = default) { - var encoder = new PngBitmapEncoder(); - encoder.Frames.Add(frame); + if (imageSource is GeneratedIconSource generatedIconSource) + { + return await generatedIconSource.ToBitmapAsync(cancellationToken).ConfigureAwait(true); + } - using var stream = new MemoryStream(); - encoder.Save(stream); + using var stream = await imageSource.ToStreamAsync(cancellationToken).ConfigureAwait(true); - var iconBytes = stream.ToArray().ConvertPngToIco(); - - return new MemoryStream(iconBytes); + return stream.ToBitmap(); } - public static Stream ToStream(this BitmapSource bitmap) + public static async Task ToIconAsync(this ImageSource imageSource, CancellationToken cancellationToken = default) { - return BitmapFrame.Create(bitmap).ToStream(); - } + if (imageSource is GeneratedIconSource generatedIconSource) + { + return await generatedIconSource.ToIconAsync(cancellationToken).ConfigureAwait(true); + } + + using var stream = await imageSource.ToStreamAsync(cancellationToken).ConfigureAwait(true); -#endif + return stream.ToSmallIcon(); + } } diff --git a/src/libs/H.NotifyIcon.Shared/Utilities/System.Drawing/StreamExtensions.cs b/src/libs/H.NotifyIcon.Shared/Utilities/System.Drawing/StreamExtensions.cs index e7cbdd4..4ce37ef 100644 --- a/src/libs/H.NotifyIcon.Shared/Utilities/System.Drawing/StreamExtensions.cs +++ b/src/libs/H.NotifyIcon.Shared/Utilities/System.Drawing/StreamExtensions.cs @@ -11,9 +11,9 @@ internal static Icon ToSmallIcon(this Stream stream) return new Icon(stream, iconSize); } - internal static System.Drawing.Bitmap ToBitmap(this Stream stream) + internal static Bitmap ToBitmap(this Stream stream) { - return new System.Drawing.Bitmap(stream); + return new Bitmap(stream); } internal static (int Width, int Height, int BitsPerPixel) GetMetadata(this Stream stream)