From 89244cb8d14886d96fdbe20915173a0633c303aa Mon Sep 17 00:00:00 2001 From: Chris Pulman Date: Sat, 14 Sep 2024 00:03:46 +0100 Subject: [PATCH] FIx issue stopping update to latest version of ScottPlot --- Directory.Build.props | 2 +- src/CrissCross.MAUI/CrissCross.MAUI.csproj | 1 + .../CrissCross.WPF.Plot.csproj | 2 +- src/CrissCross.WPF.Plot/Live/StreamerUI.cs | 9 +- .../Controls/Border/Border.cs | 15 +- .../Controls/Button/Button.cs | 1 - .../Controls/Calendar/Calendar.cs | 15 +- .../Controls/CheckBox/CheckBox.cs | 15 +- .../Controls/ComboBox/ComboBox.cs | 15 +- .../DateTimePicker/DateTimePicker.xaml.cs | 233 +++++++------- .../Controls/GifImage/AnimationCache.cs | 207 +++++++------ .../Controls/GifImage/AnimationCacheEntry.cs | 13 +- .../Decoding/GifApplicationExtension.cs | 65 ++-- .../Controls/GifImage/Decoding/GifBlock.cs | 35 ++- .../GifImage/Decoding/GifBlockKind.cs | 15 +- .../Controls/GifImage/Decoding/GifColor.cs | 15 +- .../GifImage/Decoding/GifCommentExtension.cs | 43 ++- .../GifImage/Decoding/GifDecoderException.cs | 35 ++- .../GifImage/Decoding/GifExtension.cs | 39 ++- .../Controls/GifImage/Decoding/GifFile.cs | 133 ++++---- .../Controls/GifImage/Decoding/GifFrame.cs | 55 ++-- .../Decoding/GifGraphicControlExtension.cs | 71 +++-- .../Controls/GifImage/Decoding/GifHeader.cs | 53 ++-- .../Controls/GifImage/Decoding/GifHelpers.cs | 129 ++++---- .../GifImage/Decoding/GifImageData.cs | 35 ++- .../GifImage/Decoding/GifImageDescriptor.cs | 65 ++-- .../Decoding/GifLogicalScreenDescriptor.cs | 69 +++-- .../Decoding/GifPlainTextExtension.cs | 95 +++--- .../Controls/GifImage/Decoding/GifTrailer.cs | 19 +- .../GifImage/ImageAnimationController.cs | 287 +++++++++--------- src/CrissCross.WPF.UI/Controls/Grid/Grid.cs | 15 +- .../Controls/GroupBox/GroupBox.cs | 15 +- src/CrissCross.WPF.UI/Controls/Label/Label.cs | 15 +- src/CrissCross.WPF.UI/Controls/Page/Page.cs | 15 +- .../Controls/StackPanel/StackPanel.cs | 15 +- src/CrissCross.WPF/CrissCross.WPF.csproj | 35 ++- .../CrissCross.XamForms.csproj | 5 + 37 files changed, 942 insertions(+), 959 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 0e7b282..a1fcc38 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -54,7 +54,7 @@ - + diff --git a/src/CrissCross.MAUI/CrissCross.MAUI.csproj b/src/CrissCross.MAUI/CrissCross.MAUI.csproj index b4c9188..bb8eb4a 100644 --- a/src/CrissCross.MAUI/CrissCross.MAUI.csproj +++ b/src/CrissCross.MAUI/CrissCross.MAUI.csproj @@ -16,6 +16,7 @@ 6.5 + diff --git a/src/CrissCross.WPF.Plot/CrissCross.WPF.Plot.csproj b/src/CrissCross.WPF.Plot/CrissCross.WPF.Plot.csproj index 8f42de7..b4a9f0c 100644 --- a/src/CrissCross.WPF.Plot/CrissCross.WPF.Plot.csproj +++ b/src/CrissCross.WPF.Plot/CrissCross.WPF.Plot.csproj @@ -15,7 +15,7 @@ - + diff --git a/src/CrissCross.WPF.Plot/Live/StreamerUI.cs b/src/CrissCross.WPF.Plot/Live/StreamerUI.cs index e1ac1cf..3c50303 100644 --- a/src/CrissCross.WPF.Plot/Live/StreamerUI.cs +++ b/src/CrissCross.WPF.Plot/Live/StreamerUI.cs @@ -263,13 +263,16 @@ public void UpdateStream(IObservable<(string? Name, IList? Value, IList< var datetime = new List(data.DateTime.ToList().ConvertAll(x => new DateTime(Convert.ToInt64(x)).ToOADate())); var coord = datetime.Zip(values, (d, v) => new Coordinates(d, v)); ItemName = data.Name; - Streamer!.Data.Clear(); - Streamer.Add(coord); + Streamer?.Data.Coordinates.Clear(); + foreach (var co in coord) + { + Streamer?.Data.Add(co); + } // UPDATE X AXIS if (ManualScale || AutoScale) { - Plot.Plot.Axes.SetLimitsX(doublelimits, doublenow, Streamer.Axes.XAxis); + Plot.Plot.Axes.SetLimitsX(doublelimits, doublenow, Streamer!.Axes.XAxis); } } diff --git a/src/CrissCross.WPF.UI/Controls/Border/Border.cs b/src/CrissCross.WPF.UI/Controls/Border/Border.cs index 13ed59d..d7f145d 100644 --- a/src/CrissCross.WPF.UI/Controls/Border/Border.cs +++ b/src/CrissCross.WPF.UI/Controls/Border/Border.cs @@ -2,11 +2,10 @@ // ReactiveUI Association Incorporated licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -namespace CrissCross.WPF.UI.Controls -{ - /// - /// Extended . - /// - /// - public class Border : System.Windows.Controls.Border; -} +namespace CrissCross.WPF.UI.Controls; + +/// +/// Extended . +/// +/// +public class Border : System.Windows.Controls.Border; diff --git a/src/CrissCross.WPF.UI/Controls/Button/Button.cs b/src/CrissCross.WPF.UI/Controls/Button/Button.cs index d4f242c..7407067 100644 --- a/src/CrissCross.WPF.UI/Controls/Button/Button.cs +++ b/src/CrissCross.WPF.UI/Controls/Button/Button.cs @@ -2,7 +2,6 @@ // ReactiveUI Association Incorporated licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -using System.Windows.Controls; using CrissCross.WPF.UI.Converters; namespace CrissCross.WPF.UI.Controls; diff --git a/src/CrissCross.WPF.UI/Controls/Calendar/Calendar.cs b/src/CrissCross.WPF.UI/Controls/Calendar/Calendar.cs index 9b30f9c..0f5603e 100644 --- a/src/CrissCross.WPF.UI/Controls/Calendar/Calendar.cs +++ b/src/CrissCross.WPF.UI/Controls/Calendar/Calendar.cs @@ -2,11 +2,10 @@ // ReactiveUI Association Incorporated licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -namespace CrissCross.WPF.UI.Controls -{ - /// - /// Extended . - /// - /// - public class Calendar : System.Windows.Controls.Calendar; -} +namespace CrissCross.WPF.UI.Controls; + +/// +/// Extended . +/// +/// +public class Calendar : System.Windows.Controls.Calendar; diff --git a/src/CrissCross.WPF.UI/Controls/CheckBox/CheckBox.cs b/src/CrissCross.WPF.UI/Controls/CheckBox/CheckBox.cs index 5d6c7db..2afeadb 100644 --- a/src/CrissCross.WPF.UI/Controls/CheckBox/CheckBox.cs +++ b/src/CrissCross.WPF.UI/Controls/CheckBox/CheckBox.cs @@ -2,11 +2,10 @@ // ReactiveUI Association Incorporated licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -namespace CrissCross.WPF.UI.Controls -{ - /// - /// Extended . - /// - /// - public class CheckBox : System.Windows.Controls.CheckBox; -} +namespace CrissCross.WPF.UI.Controls; + +/// +/// Extended . +/// +/// +public class CheckBox : System.Windows.Controls.CheckBox; diff --git a/src/CrissCross.WPF.UI/Controls/ComboBox/ComboBox.cs b/src/CrissCross.WPF.UI/Controls/ComboBox/ComboBox.cs index 3c7962e..6e15281 100644 --- a/src/CrissCross.WPF.UI/Controls/ComboBox/ComboBox.cs +++ b/src/CrissCross.WPF.UI/Controls/ComboBox/ComboBox.cs @@ -2,11 +2,10 @@ // ReactiveUI Association Incorporated licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -namespace CrissCross.WPF.UI.Controls -{ - /// - /// Extended . - /// - /// - public class ComboBox : System.Windows.Controls.ComboBox; -} +namespace CrissCross.WPF.UI.Controls; + +/// +/// Extended . +/// +/// +public class ComboBox : System.Windows.Controls.ComboBox; diff --git a/src/CrissCross.WPF.UI/Controls/DateTimePicker/DateTimePicker.xaml.cs b/src/CrissCross.WPF.UI/Controls/DateTimePicker/DateTimePicker.xaml.cs index fd5ea6c..16ed71a 100644 --- a/src/CrissCross.WPF.UI/Controls/DateTimePicker/DateTimePicker.xaml.cs +++ b/src/CrissCross.WPF.UI/Controls/DateTimePicker/DateTimePicker.xaml.cs @@ -6,146 +6,145 @@ using System.Windows.Controls.Primitives; using System.Windows.Input; -namespace CrissCross.WPF.UI +namespace CrissCross.WPF.UI; + +/// +/// Interaction logic for DateTimePicker.xaml. +/// +public partial class DateTimePicker : UserControl { /// - /// Interaction logic for DateTimePicker.xaml. + /// The selected date property. /// - public partial class DateTimePicker : UserControl - { - /// - /// The selected date property. - /// - public static readonly DependencyProperty SelectedDateProperty = DependencyProperty.Register( - nameof(SelectedDate), + public static readonly DependencyProperty SelectedDateProperty = DependencyProperty.Register( + nameof(SelectedDate), + typeof(DateTime), + typeof(DateTimePicker), + new FrameworkPropertyMetadata(DateTime.Now, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); + + /// + /// The display date start property. + /// + public static readonly DependencyProperty DisplayDateStartProperty = + DependencyProperty.Register( + nameof(DisplayDateStart), typeof(DateTime), typeof(DateTimePicker), - new FrameworkPropertyMetadata(DateTime.Now, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); - - /// - /// The display date start property. - /// - public static readonly DependencyProperty DisplayDateStartProperty = - DependencyProperty.Register( - nameof(DisplayDateStart), - typeof(DateTime), - typeof(DateTimePicker), - new PropertyMetadata(DateTime.MinValue, DateChanged)); - - /// - /// The display date end property. - /// - public static readonly DependencyProperty DisplayDateEndProperty = - DependencyProperty.Register( - nameof(DisplayDateEnd), - typeof(DateTime), - typeof(DateTimePicker), - new PropertyMetadata(DateTime.Now, DateChanged)); - - private const string DateTimeFormat = "dd.MM.yyyy HH:mm"; - - /// - /// Initializes a new instance of the class. - /// - public DateTimePicker() - { - InitializeComponent(); - CalDisplay.SelectedDatesChanged += CalDisplay_SelectedDatesChanged; - CalDisplay.SelectedDate = DateTime.Now; - } + new PropertyMetadata(DateTime.MinValue, DateChanged)); - /// - /// Gets or sets the selected date. - /// - /// - /// The selected date. - /// - public DateTime SelectedDate - { - get => (DateTime)GetValue(SelectedDateProperty); - set => SetValue(SelectedDateProperty, value); - } + /// + /// The display date end property. + /// + public static readonly DependencyProperty DisplayDateEndProperty = + DependencyProperty.Register( + nameof(DisplayDateEnd), + typeof(DateTime), + typeof(DateTimePicker), + new PropertyMetadata(DateTime.Now, DateChanged)); - /// - /// Gets or sets the display date start. - /// - /// - /// The display date start. - /// - public DateTime DisplayDateStart - { - get => (DateTime)GetValue(DisplayDateStartProperty); - set => SetValue(DisplayDateStartProperty, value); - } + private const string DateTimeFormat = "dd.MM.yyyy HH:mm"; - /// - /// Gets or sets the display date end. - /// - /// - /// The display date end. - /// - public DateTime DisplayDateEnd - { - get => (DateTime)GetValue(DisplayDateEndProperty); - set => SetValue(DisplayDateEndProperty, value); - } + /// + /// Initializes a new instance of the class. + /// + public DateTimePicker() + { + InitializeComponent(); + CalDisplay.SelectedDatesChanged += CalDisplay_SelectedDatesChanged; + CalDisplay.SelectedDate = DateTime.Now; + } + + /// + /// Gets or sets the selected date. + /// + /// + /// The selected date. + /// + public DateTime SelectedDate + { + get => (DateTime)GetValue(SelectedDateProperty); + set => SetValue(SelectedDateProperty, value); + } + + /// + /// Gets or sets the display date start. + /// + /// + /// The display date start. + /// + public DateTime DisplayDateStart + { + get => (DateTime)GetValue(DisplayDateStartProperty); + set => SetValue(DisplayDateStartProperty, value); + } + + /// + /// Gets or sets the display date end. + /// + /// + /// The display date end. + /// + public DateTime DisplayDateEnd + { + get => (DateTime)GetValue(DisplayDateEndProperty); + set => SetValue(DisplayDateEndProperty, value); + } - private static void DateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + private static void DateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + if (d is DateTimePicker dateTimePicker) { - if (d is DateTimePicker dateTimePicker) + if (dateTimePicker.DisplayDateStart > dateTimePicker.DisplayDateEnd) { - if (dateTimePicker.DisplayDateStart > dateTimePicker.DisplayDateEnd) - { - dateTimePicker.DisplayDateEnd = dateTimePicker.DisplayDateStart; - } - - if (dateTimePicker.DisplayDateStart > dateTimePicker.SelectedDate) - { - dateTimePicker.SelectedDate = dateTimePicker.DisplayDateStart; - } - - if (dateTimePicker.DisplayDateEnd < dateTimePicker.SelectedDate) - { - dateTimePicker.SelectedDate = dateTimePicker.DisplayDateEnd; - } - - dateTimePicker.CalDisplay_SelectedDatesChanged(null, EventArgs.Empty); + dateTimePicker.DisplayDateEnd = dateTimePicker.DisplayDateStart; } - } - private void CalDisplay_SelectedDatesChanged(object? sender, EventArgs e) - { - var hours = (Hours?.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "0"; - var minutes = (Min?.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "0"; - var timeSpan = TimeSpan.Parse(hours + ":" + minutes); - if (CalDisplay.SelectedDate.Value.Date == DateTime.Today.Date && timeSpan.CompareTo(DateTime.Now.TimeOfDay) < 0) + if (dateTimePicker.DisplayDateStart > dateTimePicker.SelectedDate) { - timeSpan = TimeSpan.FromHours(DateTime.Now.Hour + 1); + dateTimePicker.SelectedDate = dateTimePicker.DisplayDateStart; } - var date = CalDisplay.SelectedDate.Value.Date + timeSpan; - DateDisplay.Text = date.ToString(DateTimeFormat); - SelectedDate = date; - } + if (dateTimePicker.DisplayDateEnd < dateTimePicker.SelectedDate) + { + dateTimePicker.SelectedDate = dateTimePicker.DisplayDateEnd; + } - private void SaveTime_Click(object sender, RoutedEventArgs e) - { - CalDisplay_SelectedDatesChanged(SaveTime, EventArgs.Empty); - PopUpCalendarButton.IsChecked = false; + dateTimePicker.CalDisplay_SelectedDatesChanged(null, EventArgs.Empty); } + } - private void Time_SelectionChanged(object sender, SelectionChangedEventArgs e) + private void CalDisplay_SelectedDatesChanged(object? sender, EventArgs e) + { + var hours = (Hours?.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "0"; + var minutes = (Min?.SelectedItem as ComboBoxItem)?.Content?.ToString() ?? "0"; + var timeSpan = TimeSpan.Parse(hours + ":" + minutes); + if (CalDisplay.SelectedDate.Value.Date == DateTime.Today.Date && timeSpan.CompareTo(DateTime.Now.TimeOfDay) < 0) { - CalDisplay_SelectedDatesChanged(sender, e); + timeSpan = TimeSpan.FromHours(DateTime.Now.Hour + 1); } - private void CalDisplay_PreviewMouseUp(object sender, MouseButtonEventArgs e) + var date = CalDisplay.SelectedDate.Value.Date + timeSpan; + DateDisplay.Text = date.ToString(DateTimeFormat); + SelectedDate = date; + } + + private void SaveTime_Click(object sender, RoutedEventArgs e) + { + CalDisplay_SelectedDatesChanged(SaveTime, EventArgs.Empty); + PopUpCalendarButton.IsChecked = false; + } + + private void Time_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + CalDisplay_SelectedDatesChanged(sender, e); + } + + private void CalDisplay_PreviewMouseUp(object sender, MouseButtonEventArgs e) + { + // that it's not necessary to click twice after opening the calendar https://stackoverflow.com/q/6024372 + if (Mouse.Captured is CalendarItem) { - // that it's not necessary to click twice after opening the calendar https://stackoverflow.com/q/6024372 - if (Mouse.Captured is CalendarItem) - { - Mouse.Capture(null); - } + Mouse.Capture(null); } } } diff --git a/src/CrissCross.WPF.UI/Controls/GifImage/AnimationCache.cs b/src/CrissCross.WPF.UI/Controls/GifImage/AnimationCache.cs index e02b801..c805344 100644 --- a/src/CrissCross.WPF.UI/Controls/GifImage/AnimationCache.cs +++ b/src/CrissCross.WPF.UI/Controls/GifImage/AnimationCache.cs @@ -4,154 +4,153 @@ using System.Windows.Media.Imaging; -namespace CrissCross.WPF.UI.Controls +namespace CrissCross.WPF.UI.Controls; + +internal static class AnimationCache { - internal static class AnimationCache + private static readonly Dictionary _animationCache = new Dictionary(); + private static readonly Dictionary> _imageControls = new Dictionary>(); + + public static void AddControlForSource(ImageSource source, Image imageControl) + { + var cacheKey = new CacheKey(source); + if (!_imageControls.TryGetValue(cacheKey, out var controls)) + { + _imageControls[cacheKey] = controls = new HashSet(); + } + + controls.Add(imageControl); + } + + public static void RemoveControlForSource(ImageSource source, Image imageControl) + { + var cacheKey = new CacheKey(source); + if (_imageControls.TryGetValue(cacheKey, out var controls) && controls.Remove(imageControl) && controls.Count == 0) + { + _animationCache.Remove(cacheKey); + _imageControls.Remove(cacheKey); + } + } + + public static void Add(ImageSource source, AnimationCacheEntry entry) + { + var key = new CacheKey(source); + _animationCache[key] = entry; + } + + public static void Remove(ImageSource source) { - private static readonly Dictionary _animationCache = new Dictionary(); - private static readonly Dictionary> _imageControls = new Dictionary>(); + var key = new CacheKey(source); + _animationCache.Remove(key); + } + + public static AnimationCacheEntry? Get(ImageSource source) + { + var key = new CacheKey(source); + _animationCache.TryGetValue(key, out var entry); + return entry; + } - public static void AddControlForSource(ImageSource source, Image imageControl) + private readonly struct CacheKey(ImageSource source) : IEquatable + { + private readonly ImageSource _source = source; + + public override bool Equals(object? obj) { - var cacheKey = new CacheKey(source); - if (!_imageControls.TryGetValue(cacheKey, out var controls)) + if (ReferenceEquals(null, obj)) { - _imageControls[cacheKey] = controls = new HashSet(); + return false; } - controls.Add(imageControl); - } + if (Equals(obj)) + { + return true; + } - public static void RemoveControlForSource(ImageSource source, Image imageControl) - { - var cacheKey = new CacheKey(source); - if (_imageControls.TryGetValue(cacheKey, out var controls) && controls.Remove(imageControl) && controls.Count == 0) + if (obj.GetType() != GetType()) { - _animationCache.Remove(cacheKey); - _imageControls.Remove(cacheKey); + return false; } - } - public static void Add(ImageSource source, AnimationCacheEntry entry) - { - var key = new CacheKey(source); - _animationCache[key] = entry; + return Equals((CacheKey)obj); } - public static void Remove(ImageSource source) - { - var key = new CacheKey(source); - _animationCache.Remove(key); - } + public bool Equals(CacheKey other) => ImageEquals(_source, other._source); - public static AnimationCacheEntry? Get(ImageSource source) - { - var key = new CacheKey(source); - _animationCache.TryGetValue(key, out var entry); - return entry; - } + public override int GetHashCode() => ImageGetHashCode(_source); - private readonly struct CacheKey(ImageSource source) : IEquatable + private static int ImageGetHashCode(ImageSource image) { - private readonly ImageSource _source = source; - - public override bool Equals(object? obj) + if (image != null) { - if (ReferenceEquals(null, obj)) - { - return false; - } - - if (Equals(obj)) + var uri = GetUri(image); + if (uri != null) { - return true; + return uri.GetHashCode(); } - - if (obj.GetType() != GetType()) - { - return false; - } - - return Equals((CacheKey)obj); } - public bool Equals(CacheKey other) => ImageEquals(_source, other._source); - - public override int GetHashCode() => ImageGetHashCode(_source); + return 0; + } - private static int ImageGetHashCode(ImageSource image) + private static bool ImageEquals(ImageSource x, ImageSource y) + { + if (Equals(x, y)) { - if (image != null) - { - var uri = GetUri(image); - if (uri != null) - { - return uri.GetHashCode(); - } - } + return true; + } - return 0; + if ((x == null) != (y == null)) + { + return false; } - private static bool ImageEquals(ImageSource x, ImageSource y) + // They can't both be null or Equals would have returned true + // and if any is null, the previous would have detected it + // ReSharper disable PossibleNullReferenceException + if (x?.GetType() != y?.GetType()) { - if (Equals(x, y)) - { - return true; - } + return false; + } - if ((x == null) != (y == null)) + var xUri = GetUri(x); + var yUri = GetUri(y); + return xUri != null && xUri == yUri; + } + + private static Uri? GetUri(ImageSource? image) + { + if (image is BitmapImage bmp && bmp.UriSource != null) + { + if (bmp.UriSource.IsAbsoluteUri) { - return false; + return bmp.UriSource; } - // They can't both be null or Equals would have returned true - // and if any is null, the previous would have detected it - // ReSharper disable PossibleNullReferenceException - if (x?.GetType() != y?.GetType()) + if (bmp.BaseUri != null) { - return false; + return new Uri(bmp.BaseUri, bmp.UriSource); } - - var xUri = GetUri(x); - var yUri = GetUri(y); - return xUri != null && xUri == yUri; } - private static Uri? GetUri(ImageSource? image) + if (image is BitmapFrame frame) { - if (image is BitmapImage bmp && bmp.UriSource != null) + var s = frame.ToString(); + if (s != frame.GetType().FullName && Uri.TryCreate(s, UriKind.RelativeOrAbsolute, out var fUri)) { - if (bmp.UriSource.IsAbsoluteUri) - { - return bmp.UriSource; - } - - if (bmp.BaseUri != null) + if (fUri.IsAbsoluteUri) { - return new Uri(bmp.BaseUri, bmp.UriSource); + return fUri; } - } - if (image is BitmapFrame frame) - { - var s = frame.ToString(); - if (s != frame.GetType().FullName && Uri.TryCreate(s, UriKind.RelativeOrAbsolute, out var fUri)) + if (frame.BaseUri != null) { - if (fUri.IsAbsoluteUri) - { - return fUri; - } - - if (frame.BaseUri != null) - { - return new Uri(frame.BaseUri, fUri); - } + return new Uri(frame.BaseUri, fUri); } } - - return null; } + + return null; } } } diff --git a/src/CrissCross.WPF.UI/Controls/GifImage/AnimationCacheEntry.cs b/src/CrissCross.WPF.UI/Controls/GifImage/AnimationCacheEntry.cs index 257bb29..04b9c37 100644 --- a/src/CrissCross.WPF.UI/Controls/GifImage/AnimationCacheEntry.cs +++ b/src/CrissCross.WPF.UI/Controls/GifImage/AnimationCacheEntry.cs @@ -4,14 +4,13 @@ using System.Windows.Media.Animation; -namespace CrissCross.WPF.UI.Controls +namespace CrissCross.WPF.UI.Controls; + +internal class AnimationCacheEntry(ObjectKeyFrameCollection keyFrames, Duration duration, int repeatCountFromMetadata) { - internal class AnimationCacheEntry(ObjectKeyFrameCollection keyFrames, Duration duration, int repeatCountFromMetadata) - { - public ObjectKeyFrameCollection KeyFrames { get; } = keyFrames; + public ObjectKeyFrameCollection KeyFrames { get; } = keyFrames; - public Duration Duration { get; } = duration; + public Duration Duration { get; } = duration; - public int RepeatCountFromMetadata { get; } = repeatCountFromMetadata; - } + public int RepeatCountFromMetadata { get; } = repeatCountFromMetadata; } diff --git a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifApplicationExtension.cs b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifApplicationExtension.cs index 4935486..48db2b0 100644 --- a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifApplicationExtension.cs +++ b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifApplicationExtension.cs @@ -5,50 +5,49 @@ using System.IO; using System.Text; -namespace CrissCross.WPF.UI.Controls.Decoding +namespace CrissCross.WPF.UI.Controls.Decoding; + +// label 0xFF +internal sealed class GifApplicationExtension : GifExtension { - // label 0xFF - internal sealed class GifApplicationExtension : GifExtension + internal const int ExtensionLabel = 0xFF; + + private GifApplicationExtension() { - internal const int ExtensionLabel = 0xFF; + } - private GifApplicationExtension() - { - } + public int BlockSize { get; private set; } - public int BlockSize { get; private set; } + public string? ApplicationIdentifier { get; private set; } - public string? ApplicationIdentifier { get; private set; } + public byte[]? AuthenticationCode { get; private set; } - public byte[]? AuthenticationCode { get; private set; } + public byte[]? Data { get; private set; } - public byte[]? Data { get; private set; } + internal override GifBlockKind Kind => GifBlockKind.SpecialPurpose; - internal override GifBlockKind Kind => GifBlockKind.SpecialPurpose; + internal static GifApplicationExtension ReadApplication(Stream stream) + { + var ext = new GifApplicationExtension(); + ext.Read(stream); + return ext; + } - internal static GifApplicationExtension ReadApplication(Stream stream) + private void Read(Stream stream) + { + // Note: at this point, the label (0xFF) has already been read + var bytes = new byte[12]; + stream.ReadAll(bytes, 0, bytes.Length); + BlockSize = bytes[0]; // should always be 11 + if (BlockSize != 11) { - var ext = new GifApplicationExtension(); - ext.Read(stream); - return ext; + throw GifHelpers.InvalidBlockSizeException("Application Extension", 11, BlockSize); } - private void Read(Stream stream) - { - // Note: at this point, the label (0xFF) has already been read - var bytes = new byte[12]; - stream.ReadAll(bytes, 0, bytes.Length); - BlockSize = bytes[0]; // should always be 11 - if (BlockSize != 11) - { - throw GifHelpers.InvalidBlockSizeException("Application Extension", 11, BlockSize); - } - - ApplicationIdentifier = Encoding.ASCII.GetString(bytes, 1, 8); - var authCode = new byte[3]; - Array.Copy(bytes, 9, authCode, 0, 3); - AuthenticationCode = authCode; - Data = GifHelpers.ReadDataBlocks(stream, false); - } + ApplicationIdentifier = Encoding.ASCII.GetString(bytes, 1, 8); + var authCode = new byte[3]; + Array.Copy(bytes, 9, authCode, 0, 3); + AuthenticationCode = authCode; + Data = GifHelpers.ReadDataBlocks(stream, false); } } diff --git a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifBlock.cs b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifBlock.cs index 7ff9ec3..5dd2c35 100644 --- a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifBlock.cs +++ b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifBlock.cs @@ -4,27 +4,26 @@ using System.IO; -namespace CrissCross.WPF.UI.Controls.Decoding +namespace CrissCross.WPF.UI.Controls.Decoding; + +internal abstract class GifBlock { - internal abstract class GifBlock - { - internal abstract GifBlockKind Kind { get; } + internal abstract GifBlockKind Kind { get; } - internal static GifBlock ReadBlock(Stream stream, IEnumerable controlExtensions, bool metadataOnly) + internal static GifBlock ReadBlock(Stream stream, IEnumerable controlExtensions, bool metadataOnly) + { + var blockId = stream.ReadByte(); + if (blockId < 0) { - var blockId = stream.ReadByte(); - if (blockId < 0) - { - throw GifHelpers.UnexpectedEndOfStreamException(); - } - - return blockId switch - { - GifExtension.ExtensionIntroducer => GifExtension.ReadExtension(stream, controlExtensions, metadataOnly), - GifFrame.ImageSeparator => GifFrame.ReadFrame(stream, controlExtensions, metadataOnly), - GifTrailer.TrailerByte => GifTrailer.ReadTrailer(), - _ => throw GifHelpers.UnknownBlockTypeException(blockId), - }; + throw GifHelpers.UnexpectedEndOfStreamException(); } + + return blockId switch + { + GifExtension.ExtensionIntroducer => GifExtension.ReadExtension(stream, controlExtensions, metadataOnly), + GifFrame.ImageSeparator => GifFrame.ReadFrame(stream, controlExtensions, metadataOnly), + GifTrailer.TrailerByte => GifTrailer.ReadTrailer(), + _ => throw GifHelpers.UnknownBlockTypeException(blockId), + }; } } diff --git a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifBlockKind.cs b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifBlockKind.cs index 2bf4c91..f0a3e5f 100644 --- a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifBlockKind.cs +++ b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifBlockKind.cs @@ -2,13 +2,12 @@ // ReactiveUI Association Incorporated licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -namespace CrissCross.WPF.UI.Controls.Decoding +namespace CrissCross.WPF.UI.Controls.Decoding; + +internal enum GifBlockKind { - internal enum GifBlockKind - { - Control, - GraphicRendering, - SpecialPurpose, - Other - } + Control, + GraphicRendering, + SpecialPurpose, + Other } diff --git a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifColor.cs b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifColor.cs index c6c5a44..9bcb7f0 100644 --- a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifColor.cs +++ b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifColor.cs @@ -2,16 +2,15 @@ // ReactiveUI Association Incorporated licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -namespace CrissCross.WPF.UI.Controls.Decoding +namespace CrissCross.WPF.UI.Controls.Decoding; + +internal readonly record struct GifColor(byte R, byte G, byte B) { - internal readonly record struct GifColor(byte R, byte G, byte B) - { - public byte R { get; } = R; + public byte R { get; } = R; - public byte G { get; } = G; + public byte G { get; } = G; - public byte B { get; } = B; + public byte B { get; } = B; - public override readonly string ToString() => string.Format("#{0:x2}{1:x2}{2:x2}", R, G, B); - } + public override readonly string ToString() => string.Format("#{0:x2}{1:x2}{2:x2}", R, G, B); } diff --git a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifCommentExtension.cs b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifCommentExtension.cs index 57d30aa..5c0ee9b 100644 --- a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifCommentExtension.cs +++ b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifCommentExtension.cs @@ -5,35 +5,34 @@ using System.IO; using System.Text; -namespace CrissCross.WPF.UI.Controls.Decoding +namespace CrissCross.WPF.UI.Controls.Decoding; + +internal sealed class GifCommentExtension : GifExtension { - internal sealed class GifCommentExtension : GifExtension - { - internal const int ExtensionLabel = 0xFE; + internal const int ExtensionLabel = 0xFE; - private GifCommentExtension() - { - } + private GifCommentExtension() + { + } - public string? Text { get; private set; } + public string? Text { get; private set; } - internal override GifBlockKind Kind => GifBlockKind.SpecialPurpose; + internal override GifBlockKind Kind => GifBlockKind.SpecialPurpose; - internal static GifCommentExtension ReadComment(Stream stream) - { - var comment = new GifCommentExtension(); - comment.Read(stream); - return comment; - } + internal static GifCommentExtension ReadComment(Stream stream) + { + var comment = new GifCommentExtension(); + comment.Read(stream); + return comment; + } - private void Read(Stream stream) + private void Read(Stream stream) + { + // Note: at this point, the label (0xFE) has already been read + var bytes = GifHelpers.ReadDataBlocks(stream, false); + if (bytes != null) { - // Note: at this point, the label (0xFE) has already been read - var bytes = GifHelpers.ReadDataBlocks(stream, false); - if (bytes != null) - { - Text = Encoding.ASCII.GetString(bytes); - } + Text = Encoding.ASCII.GetString(bytes); } } } diff --git a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifDecoderException.cs b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifDecoderException.cs index 07efcfa..2a0721d 100644 --- a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifDecoderException.cs +++ b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifDecoderException.cs @@ -2,27 +2,26 @@ // ReactiveUI Association Incorporated licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -namespace CrissCross.WPF.UI.Controls.Decoding +namespace CrissCross.WPF.UI.Controls.Decoding; + +/// +/// Gif Decoder Exception. +/// +/// +[Serializable] +public class GifDecoderException : Exception { - /// - /// Gif Decoder Exception. - /// - /// - [Serializable] - public class GifDecoderException : Exception + internal GifDecoderException() { - internal GifDecoderException() - { - } + } - internal GifDecoderException(string message) - : base(message) - { - } + internal GifDecoderException(string message) + : base(message) + { + } - internal GifDecoderException(string message, Exception inner) - : base(message, inner) - { - } + internal GifDecoderException(string message, Exception inner) + : base(message, inner) + { } } diff --git a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifExtension.cs b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifExtension.cs index a4ecdbf..e038ab5 100644 --- a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifExtension.cs +++ b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifExtension.cs @@ -4,29 +4,28 @@ using System.IO; -namespace CrissCross.WPF.UI.Controls.Decoding +namespace CrissCross.WPF.UI.Controls.Decoding; + +internal abstract class GifExtension : GifBlock { - internal abstract class GifExtension : GifBlock - { - internal const int ExtensionIntroducer = 0x21; + internal const int ExtensionIntroducer = 0x21; - internal static GifExtension ReadExtension(Stream stream, IEnumerable controlExtensions, bool metadataOnly) + internal static GifExtension ReadExtension(Stream stream, IEnumerable controlExtensions, bool metadataOnly) + { + // Note: at this point, the Extension Introducer (0x21) has already been read + var label = stream.ReadByte(); + if (label < 0) { - // Note: at this point, the Extension Introducer (0x21) has already been read - var label = stream.ReadByte(); - if (label < 0) - { - throw GifHelpers.UnexpectedEndOfStreamException(); - } - - return label switch - { - GifGraphicControlExtension.ExtensionLabel => GifGraphicControlExtension.ReadGraphicsControl(stream), - GifCommentExtension.ExtensionLabel => GifCommentExtension.ReadComment(stream), - GifPlainTextExtension.ExtensionLabel => GifPlainTextExtension.ReadPlainText(stream, controlExtensions, metadataOnly), - GifApplicationExtension.ExtensionLabel => GifApplicationExtension.ReadApplication(stream), - _ => throw GifHelpers.UnknownExtensionTypeException(label), - }; + throw GifHelpers.UnexpectedEndOfStreamException(); } + + return label switch + { + GifGraphicControlExtension.ExtensionLabel => GifGraphicControlExtension.ReadGraphicsControl(stream), + GifCommentExtension.ExtensionLabel => GifCommentExtension.ReadComment(stream), + GifPlainTextExtension.ExtensionLabel => GifPlainTextExtension.ReadPlainText(stream, controlExtensions, metadataOnly), + GifApplicationExtension.ExtensionLabel => GifApplicationExtension.ReadApplication(stream), + _ => throw GifHelpers.UnknownExtensionTypeException(label), + }; } } diff --git a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifFile.cs b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifFile.cs index 4bb12fa..1c829cb 100644 --- a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifFile.cs +++ b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifFile.cs @@ -4,96 +4,95 @@ using System.IO; -namespace CrissCross.WPF.UI.Controls.Decoding +namespace CrissCross.WPF.UI.Controls.Decoding; + +internal sealed class GifFile { - internal sealed class GifFile + private GifFile() { - private GifFile() - { - } + } + + public GifHeader? Header { get; private set; } - public GifHeader? Header { get; private set; } + public GifColor[]? GlobalColorTable { get; set; } - public GifColor[]? GlobalColorTable { get; set; } + public IList? Frames { get; set; } - public IList? Frames { get; set; } + public IList? Extensions { get; set; } - public IList? Extensions { get; set; } + public ushort RepeatCount { get; set; } - public ushort RepeatCount { get; set; } + internal static GifFile ReadGifFile(Stream stream, bool metadataOnly) + { + var file = new GifFile(); + file.Read(stream, metadataOnly); + return file; + } + + private void Read(Stream stream, bool metadataOnly) + { + Header = GifHeader.ReadHeader(stream); - internal static GifFile ReadGifFile(Stream stream, bool metadataOnly) + if (Header.LogicalScreenDescriptor?.HasGlobalColorTable == true) { - var file = new GifFile(); - file.Read(stream, metadataOnly); - return file; + GlobalColorTable = GifHelpers.ReadColorTable(stream, Header.LogicalScreenDescriptor.GlobalColorTableSize); } - private void Read(Stream stream, bool metadataOnly) - { - Header = GifHeader.ReadHeader(stream); + ReadFrames(stream, metadataOnly); - if (Header.LogicalScreenDescriptor?.HasGlobalColorTable == true) - { - GlobalColorTable = GifHelpers.ReadColorTable(stream, Header.LogicalScreenDescriptor.GlobalColorTableSize); - } + var netscapeExtension = + Extensions? + .OfType() + .FirstOrDefault(GifHelpers.IsNetscapeExtension); - ReadFrames(stream, metadataOnly); + if (netscapeExtension != null) + { + RepeatCount = GifHelpers.GetRepeatCount(netscapeExtension); + } + else + { + RepeatCount = 1; + } + } - var netscapeExtension = - Extensions? - .OfType() - .FirstOrDefault(GifHelpers.IsNetscapeExtension); + private void ReadFrames(Stream stream, bool metadataOnly) + { + List frames = []; + List controlExtensions = []; + List specialExtensions = []; + while (true) + { + var block = GifBlock.ReadBlock(stream, controlExtensions, metadataOnly); - if (netscapeExtension != null) + if (block.Kind == GifBlockKind.GraphicRendering) { - RepeatCount = GifHelpers.GetRepeatCount(netscapeExtension); + controlExtensions = []; } - else + + if (block is GifFrame gifFrame) { - RepeatCount = 1; + frames.Add(gifFrame); } - } - - private void ReadFrames(Stream stream, bool metadataOnly) - { - List frames = []; - List controlExtensions = []; - List specialExtensions = []; - while (true) + else if (block is GifExtension gifExtension) { - var block = GifBlock.ReadBlock(stream, controlExtensions, metadataOnly); - - if (block.Kind == GifBlockKind.GraphicRendering) - { - controlExtensions = []; - } - - if (block is GifFrame gifFrame) + var extension = gifExtension; + switch (extension.Kind) { - frames.Add(gifFrame); - } - else if (block is GifExtension gifExtension) - { - var extension = gifExtension; - switch (extension.Kind) - { - case GifBlockKind.Control: - controlExtensions.Add(extension); - break; - case GifBlockKind.SpecialPurpose: - specialExtensions.Add(extension); - break; - } - } - else if (block is GifTrailer) - { - break; + case GifBlockKind.Control: + controlExtensions.Add(extension); + break; + case GifBlockKind.SpecialPurpose: + specialExtensions.Add(extension); + break; } } - - Frames = frames.AsReadOnly(); - Extensions = specialExtensions.AsReadOnly(); + else if (block is GifTrailer) + { + break; + } } + + Frames = frames.AsReadOnly(); + Extensions = specialExtensions.AsReadOnly(); } } diff --git a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifFrame.cs b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifFrame.cs index 3e2c8e1..edd899a 100644 --- a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifFrame.cs +++ b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifFrame.cs @@ -4,46 +4,45 @@ using System.IO; -namespace CrissCross.WPF.UI.Controls.Decoding +namespace CrissCross.WPF.UI.Controls.Decoding; + +internal sealed class GifFrame : GifBlock { - internal sealed class GifFrame : GifBlock - { - internal const int ImageSeparator = 0x2C; + internal const int ImageSeparator = 0x2C; - private GifFrame() - { - } + private GifFrame() + { + } - public GifImageDescriptor? Descriptor { get; private set; } + public GifImageDescriptor? Descriptor { get; private set; } - public GifColor[]? LocalColorTable { get; private set; } + public GifColor[]? LocalColorTable { get; private set; } - public IList? Extensions { get; private set; } + public IList? Extensions { get; private set; } - public GifImageData? ImageData { get; private set; } + public GifImageData? ImageData { get; private set; } - internal override GifBlockKind Kind => GifBlockKind.GraphicRendering; + internal override GifBlockKind Kind => GifBlockKind.GraphicRendering; - internal static GifFrame ReadFrame(Stream stream, IEnumerable controlExtensions, bool metadataOnly) - { - var frame = new GifFrame(); + internal static GifFrame ReadFrame(Stream stream, IEnumerable controlExtensions, bool metadataOnly) + { + var frame = new GifFrame(); - frame.Read(stream, controlExtensions, metadataOnly); + frame.Read(stream, controlExtensions, metadataOnly); - return frame; - } + return frame; + } - private void Read(Stream stream, IEnumerable controlExtensions, bool metadataOnly) + private void Read(Stream stream, IEnumerable controlExtensions, bool metadataOnly) + { + // Note: at this point, the Image Separator (0x2C) has already been read + Descriptor = GifImageDescriptor.ReadImageDescriptor(stream); + if (Descriptor.HasLocalColorTable) { - // Note: at this point, the Image Separator (0x2C) has already been read - Descriptor = GifImageDescriptor.ReadImageDescriptor(stream); - if (Descriptor.HasLocalColorTable) - { - LocalColorTable = GifHelpers.ReadColorTable(stream, Descriptor.LocalColorTableSize); - } - - ImageData = GifImageData.ReadImageData(stream, metadataOnly); - Extensions = controlExtensions.ToList().AsReadOnly(); + LocalColorTable = GifHelpers.ReadColorTable(stream, Descriptor.LocalColorTableSize); } + + ImageData = GifImageData.ReadImageData(stream, metadataOnly); + Extensions = controlExtensions.ToList().AsReadOnly(); } } diff --git a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifGraphicControlExtension.cs b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifGraphicControlExtension.cs index 0f39912..ada2309 100644 --- a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifGraphicControlExtension.cs +++ b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifGraphicControlExtension.cs @@ -4,55 +4,54 @@ using System.IO; -namespace CrissCross.WPF.UI.Controls.Decoding +namespace CrissCross.WPF.UI.Controls.Decoding; + +// label 0xF9 +internal sealed class GifGraphicControlExtension : GifExtension { - // label 0xF9 - internal sealed class GifGraphicControlExtension : GifExtension + internal const int ExtensionLabel = 0xF9; + + private GifGraphicControlExtension() { - internal const int ExtensionLabel = 0xF9; + } - private GifGraphicControlExtension() - { - } + public int BlockSize { get; private set; } - public int BlockSize { get; private set; } + public int DisposalMethod { get; private set; } - public int DisposalMethod { get; private set; } + public bool UserInput { get; private set; } - public bool UserInput { get; private set; } + public bool HasTransparency { get; private set; } - public bool HasTransparency { get; private set; } + public int Delay { get; private set; } - public int Delay { get; private set; } + public int TransparencyIndex { get; private set; } - public int TransparencyIndex { get; private set; } + internal override GifBlockKind Kind => GifBlockKind.Control; - internal override GifBlockKind Kind => GifBlockKind.Control; + internal static GifGraphicControlExtension ReadGraphicsControl(Stream stream) + { + var ext = new GifGraphicControlExtension(); + ext.Read(stream); + return ext; + } - internal static GifGraphicControlExtension ReadGraphicsControl(Stream stream) + private void Read(Stream stream) + { + // Note: at this point, the label (0xF9) has already been read + var bytes = new byte[6]; + stream.ReadAll(bytes, 0, bytes.Length); + BlockSize = bytes[0]; // should always be 4 + if (BlockSize != 4) { - var ext = new GifGraphicControlExtension(); - ext.Read(stream); - return ext; + throw GifHelpers.InvalidBlockSizeException("Graphic Control Extension", 4, BlockSize); } - private void Read(Stream stream) - { - // Note: at this point, the label (0xF9) has already been read - var bytes = new byte[6]; - stream.ReadAll(bytes, 0, bytes.Length); - BlockSize = bytes[0]; // should always be 4 - if (BlockSize != 4) - { - throw GifHelpers.InvalidBlockSizeException("Graphic Control Extension", 4, BlockSize); - } - - var packedFields = bytes[1]; - DisposalMethod = (packedFields & 0x1C) >> 2; - UserInput = (packedFields & 0x02) != 0; - HasTransparency = (packedFields & 0x01) != 0; - Delay = BitConverter.ToUInt16(bytes, 2) * 10; // milliseconds - TransparencyIndex = bytes[4]; - } + var packedFields = bytes[1]; + DisposalMethod = (packedFields & 0x1C) >> 2; + UserInput = (packedFields & 0x02) != 0; + HasTransparency = (packedFields & 0x01) != 0; + Delay = BitConverter.ToUInt16(bytes, 2) * 10; // milliseconds + TransparencyIndex = bytes[4]; } } diff --git a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifHeader.cs b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifHeader.cs index 808b920..c827a60 100644 --- a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifHeader.cs +++ b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifHeader.cs @@ -4,44 +4,43 @@ using System.IO; -namespace CrissCross.WPF.UI.Controls.Decoding +namespace CrissCross.WPF.UI.Controls.Decoding; + +internal sealed class GifHeader : GifBlock { - internal sealed class GifHeader : GifBlock + private GifHeader() { - private GifHeader() - { - } + } - public string? Signature { get; private set; } + public string? Signature { get; private set; } - public string? Version { get; private set; } + public string? Version { get; private set; } - public GifLogicalScreenDescriptor? LogicalScreenDescriptor { get; private set; } + public GifLogicalScreenDescriptor? LogicalScreenDescriptor { get; private set; } - internal override GifBlockKind Kind => GifBlockKind.Other; + internal override GifBlockKind Kind => GifBlockKind.Other; - internal static GifHeader ReadHeader(Stream stream) + internal static GifHeader ReadHeader(Stream stream) + { + var header = new GifHeader(); + header.Read(stream); + return header; + } + + private void Read(Stream stream) + { + Signature = GifHelpers.ReadString(stream, 3); + if (Signature != "GIF") { - var header = new GifHeader(); - header.Read(stream); - return header; + throw GifHelpers.InvalidSignatureException(Signature); } - private void Read(Stream stream) + Version = GifHelpers.ReadString(stream, 3); + if (Version != "87a" && Version != "89a") { - Signature = GifHelpers.ReadString(stream, 3); - if (Signature != "GIF") - { - throw GifHelpers.InvalidSignatureException(Signature); - } - - Version = GifHelpers.ReadString(stream, 3); - if (Version != "87a" && Version != "89a") - { - throw GifHelpers.UnsupportedVersionException(Version); - } - - LogicalScreenDescriptor = GifLogicalScreenDescriptor.ReadLogicalScreenDescriptor(stream); + throw GifHelpers.UnsupportedVersionException(Version); } + + LogicalScreenDescriptor = GifLogicalScreenDescriptor.ReadLogicalScreenDescriptor(stream); } } diff --git a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifHelpers.cs b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifHelpers.cs index f7ac0c2..df90379 100644 --- a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifHelpers.cs +++ b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifHelpers.cs @@ -5,94 +5,93 @@ using System.IO; using System.Text; -namespace CrissCross.WPF.UI.Controls.Decoding +namespace CrissCross.WPF.UI.Controls.Decoding; + +internal static class GifHelpers { - internal static class GifHelpers + public static string ReadString(Stream stream, int length) { - public static string ReadString(Stream stream, int length) - { - var bytes = new byte[length]; - stream.ReadAll(bytes, 0, length); - return Encoding.ASCII.GetString(bytes); - } + var bytes = new byte[length]; + stream.ReadAll(bytes, 0, length); + return Encoding.ASCII.GetString(bytes); + } - public static byte[]? ReadDataBlocks(Stream stream, bool discard) + public static byte[]? ReadDataBlocks(Stream stream, bool discard) + { + var ms = discard ? null : new MemoryStream(); + using (ms) { - var ms = discard ? null : new MemoryStream(); - using (ms) + int len; + while ((len = stream.ReadByte()) > 0) { - int len; - while ((len = stream.ReadByte()) > 0) - { - var bytes = new byte[len]; - stream.ReadAll(bytes, 0, len); - ms?.Write(bytes, 0, len); - } - - if (ms != null) - { - return ms.ToArray(); - } - - return null; + var bytes = new byte[len]; + stream.ReadAll(bytes, 0, len); + ms?.Write(bytes, 0, len); } - } - public static GifColor[] ReadColorTable(Stream stream, int size) - { - var length = 3 * size; - var bytes = new byte[length]; - stream.ReadAll(bytes, 0, length); - var colorTable = new GifColor[size]; - for (var i = 0; i < size; i++) + if (ms != null) { - var r = bytes[3 * i]; - var g = bytes[(3 * i) + 1]; - var b = bytes[(3 * i) + 2]; - colorTable[i] = new GifColor(r, g, b); + return ms.ToArray(); } - return colorTable; + return null; } + } - public static bool IsNetscapeExtension(GifApplicationExtension ext) => - ext.ApplicationIdentifier == "NETSCAPE" - && Encoding.ASCII.GetString(ext.AuthenticationCode ?? []) == "2.0"; - - public static ushort GetRepeatCount(GifApplicationExtension ext) + public static GifColor[] ReadColorTable(Stream stream, int size) + { + var length = 3 * size; + var bytes = new byte[length]; + stream.ReadAll(bytes, 0, length); + var colorTable = new GifColor[size]; + for (var i = 0; i < size; i++) { - if (ext.Data?.Length >= 3) - { - return BitConverter.ToUInt16(ext.Data, 1); - } + var r = bytes[3 * i]; + var g = bytes[(3 * i) + 1]; + var b = bytes[(3 * i) + 2]; + colorTable[i] = new GifColor(r, g, b); + } + + return colorTable; + } - return 1; + public static bool IsNetscapeExtension(GifApplicationExtension ext) => + ext.ApplicationIdentifier == "NETSCAPE" + && Encoding.ASCII.GetString(ext.AuthenticationCode ?? []) == "2.0"; + + public static ushort GetRepeatCount(GifApplicationExtension ext) + { + if (ext.Data?.Length >= 3) + { + return BitConverter.ToUInt16(ext.Data, 1); } - public static Exception UnexpectedEndOfStreamException() => new GifDecoderException("Unexpected end of stream before trailer was encountered"); + return 1; + } + + public static Exception UnexpectedEndOfStreamException() => new GifDecoderException("Unexpected end of stream before trailer was encountered"); - public static Exception UnknownBlockTypeException(int blockId) => new GifDecoderException("Unknown block type: 0x" + blockId.ToString("x2")); + public static Exception UnknownBlockTypeException(int blockId) => new GifDecoderException("Unknown block type: 0x" + blockId.ToString("x2")); - public static Exception UnknownExtensionTypeException(int extensionLabel) => new GifDecoderException("Unknown extension type: 0x" + extensionLabel.ToString("x2")); + public static Exception UnknownExtensionTypeException(int extensionLabel) => new GifDecoderException("Unknown extension type: 0x" + extensionLabel.ToString("x2")); - public static Exception InvalidBlockSizeException(string blockName, int expectedBlockSize, int actualBlockSize) => new GifDecoderException( - string.Format( - "Invalid block size for {0}. Expected {1}, but was {2}", - blockName, - expectedBlockSize, - actualBlockSize)); + public static Exception InvalidBlockSizeException(string blockName, int expectedBlockSize, int actualBlockSize) => new GifDecoderException( + string.Format( + "Invalid block size for {0}. Expected {1}, but was {2}", + blockName, + expectedBlockSize, + actualBlockSize)); - public static Exception InvalidSignatureException(string signature) => new GifDecoderException("Invalid file signature: " + signature); + public static Exception InvalidSignatureException(string signature) => new GifDecoderException("Invalid file signature: " + signature); - public static Exception UnsupportedVersionException(string version) => new GifDecoderException("Unsupported version: " + version); + public static Exception UnsupportedVersionException(string version) => new GifDecoderException("Unsupported version: " + version); - public static void ReadAll(this Stream stream, byte[] buffer, int offset, int count) + public static void ReadAll(this Stream stream, byte[] buffer, int offset, int count) + { + var totalRead = 0; + while (totalRead < count) { - var totalRead = 0; - while (totalRead < count) - { - totalRead += stream.Read(buffer, offset + totalRead, count - totalRead); - } + totalRead += stream.Read(buffer, offset + totalRead, count - totalRead); } } } diff --git a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifImageData.cs b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifImageData.cs index 48e6b49..f4b9688 100644 --- a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifImageData.cs +++ b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifImageData.cs @@ -4,29 +4,28 @@ using System.IO; -namespace CrissCross.WPF.UI.Controls.Decoding +namespace CrissCross.WPF.UI.Controls.Decoding; + +internal sealed class GifImageData { - internal sealed class GifImageData + private GifImageData() { - private GifImageData() - { - } + } - public byte LzwMinimumCodeSize { get; set; } + public byte LzwMinimumCodeSize { get; set; } - public byte[]? CompressedData { get; set; } + public byte[]? CompressedData { get; set; } - internal static GifImageData ReadImageData(Stream stream, bool metadataOnly) - { - var imgData = new GifImageData(); - imgData.Read(stream, metadataOnly); - return imgData; - } + internal static GifImageData ReadImageData(Stream stream, bool metadataOnly) + { + var imgData = new GifImageData(); + imgData.Read(stream, metadataOnly); + return imgData; + } - private void Read(Stream stream, bool metadataOnly) - { - LzwMinimumCodeSize = (byte)stream.ReadByte(); - CompressedData = GifHelpers.ReadDataBlocks(stream, metadataOnly); - } + private void Read(Stream stream, bool metadataOnly) + { + LzwMinimumCodeSize = (byte)stream.ReadByte(); + CompressedData = GifHelpers.ReadDataBlocks(stream, metadataOnly); } } diff --git a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifImageDescriptor.cs b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifImageDescriptor.cs index 550e7bf..34b8aad 100644 --- a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifImageDescriptor.cs +++ b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifImageDescriptor.cs @@ -4,50 +4,49 @@ using System.IO; -namespace CrissCross.WPF.UI.Controls.Decoding +namespace CrissCross.WPF.UI.Controls.Decoding; + +internal sealed class GifImageDescriptor { - internal sealed class GifImageDescriptor + private GifImageDescriptor() { - private GifImageDescriptor() - { - } + } - public int Left { get; private set; } + public int Left { get; private set; } - public int Top { get; private set; } + public int Top { get; private set; } - public int Width { get; private set; } + public int Width { get; private set; } - public int Height { get; private set; } + public int Height { get; private set; } - public bool HasLocalColorTable { get; private set; } + public bool HasLocalColorTable { get; private set; } - public bool Interlace { get; private set; } + public bool Interlace { get; private set; } - public bool IsLocalColorTableSorted { get; private set; } + public bool IsLocalColorTableSorted { get; private set; } - public int LocalColorTableSize { get; private set; } + public int LocalColorTableSize { get; private set; } - internal static GifImageDescriptor ReadImageDescriptor(Stream stream) - { - var descriptor = new GifImageDescriptor(); - descriptor.Read(stream); - return descriptor; - } + internal static GifImageDescriptor ReadImageDescriptor(Stream stream) + { + var descriptor = new GifImageDescriptor(); + descriptor.Read(stream); + return descriptor; + } - private void Read(Stream stream) - { - var bytes = new byte[9]; - stream.ReadAll(bytes, 0, bytes.Length); - Left = BitConverter.ToUInt16(bytes, 0); - Top = BitConverter.ToUInt16(bytes, 2); - Width = BitConverter.ToUInt16(bytes, 4); - Height = BitConverter.ToUInt16(bytes, 6); - var packedFields = bytes[8]; - HasLocalColorTable = (packedFields & 0x80) != 0; - Interlace = (packedFields & 0x40) != 0; - IsLocalColorTableSorted = (packedFields & 0x20) != 0; - LocalColorTableSize = 1 << ((packedFields & 0x07) + 1); - } + private void Read(Stream stream) + { + var bytes = new byte[9]; + stream.ReadAll(bytes, 0, bytes.Length); + Left = BitConverter.ToUInt16(bytes, 0); + Top = BitConverter.ToUInt16(bytes, 2); + Width = BitConverter.ToUInt16(bytes, 4); + Height = BitConverter.ToUInt16(bytes, 6); + var packedFields = bytes[8]; + HasLocalColorTable = (packedFields & 0x80) != 0; + Interlace = (packedFields & 0x40) != 0; + IsLocalColorTableSorted = (packedFields & 0x20) != 0; + LocalColorTableSize = 1 << ((packedFields & 0x07) + 1); } } diff --git a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifLogicalScreenDescriptor.cs b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifLogicalScreenDescriptor.cs index 7a54320..3bcae53 100644 --- a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifLogicalScreenDescriptor.cs +++ b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifLogicalScreenDescriptor.cs @@ -4,50 +4,49 @@ using System.IO; -namespace CrissCross.WPF.UI.Controls.Decoding -{ - internal class GifLogicalScreenDescriptor - { - public int Width { get; private set; } +namespace CrissCross.WPF.UI.Controls.Decoding; - public int Height { get; private set; } +internal class GifLogicalScreenDescriptor +{ + public int Width { get; private set; } - public bool HasGlobalColorTable { get; private set; } + public int Height { get; private set; } - public int ColorResolution { get; private set; } + public bool HasGlobalColorTable { get; private set; } - public bool IsGlobalColorTableSorted { get; private set; } + public int ColorResolution { get; private set; } - public int GlobalColorTableSize { get; private set; } + public bool IsGlobalColorTableSorted { get; private set; } - public int BackgroundColorIndex { get; private set; } + public int GlobalColorTableSize { get; private set; } - public double PixelAspectRatio { get; private set; } + public int BackgroundColorIndex { get; private set; } - internal static GifLogicalScreenDescriptor ReadLogicalScreenDescriptor(Stream stream) - { - var descriptor = new GifLogicalScreenDescriptor(); - descriptor.Read(stream); - return descriptor; - } + public double PixelAspectRatio { get; private set; } - private void Read(Stream stream) - { - var bytes = new byte[7]; - stream.ReadAll(bytes, 0, bytes.Length); + internal static GifLogicalScreenDescriptor ReadLogicalScreenDescriptor(Stream stream) + { + var descriptor = new GifLogicalScreenDescriptor(); + descriptor.Read(stream); + return descriptor; + } - Width = BitConverter.ToUInt16(bytes, 0); - Height = BitConverter.ToUInt16(bytes, 2); - var packedFields = bytes[4]; - HasGlobalColorTable = (packedFields & 0x80) != 0; - ColorResolution = ((packedFields & 0x70) >> 4) + 1; - IsGlobalColorTableSorted = (packedFields & 0x08) != 0; - GlobalColorTableSize = 1 << ((packedFields & 0x07) + 1); - BackgroundColorIndex = bytes[5]; - PixelAspectRatio = - bytes[5] == 0 - ? 0.0 - : (15 + bytes[5]) / 64.0; - } + private void Read(Stream stream) + { + var bytes = new byte[7]; + stream.ReadAll(bytes, 0, bytes.Length); + + Width = BitConverter.ToUInt16(bytes, 0); + Height = BitConverter.ToUInt16(bytes, 2); + var packedFields = bytes[4]; + HasGlobalColorTable = (packedFields & 0x80) != 0; + ColorResolution = ((packedFields & 0x70) >> 4) + 1; + IsGlobalColorTableSorted = (packedFields & 0x08) != 0; + GlobalColorTableSize = 1 << ((packedFields & 0x07) + 1); + BackgroundColorIndex = bytes[5]; + PixelAspectRatio = + bytes[5] == 0 + ? 0.0 + : (15 + bytes[5]) / 64.0; } } diff --git a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifPlainTextExtension.cs b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifPlainTextExtension.cs index a947563..3ee7a0e 100644 --- a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifPlainTextExtension.cs +++ b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifPlainTextExtension.cs @@ -5,72 +5,71 @@ using System.IO; using System.Text; -namespace CrissCross.WPF.UI.Controls.Decoding +namespace CrissCross.WPF.UI.Controls.Decoding; + +// label 0x01 +internal sealed class GifPlainTextExtension : GifExtension { - // label 0x01 - internal sealed class GifPlainTextExtension : GifExtension + internal const int ExtensionLabel = 0x01; + + private GifPlainTextExtension() { - internal const int ExtensionLabel = 0x01; + } - private GifPlainTextExtension() - { - } + public int BlockSize { get; private set; } - public int BlockSize { get; private set; } + public int Left { get; private set; } - public int Left { get; private set; } + public int Top { get; private set; } - public int Top { get; private set; } + public int Width { get; private set; } - public int Width { get; private set; } + public int Height { get; private set; } - public int Height { get; private set; } + public int CellWidth { get; private set; } - public int CellWidth { get; private set; } + public int CellHeight { get; private set; } - public int CellHeight { get; private set; } + public int ForegroundColorIndex { get; private set; } - public int ForegroundColorIndex { get; private set; } + public int BackgroundColorIndex { get; private set; } - public int BackgroundColorIndex { get; private set; } + public string? Text { get; private set; } - public string? Text { get; private set; } + public IList? Extensions { get; private set; } - public IList? Extensions { get; private set; } + internal override GifBlockKind Kind => GifBlockKind.GraphicRendering; - internal override GifBlockKind Kind => GifBlockKind.GraphicRendering; + internal static GifPlainTextExtension ReadPlainText(Stream stream, IEnumerable controlExtensions, bool metadataOnly) + { + var plainText = new GifPlainTextExtension(); + plainText.Read(stream, controlExtensions, metadataOnly); + return plainText; + } - internal static GifPlainTextExtension ReadPlainText(Stream stream, IEnumerable controlExtensions, bool metadataOnly) - { - var plainText = new GifPlainTextExtension(); - plainText.Read(stream, controlExtensions, metadataOnly); - return plainText; - } + private void Read(Stream stream, IEnumerable controlExtensions, bool metadataOnly) + { + // Note: at this point, the label (0x01) has already been read + var bytes = new byte[13]; + stream.ReadAll(bytes, 0, bytes.Length); - private void Read(Stream stream, IEnumerable controlExtensions, bool metadataOnly) + BlockSize = bytes[0]; + if (BlockSize != 12) { - // Note: at this point, the label (0x01) has already been read - var bytes = new byte[13]; - stream.ReadAll(bytes, 0, bytes.Length); - - BlockSize = bytes[0]; - if (BlockSize != 12) - { - throw GifHelpers.InvalidBlockSizeException("Plain Text Extension", 12, BlockSize); - } - - Left = BitConverter.ToUInt16(bytes, 1); - Top = BitConverter.ToUInt16(bytes, 3); - Width = BitConverter.ToUInt16(bytes, 5); - Height = BitConverter.ToUInt16(bytes, 7); - CellWidth = bytes[9]; - CellHeight = bytes[10]; - ForegroundColorIndex = bytes[11]; - BackgroundColorIndex = bytes[12]; - - var dataBytes = GifHelpers.ReadDataBlocks(stream, metadataOnly); - Text = Encoding.ASCII.GetString(dataBytes ?? []); - Extensions = controlExtensions.ToList().AsReadOnly(); + throw GifHelpers.InvalidBlockSizeException("Plain Text Extension", 12, BlockSize); } + + Left = BitConverter.ToUInt16(bytes, 1); + Top = BitConverter.ToUInt16(bytes, 3); + Width = BitConverter.ToUInt16(bytes, 5); + Height = BitConverter.ToUInt16(bytes, 7); + CellWidth = bytes[9]; + CellHeight = bytes[10]; + ForegroundColorIndex = bytes[11]; + BackgroundColorIndex = bytes[12]; + + var dataBytes = GifHelpers.ReadDataBlocks(stream, metadataOnly); + Text = Encoding.ASCII.GetString(dataBytes ?? []); + Extensions = controlExtensions.ToList().AsReadOnly(); } } diff --git a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifTrailer.cs b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifTrailer.cs index 926fba3..1b48d88 100644 --- a/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifTrailer.cs +++ b/src/CrissCross.WPF.UI/Controls/GifImage/Decoding/GifTrailer.cs @@ -2,18 +2,17 @@ // ReactiveUI Association Incorporated licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -namespace CrissCross.WPF.UI.Controls.Decoding +namespace CrissCross.WPF.UI.Controls.Decoding; + +internal sealed class GifTrailer : GifBlock { - internal sealed class GifTrailer : GifBlock - { - internal const int TrailerByte = 0x3B; + internal const int TrailerByte = 0x3B; - private GifTrailer() - { - } + private GifTrailer() + { + } - internal override GifBlockKind Kind => GifBlockKind.Other; + internal override GifBlockKind Kind => GifBlockKind.Other; - internal static GifTrailer ReadTrailer() => new(); - } + internal static GifTrailer ReadTrailer() => new(); } diff --git a/src/CrissCross.WPF.UI/Controls/GifImage/ImageAnimationController.cs b/src/CrissCross.WPF.UI/Controls/GifImage/ImageAnimationController.cs index 2875ea5..f7bdfc4 100644 --- a/src/CrissCross.WPF.UI/Controls/GifImage/ImageAnimationController.cs +++ b/src/CrissCross.WPF.UI/Controls/GifImage/ImageAnimationController.cs @@ -4,183 +4,182 @@ using System.Windows.Media.Animation; -namespace CrissCross.WPF.UI.Controls +namespace CrissCross.WPF.UI.Controls; + +/// +/// Provides a way to pause, resume or seek a GIF animation. +/// +internal class ImageAnimationController : IDisposable { - /// - /// Provides a way to pause, resume or seek a GIF animation. - /// - internal class ImageAnimationController : IDisposable - { - private static readonly DependencyPropertyDescriptor _sourceDescriptor; + private static readonly DependencyPropertyDescriptor _sourceDescriptor; - private readonly Image _image; - private readonly ObjectAnimationUsingKeyFrames _animation; - private readonly AnimationClock _clock; - private readonly ClockController _clockController; + private readonly Image _image; + private readonly ObjectAnimationUsingKeyFrames _animation; + private readonly AnimationClock _clock; + private readonly ClockController _clockController; - private bool _isSuspended; + private bool _isSuspended; - static ImageAnimationController() => _sourceDescriptor = DependencyPropertyDescriptor.FromProperty(Image.SourceProperty, typeof(Image)); + static ImageAnimationController() => _sourceDescriptor = DependencyPropertyDescriptor.FromProperty(Image.SourceProperty, typeof(Image)); - internal ImageAnimationController(Image image, ObjectAnimationUsingKeyFrames animation, bool autoStart) - { - _image = image; - _animation = animation; - _animation.Completed += AnimationCompleted; - _clock = _animation.CreateClock(); - _clockController = _clock.Controller; - _sourceDescriptor.AddValueChanged(image, ImageSourceChanged); - - // ReSharper disable once PossibleNullReferenceException - _clockController.Pause(); + internal ImageAnimationController(Image image, ObjectAnimationUsingKeyFrames animation, bool autoStart) + { + _image = image; + _animation = animation; + _animation.Completed += AnimationCompleted; + _clock = _animation.CreateClock(); + _clockController = _clock.Controller; + _sourceDescriptor.AddValueChanged(image, ImageSourceChanged); - _image.ApplyAnimationClock(Image.SourceProperty, _clock); + // ReSharper disable once PossibleNullReferenceException + _clockController.Pause(); - IsPaused = !autoStart; - if (autoStart) - { - _clockController.Resume(); - } - } + _image.ApplyAnimationClock(Image.SourceProperty, _clock); - /// - /// Finalizes an instance of the class. - /// - ~ImageAnimationController() + IsPaused = !autoStart; + if (autoStart) { - Dispose(false); + _clockController.Resume(); } + } + + /// + /// Finalizes an instance of the class. + /// + ~ImageAnimationController() + { + Dispose(false); + } + + /// + /// Raised when the current frame changes. + /// + public event EventHandler? CurrentFrameChanged; - /// - /// Raised when the current frame changes. - /// - public event EventHandler? CurrentFrameChanged; - - /// - /// Gets the number of frames in the image. - /// - public int FrameCount => _animation.KeyFrames.Count; - - /// - /// Gets the duration of the animation. - /// - public TimeSpan Duration => _animation.Duration.HasTimeSpan - ? _animation.Duration.TimeSpan - : TimeSpan.Zero; - - /// - /// Gets a value indicating whether returns a value that indicates whether the animation is paused. - /// - public bool IsPaused { get; private set; } - - /// - /// Gets a value indicating whether returns a value that indicates whether the animation is complete. - /// - public bool IsComplete => _clock.CurrentState == ClockState.Filling; - - /// - /// Gets the current frame index. - /// - public int CurrentFrame + /// + /// Gets the number of frames in the image. + /// + public int FrameCount => _animation.KeyFrames.Count; + + /// + /// Gets the duration of the animation. + /// + public TimeSpan Duration => _animation.Duration.HasTimeSpan + ? _animation.Duration.TimeSpan + : TimeSpan.Zero; + + /// + /// Gets a value indicating whether returns a value that indicates whether the animation is paused. + /// + public bool IsPaused { get; private set; } + + /// + /// Gets a value indicating whether returns a value that indicates whether the animation is complete. + /// + public bool IsComplete => _clock.CurrentState == ClockState.Filling; + + /// + /// Gets the current frame index. + /// + public int CurrentFrame + { + get { - get + var time = _clock.CurrentTime; + var frameAndIndex = + _animation.KeyFrames + .Cast() + .Select((f, i) => new { Time = f.KeyTime.TimeSpan, Index = i }) + .FirstOrDefault(fi => fi.Time >= time); + if (frameAndIndex != null) { - var time = _clock.CurrentTime; - var frameAndIndex = - _animation.KeyFrames - .Cast() - .Select((f, i) => new { Time = f.KeyTime.TimeSpan, Index = i }) - .FirstOrDefault(fi => fi.Time >= time); - if (frameAndIndex != null) - { - return frameAndIndex.Index; - } - - return -1; + return frameAndIndex.Index; } + + return -1; } + } + + /// + /// Seeks the animation to the specified frame index. + /// + /// The index of the frame to seek to. + public void GotoFrame(int index) + { + var frame = _animation.KeyFrames[index]; + _clockController.Seek(frame.KeyTime.TimeSpan, TimeSeekOrigin.BeginTime); + } + + /// + /// Pauses the animation. + /// + public void Pause() + { + IsPaused = true; + _clockController.Pause(); + } - /// - /// Seeks the animation to the specified frame index. - /// - /// The index of the frame to seek to. - public void GotoFrame(int index) + /// + /// Starts or resumes the animation. If the animation is complete, it restarts from the beginning. + /// + public void Play() + { + IsPaused = false; + if (!_isSuspended) { - var frame = _animation.KeyFrames[index]; - _clockController.Seek(frame.KeyTime.TimeSpan, TimeSeekOrigin.BeginTime); + _clockController.Resume(); } + } + + /// + /// Disposes the current object. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } - /// - /// Pauses the animation. - /// - public void Pause() + internal void SetSuspended(bool isSuspended) + { + if (isSuspended == _isSuspended) { - IsPaused = true; - _clockController.Pause(); + return; } - /// - /// Starts or resumes the animation. If the animation is complete, it restarts from the beginning. - /// - public void Play() + var wasSuspended = _isSuspended; + _isSuspended = isSuspended; + if (wasSuspended) { - IsPaused = false; - if (!_isSuspended) + if (!IsPaused) { _clockController.Resume(); } } - - /// - /// Disposes the current object. - /// - public void Dispose() + else { - Dispose(true); - GC.SuppressFinalize(this); - } - - internal void SetSuspended(bool isSuspended) - { - if (isSuspended == _isSuspended) - { - return; - } - - var wasSuspended = _isSuspended; - _isSuspended = isSuspended; - if (wasSuspended) - { - if (!IsPaused) - { - _clockController.Resume(); - } - } - else - { - _clockController.Pause(); - } + _clockController.Pause(); } + } - /// - /// Disposes the current object. - /// - /// true to dispose both managed an unmanaged resources, false to dispose only managed resources. - protected virtual void Dispose(bool disposing) + /// + /// Disposes the current object. + /// + /// true to dispose both managed an unmanaged resources, false to dispose only managed resources. + protected virtual void Dispose(bool disposing) + { + if (disposing) { - if (disposing) - { - _image.BeginAnimation(Image.SourceProperty, null); - _animation.Completed -= AnimationCompleted; - _sourceDescriptor.RemoveValueChanged(_image, ImageSourceChanged); - _image.Source = null!; - } + _image.BeginAnimation(Image.SourceProperty, null); + _animation.Completed -= AnimationCompleted; + _sourceDescriptor.RemoveValueChanged(_image, ImageSourceChanged); + _image.Source = null!; } + } - private void AnimationCompleted(object? sender, EventArgs e) => _image.RaiseEvent(new System.Windows.RoutedEventArgs(ImageBehavior.AnimationCompletedEvent, _image)); + private void AnimationCompleted(object? sender, EventArgs e) => _image.RaiseEvent(new System.Windows.RoutedEventArgs(ImageBehavior.AnimationCompletedEvent, _image)); - private void ImageSourceChanged(object? sender, EventArgs e) => OnCurrentFrameChanged(); + private void ImageSourceChanged(object? sender, EventArgs e) => OnCurrentFrameChanged(); - private void OnCurrentFrameChanged() => CurrentFrameChanged?.Invoke(this, EventArgs.Empty); - } + private void OnCurrentFrameChanged() => CurrentFrameChanged?.Invoke(this, EventArgs.Empty); } diff --git a/src/CrissCross.WPF.UI/Controls/Grid/Grid.cs b/src/CrissCross.WPF.UI/Controls/Grid/Grid.cs index 4fce236..75224ba 100644 --- a/src/CrissCross.WPF.UI/Controls/Grid/Grid.cs +++ b/src/CrissCross.WPF.UI/Controls/Grid/Grid.cs @@ -2,11 +2,10 @@ // ReactiveUI Association Incorporated licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -namespace CrissCross.WPF.UI.Controls -{ - /// - /// Extended . - /// - /// - public class Grid : System.Windows.Controls.Grid; -} +namespace CrissCross.WPF.UI.Controls; + +/// +/// Extended . +/// +/// +public class Grid : System.Windows.Controls.Grid; diff --git a/src/CrissCross.WPF.UI/Controls/GroupBox/GroupBox.cs b/src/CrissCross.WPF.UI/Controls/GroupBox/GroupBox.cs index cd39a23..a58396d 100644 --- a/src/CrissCross.WPF.UI/Controls/GroupBox/GroupBox.cs +++ b/src/CrissCross.WPF.UI/Controls/GroupBox/GroupBox.cs @@ -2,11 +2,10 @@ // ReactiveUI Association Incorporated licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -namespace CrissCross.WPF.UI.Controls -{ - /// - /// Extended . - /// - /// - public class GroupBox : System.Windows.Controls.GroupBox; -} +namespace CrissCross.WPF.UI.Controls; + +/// +/// Extended . +/// +/// +public class GroupBox : System.Windows.Controls.GroupBox; diff --git a/src/CrissCross.WPF.UI/Controls/Label/Label.cs b/src/CrissCross.WPF.UI/Controls/Label/Label.cs index ae760ad..795d80c 100644 --- a/src/CrissCross.WPF.UI/Controls/Label/Label.cs +++ b/src/CrissCross.WPF.UI/Controls/Label/Label.cs @@ -2,11 +2,10 @@ // ReactiveUI Association Incorporated licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -namespace CrissCross.WPF.UI.Controls -{ - /// - /// Extended . - /// - /// - public class Label : System.Windows.Controls.Label; -} +namespace CrissCross.WPF.UI.Controls; + +/// +/// Extended . +/// +/// +public class Label : System.Windows.Controls.Label; diff --git a/src/CrissCross.WPF.UI/Controls/Page/Page.cs b/src/CrissCross.WPF.UI/Controls/Page/Page.cs index a8f4a6a..30dd125 100644 --- a/src/CrissCross.WPF.UI/Controls/Page/Page.cs +++ b/src/CrissCross.WPF.UI/Controls/Page/Page.cs @@ -2,11 +2,10 @@ // ReactiveUI Association Incorporated licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -namespace CrissCross.WPF.UI.Controls -{ - /// - /// Extended . - /// - /// - public class Page : System.Windows.Controls.Page; -} +namespace CrissCross.WPF.UI.Controls; + +/// +/// Extended . +/// +/// +public class Page : System.Windows.Controls.Page; diff --git a/src/CrissCross.WPF.UI/Controls/StackPanel/StackPanel.cs b/src/CrissCross.WPF.UI/Controls/StackPanel/StackPanel.cs index 84d2943..21390e6 100644 --- a/src/CrissCross.WPF.UI/Controls/StackPanel/StackPanel.cs +++ b/src/CrissCross.WPF.UI/Controls/StackPanel/StackPanel.cs @@ -2,11 +2,10 @@ // ReactiveUI Association Incorporated licenses this file to you under the MIT license. // See the LICENSE file in the project root for full license information. -namespace CrissCross.WPF.UI.Controls -{ - /// - /// Extended . - /// - /// - public class StackPanel : System.Windows.Controls.StackPanel; -} +namespace CrissCross.WPF.UI.Controls; + +/// +/// Extended . +/// +/// +public class StackPanel : System.Windows.Controls.StackPanel; diff --git a/src/CrissCross.WPF/CrissCross.WPF.csproj b/src/CrissCross.WPF/CrissCross.WPF.csproj index 253399b..6b6e3b1 100644 --- a/src/CrissCross.WPF/CrissCross.WPF.csproj +++ b/src/CrissCross.WPF/CrissCross.WPF.csproj @@ -1,21 +1,26 @@  - - $(CrissCrossWinTargetFrameworks) - true - enable - True - True - true - + + $(CrissCrossWinTargetFrameworks) + true + enable + True + True + true + - - - - + + + + + - - - + + + + + + + diff --git a/src/CrissCross.XamForms/CrissCross.XamForms.csproj b/src/CrissCross.XamForms/CrissCross.XamForms.csproj index 3ef0209..7467804 100644 --- a/src/CrissCross.XamForms/CrissCross.XamForms.csproj +++ b/src/CrissCross.XamForms/CrissCross.XamForms.csproj @@ -7,9 +7,14 @@ + + + + +