diff --git a/RGB.NET.Core/Groups/AbstractLedGroup.cs b/RGB.NET.Core/Groups/AbstractLedGroup.cs index 86659af..7135a21 100644 --- a/RGB.NET.Core/Groups/AbstractLedGroup.cs +++ b/RGB.NET.Core/Groups/AbstractLedGroup.cs @@ -1,4 +1,5 @@ -using System.Collections; +using System; +using System.Collections; using System.Collections.Generic; using System.Linq; @@ -61,5 +62,14 @@ public abstract class AbstractLedGroup : AbstractDecoratable /// public IEnumerator GetEnumerator() => GetLeds().GetEnumerator(); + /// + IDisposable? ILedGroup.ToListUnsafe(out IList leds) => ToListUnsafe(out leds); + + protected virtual IDisposable? ToListUnsafe(out IList leds) + { + leds = ToList(); + return null; + } + #endregion } \ No newline at end of file diff --git a/RGB.NET.Core/Groups/ILedGroup.cs b/RGB.NET.Core/Groups/ILedGroup.cs index 97ed8b2..1970afc 100644 --- a/RGB.NET.Core/Groups/ILedGroup.cs +++ b/RGB.NET.Core/Groups/ILedGroup.cs @@ -1,6 +1,7 @@ // ReSharper disable UnusedMemberInSuper.Global // ReSharper disable UnusedMember.Global +using System; using System.Collections.Generic; namespace RGB.NET.Core; @@ -45,4 +46,6 @@ public interface ILedGroup : IDecoratable, IEnumerable /// /// A list containing all in this group. IList ToList(); + + internal IDisposable? ToListUnsafe(out IList leds); } \ No newline at end of file diff --git a/RGB.NET.Core/Groups/ListLedGroup.cs b/RGB.NET.Core/Groups/ListLedGroup.cs index 9c51906..c6f7d54 100644 --- a/RGB.NET.Core/Groups/ListLedGroup.cs +++ b/RGB.NET.Core/Groups/ListLedGroup.cs @@ -1,7 +1,9 @@ // ReSharper disable MemberCanBePrivate.Global // ReSharper disable UnusedMember.Global +using System; using System.Collections.Generic; +using System.Threading; namespace RGB.NET.Core; @@ -135,5 +137,12 @@ public class ListLedGroup : AbstractLedGroup return new List(GroupLeds); } + protected override IDisposable ToListUnsafe(out IList leds) + { + Monitor.Enter(GroupLeds); + leds = GroupLeds; + return new ActionDisposable(() => Monitor.Exit(GroupLeds)); + } + #endregion } \ No newline at end of file diff --git a/RGB.NET.Core/Misc/ActionDisposable.cs b/RGB.NET.Core/Misc/ActionDisposable.cs new file mode 100644 index 0000000..843605e --- /dev/null +++ b/RGB.NET.Core/Misc/ActionDisposable.cs @@ -0,0 +1,27 @@ +using System; + +namespace RGB.NET.Core; + +public sealed class ActionDisposable : IDisposable +{ + #region Properties & Fields + + private readonly Action _onDispose; + + #endregion + + #region Constructors + + public ActionDisposable(Action onDispose) + { + this._onDispose = onDispose; + } + + #endregion + + #region Methods + + public void Dispose() => _onDispose(); + + #endregion +} \ No newline at end of file diff --git a/RGB.NET.Core/RGBSurface.cs b/RGB.NET.Core/RGBSurface.cs index fc8003f..5e022ed 100644 --- a/RGB.NET.Core/RGBSurface.cs +++ b/RGB.NET.Core/RGBSurface.cs @@ -208,26 +208,28 @@ public sealed class RGBSurface : AbstractBindable, IDisposable if ((brush == null) || !brush.IsEnabled) return; - IList leds = ledGroup.ToList(); - - IEnumerable<(RenderTarget renderTarget, Color color)> render; - switch (brush.CalculationMode) + using (ledGroup.ToListUnsafe(out IList leds)) { - case RenderMode.Relative: - Rectangle brushRectangle = new(leds); - Point offset = new(-brushRectangle.Location.X, -brushRectangle.Location.Y); - brushRectangle = brushRectangle.SetLocation(new Point(0, 0)); - render = brush.Render(brushRectangle, leds.Select(led => new RenderTarget(led, led.AbsoluteBoundary.Translate(offset)))); - break; - case RenderMode.Absolute: - render = brush.Render(Boundary, leds.Select(led => new RenderTarget(led, led.AbsoluteBoundary))); - break; - default: - throw new ArgumentException($"The CalculationMode '{brush.CalculationMode}' is not valid."); - } + IEnumerable<(RenderTarget renderTarget, Color color)> render; + switch (brush.CalculationMode) + { + case RenderMode.Relative: + Rectangle brushRectangle = new(leds); + Point offset = new(-brushRectangle.Location.X, -brushRectangle.Location.Y); + brushRectangle = brushRectangle.SetLocation(new Point(0, 0)); + render = brush.Render(brushRectangle, + leds.Select(led => new RenderTarget(led, led.AbsoluteBoundary.Translate(offset)))); + break; + case RenderMode.Absolute: + render = brush.Render(Boundary, leds.Select(led => new RenderTarget(led, led.AbsoluteBoundary))); + break; + default: + throw new ArgumentException($"The CalculationMode '{brush.CalculationMode}' is not valid."); + } - foreach ((RenderTarget renderTarget, Color c) in render) - renderTarget.Led.Color = c; + foreach ((RenderTarget renderTarget, Color c) in render) + renderTarget.Led.Color = c; + } } ///