diff --git a/Application/ASD.CellUniverse.Infrastructure/Services/ApplicationFacade.cs b/Application/ASD.CellUniverse.Infrastructure/Services/ApplicationFacade.cs index aaa5812..4a0c4da 100644 --- a/Application/ASD.CellUniverse.Infrastructure/Services/ApplicationFacade.cs +++ b/Application/ASD.CellUniverse.Infrastructure/Services/ApplicationFacade.cs @@ -30,7 +30,7 @@ public class ApplicationFacade : BindableBase, IApplicationFacade { } public int GenerationWidth { - get => matrix.GetLength(0); + get => generationWidth; set { if (matrixReadyToChange) { Set(ref generationWidth, ValidWidth(value)); @@ -40,7 +40,7 @@ public int GenerationWidth { } public int GenerationHeight { - get => matrix.GetLength(1); + get => generationHeight; set { if (matrixReadyToChange) { Set(ref generationHeight, ValidHeight(value)); @@ -115,8 +115,9 @@ private void ConfigureController(IGenerationController controller) { private Task OnStop() { // tmp fpsGenerator.Stop(); + Matrix = null; var resultTask = new Task(() => { - ChangeResolution(matrix.GetLength(0), matrix.GetLength(1)); + ChangeResolution(GenerationWidth, GenerationHeight); }); resultTask.Start(); return resultTask; diff --git a/Application/ASD.CellUniverse.Infrastructure/Services/MPSGenerationService.cs b/Application/ASD.CellUniverse.Infrastructure/Services/MPSGenerationService.cs index b7d1e0a..65f2747 100644 --- a/Application/ASD.CellUniverse.Infrastructure/Services/MPSGenerationService.cs +++ b/Application/ASD.CellUniverse.Infrastructure/Services/MPSGenerationService.cs @@ -28,7 +28,7 @@ internal MPSGenerationService() { fpsCollection = new DoubleCollection { 1.0, 2.0, 3.0, 5.0, 15.0, 30.0, 60.0, 120.0, 125.0 }; timer = new DispatcherTimer(); timer.Tick += (s, e) => NextFrameTime?.Invoke(); - MPS = fpsCollection.TakeWhile(f => f < 120.0).Last(); + MPS = fpsCollection.TakeWhile(f => f < 60.0).Last(); } private double ValidFps(double fps) => fps < MinFPS ? MinFPS : fps > MaxFPS ? MaxFPS : fps; diff --git a/Application/ASD.CellUniverse.Resources/Controls/MatrixLED.cs b/Application/ASD.CellUniverse.Resources/Controls/MatrixLED.cs index 769d6b6..9e2e25e 100644 --- a/Application/ASD.CellUniverse.Resources/Controls/MatrixLED.cs +++ b/Application/ASD.CellUniverse.Resources/Controls/MatrixLED.cs @@ -1,22 +1,25 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Media; -using System.Windows.Media.Animation; using System.Windows.Media.Imaging; namespace ASD.CellUniverse.Resources.Controls { - + using System.Windows.Media.Animation; using Helpers; public class MatrixLED : FrameworkElement { private int cellSize; private Size contentSize; + private Fade fade = new Fade(64); + private WriteableBitmap mask; + private WriteableBitmap fadeMask; public uint[,] Source { get => (uint[,])GetValue(SourceProperty); set => SetValue(SourceProperty, value); } @@ -38,7 +41,7 @@ public class MatrixLED : FrameworkElement { public static readonly DependencyProperty ShowFadeProperty = DependencyProperty.Register(nameof( ShowFade), typeof(bool), typeof(MatrixLED), new FrameworkPropertyMetadata( - false)); + false, OnShowFadeChanged)); static MatrixLED() => StyleProperty.OverrideMetadata(typeof(MatrixLED), new FrameworkPropertyMetadata(CreateStyle())); @@ -48,6 +51,15 @@ private static Style CreateStyle() { style.Seal(); return style; } + private static void OnShowFadeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { + if ((bool)e.NewValue) { + (d as MatrixLED).fade = new Fade(64); + } + else { + (d as MatrixLED).fade = null; + } + } + protected override Size MeasureOverride(Size constraint) { var count = Source == null || Source.Length < 1 @@ -66,29 +78,41 @@ protected override Size ArrangeOverride(Size arrangeSize) { return MeasureArrangeHelper.ComputeSize(arrangeSize, contentSize); } - private FadeHelper fh; - protected override void OnRender(DrawingContext dc) { + - RepaintLedsMask(); + protected override void OnRender(DrawingContext dc) { dc.DrawRectangle(Background, null, new Rect(new Point(), RenderSize)); - // bad - code of fade using =) if (ShowFade) { - if (fh == null) { - fh = new FadeHelper(1000, 60, Foreground, RenderSize); - } - if (fh.RenderSize != RenderSize) { - fh.RenderSize = RenderSize; - } - fh.Redraw(dc); - fh.AddImage(mask.Clone()); - } + fade.Add(Source); + RepaintFadeMask(); + + //dc.PushOpacity(0.0, new DoubleAnimation(1.0, 0.0, TimeSpan.FromMilliseconds(1000)).CreateClock()); + dc.PushOpacityMask(new ImageBrush(fadeMask)); + dc.DrawRectangle(Foreground, null, new Rect(new Point(), RenderSize)); + dc.Pop(); + //dc.Pop(); + } + + RepaintLedsMask(); dc.PushOpacityMask(new ImageBrush(mask)); dc.DrawRectangle(Foreground, null, new Rect(new Point(), RenderSize)); + + } + + private void RepaintFadeMask() { + fadeMask = BitmapHelper.Valid(fadeMask, contentSize); + if (fade != null && fade.Length > 0) { + using (var context = new WriteableContext(fadeMask)) { + + if (cellSize == 1) { context.WritePixels(fade); } + else { context.WriteCells(fade, cellSize); } + } + } } private void RepaintLedsMask() { @@ -102,54 +126,69 @@ private void RepaintLedsMask() { } } - // bad-impl.fade - private class FadeHelper { + private class Fade { + private Queue history; private int historySize; - private Brush brush; - public Size RenderSize { get; set; } + private uint[,] data; + public int Length => data.Length; - private TimeSpan time; - private Queue bitmaps; + public static implicit operator uint[,](Fade fade) => fade.data; - private DoubleAnimation fadeAnimation; - - public void AddImage(ImageSource image) { - bitmaps.Enqueue(image); - if (bitmaps.Count > historySize) { - bitmaps.Dequeue(); - } + public Fade(int historySize) { + this.historySize = historySize > 0 ? historySize : 1; + history = new Queue(historySize); + data = new uint[1, 1]; } - public void Redraw(DrawingContext dc) { + public void Add(uint[,] source) { + if (source == null || source.Length < 1) { + Clear(); + return; + } + if (history.Count > 0 && + (history.First().GetLength(0) != source.GetLength(0) || + history.First().GetLength(1) != source.GetLength(1))) { + Clear(); + } + history.Enqueue(source); - dc.PushOpacity(0.0, fadeAnimation.CreateClock()); + while (history.Count > historySize) { + history.Dequeue(); + } + if (history.Count > 0) { + data = Merge(history); + } + } - for (var i = 0; i < bitmaps.Count; ++i) { - dc.PushOpacityMask(new ImageBrush(bitmaps.ElementAt(i))); - dc.PushOpacity((1.0 / historySize)/* * (i + 1)*/); + public void Clear() { + history = new Queue(historySize); + data = new uint[1, 1]; + } - dc.DrawRectangle(brush, null, new Rect(new Point(), RenderSize)); + private uint[,] Merge(IEnumerable layers) { - dc.Pop(); - dc.Pop(); - } - dc.Pop(); - } + var sumAlpha = new uint[layers.First().GetLength(0), layers.First().GetLength(1)]; - public FadeHelper(int durationMilliseconds, int historySize, Brush brush, Size renderSize) { + Parallel.ForEach(layers, (layer) => { + for (var y = 0; y < layer.GetLength(1); ++y) { + for (var x = 0; x < layer.GetLength(0); ++x) { + sumAlpha[x, y] += (layer[x, y] >> 24); + } + } + }); - this.historySize = historySize; - this.brush = brush; - this.RenderSize = renderSize; - // ---- + var alpha = new uint[sumAlpha.GetLength(0), sumAlpha.GetLength(1)]; - bitmaps = new Queue(historySize); + for (var y = 0; y < sumAlpha.GetLength(1); ++y) { + for (var x = 0; x < sumAlpha.GetLength(0); ++x) { + alpha[x, y] = (sumAlpha[x, y] / (uint)historySize) << 24; + } + } - time = TimeSpan.FromMilliseconds(durationMilliseconds); - fadeAnimation = new DoubleAnimation(1.0, 0.0, time); + return alpha; } } } diff --git a/Application/ASD.CellUniverse/Shell.xaml b/Application/ASD.CellUniverse/Shell.xaml index b5d00e6..4fea875 100644 --- a/Application/ASD.CellUniverse/Shell.xaml +++ b/Application/ASD.CellUniverse/Shell.xaml @@ -33,7 +33,7 @@ Source="{Binding Facade.Matrix}" Foreground="{StaticResource NearStarBrush}" RenderOptions.BitmapScalingMode="{Binding SelectedScalingMode}" - ShowFade="False"/> + ShowFade="True"/>