diff --git a/src/Artemis.Core/Extensions/SKPaintExtensions.cs b/src/Artemis.Core/Extensions/SKPaintExtensions.cs
new file mode 100644
index 000000000..ec2ebe6e2
--- /dev/null
+++ b/src/Artemis.Core/Extensions/SKPaintExtensions.cs
@@ -0,0 +1,16 @@
+using SkiaSharp;
+
+namespace Artemis.Core
+{
+ internal static class SKPaintExtensions
+ {
+ internal static void DisposeSelfAndProperties(this SKPaint paint)
+ {
+ paint.ImageFilter?.Dispose();
+ paint.ColorFilter?.Dispose();
+ paint.MaskFilter?.Dispose();
+ paint.Shader?.Dispose();
+ paint.Dispose();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/Folder.cs b/src/Artemis.Core/Models/Profile/Folder.cs
index 0c8b909eb..1d273c71b 100644
--- a/src/Artemis.Core/Models/Profile/Folder.cs
+++ b/src/Artemis.Core/Models/Profile/Folder.cs
@@ -168,7 +168,7 @@ namespace Artemis.Core
#region Rendering
///
- public override void Render(SKCanvas canvas)
+ public override void Render(SKCanvas canvas, SKPoint basePosition)
{
if (Disposed)
throw new ObjectDisposedException("Folder");
@@ -189,41 +189,38 @@ namespace Artemis.Core
baseLayerEffect.Update(Timeline.Delta.TotalSeconds);
}
+ SKPaint layerPaint = new();
try
{
- canvas.Save();
- Renderer.Open(Path, Parent as Folder);
- if (Renderer.Surface == null || Renderer.Path == null || Renderer.Paint == null)
- throw new ArtemisCoreException("Failed to open folder render context");
-
- SKRect rendererBounds = Renderer.Path.Bounds;
+ SKRect rendererBounds = SKRect.Create(0, 0, Path.Bounds.Width, Path.Bounds.Height);
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
- baseLayerEffect.PreProcess(Renderer.Surface.Canvas, rendererBounds, Renderer.Paint);
+ baseLayerEffect.PreProcess(canvas, rendererBounds, layerPaint);
+
+ canvas.SaveLayer(layerPaint);
+ canvas.Translate(Path.Bounds.Left - basePosition.X, Path.Bounds.Top - basePosition.Y);
// If required, apply the opacity override of the module to the root folder
if (IsRootFolder && Profile.Module.OpacityOverride < 1)
{
double multiplier = Easings.SineEaseInOut(Profile.Module.OpacityOverride);
- Renderer.Paint.Color = Renderer.Paint.Color.WithAlpha((byte) (Renderer.Paint.Color.Alpha * multiplier));
+ layerPaint.Color = layerPaint.Color.WithAlpha((byte) (layerPaint.Color.Alpha * multiplier));
}
// No point rendering if the alpha was set to zero by one of the effects
- if (Renderer.Paint.Color.Alpha == 0)
+ if (layerPaint.Color.Alpha == 0)
return;
// Iterate the children in reverse because the first layer must be rendered last to end up on top
for (int index = Children.Count - 1; index > -1; index--)
- Children[index].Render(Renderer.Surface.Canvas);
+ Children[index].Render(canvas, new SKPoint(Path.Bounds.Left, Path.Bounds.Top));
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
- baseLayerEffect.PostProcess(Renderer.Surface.Canvas, rendererBounds, Renderer.Paint);
-
- canvas.DrawSurface(Renderer.Surface, Renderer.TargetLocation, Renderer.Paint);
+ baseLayerEffect.PostProcess(canvas, rendererBounds, layerPaint);
}
finally
{
canvas.Restore();
- Renderer.Close();
+ layerPaint.DisposeSelfAndProperties();
}
Timeline.ClearDelta();
@@ -239,7 +236,6 @@ namespace Artemis.Core
foreach (ProfileElement profileElement in Children)
profileElement.Dispose();
- Renderer.Dispose();
base.Dispose(disposing);
}
diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs
index 2208c7d0e..3feac187a 100644
--- a/src/Artemis.Core/Models/Profile/Layer.cs
+++ b/src/Artemis.Core/Models/Profile/Layer.cs
@@ -154,7 +154,6 @@ namespace Artemis.Core
_layerBrush?.Dispose();
_general.Dispose();
_transform.Dispose();
- Renderer.Dispose();
base.Dispose(disposing);
}
@@ -183,7 +182,7 @@ namespace Artemis.Core
General.ShapeType.CurrentValueSet += ShapeTypeOnCurrentValueSet;
ApplyShapeType();
ActivateLayerBrush();
-
+
Reset();
}
@@ -278,7 +277,7 @@ namespace Artemis.Core
}
///
- public override void Render(SKCanvas canvas)
+ public override void Render(SKCanvas canvas, SKPoint basePosition)
{
if (Disposed)
throw new ObjectDisposedException("Layer");
@@ -290,9 +289,9 @@ namespace Artemis.Core
if (LayerBrush == null || LayerBrush?.BaseProperties?.PropertiesInitialized == false)
return;
- RenderTimeline(Timeline, canvas);
+ RenderTimeline(Timeline, canvas, basePosition);
foreach (Timeline extraTimeline in Timeline.ExtraTimelines.ToList())
- RenderTimeline(extraTimeline, canvas);
+ RenderTimeline(extraTimeline, canvas, basePosition);
Timeline.ClearDelta();
}
@@ -313,7 +312,7 @@ namespace Artemis.Core
}
}
- private void RenderTimeline(Timeline timeline, SKCanvas canvas)
+ private void RenderTimeline(Timeline timeline, SKCanvas canvas, SKPoint basePosition)
{
if (Path == null || LayerBrush == null)
throw new ArtemisCoreException("The layer is not yet ready for rendering");
@@ -322,26 +321,31 @@ namespace Artemis.Core
return;
ApplyTimeline(timeline);
-
+
if (LayerBrush?.BrushType != LayerBrushType.Regular)
return;
+ SKPaint layerPaint = new();
try
{
canvas.Save();
- Renderer.Open(Path, Parent as Folder);
- if (Renderer.Surface == null || Renderer.Path == null || Renderer.Paint == null)
- throw new ArtemisCoreException("Failed to open layer render context");
+ canvas.Translate(Path.Bounds.Left - basePosition.X, Path.Bounds.Top - basePosition.Y);
+ using SKPath clipPath = new(Path);
+ clipPath.Transform(SKMatrix.CreateTranslation(Path.Bounds.Left * -1, Path.Bounds.Top * -1));
+ canvas.ClipPath(clipPath);
+
+ SKRect layerBounds = SKRect.Create(0, 0, Path.Bounds.Width, Path.Bounds.Height);
// Apply blend mode and color
- Renderer.Paint.BlendMode = General.BlendMode.CurrentValue;
- Renderer.Paint.Color = new SKColor(0, 0, 0, (byte) (Transform.Opacity.CurrentValue * 2.55f));
+ layerPaint.BlendMode = General.BlendMode.CurrentValue;
+ layerPaint.Color = new SKColor(0, 0, 0, (byte) (Transform.Opacity.CurrentValue * 2.55f));
using SKPath renderPath = new();
+
if (General.ShapeType.CurrentValue == LayerShapeType.Rectangle)
- renderPath.AddRect(Renderer.Path.Bounds);
+ renderPath.AddRect(layerBounds);
else
- renderPath.AddOval(Renderer.Path.Bounds);
+ renderPath.AddOval(layerBounds);
if (General.TransformMode.CurrentValue == LayerTransformMode.Normal)
{
@@ -356,54 +360,50 @@ namespace Artemis.Core
if (LayerBrush.SupportsTransformation)
{
SKMatrix rotationMatrix = GetTransformMatrix(true, false, false, true);
- Renderer.Surface.Canvas.SetMatrix(Renderer.Surface.Canvas.TotalMatrix.PreConcat(rotationMatrix));
+ canvas.SetMatrix(canvas.TotalMatrix.PreConcat(rotationMatrix));
}
- // If a brush is a bad boy and tries to color outside the lines, ensure that its clipped off
- Renderer.Surface.Canvas.ClipPath(renderPath);
- DelegateRendering(renderPath.Bounds);
+ DelegateRendering(canvas, renderPath, renderPath.Bounds, layerPaint);
}
else if (General.TransformMode.CurrentValue == LayerTransformMode.Clip)
{
SKMatrix renderPathMatrix = GetTransformMatrix(true, true, true, true);
renderPath.Transform(renderPathMatrix);
- // If a brush is a bad boy and tries to color outside the lines, ensure that its clipped off
- Renderer.Surface.Canvas.ClipPath(renderPath);
- DelegateRendering(Renderer.Path.Bounds);
+ DelegateRendering(canvas, renderPath, layerBounds, layerPaint);
}
-
- canvas.DrawSurface(Renderer.Surface, Renderer.TargetLocation, Renderer.Paint);
}
finally
{
- try
- {
- canvas.Restore();
- }
- catch
- {
- // ignored
- }
-
- Renderer.Close();
+ canvas.Restore();
+ layerPaint.DisposeSelfAndProperties();
}
}
- private void DelegateRendering(SKRect bounds)
+ private void DelegateRendering(SKCanvas canvas, SKPath renderPath, SKRect bounds, SKPaint layerPaint)
{
if (LayerBrush == null)
throw new ArtemisCoreException("The layer is not yet ready for rendering");
- if (Renderer.Surface == null || Renderer.Paint == null)
- throw new ArtemisCoreException("Failed to open layer render context");
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
- baseLayerEffect.PreProcess(Renderer.Surface.Canvas, bounds, Renderer.Paint);
+ baseLayerEffect.PreProcess(canvas, bounds, layerPaint);
- LayerBrush.InternalRender(Renderer.Surface.Canvas, bounds, Renderer.Paint);
+ try
+ {
+ canvas.SaveLayer(layerPaint);
- foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
- baseLayerEffect.PostProcess(Renderer.Surface.Canvas, bounds, Renderer.Paint);
+ // If a brush is a bad boy and tries to color outside the lines, ensure that its clipped off
+ canvas.ClipPath(renderPath);
+ LayerBrush.InternalRender(canvas, bounds, layerPaint);
+
+ foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
+ baseLayerEffect.PostProcess(canvas, bounds, layerPaint);
+ }
+
+ finally
+ {
+ canvas.Restore();
+ }
}
internal void CalculateRenderProperties()
diff --git a/src/Artemis.Core/Models/Profile/Profile.cs b/src/Artemis.Core/Models/Profile/Profile.cs
index 1432d3157..96616f104 100644
--- a/src/Artemis.Core/Models/Profile/Profile.cs
+++ b/src/Artemis.Core/Models/Profile/Profile.cs
@@ -81,7 +81,7 @@ namespace Artemis.Core
}
///
- public override void Render(SKCanvas canvas)
+ public override void Render(SKCanvas canvas, SKPoint basePosition)
{
lock (_lock)
{
@@ -91,7 +91,7 @@ namespace Artemis.Core
throw new ArtemisCoreException($"Cannot render inactive profile: {this}");
foreach (ProfileElement profileElement in Children)
- profileElement.Render(canvas);
+ profileElement.Render(canvas, basePosition);
}
}
diff --git a/src/Artemis.Core/Models/Profile/ProfileElement.cs b/src/Artemis.Core/Models/Profile/ProfileElement.cs
index 086a27886..1072e4a94 100644
--- a/src/Artemis.Core/Models/Profile/ProfileElement.cs
+++ b/src/Artemis.Core/Models/Profile/ProfileElement.cs
@@ -104,7 +104,7 @@ namespace Artemis.Core
///
/// Renders the element
///
- public abstract void Render(SKCanvas canvas);
+ public abstract void Render(SKCanvas canvas, SKPoint basePosition);
///
/// Resets the internal state of the element
diff --git a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs
index 7af947574..899a01ff9 100644
--- a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs
+++ b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs
@@ -19,7 +19,6 @@ namespace Artemis.Core
internal RenderProfileElement(Profile profile) : base(profile)
{
Timeline = new Timeline();
- Renderer = new Renderer();
ExpandedPropertyGroups = new List();
LayerEffectsList = new List();
@@ -127,7 +126,6 @@ namespace Artemis.Core
{
base.Parent = value;
OnPropertyChanged(nameof(Parent));
- Renderer.Invalidate();
}
}
@@ -144,7 +142,6 @@ namespace Artemis.Core
// I can't really be sure about the performance impact of calling Bounds often but
// SkiaSharp calls SkiaApi.sk_path_get_bounds (Handle, &rect); which sounds expensive
Bounds = value?.Bounds ?? SKRect.Empty;
- Renderer.Invalidate();
}
}
@@ -157,7 +154,6 @@ namespace Artemis.Core
private set => SetAndNotify(ref _bounds, value);
}
- internal Renderer Renderer { get; }
#region Property group expansion
diff --git a/src/Artemis.Core/Models/Profile/Renderer.cs b/src/Artemis.Core/Models/Profile/Renderer.cs
index f8cd1025e..4de0fa2d5 100644
--- a/src/Artemis.Core/Models/Profile/Renderer.cs
+++ b/src/Artemis.Core/Models/Profile/Renderer.cs
@@ -108,5 +108,11 @@ namespace Artemis.Core
_disposed = true;
}
+
+ ~Renderer()
+ {
+ if (IsOpen)
+ Close();
+ }
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Plugins/Modules/ProfileModule.cs b/src/Artemis.Core/Plugins/Modules/ProfileModule.cs
index ed9201809..fb1a8ccad 100644
--- a/src/Artemis.Core/Plugins/Modules/ProfileModule.cs
+++ b/src/Artemis.Core/Plugins/Modules/ProfileModule.cs
@@ -177,7 +177,7 @@ namespace Artemis.Core.Modules
lock (_lock)
{
// Render the profile
- ActiveProfile?.Render(canvas);
+ ActiveProfile?.Render(canvas, SKPoint.Empty);
}
ProfileRendered(deltaTime, canvas, canvasInfo);
diff --git a/src/Artemis.Core/Utilities/IntroAnimation.cs b/src/Artemis.Core/Utilities/IntroAnimation.cs
index 4b6d36ffd..06cd3d765 100644
--- a/src/Artemis.Core/Utilities/IntroAnimation.cs
+++ b/src/Artemis.Core/Utilities/IntroAnimation.cs
@@ -30,7 +30,7 @@ namespace Artemis.Core
public void Render(double deltaTime, SKCanvas canvas)
{
AnimationProfile.Update(deltaTime);
- AnimationProfile.Render(canvas);
+ AnimationProfile.Render(canvas, SKPoint.Empty);
}
private Profile CreateIntroProfile()
diff --git a/src/Artemis.UI/Screens/Settings/Debug/Tabs/RenderDebugViewModel.cs b/src/Artemis.UI/Screens/Settings/Debug/Tabs/RenderDebugViewModel.cs
index 6e0f9cdf2..d7532cb2c 100644
--- a/src/Artemis.UI/Screens/Settings/Debug/Tabs/RenderDebugViewModel.cs
+++ b/src/Artemis.UI/Screens/Settings/Debug/Tabs/RenderDebugViewModel.cs
@@ -90,9 +90,24 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
private void CoreServiceOnFrameRendered(object sender, FrameRenderedEventArgs e)
{
+ using SKImage skImage = e.Texture.Surface.Snapshot();
+ SKImageInfo bitmapInfo = e.Texture.ImageInfo;
+
+ if (_frameTargetPath != null)
+ {
+ using (SKData data = skImage.Encode(SKEncodedImageFormat.Png, 100))
+ {
+ using (FileStream stream = File.OpenWrite(_frameTargetPath))
+ {
+ data.SaveTo(stream);
+ }
+ }
+
+ _frameTargetPath = null;
+ }
+
Execute.OnUIThreadSync(() =>
{
- SKImageInfo bitmapInfo = e.Texture.ImageInfo;
RenderHeight = bitmapInfo.Height;
RenderWidth = bitmapInfo.Width;
@@ -103,25 +118,10 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
return;
}
- using SKImage skImage = e.Texture.Surface.Snapshot();
-
- if (_frameTargetPath != null)
- {
- using (SKData data = skImage.Encode(SKEncodedImageFormat.Png, 100))
- {
- using (FileStream stream = File.OpenWrite(_frameTargetPath))
- {
- data.SaveTo(stream);
- }
- }
-
- _frameTargetPath = null;
- }
-
- SKImageInfo info = new(skImage.Width, skImage.Height);
writable.Lock();
- using (SKPixmap pixmap = new(info, writable.BackBuffer, writable.BackBufferStride))
+ using (SKPixmap pixmap = new(bitmapInfo, writable.BackBuffer, writable.BackBufferStride))
{
+ // ReSharper disable once AccessToDisposedClosure - Looks fine
skImage.ReadPixels(pixmap, 0, 0);
}
@@ -140,6 +140,7 @@ namespace Artemis.UI.Screens.Settings.Debug.Tabs
_frames = 0;
_frameCountStart = DateTime.Now;
}
+
_frames++;
}
}
diff --git a/src/Artemis.sln.DotSettings b/src/Artemis.sln.DotSettings
index ca870ba3a..3a843dade 100644
--- a/src/Artemis.sln.DotSettings
+++ b/src/Artemis.sln.DotSettings
@@ -225,6 +225,7 @@
True
True
True
+ True
True