diff options
Diffstat (limited to 'Models')
| -rw-r--r-- | Models/AvalarPipeline.cs | 47 | ||||
| -rw-r--r-- | Models/IPipelineStep.cs | 13 | ||||
| -rw-r--r-- | Models/ImageModel.cs | 69 | ||||
| -rw-r--r-- | Models/Interfaces/IImageModel.cs | 5 | ||||
| -rw-r--r-- | Models/Interfaces/ISettingsModel.cs | 14 | ||||
| -rw-r--r-- | Models/Interfaces/SettingsModel.cs | 12 | ||||
| -rw-r--r-- | Models/PipelineStep.cs | 65 |
7 files changed, 202 insertions, 23 deletions
diff --git a/Models/AvalarPipeline.cs b/Models/AvalarPipeline.cs new file mode 100644 index 0000000..14e32d0 --- /dev/null +++ b/Models/AvalarPipeline.cs @@ -0,0 +1,47 @@ +using Avalonia.Media.Imaging; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Avalar.Models +{ + public class AvalarPipeline + { + private readonly IEnumerable<IPipelineStep> m_steps; + + public AvalarPipeline(IEnumerable<IPipelineStep> steps) + { + m_steps = steps ?? throw new ArgumentNullException(nameof(steps)); + foreach (var step in m_steps) + { + step.PropertyChanged += OnPipelineComponentChanged; + } + } + + private void OnPipelineComponentChanged(object sender, PropertyChangedEventArgs e) + { + foreach (var step in m_steps.SkipWhile(step => step != sender)) + { + step.InvalidateResult(); + } + + PipelineChanged?.Invoke(this, EventArgs.Empty); + } + + public event EventHandler PipelineChanged; + + public async Task<IBitmap> Run(IBitmap bitmap, CancellationToken token) + { + var input = bitmap; + foreach(var step in m_steps) + { + token.ThrowIfCancellationRequested(); + input = await step.Run(input, token).ConfigureAwait(false); + } + return input; + } + } +} diff --git a/Models/IPipelineStep.cs b/Models/IPipelineStep.cs new file mode 100644 index 0000000..5f53c42 --- /dev/null +++ b/Models/IPipelineStep.cs @@ -0,0 +1,13 @@ +using Avalonia.Media.Imaging; +using System.ComponentModel; +using System.Threading.Tasks; + +namespace Avalar.Models +{ + public interface IPipelineStep : INotifyPropertyChanged + { + public Task<IBitmap> Run(IBitmap bitmap, System.Threading.CancellationToken token); + + public void InvalidateResult(); + } +}
\ No newline at end of file diff --git a/Models/ImageModel.cs b/Models/ImageModel.cs index e0bb1af..047d2e0 100644 --- a/Models/ImageModel.cs +++ b/Models/ImageModel.cs @@ -1,28 +1,61 @@ using Avalar.Models.Interfaces; +using Avalar.Utils; +using Avalar.Services.Interfaces; using Avalonia.Media.Imaging; using ReactiveUI; using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; namespace Avalar.Models { public class ImageModel : ReactiveObject, IImageModel, ISettingsModel, IDisposable { - + private readonly IResizer m_Resizer; + private readonly IBrightness m_Brightness; + private CancellationTokenSource m_CancellationTokenSource; + private AvalarPipeline m_pipeline; private IBitmap m_OriginalBitmap; - private IBitmap m_LatestResult; + public ImageModel(IServiceProvider serviceProvider) + { + m_Resizer = serviceProvider.GetService<IResizer>(); + m_Brightness = serviceProvider.GetService<IBrightness>(); - private double m_Width; - public double Width { - get => m_Width; - set => this.RaiseAndSetIfChanged(ref m_Width, value); + m_pipeline = new AvalarPipeline(new List<IPipelineStep> + { + m_Resizer.AsPipelineStep(), + m_Brightness.AsPipelineStep() + }); + + m_pipeline.PipelineChanged += OnPipelineChanged; } - private double m_Height; - public double Height + public async Task RunPipeline() { - get => m_Height; - set => this.RaiseAndSetIfChanged(ref m_Height, value); + try + { + LatestResult = await m_pipeline.Run(m_OriginalBitmap, m_CancellationTokenSource.Token).ConfigureAwait(false); + } catch (OperationCanceledException) + { + + } + } + + private async void OnPipelineChanged(object sender, EventArgs e) + { + m_CancellationTokenSource?.Cancel(); + m_CancellationTokenSource = new CancellationTokenSource(); + + await RunPipeline().ConfigureAwait(false); + } + + private IBitmap m_LatestResult; + public IBitmap LatestResult + { + get => m_LatestResult; + set => this.RaiseAndSetIfChanged(ref m_LatestResult, value); } public IBitmap LoadImage(string imagePath) @@ -48,9 +81,25 @@ namespace Avalar.Models { m_OriginalBitmap.Dispose(); m_LatestResult.Dispose(); + m_CancellationTokenSource.Dispose(); } disposed = true; } + + public void SetWidth(uint width) + { + m_Resizer.Width = width; + } + + public void SetHeight(uint height) + { + m_Resizer.Height = height; + } + + public void SetBrightnessDelta(int brightnessDelta) + { + m_Brightness.Brightness = brightnessDelta; + } } } diff --git a/Models/Interfaces/IImageModel.cs b/Models/Interfaces/IImageModel.cs index 897a221..d8f5870 100644 --- a/Models/Interfaces/IImageModel.cs +++ b/Models/Interfaces/IImageModel.cs @@ -1,9 +1,12 @@ using Avalonia.Media.Imaging; +using System.ComponentModel; namespace Avalar.Models.Interfaces { - public interface IImageModel + public interface IImageModel : INotifyPropertyChanged { IBitmap LoadImage(string imagePath); + + IBitmap LatestResult { get; } } } diff --git a/Models/Interfaces/ISettingsModel.cs b/Models/Interfaces/ISettingsModel.cs new file mode 100644 index 0000000..acdb187 --- /dev/null +++ b/Models/Interfaces/ISettingsModel.cs @@ -0,0 +1,14 @@ + +using Avalonia; + +namespace Avalar.Models.Interfaces +{ + public interface ISettingsModel + { + public void SetWidth(uint width); + + public void SetHeight(uint height); + + public void SetBrightnessDelta(int brightnessDelta); + } +} diff --git a/Models/Interfaces/SettingsModel.cs b/Models/Interfaces/SettingsModel.cs deleted file mode 100644 index 6149c1e..0000000 --- a/Models/Interfaces/SettingsModel.cs +++ /dev/null @@ -1,12 +0,0 @@ - -using Avalonia; - -namespace Avalar.Models.Interfaces -{ - interface ISettingsModel - { - public double Width { get; set; } - - public double Height { get; set; } - } -} diff --git a/Models/PipelineStep.cs b/Models/PipelineStep.cs new file mode 100644 index 0000000..e8a4bf1 --- /dev/null +++ b/Models/PipelineStep.cs @@ -0,0 +1,65 @@ +using Avalar.Services; +using Avalonia.Media.Imaging; +using ReactiveUI; +using System; +using System.ComponentModel; +using System.Threading; +using System.Threading.Tasks; + +namespace Avalar.Models +{ + public class PipelineStep<T> : IPipelineStep where T : INotifyPropertyChanged, IProcessor + { + public PipelineStep(T processor) + { + m_Processor = processor ?? throw new ArgumentNullException(nameof(processor)); + m_Processor.PropertyChanged += OnPropertyChanged; + } + + private void OnPropertyChanged(object sender, PropertyChangedEventArgs e) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Result))); + } + + public void InvalidateResult() + { + Result = null; + } + + private T m_Processor; + + public event PropertyChangedEventHandler PropertyChanged; + + private IBitmap Result { get; set; } + + public Task<IBitmap> Run(IBitmap bitmap, CancellationToken token) + { + if (Result != null) + { + return Task.FromResult(Result); + } + else + { + return Task.Run(() => + { + Result = m_Processor.Process(bitmap, token); + token.ThrowIfCancellationRequested(); + return Result; + }); + } + } + } + + public static class PipelineStep + { + public static PipelineStep<T> Create<T>(T processor) where T : INotifyPropertyChanged, IProcessor + { + return new PipelineStep<T>(processor); + } + + public static PipelineStep<T> AsPipelineStep<T>(this T processor) where T : INotifyPropertyChanged, IProcessor + { + return Create(processor); + } + } +} |
