diff --git a/src/Artemis.Core/Artemis.Core.csproj b/src/Artemis.Core/Artemis.Core.csproj
index c5242f460..61acb02d0 100644
--- a/src/Artemis.Core/Artemis.Core.csproj
+++ b/src/Artemis.Core/Artemis.Core.csproj
@@ -144,6 +144,7 @@
+
diff --git a/src/Artemis.Core/Events/DeviceEventArgs.cs b/src/Artemis.Core/Events/DeviceEventArgs.cs
index dc9f6dff9..1401cee36 100644
--- a/src/Artemis.Core/Events/DeviceEventArgs.cs
+++ b/src/Artemis.Core/Events/DeviceEventArgs.cs
@@ -5,10 +5,6 @@ namespace Artemis.Core.Events
{
public class DeviceEventArgs : EventArgs
{
- public DeviceEventArgs()
- {
- }
-
public DeviceEventArgs(IRGBDevice device)
{
Device = device;
diff --git a/src/Artemis.Core/Events/FrameEventArgs.cs b/src/Artemis.Core/Events/FrameEventArgs.cs
new file mode 100644
index 000000000..b21cd9e91
--- /dev/null
+++ b/src/Artemis.Core/Events/FrameEventArgs.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using Artemis.Core.Plugins.Abstract;
+using RGB.NET.Core;
+
+namespace Artemis.Core.Events
+{
+ public class FrameEventArgs : EventArgs
+ {
+ public FrameEventArgs(List modules, Bitmap bitmap, double deltaTime, RGBSurface rgbSurface)
+ {
+ Modules = modules;
+ Bitmap = bitmap;
+ DeltaTime = deltaTime;
+ RgbSurface = rgbSurface;
+ }
+
+ public List Modules { get; }
+ public Bitmap Bitmap { get; }
+ public double DeltaTime { get; }
+ public RGBSurface RgbSurface { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/RGB.NET/GraphicsDecorator.cs b/src/Artemis.Core/RGB.NET/GraphicsDecorator.cs
index de5ee5225..f1f3187a6 100644
--- a/src/Artemis.Core/RGB.NET/GraphicsDecorator.cs
+++ b/src/Artemis.Core/RGB.NET/GraphicsDecorator.cs
@@ -2,27 +2,24 @@
using System.Drawing;
using System.Linq;
using RGB.NET.Core;
-using RGB.NET.Groups;
using Color = RGB.NET.Core.Color;
using Rectangle = RGB.NET.Core.Rectangle;
namespace Artemis.Core.RGB.NET
{
- public class GraphicsDecorator : AbstractDecorator, IBrushDecorator
+ public class GraphicsDecorator : AbstractDecorator, IBrushDecorator, IDisposable
{
- private readonly DirectBitmap _bitmap;
+ private DirectBitmap _bitmap;
- public GraphicsDecorator(ListLedGroup ledGroup)
+ public GraphicsDecorator(ILedGroup ledGroup)
{
var leds = ledGroup.GetLeds().ToList();
if (!leds.Any())
- {
_bitmap = null;
- }
else
{
- var width = leds.Max(l => l.AbsoluteLedRectangle.X + l.AbsoluteLedRectangle.Width);
- var height = leds.Max(l => l.AbsoluteLedRectangle.Y + l.AbsoluteLedRectangle.Height);
+ var width = Math.Min(leds.Max(l => l.AbsoluteLedRectangle.X + l.AbsoluteLedRectangle.Width), 2000);
+ var height = Math.Min(leds.Max(l => l.AbsoluteLedRectangle.Y + l.AbsoluteLedRectangle.Height), 2000);
_bitmap = new DirectBitmap((int) width, (int) height);
}
@@ -40,9 +37,25 @@ namespace Artemis.Core.RGB.NET
return new Color(0, 0, 0);
}
+ public override void OnDetached(IDecoratable decoratable)
+ {
+ Dispose();
+ }
+
+ public void Dispose()
+ {
+ _bitmap?.Dispose();
+ _bitmap = null;
+ }
+
public Graphics GetGraphics()
{
return _bitmap == null ? null : Graphics.FromImage(_bitmap.Bitmap);
}
+
+ public Bitmap GetBitmap()
+ {
+ return _bitmap?.Bitmap;
+ }
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Services/CoreService.cs b/src/Artemis.Core/Services/CoreService.cs
index be72126ef..7be6e9fe2 100644
--- a/src/Artemis.Core/Services/CoreService.cs
+++ b/src/Artemis.Core/Services/CoreService.cs
@@ -1,7 +1,8 @@
using System;
+using System.Linq;
using System.Threading.Tasks;
+using Artemis.Core.Events;
using Artemis.Core.Exceptions;
-using Artemis.Core.Models.Surface;
using Artemis.Core.Plugins.Abstract;
using Artemis.Core.Services.Interfaces;
using Artemis.Core.Services.Storage;
@@ -46,11 +47,17 @@ namespace Artemis.Core.Services
throw new ArtemisCoreException("Cannot initialize the core as it is already initialized.");
_logger.Information("Initializing Artemis Core version {version}", typeof(CoreService).Assembly.GetName().Version);
-
+
// Initialize the services
await Task.Run(() => _pluginService.CopyBuiltInPlugins());
await Task.Run(() => _pluginService.LoadPlugins());
+ var surfaceConfig = _surfaceService.ActiveSurfaceConfiguration;
+ if (surfaceConfig != null)
+ _logger.Information("Initialized with active surface configuration {surfaceConfig}-{guid}", surfaceConfig.Name, surfaceConfig.Guid);
+ else
+ _logger.Information("Initialized without an active surface configuration");
+
OnInitialized();
}
@@ -79,6 +86,8 @@ namespace Artemis.Core.Services
foreach (var module in modules)
module.Render(args.DeltaTime, _rgbService.Surface, g);
}
+
+ OnFrameRendered(new FrameEventArgs(modules, _rgbService.GraphicsDecorator.GetBitmap(), args.DeltaTime, _rgbService.Surface));
}
catch (Exception e)
{
@@ -89,6 +98,7 @@ namespace Artemis.Core.Services
#region Events
public event EventHandler Initialized;
+ public event EventHandler FrameRendered;
private void OnInitialized()
{
@@ -97,5 +107,10 @@ namespace Artemis.Core.Services
}
#endregion
+
+ protected virtual void OnFrameRendered(FrameEventArgs e)
+ {
+ FrameRendered?.Invoke(this, e);
+ }
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Services/Interfaces/ICoreService.cs b/src/Artemis.Core/Services/Interfaces/ICoreService.cs
index aba2228cd..c82b1eb6c 100644
--- a/src/Artemis.Core/Services/Interfaces/ICoreService.cs
+++ b/src/Artemis.Core/Services/Interfaces/ICoreService.cs
@@ -1,4 +1,5 @@
using System;
+using Artemis.Core.Events;
namespace Artemis.Core.Services.Interfaces
{
@@ -13,5 +14,10 @@ namespace Artemis.Core.Services.Interfaces
/// Occurs the core has finished initializing
///
event EventHandler Initialized;
+
+ ///
+ /// Occurs whenever a frame has finished rendering
+ ///
+ event EventHandler FrameRendered;
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Services/RgbService.cs b/src/Artemis.Core/Services/RgbService.cs
index 2cbb51c79..8544aaea1 100644
--- a/src/Artemis.Core/Services/RgbService.cs
+++ b/src/Artemis.Core/Services/RgbService.cs
@@ -30,7 +30,7 @@ namespace Artemis.Core.Services
Surface.Exception += SurfaceOnException;
_loadedDevices = new List();
- _updateTrigger = new TimerUpdateTrigger {UpdateFrequency = 1.0 / 30};
+ _updateTrigger = new TimerUpdateTrigger {UpdateFrequency = 1.0 / 25};
Surface.RegisterUpdateTrigger(_updateTrigger);
}
@@ -97,10 +97,12 @@ namespace Artemis.Core.Services
public void UpdateGraphicsDecorator()
{
+ // TODO: Create new one first, then clean up the old one for a smoother transition
+
// Clean up the old background if present
if (_background != null)
{
- _background.RemoveAllDecorators();
+ _background.Brush?.RemoveAllDecorators();
_background.Detach();
}
diff --git a/src/Artemis.Core/Services/Storage/SurfaceService.cs b/src/Artemis.Core/Services/Storage/SurfaceService.cs
index fc78c4c2e..900359b9c 100644
--- a/src/Artemis.Core/Services/Storage/SurfaceService.cs
+++ b/src/Artemis.Core/Services/Storage/SurfaceService.cs
@@ -140,8 +140,7 @@ namespace Artemis.Core.Services.Storage
MatchDeviceConfiguration(e.Device, surfaceConfiguration);
}
- foreach (var deviceConfiguration in ActiveSurfaceConfiguration.DeviceConfigurations)
- deviceConfiguration.ApplyToDevice();
+ UpdateSurfaceConfiguration(ActiveSurfaceConfiguration, true);
}
#endregion
@@ -169,7 +168,7 @@ namespace Artemis.Core.Services.Storage
// When all surface configs are loaded, apply the active surface config
var active = SurfaceConfigurations.FirstOrDefault(c => c.IsActive);
if (active != null)
- ActiveSurfaceConfiguration = active;
+ SetActiveSurfaceConfiguration(active);
}
#endregion
diff --git a/src/Artemis.Plugins.Modules.General/GeneralModule.cs b/src/Artemis.Plugins.Modules.General/GeneralModule.cs
index 31652b650..a9293a0c4 100644
--- a/src/Artemis.Plugins.Modules.General/GeneralModule.cs
+++ b/src/Artemis.Plugins.Modules.General/GeneralModule.cs
@@ -3,32 +3,30 @@ using System.Drawing;
using Artemis.Core.Extensions;
using Artemis.Core.Plugins.Abstract;
using Artemis.Core.Plugins.Models;
-using Artemis.Core.Services.Interfaces;
using Artemis.Plugins.Modules.General.ViewModels;
using RGB.NET.Core;
using Stylet;
using Color = System.Drawing.Color;
+using Rectangle = System.Drawing.Rectangle;
namespace Artemis.Plugins.Modules.General
{
public class GeneralModule : Module
{
private readonly PluginSettings _settings;
- private readonly RGBSurface _surface;
- private Color _color;
- public GeneralModule(PluginInfo pluginInfo, IRgbService rgbService, PluginSettings settings) : base(pluginInfo)
+ public GeneralModule(PluginInfo pluginInfo, PluginSettings settings) : base(pluginInfo)
{
_settings = settings;
DisplayName = "General";
ExpandsMainDataModel = true;
- _surface = rgbService.Surface;
-
var testSetting = _settings.GetSetting("TestSetting", DateTime.Now);
- _color = ColorHelpers.GetRandomRainbowColor();
+ Colors = new Color[1000];
}
+ public Color[] Colors { get; set; }
+
public override void EnablePlugin()
{
}
@@ -39,16 +37,59 @@ namespace Artemis.Plugins.Modules.General
public override void Update(double deltaTime)
{
- _color = ColorHelpers.ShiftColor(_color, (int) (deltaTime * 200));
+ for (var i = 0; i < Colors.Length; i++)
+ {
+ var color = Colors[i];
+ Colors[i] = ColorHelpers.ShiftColor(color, (int) (deltaTime * 200));
+ }
}
public override void Render(double deltaTime, RGBSurface surface, Graphics graphics)
{
- // Lets do this in the least performant way possible
- foreach (var surfaceLed in _surface.Leds)
+ // Per-device coloring, slower
+ // RenderPerDevice(surface, graphics);
+
+ // Per-LED coloring, slowest
+ RenderPerLed(surface, graphics);
+ }
+
+ public void RenderFullSurface(RGBSurface surface, Graphics graphics)
+ {
+ }
+
+ public void RenderPerDevice(RGBSurface surface, Graphics graphics)
+ {
+ var index = 0;
+ foreach (var device in surface.Devices)
{
- var rectangle = surfaceLed.AbsoluteLedRectangle.ToDrawingRectangle();
- graphics.FillRectangle(new SolidBrush(_color), rectangle);
+ var color = Colors[index];
+ if (color.A == 0)
+ {
+ color = ColorHelpers.GetRandomRainbowColor();
+ Colors[index] = color;
+ }
+
+ var rectangle = new Rectangle((int) device.Location.X, (int) device.Location.Y, (int) device.Size.Width, (int) device.Size.Height);
+ graphics.FillRectangle(new SolidBrush(color), rectangle);
+ index++;
+ }
+ }
+
+ public void RenderPerLed(RGBSurface surface, Graphics graphics)
+ {
+ var index = 0;
+ foreach (var led in surface.Leds)
+ {
+ var color = Colors[index];
+ if (color.A == 0)
+ {
+ color = ColorHelpers.GetRandomRainbowColor();
+ Colors[index] = color;
+ }
+
+ var rectangle = led.AbsoluteLedRectangle.ToDrawingRectangle();
+ graphics.FillRectangle(new SolidBrush(color), rectangle);
+ index++;
}
}
diff --git a/src/Artemis.UI/Artemis.UI.csproj b/src/Artemis.UI/Artemis.UI.csproj
index c98b1afcc..6938f4f6d 100644
--- a/src/Artemis.UI/Artemis.UI.csproj
+++ b/src/Artemis.UI/Artemis.UI.csproj
@@ -165,6 +165,7 @@
+
@@ -186,6 +187,10 @@
MSBuild:Compile
Designer
+
+ Designer
+ MSBuild:Compile
+
Designer
MSBuild:Compile
diff --git a/src/Artemis.UI/ViewModels/Controls/SurfaceEditor/SurfaceDeviceViewModel.cs b/src/Artemis.UI/ViewModels/Controls/SurfaceEditor/SurfaceDeviceViewModel.cs
index c591f7176..8d5b5bc86 100644
--- a/src/Artemis.UI/ViewModels/Controls/SurfaceEditor/SurfaceDeviceViewModel.cs
+++ b/src/Artemis.UI/ViewModels/Controls/SurfaceEditor/SurfaceDeviceViewModel.cs
@@ -51,7 +51,9 @@ namespace Artemis.UI.ViewModels.Controls.SurfaceEditor
public IReadOnlyCollection Leds => _leds.AsReadOnly();
- public Rect DeviceRectangle => new Rect(X, Y, DeviceConfiguration.Device.Size.Width, DeviceConfiguration.Device.Size.Height);
+ public Rect DeviceRectangle => DeviceConfiguration.Device == null
+ ? new Rect()
+ : new Rect(X, Y, DeviceConfiguration.Device.Size.Width, DeviceConfiguration.Device.Size.Height);
public void StartMouseDrag(Point mouseStartPosition)
{
diff --git a/src/Artemis.UI/ViewModels/Screens/DebugViewModel.cs b/src/Artemis.UI/ViewModels/Screens/DebugViewModel.cs
new file mode 100644
index 000000000..5e07755b0
--- /dev/null
+++ b/src/Artemis.UI/ViewModels/Screens/DebugViewModel.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Drawing;
+using System.Runtime.InteropServices;
+using System.Windows;
+using System.Windows.Interop;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using Artemis.Core.Events;
+using Artemis.Core.Services.Interfaces;
+using Stylet;
+
+namespace Artemis.UI.ViewModels.Screens
+{
+ public class DebugViewModel : Screen
+ {
+ private readonly ICoreService _coreService;
+ private readonly IRgbService _rgbService;
+
+ public DebugViewModel(ICoreService coreService, IRgbService rgbService)
+ {
+ _coreService = coreService;
+ _rgbService = rgbService;
+
+ _coreService.FrameRendered += CoreServiceOnFrameRendered;
+ }
+
+ public ImageSource CurrentFrame { get; set; }
+ public double CurrentFps { get; set; }
+
+ public void ForceGarbageCollection()
+ {
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ }
+
+ private void CoreServiceOnFrameRendered(object sender, FrameEventArgs e)
+ {
+ var imageSource = ImageSourceFromBitmap(e.Bitmap);
+ imageSource.Freeze();
+ CurrentFps = Math.Round(1.0 / e.DeltaTime, 2);
+ Execute.OnUIThread(() => CurrentFrame = imageSource);
+ }
+
+ protected override void OnClose()
+ {
+ _coreService.FrameRendered -= CoreServiceOnFrameRendered;
+ base.OnClose();
+ }
+
+ // This is much quicker than saving the bitmap into a memory stream and converting it
+ private static ImageSource ImageSourceFromBitmap(Bitmap bmp)
+ {
+ var handle = bmp.GetHbitmap();
+ try
+ {
+ return Imaging.CreateBitmapSourceFromHBitmap(handle, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
+ }
+ finally
+ {
+ DeleteObject(handle);
+ }
+ }
+
+ [DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool DeleteObject([In] IntPtr hObject);
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.UI/ViewModels/Screens/SettingsViewModel.cs b/src/Artemis.UI/ViewModels/Screens/SettingsViewModel.cs
index 905102932..432fb0e8e 100644
--- a/src/Artemis.UI/ViewModels/Screens/SettingsViewModel.cs
+++ b/src/Artemis.UI/ViewModels/Screens/SettingsViewModel.cs
@@ -2,14 +2,20 @@
using Artemis.Core.Services.Interfaces;
using Artemis.UI.ViewModels.Controls.Settings;
using Artemis.UI.ViewModels.Interfaces;
+using Ninject;
using Stylet;
namespace Artemis.UI.ViewModels.Screens
{
public class SettingsViewModel : Screen, ISettingsViewModel
{
- public SettingsViewModel(IRgbService rgbService)
+ private readonly IKernel _kernel;
+ private readonly IWindowManager _windowManager;
+
+ public SettingsViewModel(IKernel kernel, IRgbService rgbService, IWindowManager windowManager)
{
+ _kernel = kernel;
+ _windowManager = windowManager;
DeviceSettingsViewModels = new BindableCollection();
foreach (var device in rgbService.LoadedDevices)
DeviceSettingsViewModels.Add(new RgbDeviceSettingsViewModel(device));
@@ -20,6 +26,11 @@ namespace Artemis.UI.ViewModels.Screens
public BindableCollection DeviceSettingsViewModels { get; set; }
public string Title => "Settings";
+ public void ShowDebugger()
+ {
+ _windowManager.ShowWindow(_kernel.Get());
+ }
+
private void UpdateDevices(object sender, DeviceEventArgs deviceEventArgs)
{
DeviceSettingsViewModels.Add(new RgbDeviceSettingsViewModel(deviceEventArgs.Device));
diff --git a/src/Artemis.UI/Views/Screens/DebugView.xaml b/src/Artemis.UI/Views/Screens/DebugView.xaml
new file mode 100644
index 000000000..145a67e84
--- /dev/null
+++ b/src/Artemis.UI/Views/Screens/DebugView.xaml
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ In this window you can view the inner workings of Artemis.
+ Please not that having this window open can have a performance impact on your system.
+
+
+
+
+
+
+
+
+
+ This image shows what is being rendered and dispatched to RGB.NET
+
+
+ FPS:
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Views/Screens/SettingsView.xaml b/src/Artemis.UI/Views/Screens/SettingsView.xaml
index f80da523b..e2bf289bd 100644
--- a/src/Artemis.UI/Views/Screens/SettingsView.xaml
+++ b/src/Artemis.UI/Views/Screens/SettingsView.xaml
@@ -1,6 +1,7 @@
General
-
+
General settings like start up with Windows etc.
-
+
+
+
Devices