summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDennis Brentjes <d.brentjes@gmail.com>2020-01-01 22:50:08 +0100
committerDennis Brentjes <d.brentjes@gmail.com>2020-01-01 22:50:08 +0100
commit2481d1857b08879a3d15e679a302667c7d8976f7 (patch)
treec8ae66b72a3469e65239c94d1d52795fb75700b2
parentfbe866f7b26c10bb54d72c029f8c628988a90be2 (diff)
downloadavalar-2481d1857b08879a3d15e679a302667c7d8976f7.tar.gz
avalar-2481d1857b08879a3d15e679a302667c7d8976f7.tar.bz2
avalar-2481d1857b08879a3d15e679a302667c7d8976f7.zip
Adds a working but slightly buggy pipeline implementation.
-rw-r--r--MainWindow.xaml.cs10
-rw-r--r--Models/AvalarPipeline.cs47
-rw-r--r--Models/IPipelineStep.cs13
-rw-r--r--Models/ImageModel.cs69
-rw-r--r--Models/Interfaces/IImageModel.cs5
-rw-r--r--Models/Interfaces/ISettingsModel.cs14
-rw-r--r--Models/Interfaces/SettingsModel.cs12
-rw-r--r--Models/PipelineStep.cs65
-rw-r--r--Services/IProcessor.cs6
-rw-r--r--Services/ImageMagickBrightness.cs25
-rw-r--r--Services/ImageMagickResizer.cs34
-rw-r--r--Services/Interfaces/IBrightness.cs8
-rw-r--r--Services/Interfaces/IResizer.cs9
-rw-r--r--Services/Resizer/IResizer.cs7
-rw-r--r--Services/Resizer/ImageMagickResizer.cs26
-rw-r--r--Utils/BitmapExtensions.cs25
-rw-r--r--Utils/ServiceProviderExtensions.cs13
-rw-r--r--ViewModels/Image/ImageLoadedViewModel.cs26
-rw-r--r--ViewModels/Image/ImageViewModel.cs10
-rw-r--r--ViewModels/Settings/ISettingLimits.cs9
-rw-r--r--ViewModels/Settings/SettingLimits.cs20
-rw-r--r--ViewModels/Settings/SettingsViewModel.cs52
-rw-r--r--Views/Settings/Settings.xaml8
23 files changed, 437 insertions, 76 deletions
diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs
index b2a5090..1ddc1a6 100644
--- a/MainWindow.xaml.cs
+++ b/MainWindow.xaml.cs
@@ -1,5 +1,6 @@
using Avalar.Models;
-using Avalar.Services.Resizer;
+using Avalar.Services;
+using Avalar.Services.Interfaces;
using Avalar.Viewmodels.Image;
using Avalar.ViewModels.Settings;
using Avalar.Views;
@@ -20,15 +21,18 @@ namespace Avalar
{
InitializeComponent();
var serviceCollection = new ServiceCollection();
+
serviceCollection.AddScoped(typeof(IResizer), typeof(ImageMagickResizer));
+ serviceCollection.AddScoped(typeof(IBrightness), typeof(ImageMagickBrightness));
+
serviceCollection.AddScoped(typeof(IAvalarViewService), (sp) => new AvalarViewService(this));
var serviceProvider = serviceCollection.BuildServiceProvider();
- ImageM = new ImageModel();
+ ImageM = new ImageModel(serviceProvider);
ImageVm = new ImageViewModel(ImageM, serviceProvider);
- SettingsVm = new SettingsViewModel();
+ SettingsVm = new SettingsViewModel(ImageM);
DataContext = this;
}
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);
+ }
+ }
+}
diff --git a/Services/IProcessor.cs b/Services/IProcessor.cs
index 11e83ab..00bd6a8 100644
--- a/Services/IProcessor.cs
+++ b/Services/IProcessor.cs
@@ -1,9 +1,11 @@
using Avalonia.Media.Imaging;
+using System.ComponentModel;
+using System.Threading.Tasks;
namespace Avalar.Services
{
- public interface IProcessor
+ public interface IProcessor : INotifyPropertyChanged
{
- IBitmap Process(IBitmap bitmap);
+ public IBitmap Process(IBitmap input, System.Threading.CancellationToken token);
}
}
diff --git a/Services/ImageMagickBrightness.cs b/Services/ImageMagickBrightness.cs
new file mode 100644
index 0000000..f69247e
--- /dev/null
+++ b/Services/ImageMagickBrightness.cs
@@ -0,0 +1,25 @@
+using Avalar.Services.Interfaces;
+using Avalonia.Media.Imaging;
+using ImageMagick;
+using ReactiveUI;
+using System.IO;
+using System.Threading;
+
+namespace Avalar.Services
+{
+ public class ImageMagickBrightness : ReactiveObject, IBrightness
+ {
+ int m_Brightness;
+ public int Brightness {
+ get => m_Brightness;
+ set => this.RaiseAndSetIfChanged(ref m_Brightness, value);
+ }
+
+ public IBitmap Process(IBitmap input, CancellationToken token)
+ {
+ using var image = input.ToMagickImage();
+ image.BrightnessContrast(new Percentage(Brightness), new Percentage(0));
+ return image.ToBitmap();
+ }
+ }
+}
diff --git a/Services/ImageMagickResizer.cs b/Services/ImageMagickResizer.cs
new file mode 100644
index 0000000..b7ce6da
--- /dev/null
+++ b/Services/ImageMagickResizer.cs
@@ -0,0 +1,34 @@
+
+using Avalar.Services.Interfaces;
+using Avalonia.Media.Imaging;
+using ImageMagick;
+using ReactiveUI;
+using System;
+using System.Threading;
+
+namespace Avalar.Services
+{
+ public class ImageMagickResizer : ReactiveObject, IResizer
+ {
+ private uint m_Width;
+ public uint Width {
+ get => m_Width;
+ set => this.RaiseAndSetIfChanged(ref m_Width, value);
+ }
+
+ private uint m_Height;
+ public uint Height {
+ get => m_Height;
+ set => this.RaiseAndSetIfChanged(ref m_Height, value);
+ }
+
+ public IBitmap Process(IBitmap input, CancellationToken token)
+ {
+ using var image = input.ToMagickImage();
+ var geometry = new MagickGeometry(Convert.ToInt32(Width), Convert.ToInt32(Height));
+ geometry.IgnoreAspectRatio = true;
+ image.Resize(geometry);
+ return image.ToBitmap();
+ }
+ }
+}
diff --git a/Services/Interfaces/IBrightness.cs b/Services/Interfaces/IBrightness.cs
new file mode 100644
index 0000000..cf8f631
--- /dev/null
+++ b/Services/Interfaces/IBrightness.cs
@@ -0,0 +1,8 @@
+
+namespace Avalar.Services.Interfaces
+{
+ interface IBrightness : IProcessor
+ {
+ public int Brightness { get; set; }
+ }
+}
diff --git a/Services/Interfaces/IResizer.cs b/Services/Interfaces/IResizer.cs
new file mode 100644
index 0000000..d7e5cdd
--- /dev/null
+++ b/Services/Interfaces/IResizer.cs
@@ -0,0 +1,9 @@
+
+namespace Avalar.Services.Interfaces
+{
+ public interface IResizer : IProcessor
+ {
+ public uint Width { get; set; }
+ public uint Height { get; set; }
+ }
+}
diff --git a/Services/Resizer/IResizer.cs b/Services/Resizer/IResizer.cs
deleted file mode 100644
index e71d4f1..0000000
--- a/Services/Resizer/IResizer.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-
-namespace Avalar.Services.Resizer
-{
- public interface IResizer : IProcessor
- {
- }
-}
diff --git a/Services/Resizer/ImageMagickResizer.cs b/Services/Resizer/ImageMagickResizer.cs
deleted file mode 100644
index e48e576..0000000
--- a/Services/Resizer/ImageMagickResizer.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-
-using Avalonia.Media.Imaging;
-using ImageMagick;
-using System.IO;
-
-namespace Avalar.Services.Resizer
-{
- public class ImageMagickResizer : IResizer
- {
- public IBitmap Process(IBitmap bitmap)
- {
- using (var stream = new MemoryStream())
- {
- bitmap?.Save(stream);
- using (var image = new MagickImage(stream))
- {
- image.Resize(300, 300);
- using (var stream2 = new MemoryStream())
- {
- return new Bitmap(stream2);
- }
- }
- }
- }
- }
-}
diff --git a/Utils/BitmapExtensions.cs b/Utils/BitmapExtensions.cs
new file mode 100644
index 0000000..89f0257
--- /dev/null
+++ b/Utils/BitmapExtensions.cs
@@ -0,0 +1,25 @@
+using Avalonia.Media.Imaging;
+using ImageMagick;
+using System.IO;
+
+namespace Avalar.Services
+{
+ public static class BitmapExtensions
+ {
+ public static MagickImage ToMagickImage(this IBitmap bitmap)
+ {
+ using var stream = new MemoryStream();
+ bitmap?.Save(stream);
+ stream.Seek(0, SeekOrigin.Begin);
+ return new MagickImage(stream);
+ }
+
+ public static IBitmap ToBitmap(this MagickImage image)
+ {
+ using var stream = new MemoryStream();
+ image?.Write(stream, MagickFormat.Png);
+ stream.Seek(0, SeekOrigin.Begin);
+ return new Bitmap(stream);
+ }
+ }
+}
diff --git a/Utils/ServiceProviderExtensions.cs b/Utils/ServiceProviderExtensions.cs
new file mode 100644
index 0000000..1ed41f0
--- /dev/null
+++ b/Utils/ServiceProviderExtensions.cs
@@ -0,0 +1,13 @@
+
+using System;
+
+namespace Avalar.Utils
+{
+ public static class ServiceProviderExtensions
+ {
+ public static T GetService<T>(this IServiceProvider serviceProvider)
+ {
+ return (T)serviceProvider?.GetService(typeof(T));
+ }
+ }
+}
diff --git a/ViewModels/Image/ImageLoadedViewModel.cs b/ViewModels/Image/ImageLoadedViewModel.cs
index f9e322c..0489ac1 100644
--- a/ViewModels/Image/ImageLoadedViewModel.cs
+++ b/ViewModels/Image/ImageLoadedViewModel.cs
@@ -1,23 +1,37 @@
-using Avalonia.Media.Imaging;
+using Avalar.Models.Interfaces;
+using Avalonia.Media.Imaging;
+using Avalonia.Threading;
using ReactiveUI;
+using System;
+using System.ComponentModel;
namespace Avalar.ViewModels.Image
{
public class ImageLoadedViewModel : ReactiveObject
{
- public ImageLoadedViewModel(IBitmap bitmap)
+ private IImageModel m_ImageModel;
+
+ public ImageLoadedViewModel(IBitmap bitmap, IImageModel imageModel)
{
Bitmap = bitmap;
+ m_ImageModel = imageModel ?? throw new ArgumentNullException(nameof(imageModel));
+
+ m_ImageModel.PropertyChanged += OnImageModelChanged;
+ }
+
+ private void OnImageModelChanged(object sender, PropertyChangedEventArgs e)
+ {
+ Dispatcher.UIThread.InvokeAsync(() =>
+ {
+ Bitmap = m_ImageModel.LatestResult;
+ });
}
private IBitmap m_Bitmap;
public IBitmap Bitmap
{
get => m_Bitmap;
- set
- {
- this.RaiseAndSetIfChanged(ref m_Bitmap, value);
- }
+ set => this.RaiseAndSetIfChanged(ref m_Bitmap, value);
}
}
}
diff --git a/ViewModels/Image/ImageViewModel.cs b/ViewModels/Image/ImageViewModel.cs
index 3b41919..4b17343 100644
--- a/ViewModels/Image/ImageViewModel.cs
+++ b/ViewModels/Image/ImageViewModel.cs
@@ -1,8 +1,6 @@
-
-using Avalar.Models.Interfaces;
+using Avalar.Models.Interfaces;
using Avalar.ViewModels.Image;
using Avalar.Views;
-using Avalonia.Media.Imaging;
using ReactiveUI;
using System;
using System.Linq;
@@ -27,9 +25,7 @@ namespace Avalar.Viewmodels.Image
public object ChildViewModel
{
get => m_ChildViewModel;
- set {
- this.RaiseAndSetIfChanged(ref m_ChildViewModel, value);
- }
+ set => this.RaiseAndSetIfChanged(ref m_ChildViewModel, value);
}
public async void OpenFile()
@@ -37,7 +33,7 @@ namespace Avalar.Viewmodels.Image
var result = await ViewService.ShowOpenImageFileDialog().ConfigureAwait(true);
if (result.Length != 1) return;
- ChildViewModel = new ImageLoadedViewModel(ImageModel.LoadImage(result.First()));
+ ChildViewModel = new ImageLoadedViewModel(ImageModel.LoadImage(result.First()), ImageModel);
}
}
}
diff --git a/ViewModels/Settings/ISettingLimits.cs b/ViewModels/Settings/ISettingLimits.cs
new file mode 100644
index 0000000..a4927f0
--- /dev/null
+++ b/ViewModels/Settings/ISettingLimits.cs
@@ -0,0 +1,9 @@
+namespace Avalar.ViewModels.Settings
+{
+ public interface ISettingLimits
+ {
+ uint MaxWidth { get; }
+
+ uint MaxHeight { get; }
+ }
+} \ No newline at end of file
diff --git a/ViewModels/Settings/SettingLimits.cs b/ViewModels/Settings/SettingLimits.cs
new file mode 100644
index 0000000..8a0f3ea
--- /dev/null
+++ b/ViewModels/Settings/SettingLimits.cs
@@ -0,0 +1,20 @@
+using Avalonia.Media.Imaging;
+using System;
+
+namespace Avalar.ViewModels.Settings
+{
+ public class SettingLimits : ISettingLimits
+ {
+ public SettingLimits(IBitmap bitmap)
+ {
+ var _ = bitmap ?? throw new ArgumentNullException(nameof(bitmap));
+
+ MaxWidth = Convert.ToUInt32(bitmap.Size.Width);
+ MaxHeight = Convert.ToUInt32(bitmap.Size.Height);
+ }
+
+ public uint MaxWidth { get; }
+
+ public uint MaxHeight { get; }
+ }
+}
diff --git a/ViewModels/Settings/SettingsViewModel.cs b/ViewModels/Settings/SettingsViewModel.cs
index 730b662..17c27cc 100644
--- a/ViewModels/Settings/SettingsViewModel.cs
+++ b/ViewModels/Settings/SettingsViewModel.cs
@@ -1,7 +1,57 @@

+using Avalar.Models.Interfaces;
+using ReactiveUI;
+using System;
+using System.ComponentModel;
+using System.Threading.Tasks;
+
namespace Avalar.ViewModels.Settings
{
- public class SettingsViewModel
+ public class SettingsViewModel : ReactiveObject
{
+ private readonly ISettingsModel m_SettingsModel;
+ public SettingsViewModel(ISettingsModel settingsModel)
+ {
+ m_SettingsModel = settingsModel;
+
+ PropertyChanged += OnPropertyChanged;
+ }
+
+ private async void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if(e.PropertyName == nameof(Width))
+ {
+ await Task.Run(() => m_SettingsModel.SetWidth(Width)).ConfigureAwait(false);
+ }
+ if (e.PropertyName == nameof(Height))
+ {
+ await Task.Run(() => m_SettingsModel.SetHeight(Height)).ConfigureAwait(false);
+ }
+ if (e.PropertyName == nameof(BrightnessDelta))
+ {
+ await Task.Run(() => m_SettingsModel.SetBrightnessDelta(BrightnessDelta)).ConfigureAwait(false);
+ }
+ }
+
+ private uint m_Width;
+ public uint Width
+ {
+ get => m_Width;
+ set => this.RaiseAndSetIfChanged(ref m_Width, value);
+ }
+
+ private uint m_Height;
+ public uint Height
+ {
+ get => m_Height;
+ set => this.RaiseAndSetIfChanged(ref m_Height, value);
+ }
+
+ private int m_BrightnessDelta;
+ public int BrightnessDelta
+ {
+ get => m_BrightnessDelta;
+ set => this.RaiseAndSetIfChanged(ref m_BrightnessDelta, value);
+ }
}
}
diff --git a/Views/Settings/Settings.xaml b/Views/Settings/Settings.xaml
index fb547e7..5a83bfa 100644
--- a/Views/Settings/Settings.xaml
+++ b/Views/Settings/Settings.xaml
@@ -5,5 +5,11 @@
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Avalar.Views.Settings.SettingsControl"
MinWidth="400">
- Welcome to Avalonia!
+ <Grid>
+ <StackPanel Orientation="Vertical">
+ <Slider Value="{Binding Width}" Minimum="0" Maximum="2000"/>
+ <Slider Value="{Binding Height}" Minimum="0" Maximum="2000"/>
+ <Slider Value="{Binding BrightnessDelta}" Minimum="-100" Maximum="100"/>
+ </StackPanel>
+ </Grid>
</UserControl>