diff --git a/src/Artemis.Core/Models/Profile/Abstract/ProfileElement.cs b/src/Artemis.Core/Models/Profile/Abstract/ProfileElement.cs
index ca259edd1..d007b077b 100644
--- a/src/Artemis.Core/Models/Profile/Abstract/ProfileElement.cs
+++ b/src/Artemis.Core/Models/Profile/Abstract/ProfileElement.cs
@@ -45,7 +45,7 @@ namespace Artemis.Core.Models.Profile.Abstract
///
/// Renders the element
///
- public abstract void Render(double deltaTime, ArtemisSurface surface, SKCanvas canvas);
+ public abstract void Render(double deltaTime, SKCanvas canvas);
///
/// Applies the profile element's properties to the underlying storage entity
diff --git a/src/Artemis.Core/Models/Profile/Folder.cs b/src/Artemis.Core/Models/Profile/Folder.cs
index 5d72238ee..c23d6bf1d 100644
--- a/src/Artemis.Core/Models/Profile/Folder.cs
+++ b/src/Artemis.Core/Models/Profile/Folder.cs
@@ -56,11 +56,11 @@ namespace Artemis.Core.Models.Profile
profileElement.Update(deltaTime);
}
- public override void Render(double deltaTime, ArtemisSurface surface, SKCanvas canvas)
+ public override void Render(double deltaTime, SKCanvas canvas)
{
// Folders don't render but their children do
foreach (var profileElement in Children)
- profileElement.Render(deltaTime, surface, canvas);
+ profileElement.Render(deltaTime, canvas);
}
public Folder AddFolder(string name)
diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs
index ff30fffb9..7a1ab2b63 100644
--- a/src/Artemis.Core/Models/Profile/Layer.cs
+++ b/src/Artemis.Core/Models/Profile/Layer.cs
@@ -6,7 +6,6 @@ using Artemis.Core.Extensions;
using Artemis.Core.Models.Profile.Abstract;
using Artemis.Core.Models.Surface;
using Artemis.Core.Plugins.LayerElement;
-using Artemis.Core.Services.Interfaces;
using Artemis.Storage.Entities.Profile;
using Newtonsoft.Json;
using SkiaSharp;
@@ -17,8 +16,6 @@ namespace Artemis.Core.Models.Profile
{
private readonly List _layerElements;
private List _leds;
- private SKBitmap _renderBitmap;
- private SKCanvas _renderCanvas;
public Layer(Profile profile, ProfileElement parent, string name)
{
@@ -58,47 +55,33 @@ namespace Artemis.Core.Models.Profile
public override void Update(double deltaTime)
{
- foreach (var layerElement in LayerElements)
- layerElement.Update(deltaTime);
+ lock (_layerElements)
+ {
+ foreach (var layerElement in LayerElements)
+ layerElement.Update(deltaTime);
+ }
}
- public override void Render(double deltaTime, ArtemisSurface surface, SKCanvas canvas)
+ public override void Render(double deltaTime, SKCanvas canvas)
{
if (RenderPath == null)
return;
- // TODO Just lock the whole thing, this is asking for deadlock
- lock (_renderBitmap)
+ lock (_layerElements)
{
- lock (LayerElements)
+ canvas.Save();
+ using (var framePath = new SKPath(RenderPath))
{
- canvas.Save();
+ canvas.ClipPath(framePath);
+
foreach (var layerElement in LayerElements)
- layerElement.RenderPreProcess(surface, canvas);
-
- _renderCanvas.Clear();
+ layerElement.RenderPreProcess(framePath, canvas);
foreach (var layerElement in LayerElements)
- layerElement.Render(surface, _renderCanvas);
-
- var baseShader = SKShader.CreateBitmap(_renderBitmap, SKShaderTileMode.Repeat, SKShaderTileMode.Repeat, SKMatrix.MakeTranslation(RenderRectangle.Left, RenderRectangle.Top));
+ layerElement.Render(framePath, canvas);
foreach (var layerElement in LayerElements)
- {
- var newBaseShader = layerElement.RenderPostProcess(surface, _renderBitmap, baseShader);
- if (newBaseShader == null)
- continue;
-
- // Dispose the old base shader if the layer element provided a new one
- if (!ReferenceEquals(baseShader, newBaseShader))
- baseShader.Dispose();
-
- baseShader = newBaseShader;
- }
-
- canvas.ClipPath(RenderPath);
- canvas.DrawRect(RenderRectangle, new SKPaint {Shader = baseShader, FilterQuality = SKFilterQuality.Low});
- baseShader.Dispose();
- canvas.Restore();
+ layerElement.RenderPostProcess(framePath, canvas);
}
+ canvas.Restore();
}
}
@@ -165,7 +148,7 @@ namespace Artemis.Core.Models.Profile
internal void AddLayerElement(LayerElement layerElement)
{
- lock (LayerElements)
+ lock (_layerElements)
{
_layerElements.Add(layerElement);
}
@@ -173,7 +156,7 @@ namespace Artemis.Core.Models.Profile
internal void RemoveLayerElement(LayerElement layerElement)
{
- lock (LayerElements)
+ lock (_layerElements)
{
_layerElements.Remove(layerElement);
}
@@ -216,25 +199,6 @@ namespace Artemis.Core.Models.Profile
path.AddRect(artemisLed.AbsoluteRenderRectangle);
RenderPath = path;
-
- if (_renderBitmap != null)
- {
- lock (_renderBitmap)
- {
- var oldBitmap = _renderBitmap;
- var oldCanvas = _renderCanvas;
- _renderBitmap = new SKBitmap(new SKImageInfo((int) RenderRectangle.Width, (int) RenderRectangle.Height));
- _renderCanvas = new SKCanvas(_renderBitmap);
- oldBitmap?.Dispose();
- oldCanvas?.Dispose();
- }
- }
- else
- {
- _renderBitmap = new SKBitmap(new SKImageInfo((int) RenderRectangle.Width, (int) RenderRectangle.Height));
- _renderCanvas = new SKCanvas(_renderBitmap);
- }
-
OnRenderPropertiesUpdated();
}
diff --git a/src/Artemis.Core/Models/Profile/Profile.cs b/src/Artemis.Core/Models/Profile/Profile.cs
index 640e7c4a8..bd0c57909 100644
--- a/src/Artemis.Core/Models/Profile/Profile.cs
+++ b/src/Artemis.Core/Models/Profile/Profile.cs
@@ -56,7 +56,7 @@ namespace Artemis.Core.Models.Profile
}
}
- public override void Render(double deltaTime, ArtemisSurface surface, SKCanvas canvas)
+ public override void Render(double deltaTime, SKCanvas canvas)
{
lock (this)
{
@@ -64,7 +64,7 @@ namespace Artemis.Core.Models.Profile
throw new ArtemisCoreException($"Cannot render inactive profile: {this}");
foreach (var profileElement in Children)
- profileElement.Render(deltaTime, surface, canvas);
+ profileElement.Render(deltaTime, canvas);
}
}
diff --git a/src/Artemis.Core/Plugins/Abstract/ProfileModule.cs b/src/Artemis.Core/Plugins/Abstract/ProfileModule.cs
index 10954ed4f..7fd760828 100644
--- a/src/Artemis.Core/Plugins/Abstract/ProfileModule.cs
+++ b/src/Artemis.Core/Plugins/Abstract/ProfileModule.cs
@@ -31,7 +31,7 @@ namespace Artemis.Core.Plugins.Abstract
lock (this)
{
// Render the profile
- ActiveProfile?.Render(deltaTime, surface, canvas);
+ ActiveProfile?.Render(deltaTime, canvas);
}
}
diff --git a/src/Artemis.Core/Plugins/LayerElement/LayerElement.cs b/src/Artemis.Core/Plugins/LayerElement/LayerElement.cs
index 6eea5d068..bb5701182 100644
--- a/src/Artemis.Core/Plugins/LayerElement/LayerElement.cs
+++ b/src/Artemis.Core/Plugins/LayerElement/LayerElement.cs
@@ -1,6 +1,5 @@
using System;
using Artemis.Core.Models.Profile;
-using Artemis.Core.Models.Surface;
using SkiaSharp;
namespace Artemis.Core.Plugins.LayerElement
@@ -20,6 +19,10 @@ namespace Artemis.Core.Plugins.LayerElement
public LayerElementSettings Settings { get; }
public LayerElementDescriptor Descriptor { get; }
+ public virtual void Dispose()
+ {
+ }
+
///
/// Called by the profile editor to populate the layer element properties panel
///
@@ -38,9 +41,9 @@ namespace Artemis.Core.Plugins.LayerElement
/// Allows you to perform rendering on the surface before any layer-clipping is applied
/// Called before rendering, in the order configured on the layer
///
- /// The surface the layer is rendered on
+ ///
/// The entire surface canvas
- public virtual void RenderPreProcess(ArtemisSurface surface, SKCanvas canvas)
+ public virtual void RenderPreProcess(SKPath framePath, SKCanvas canvas)
{
}
@@ -49,9 +52,9 @@ namespace Artemis.Core.Plugins.LayerElement
/// and matches it's width and height.
/// Called during rendering, in the order configured on the layer
///
- /// The surface the layer is rendered on
+ ///
/// The layer canvas
- public virtual void Render(ArtemisSurface surface, SKCanvas canvas)
+ public virtual void Render(SKPath framePath, SKCanvas canvas)
{
}
@@ -60,16 +63,12 @@ namespace Artemis.Core.Plugins.LayerElement
/// .
/// Called after rendering, in the order configured on the layer.
///
- /// The surface the layer is rendered on
+ ///
+ ///
/// The bitmap created from the layer canvas
/// The current shader used to draw the bitmap on the surface canvas
/// The resulting shader used to draw the bitmap on the surface canvas
- public virtual SKShader RenderPostProcess(ArtemisSurface surface, SKBitmap bitmap, SKShader shader)
- {
- return shader;
- }
-
- public virtual void Dispose()
+ public virtual void RenderPostProcess(SKPath framePath, SKCanvas canvas)
{
}
}
diff --git a/src/Artemis.Plugins.LayerElements.Animations/RotationLayerElement.cs b/src/Artemis.Plugins.LayerElements.Animations/RotationLayerElement.cs
index 89dac09c2..08f4f9953 100644
--- a/src/Artemis.Plugins.LayerElements.Animations/RotationLayerElement.cs
+++ b/src/Artemis.Plugins.LayerElements.Animations/RotationLayerElement.cs
@@ -1,6 +1,5 @@
using System;
using Artemis.Core.Models.Profile;
-using Artemis.Core.Models.Surface;
using Artemis.Core.Plugins.LayerElement;
using SkiaSharp;
@@ -26,15 +25,10 @@ namespace Artemis.Plugins.LayerElements.Animations
Rotation = 0;
}
- public override SKShader RenderPostProcess(ArtemisSurface surface, SKBitmap bitmap, SKShader shader)
+ public override void RenderPreProcess(SKPath framePath, SKCanvas canvas)
{
- var rect = Layer.AbsoluteRenderRectangle;
- var center = new SKPoint(rect.MidX, rect.MidY);
-
- var required = (float) Math.Sqrt(rect.Width * rect.Width + rect.Height * rect.Height);
- var minSide = Math.Min(rect.Width, rect.Height);
- var scale = required / minSide;
- return SKShader.CreateLocalMatrix(SKShader.CreateLocalMatrix(shader, SKMatrix.MakeScale(scale, scale, center.X, center.Y)), SKMatrix.MakeRotationDegrees(Rotation, center.X, center.Y));
+ canvas.RotateDegrees(Rotation, Layer.RenderRectangle.MidX, Layer.RenderRectangle.MidY);
+ framePath.Transform(SKMatrix.MakeRotationDegrees(Rotation * -1, Layer.RenderRectangle.MidX, Layer.RenderRectangle.MidY));
}
}
}
\ No newline at end of file
diff --git a/src/Artemis.Plugins.LayerElements.Animations/SlideLayerElement.cs b/src/Artemis.Plugins.LayerElements.Animations/SlideLayerElement.cs
index 8f471d319..9eb9bd5dd 100644
--- a/src/Artemis.Plugins.LayerElements.Animations/SlideLayerElement.cs
+++ b/src/Artemis.Plugins.LayerElements.Animations/SlideLayerElement.cs
@@ -1,6 +1,5 @@
using System;
using Artemis.Core.Models.Profile;
-using Artemis.Core.Models.Surface;
using Artemis.Core.Plugins.LayerElement;
using SkiaSharp;
@@ -26,9 +25,10 @@ namespace Artemis.Plugins.LayerElements.Animations
MovePercentage = 0;
}
- public override SKShader RenderPostProcess(ArtemisSurface surface, SKBitmap bitmap, SKShader shader)
+ public override void RenderPreProcess(SKPath framePath, SKCanvas canvas)
{
- return SKShader.CreateLocalMatrix(shader, SKMatrix.MakeTranslation(Layer.RenderRectangle.Width / 100 * MovePercentage * -1, 0));
+ canvas.Translate(Layer.RenderRectangle.Width / 100 * MovePercentage * -1, 0);
+ framePath.Transform(SKMatrix.MakeTranslation(Layer.RenderRectangle.Width / 100 * MovePercentage, 0));
}
}
}
\ No newline at end of file
diff --git a/src/Artemis.Plugins.LayerElements.Noise/NoiseLayerElement.cs b/src/Artemis.Plugins.LayerElements.Noise/NoiseLayerElement.cs
index 3610e6619..e6afc64f6 100644
--- a/src/Artemis.Plugins.LayerElements.Noise/NoiseLayerElement.cs
+++ b/src/Artemis.Plugins.LayerElements.Noise/NoiseLayerElement.cs
@@ -1,6 +1,5 @@
using System;
using Artemis.Core.Models.Profile;
-using Artemis.Core.Models.Surface;
using Artemis.Core.Plugins.LayerElement;
using SkiaSharp;
@@ -11,12 +10,13 @@ namespace Artemis.Plugins.LayerElements.Noise
private static Random _rand = new Random();
private readonly OpenSimplexNoise _noise;
private float _z;
+ private const int Scale = 6;
public NoiseLayerElement(Layer layer, Guid guid, NoiseLayerElementSettings settings, LayerElementDescriptor descriptor) : base(layer, guid, settings, descriptor)
{
Settings = settings;
- _z = 1;
+ _z = _rand.Next(0, 4096);
_noise = new OpenSimplexNoise(Guid.GetHashCode());
}
@@ -39,25 +39,32 @@ namespace Artemis.Plugins.LayerElements.Noise
return new NoiseLayerElementViewModel(this);
}
- public override void Render(ArtemisSurface surface, SKCanvas canvas)
+ public override void Render(SKPath framePath, SKCanvas canvas)
{
// Scale down the render path
- var width = Layer.AbsoluteRenderRectangle.Width / 4;
- var height = Layer.AbsoluteRenderRectangle.Height / 4;
+ var width = (int) Math.Round(Math.Max(Layer.RenderRectangle.Width, Layer.RenderRectangle.Height) / Scale);
+ var height = (int) Math.Round(Math.Max(Layer.RenderRectangle.Width, Layer.RenderRectangle.Height) / Scale);
- using (var bitmap = new SKBitmap(new SKImageInfo((int) Layer.AbsoluteRenderRectangle.Width, (int) Layer.AbsoluteRenderRectangle.Height)))
+ using (var bitmap = new SKBitmap(new SKImageInfo(width, height)))
{
for (var x = 0; x < width; x++)
{
for (var y = 0; y < height; y++)
{
- // Not setting pixels outside the layer clip would be faster but right now rotation takes place after the rendering, must change that first
+ // This check is actually more expensive then _noise.Evaluate() ^.^'
+ // if (!framePath.Contains(x * Scale, y * Scale))
+ // continue;
+
var v = _noise.Evaluate(Settings.XScale * x / width, Settings.YScale * y / height, _z);
bitmap.SetPixel(x, y, new SKColor(0, 0, 0, (byte) ((v + 1) * 127)));
}
}
-
- canvas.DrawBitmap(bitmap, SKRect.Create(0, 0, width, height), Layer.AbsoluteRenderRectangle, new SKPaint {BlendMode = Settings.BlendMode});
+
+ using (var sh = SKShader.CreateBitmap(bitmap, SKShaderTileMode.Mirror, SKShaderTileMode.Mirror, SKMatrix.MakeScale(Scale, Scale)))
+ using (var paint = new SKPaint {Shader = sh})
+ {
+ canvas.DrawPath(framePath, paint);
+ }
}
}
}
diff --git a/src/Artemis.Plugins.LayerTypes.Brush/BrushLayerElement.cs b/src/Artemis.Plugins.LayerTypes.Brush/BrushLayerElement.cs
index 0e3f570f0..b0571d11d 100644
--- a/src/Artemis.Plugins.LayerTypes.Brush/BrushLayerElement.cs
+++ b/src/Artemis.Plugins.LayerTypes.Brush/BrushLayerElement.cs
@@ -34,7 +34,7 @@ namespace Artemis.Plugins.LayerElements.Brush
private void CreateShader()
{
- var center = new SKPoint(Layer.AbsoluteRenderRectangle.MidX, Layer.AbsoluteRenderRectangle.MidY);
+ var center = new SKPoint(Layer.RenderRectangle.MidX, Layer.RenderRectangle.MidY);
SKShader shader;
switch (Settings.BrushType)
{
@@ -42,10 +42,10 @@ namespace Artemis.Plugins.LayerElements.Brush
shader = SKShader.CreateColor(_testColors.First());
break;
case BrushType.LinearGradient:
- shader = SKShader.CreateLinearGradient(new SKPoint(0, 0), new SKPoint(Layer.AbsoluteRenderRectangle.Width, 0), _testColors.ToArray(), SKShaderTileMode.Clamp);
+ shader = SKShader.CreateLinearGradient(new SKPoint(0, 0), new SKPoint(Layer.RenderRectangle.Width, 0), _testColors.ToArray(), SKShaderTileMode.Repeat);
break;
case BrushType.RadialGradient:
- shader = SKShader.CreateRadialGradient(center, Math.Min(Layer.AbsoluteRenderRectangle.Width, Layer.AbsoluteRenderRectangle.Height), _testColors.ToArray(), SKShaderTileMode.Clamp);
+ shader = SKShader.CreateRadialGradient(center, Math.Min(Layer.RenderRectangle.Width, Layer.RenderRectangle.Height), _testColors.ToArray(), SKShaderTileMode.Repeat);
break;
case BrushType.SweepGradient:
shader = SKShader.CreateSweepGradient(center, _testColors.ToArray(), null, SKShaderTileMode.Clamp, 0, 360);
@@ -69,9 +69,9 @@ namespace Artemis.Plugins.LayerElements.Brush
return new BrushLayerElementViewModel(this);
}
- public override void Render(ArtemisSurface surface, SKCanvas canvas)
+ public override void Render(SKPath framePath, SKCanvas canvas)
{
- canvas.DrawRect(Layer.AbsoluteRenderRectangle, _paint);
+ canvas.DrawPath(framePath, _paint);
}
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileViewModel.cs
index b05ac4275..00a656046 100644
--- a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileViewModel.cs
+++ b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileViewModel.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
@@ -81,8 +82,15 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
private void ApplySurfaceConfiguration(ArtemisSurface surface)
{
+ List devices;
+ lock (Devices)
+ {
+ devices = new List();
+ devices.AddRange(surface.Devices);
+ }
+
// Make sure all devices have an up-to-date VM
- foreach (var surfaceDeviceConfiguration in surface.Devices)
+ foreach (var surfaceDeviceConfiguration in devices)
{
// Create VMs for missing devices
var viewModel = Devices.FirstOrDefault(vm => vm.Device.RgbDevice == surfaceDeviceConfiguration.RgbDevice);
@@ -104,7 +112,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
else
viewModel.Device = surfaceDeviceConfiguration;
}
-
+
// Sort the devices by ZIndex
Execute.PostToUIThread(() =>
{