diff --git a/src/Artemis.Core/Models/Profile/Folder.cs b/src/Artemis.Core/Models/Profile/Folder.cs
index 6c28c72a1..724244b6f 100644
--- a/src/Artemis.Core/Models/Profile/Folder.cs
+++ b/src/Artemis.Core/Models/Profile/Folder.cs
@@ -4,6 +4,7 @@ using System.Linq;
using Artemis.Core.LayerEffects;
using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Entities.Profile.Abstract;
+using Newtonsoft.Json;
using SkiaSharp;
namespace Artemis.Core
@@ -13,8 +14,6 @@ namespace Artemis.Core
///
public sealed class Folder : RenderProfileElement
{
- private SKBitmap _folderBitmap;
-
///
/// Creates a new instance of the class and adds itself to the child collection of the provided
///
@@ -30,6 +29,7 @@ namespace Artemis.Core
Profile = Parent.Profile;
Name = name;
Enabled = true;
+ Renderer = new Renderer();
_layerEffects = new List();
_expandedPropertyGroups = new List();
@@ -47,6 +47,7 @@ namespace Artemis.Core
Name = folderEntity.Name;
Enabled = folderEntity.Enabled;
Order = folderEntity.Order;
+ Renderer = new Renderer();
_layerEffects = new List();
_expandedPropertyGroups = new List();
@@ -68,6 +69,8 @@ namespace Artemis.Core
internal override RenderElementEntity RenderElementEntity => FolderEntity;
+ internal Renderer Renderer { get; }
+
///
public override List GetAllLayerProperties()
{
@@ -124,6 +127,18 @@ namespace Artemis.Core
CalculateRenderProperties();
}
+ ///
+ /// Creates a deep copy of the layer
+ ///
+ /// The newly created copy
+ public Folder CreateCopy()
+ {
+ FolderEntity entityCopy = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(FolderEntity));
+ entityCopy.Id = Guid.NewGuid();
+
+ return new Folder(Profile, Parent, entityCopy);
+ }
+
public override string ToString()
{
return $"[Folder] {nameof(Name)}: {Name}, {nameof(Order)}: {Order}";
@@ -154,8 +169,8 @@ namespace Artemis.Core
foreach (ProfileElement profileElement in Children)
profileElement.Dispose();
+ Renderer.Dispose();
- _folderBitmap?.Dispose();
base.Dispose(disposing);
}
@@ -199,16 +214,13 @@ namespace Artemis.Core
#region Rendering
- public override void Render(SKCanvas canvas, SKImageInfo canvasInfo)
+ public override void Render(SKCanvas canvas)
{
if (Disposed)
throw new ObjectDisposedException("Folder");
- if (!Enabled || !Children.Any(c => c.Enabled))
- return;
-
// Ensure the folder is ready
- if (Path == null)
+ if (!Enabled || !Children.Any(c => c.Enabled) || Path == null)
return;
// No point rendering if none of the children are going to render
@@ -217,81 +229,56 @@ namespace Artemis.Core
lock (Timeline)
{
- RenderFolder(Timeline, canvas, canvasInfo);
+
+ foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
+ {
+ baseLayerEffect.BaseProperties?.Update(Timeline);
+ baseLayerEffect.Update(Timeline.Delta.TotalSeconds);
+ }
+
+ try
+ {
+ canvas.Save();
+ Renderer.Open(Path, Parent as Folder);
+
+ if (Renderer.Canvas == null || Renderer.Path == null || Renderer.Paint == null)
+ throw new ArtemisCoreException("Failed to open folder render context");
+
+ // Renderer.ApplyClip(canvas);
+
+ foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
+ baseLayerEffect.PreProcess(Renderer.Canvas, Renderer.Path, Renderer.Paint);
+
+ // 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));
+ }
+
+ // No point rendering if the alpha was set to zero by one of the effects
+ if (Renderer.Paint.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.Canvas);
+
+ foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
+ baseLayerEffect.PostProcess(Renderer.Canvas, Renderer.Path, Renderer.Paint);
+
+ canvas.DrawBitmap(Renderer.Bitmap, Renderer.TargetLocation, Renderer.Paint);
+ }
+ finally
+ {
+ canvas.Restore();
+ Renderer.Close();
+ }
+
Timeline.ClearDelta();
}
}
- private void PrepareForRender(Timeline timeline)
- {
- foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
- {
- baseLayerEffect.BaseProperties?.Update(timeline);
- baseLayerEffect.Update(timeline.Delta.TotalSeconds);
- }
- }
-
- private void RenderFolder(Timeline timeline, SKCanvas canvas, SKImageInfo canvasInfo)
- {
- PrepareForRender(timeline);
-
- if (_folderBitmap == null)
- {
- _folderBitmap = new SKBitmap(new SKImageInfo((int) Path.Bounds.Width, (int) Path.Bounds.Height));
- }
- else if (_folderBitmap.Info.Width != (int) Path.Bounds.Width || _folderBitmap.Info.Height != (int) Path.Bounds.Height)
- {
- _folderBitmap.Dispose();
- _folderBitmap = new SKBitmap(new SKImageInfo((int) Path.Bounds.Width, (int) Path.Bounds.Height));
- }
-
- using SKPath folderPath = new SKPath(Path);
- using SKCanvas folderCanvas = new SKCanvas(_folderBitmap);
- using SKPaint folderPaint = new SKPaint();
- folderCanvas.Clear();
-
- folderPath.Transform(SKMatrix.MakeTranslation(folderPath.Bounds.Left * -1, folderPath.Bounds.Top * -1));
-
- SKPoint targetLocation = Path.Bounds.Location;
- if (Parent is Folder parentFolder)
- targetLocation -= parentFolder.Path.Bounds.Location;
-
- canvas.Save();
-
- using SKPath clipPath = new SKPath(folderPath);
- clipPath.Transform(SKMatrix.MakeTranslation(targetLocation.X, targetLocation.Y));
- canvas.ClipPath(clipPath);
-
- foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
- baseLayerEffect.PreProcess(folderCanvas, _folderBitmap.Info, folderPath, folderPaint);
-
- // No point rendering if the alpha was set to zero by one of the effects
- if (folderPaint.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--)
- {
- folderCanvas.Save();
- ProfileElement profileElement = Children[index];
- profileElement.Render(folderCanvas, _folderBitmap.Info);
- folderCanvas.Restore();
- }
-
- // 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);
- folderPaint.Color = folderPaint.Color.WithAlpha((byte) (folderPaint.Color.Alpha * multiplier));
- }
-
- foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
- baseLayerEffect.PostProcess(canvas, canvasInfo, folderPath, folderPaint);
- canvas.DrawBitmap(_folderBitmap, targetLocation, folderPaint);
-
- canvas.Restore();
- }
-
#endregion
#region Events
diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs
index 70da32cd7..a0be455f1 100644
--- a/src/Artemis.Core/Models/Profile/Layer.cs
+++ b/src/Artemis.Core/Models/Profile/Layer.cs
@@ -6,6 +6,7 @@ using Artemis.Core.LayerBrushes;
using Artemis.Core.LayerEffects;
using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Entities.Profile.Abstract;
+using Newtonsoft.Json;
using SkiaSharp;
namespace Artemis.Core
@@ -16,7 +17,6 @@ namespace Artemis.Core
public sealed class Layer : RenderProfileElement
{
private LayerGeneralProperties _general;
- private SKBitmap _layerBitmap;
private BaseLayerBrush _layerBrush;
private LayerShape _layerShape;
private List _leds;
@@ -39,6 +39,7 @@ namespace Artemis.Core
Enabled = true;
General = new LayerGeneralProperties();
Transform = new LayerTransformProperties();
+ Renderer = new Renderer();
_layerEffects = new List();
_leds = new List();
@@ -57,6 +58,7 @@ namespace Artemis.Core
Parent = parent;
General = new LayerGeneralProperties();
Transform = new LayerTransformProperties();
+ Renderer = new Renderer();
_layerEffects = new List();
_leds = new List();
@@ -112,6 +114,27 @@ namespace Artemis.Core
internal override RenderElementEntity RenderElementEntity => LayerEntity;
+ internal Renderer Renderer { get; }
+
+ ///
+ /// Creates a deep copy of the layer
+ ///
+ /// The newly created copy
+ public Layer CreateCopy()
+ {
+ LayerEntity entityCopy = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(LayerEntity));
+ entityCopy.Id = Guid.NewGuid();
+ entityCopy.Name = entityCopy.Name + " - Copy";
+
+ Layer copy = new Layer(Profile, Parent, entityCopy);
+ copy.ChangeLayerBrush(LayerBrush.Descriptor);
+ copy.AddLeds(Leds);
+
+ Parent.AddChild(copy, Order + 1);
+
+ return copy;
+ }
+
///
public override List GetAllLayerProperties()
{
@@ -142,10 +165,9 @@ namespace Artemis.Core
// Brush first in case it depends on any of the other disposables during it's own disposal
_layerBrush?.Dispose();
-
_general?.Dispose();
- _layerBitmap?.Dispose();
_transform?.Dispose();
+ Renderer.Dispose();
base.Dispose(disposing);
}
@@ -272,16 +294,13 @@ namespace Artemis.Core
}
///
- public override void Render(SKCanvas canvas, SKImageInfo canvasInfo)
+ public override void Render(SKCanvas canvas)
{
if (Disposed)
throw new ObjectDisposedException("Layer");
- if (!Enabled)
- return;
-
// Ensure the layer is ready
- if (Path == null || LayerShape?.Path == null || !General.PropertiesInitialized || !Transform.PropertiesInitialized)
+ if (!Enabled || Path == null || LayerShape?.Path == null || !General.PropertiesInitialized || !Transform.PropertiesInitialized)
return;
// Ensure the brush is ready
if (LayerBrush?.BaseProperties?.PropertiesInitialized == false || LayerBrush?.BrushType != LayerBrushType.Regular)
@@ -289,14 +308,14 @@ namespace Artemis.Core
lock (Timeline)
{
- RenderLayer(Timeline, canvas);
+ RenderTimeline(Timeline, canvas);
foreach (Timeline extraTimeline in Timeline.ExtraTimelines)
- RenderLayer(extraTimeline, canvas);
+ RenderTimeline(extraTimeline, canvas);
Timeline.ClearDelta();
}
}
- private void PrepareForRender(Timeline timeline)
+ private void ApplyTimeline(Timeline timeline)
{
General.Update(timeline);
Transform.Update(timeline);
@@ -310,127 +329,79 @@ namespace Artemis.Core
}
}
- private void RenderLayer(Timeline timeline, SKCanvas canvas)
+ private void RenderTimeline(Timeline timeline, SKCanvas canvas)
{
if (timeline.IsFinished)
return;
- PrepareForRender(timeline);
+ ApplyTimeline(timeline);
- if (_layerBitmap == null)
+ try
{
- _layerBitmap = new SKBitmap(new SKImageInfo((int) Path.Bounds.Width, (int) Path.Bounds.Height));
+ canvas.Save();
+ Renderer.Open(Path, Parent as Folder);
+
+ if (Renderer.Canvas == null || Renderer.Path == null || Renderer.Paint == null)
+ throw new ArtemisCoreException("Failed to open layer render context");
+
+ // 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));
+
+ // Clip anything outside the LED selection bounds
+ canvas.ClipPath(Renderer.ClipPath);
+
+ using SKPath renderPath = new SKPath();
+ renderPath.AddRect(Renderer.Path.Bounds);
+
+ if (General.TransformMode.CurrentValue == LayerTransformMode.Normal)
+ {
+ // Apply transformation except rotation to the render path
+ if (LayerBrush.SupportsTransformation)
+ {
+ SKMatrix renderPathMatrix = GetTransformMatrix(true, true, true, false);
+ renderPath.Transform(renderPathMatrix);
+ }
+
+ // Apply rotation to the canvas
+ if (LayerBrush.SupportsTransformation)
+ {
+ SKMatrix rotationMatrix = GetTransformMatrix(true, false, false, true);
+ Renderer.Canvas.SetMatrix(Renderer.Canvas.TotalMatrix.PreConcat(rotationMatrix));
+ }
+
+ // If a brush is a bad boy and tries to color outside the lines, ensure that its clipped off
+ Renderer.Canvas.ClipPath(renderPath);
+ DelegateRendering(renderPath);
+ }
+ 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.Canvas.ClipPath(renderPath);
+ DelegateRendering(Renderer.Path);
+ }
+
+ canvas.DrawBitmap(Renderer.Bitmap, Renderer.TargetLocation, Renderer.Paint);
}
- else if (_layerBitmap.Info.Width != (int) Path.Bounds.Width || _layerBitmap.Info.Height != (int) Path.Bounds.Height)
+ finally
{
- _layerBitmap.Dispose();
- _layerBitmap = new SKBitmap(new SKImageInfo((int) Path.Bounds.Width, (int) Path.Bounds.Height));
+ canvas.Restore();
+ Renderer.Close();
}
+ }
- using SKPath layerPath = new SKPath(Path);
- using SKCanvas layerCanvas = new SKCanvas(_layerBitmap);
- using SKPaint layerPaint = new SKPaint {FilterQuality = SKFilterQuality.Low};
- layerCanvas.Clear();
+ private void DelegateRendering(SKPath path)
+ {
+ foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
+ baseLayerEffect.PreProcess(Renderer.Canvas, path, Renderer.Paint);
- layerPath.Transform(SKMatrix.MakeTranslation(layerPath.Bounds.Left * -1, layerPath.Bounds.Top * -1));
+ LayerBrush.InternalRender(Renderer.Canvas, path, Renderer.Paint);
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
- baseLayerEffect.PreProcess(layerCanvas, _layerBitmap.Info, layerPath, layerPaint);
-
- // No point rendering if the alpha was set to zero by one of the effects
- if (layerPaint.Color.Alpha == 0)
- return;
-
- if (!LayerBrush.SupportsTransformation)
- SimpleRender(layerCanvas, _layerBitmap.Info, layerPaint, layerPath);
- else if (General.ResizeMode.CurrentValue == LayerResizeMode.Normal)
- StretchRender(layerCanvas, _layerBitmap.Info, layerPaint, layerPath);
- else if (General.ResizeMode.CurrentValue == LayerResizeMode.Clip)
- ClipRender(layerCanvas, _layerBitmap.Info, layerPaint, layerPath);
-
- using SKPaint canvasPaint = new SKPaint
- {
- BlendMode = General.BlendMode.CurrentValue,
- Color = new SKColor(0, 0, 0, (byte) (Transform.Opacity.CurrentValue * 2.55f))
- };
- foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled))
- baseLayerEffect.PostProcess(layerCanvas, _layerBitmap.Info, layerPath, canvasPaint);
-
- SKPoint targetLocation = new SKPoint(0, 0);
- if (Parent is Folder parentFolder)
- targetLocation = Path.Bounds.Location - parentFolder.Path.Bounds.Location;
-
- using SKPath canvasPath = new SKPath(Path);
- canvasPath.Transform(SKMatrix.MakeTranslation(
- (canvasPath.Bounds.Left - targetLocation.X) * -1,
- (canvasPath.Bounds.Top - targetLocation.Y) * -1)
- );
- // canvas.ClipPath(canvasPath);
- canvas.DrawBitmap(_layerBitmap, targetLocation, canvasPaint);
- }
-
- private void SimpleRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPaint paint, SKPath layerPath)
- {
- using SKPath renderPath = new SKPath(LayerShape.Path);
- LayerBrush.InternalRender(canvas, canvasInfo, renderPath, paint);
- }
-
- private void StretchRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPaint paint, SKPath layerPath)
- {
- // Apply transformations
- SKSize sizeProperty = Transform.Scale.CurrentValue;
- float rotationProperty = Transform.Rotation.CurrentValue;
-
- SKPoint anchorPosition = GetLayerAnchorPosition(layerPath);
- SKPoint anchorProperty = Transform.AnchorPoint.CurrentValue;
-
- // Translation originates from the unscaled center of the shape and is tied to the anchor
- float x = anchorPosition.X - layerPath.Bounds.MidX - anchorProperty.X * layerPath.Bounds.Width;
- float y = anchorPosition.Y - layerPath.Bounds.MidY - anchorProperty.Y * layerPath.Bounds.Height;
-
- // Apply these before translation because anchorPosition takes translation into account
- canvas.RotateDegrees(rotationProperty, anchorPosition.X, anchorPosition.Y);
- canvas.Scale(sizeProperty.Width / 100f, sizeProperty.Height / 100f, anchorPosition.X, anchorPosition.Y);
- canvas.Translate(x, y);
-
- using SKPath renderPath = new SKPath(LayerShape.Path);
- LayerBrush.InternalRender(canvas, canvasInfo, renderPath, paint);
- }
-
- private void ClipRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPaint paint, SKPath layerPath)
- {
- // Apply transformation
- SKSize sizeProperty = Transform.Scale.CurrentValue;
- float rotationProperty = Transform.Rotation.CurrentValue;
-
- SKPoint anchorPosition = GetLayerAnchorPosition(layerPath);
- SKPoint anchorProperty = Transform.AnchorPoint.CurrentValue;
-
- // Translation originates from the unscaled center of the shape and is tied to the anchor
- float x = anchorPosition.X - layerPath.Bounds.MidX - anchorProperty.X * layerPath.Bounds.Width;
- float y = anchorPosition.Y - layerPath.Bounds.MidY - anchorProperty.Y * layerPath.Bounds.Height;
-
- using SKPath clipPath = new SKPath(LayerShape.Path);
- clipPath.Transform(SKMatrix.MakeTranslation(x, y));
- clipPath.Transform(SKMatrix.MakeScale(sizeProperty.Width / 100f, sizeProperty.Height / 100f, anchorPosition.X, anchorPosition.Y));
- clipPath.Transform(SKMatrix.MakeRotationDegrees(rotationProperty, anchorPosition.X, anchorPosition.Y));
- canvas.ClipPath(clipPath);
-
- canvas.RotateDegrees(rotationProperty, anchorPosition.X, anchorPosition.Y);
- canvas.Translate(x, y);
-
- // Render the layer in the largest required bounds, this still creates stretching in some situations
- // but the only alternative I see right now is always forcing brushes to render on the entire canvas
- SKRect boundsRect = new SKRect(
- Math.Min(clipPath.Bounds.Left - x, Bounds.Left - x),
- Math.Min(clipPath.Bounds.Top - y, Bounds.Top - y),
- Math.Max(clipPath.Bounds.Right - x, Bounds.Right - x),
- Math.Max(clipPath.Bounds.Bottom - y, Bounds.Bottom - y)
- );
- using SKPath renderPath = new SKPath();
- renderPath.AddRect(boundsRect);
-
- LayerBrush.InternalRender(canvas, canvasInfo, renderPath, paint);
+ baseLayerEffect.PostProcess(Renderer.Canvas, path, Renderer.Paint);
}
internal void CalculateRenderProperties()
@@ -462,7 +433,7 @@ namespace Artemis.Core
OnRenderPropertiesUpdated();
}
- internal SKPoint GetLayerAnchorPosition(SKPath layerPath, bool zeroBased = false)
+ internal SKPoint GetLayerAnchorPosition(SKPath layerPath, bool applyTranslation, bool zeroBased)
{
if (Disposed)
throw new ObjectDisposedException("Layer");
@@ -475,45 +446,15 @@ namespace Artemis.Core
: new SKPoint(layerPath.Bounds.MidX, layerPath.Bounds.MidY);
// Apply translation
- position.X += positionProperty.X * layerPath.Bounds.Width;
- position.Y += positionProperty.Y * layerPath.Bounds.Height;
+ if (applyTranslation)
+ {
+ position.X += positionProperty.X * layerPath.Bounds.Width;
+ position.Y += positionProperty.Y * layerPath.Bounds.Height;
+ }
return position;
}
- ///
- /// Excludes the provided path from the translations applied to the layer by applying translations that cancel the
- /// layer translations out
- ///
- ///
- public void IncludePathInTranslation(SKPath path, bool zeroBased)
- {
- if (Disposed)
- throw new ObjectDisposedException("Layer");
-
- SKSize sizeProperty = Transform.Scale.CurrentValue;
- float rotationProperty = Transform.Rotation.CurrentValue;
-
- SKPoint anchorPosition = GetLayerAnchorPosition(Path, zeroBased);
- SKPoint anchorProperty = Transform.AnchorPoint.CurrentValue;
-
- // Translation originates from the unscaled center of the shape and is tied to the anchor
- float x = anchorPosition.X - (zeroBased ? Bounds.MidX - Bounds.Left : Bounds.MidX) - anchorProperty.X * Bounds.Width;
- float y = anchorPosition.Y - (zeroBased ? Bounds.MidY - Bounds.Top : Bounds.MidY) - anchorProperty.Y * Bounds.Height;
-
- if (General.ResizeMode == LayerResizeMode.Normal)
- {
- path.Transform(SKMatrix.MakeTranslation(x, y));
- path.Transform(SKMatrix.MakeScale(sizeProperty.Width / 100f, sizeProperty.Height / 100f, anchorPosition.X, anchorPosition.Y));
- path.Transform(SKMatrix.MakeRotationDegrees(rotationProperty, anchorPosition.X, anchorPosition.Y));
- }
- else
- {
- path.Transform(SKMatrix.MakeTranslation(x, y));
- path.Transform(SKMatrix.MakeRotationDegrees(rotationProperty * -1, anchorPosition.X, anchorPosition.Y));
- }
- }
-
///
/// Creates a transformation matrix that applies the current transformation settings
///
@@ -522,7 +463,7 @@ namespace Artemis.Core
/// surface
///
/// The transformation matrix containing the current transformation settings
- public SKMatrix GetTransformMatrix(bool zeroBased)
+ public SKMatrix GetTransformMatrix(bool zeroBased, bool includeTranslation, bool includeScale, bool includeRotation)
{
if (Disposed)
throw new ObjectDisposedException("Layer");
@@ -530,98 +471,38 @@ namespace Artemis.Core
SKSize sizeProperty = Transform.Scale.CurrentValue;
float rotationProperty = Transform.Rotation.CurrentValue;
- SKPoint anchorPosition = GetLayerAnchorPosition(Path, zeroBased);
+ SKPoint anchorPosition = GetLayerAnchorPosition(Path, true, zeroBased);
SKPoint anchorProperty = Transform.AnchorPoint.CurrentValue;
// Translation originates from the unscaled center of the shape and is tied to the anchor
float x = anchorPosition.X - (zeroBased ? Bounds.MidX - Bounds.Left : Bounds.MidX) - anchorProperty.X * Bounds.Width;
float y = anchorPosition.Y - (zeroBased ? Bounds.MidY - Bounds.Top : Bounds.MidY) - anchorProperty.Y * Bounds.Height;
- if (General.ResizeMode == LayerResizeMode.Normal)
+ SKMatrix transform = SKMatrix.Empty;
+
+ if (includeTranslation)
{
- SKMatrix transform = SKMatrix.MakeTranslation(x, y);
- transform = transform.PostConcat(SKMatrix.MakeRotationDegrees(rotationProperty, anchorPosition.X, anchorPosition.Y));
- transform = transform.PostConcat(SKMatrix.MakeScale(sizeProperty.Width / 100f, sizeProperty.Height / 100f, anchorPosition.X, anchorPosition.Y));
- return transform;
- }
- else
- {
- SKMatrix transform = SKMatrix.MakeTranslation(x, y);
- transform = transform.PostConcat(SKMatrix.MakeRotationDegrees(rotationProperty * -1, anchorPosition.X, anchorPosition.Y));
- return transform;
- }
- }
-
- ///
- /// Excludes the provided path from the translations applied to the layer by applying translations that cancel the
- /// layer translations out
- ///
- public void ExcludePathFromTranslation(SKPath path, bool zeroBased)
- {
- if (Disposed)
- throw new ObjectDisposedException("Layer");
-
- SKSize sizeProperty = Transform.Scale.CurrentValue;
- float rotationProperty = Transform.Rotation.CurrentValue;
-
- SKPoint anchorPosition = GetLayerAnchorPosition(Path, zeroBased);
- SKPoint anchorProperty = Transform.AnchorPoint.CurrentValue;
-
- // Translation originates from the unscaled center of the shape and is tied to the anchor
- float x = anchorPosition.X - (zeroBased ? Bounds.MidX - Bounds.Left : Bounds.MidX) - anchorProperty.X * Bounds.Width;
- float y = anchorPosition.Y - (zeroBased ? Bounds.MidY - Bounds.Top : Bounds.MidY) - anchorProperty.Y * Bounds.Height;
-
- float reversedXScale = 1f / (sizeProperty.Width / 100f);
- float reversedYScale = 1f / (sizeProperty.Height / 100f);
-
- if (General.ResizeMode == LayerResizeMode.Normal)
- {
- path.Transform(SKMatrix.MakeRotationDegrees(rotationProperty * -1, anchorPosition.X, anchorPosition.Y));
- path.Transform(SKMatrix.MakeScale(reversedXScale, reversedYScale, anchorPosition.X, anchorPosition.Y));
- path.Transform(SKMatrix.MakeTranslation(x * -1, y * -1));
- }
- else
- {
- path.Transform(SKMatrix.MakeRotationDegrees(rotationProperty * -1, anchorPosition.X, anchorPosition.Y));
- path.Transform(SKMatrix.MakeTranslation(x * -1, y * -1));
- }
- }
-
- ///
- /// Excludes the provided canvas from the translations applied to the layer by applying translations that cancel the
- /// layer translations out
- ///
- /// The number of transformations applied
- public int ExcludeCanvasFromTranslation(SKCanvas canvas, bool zeroBased)
- {
- if (Disposed)
- throw new ObjectDisposedException("Layer");
-
- SKSize sizeProperty = Transform.Scale.CurrentValue;
- float rotationProperty = Transform.Rotation.CurrentValue;
-
- SKPoint anchorPosition = GetLayerAnchorPosition(Path, zeroBased);
- SKPoint anchorProperty = Transform.AnchorPoint.CurrentValue;
-
- // Translation originates from the unscaled center of the shape and is tied to the anchor
- float x = anchorPosition.X - (zeroBased ? Bounds.MidX - Bounds.Left : Bounds.MidX) - anchorProperty.X * Bounds.Width;
- float y = anchorPosition.Y - (zeroBased ? Bounds.MidY - Bounds.Top : Bounds.MidY) - anchorProperty.Y * Bounds.Height;
-
- float reversedXScale = 1f / (sizeProperty.Width / 100f);
- float reversedYScale = 1f / (sizeProperty.Height / 100f);
-
- if (General.ResizeMode == LayerResizeMode.Normal)
- {
- canvas.Translate(x * -1, y * -1);
- canvas.Scale(reversedXScale, reversedYScale, anchorPosition.X, anchorPosition.Y);
- canvas.RotateDegrees(rotationProperty * -1, anchorPosition.X, anchorPosition.Y);
-
- return 3;
+ // transform is always SKMatrix.Empty here...
+ transform = SKMatrix.MakeTranslation(x, y);
}
- canvas.RotateDegrees(rotationProperty * -1, anchorPosition.X, anchorPosition.Y);
- canvas.Translate(x * -1, y * -1);
- return 2;
+ if (includeScale)
+ {
+ if (transform == SKMatrix.Empty)
+ transform = SKMatrix.MakeScale(sizeProperty.Width / 100f, sizeProperty.Height / 100f, anchorPosition.X, anchorPosition.Y);
+ else
+ transform = transform.PostConcat(SKMatrix.MakeScale(sizeProperty.Width / 100f, sizeProperty.Height / 100f, anchorPosition.X, anchorPosition.Y));
+ }
+
+ if (includeRotation)
+ {
+ if (transform == SKMatrix.Empty)
+ transform = SKMatrix.MakeRotationDegrees(rotationProperty, anchorPosition.X, anchorPosition.Y);
+ else
+ transform = transform.PostConcat(SKMatrix.MakeRotationDegrees(rotationProperty, anchorPosition.X, anchorPosition.Y));
+ }
+
+ return transform;
}
#endregion
@@ -811,7 +692,7 @@ namespace Artemis.Core
Rectangle
}
- public enum LayerResizeMode
+ public enum LayerTransformMode
{
Normal,
Clip
diff --git a/src/Artemis.Core/Models/Profile/LayerGeneralProperties.cs b/src/Artemis.Core/Models/Profile/LayerGeneralProperties.cs
index c5e6d0c2c..4345c4be7 100644
--- a/src/Artemis.Core/Models/Profile/LayerGeneralProperties.cs
+++ b/src/Artemis.Core/Models/Profile/LayerGeneralProperties.cs
@@ -8,19 +8,18 @@ namespace Artemis.Core
[PropertyDescription(Name = "Shape type", Description = "The type of shape to draw in this layer")]
public EnumLayerProperty ShapeType { get; set; }
- [PropertyDescription(Name = "Resize mode", Description = "How to make the shape adjust to scale changes")]
- public EnumLayerProperty ResizeMode { get; set; }
-
[PropertyDescription(Name = "Blend mode", Description = "How to blend this layer into the resulting image")]
public EnumLayerProperty BlendMode { get; set; }
+ [PropertyDescription(Name = "Transform mode", Description = "How the transformation properties are applied to the layer")]
+ public EnumLayerProperty TransformMode { get; set; }
+
[PropertyDescription(Name = "Brush type", Description = "The type of brush to use for this layer")]
public LayerBrushReferenceLayerProperty BrushReference { get; set; }
protected override void PopulateDefaults()
{
ShapeType.DefaultValue = LayerShapeType.Rectangle;
- ResizeMode.DefaultValue = LayerResizeMode.Normal;
BlendMode.DefaultValue = SKBlendMode.SrcOver;
}
diff --git a/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs b/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs
index 48afecab9..f27827c74 100644
--- a/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs
+++ b/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs
@@ -290,6 +290,7 @@ namespace Artemis.Core
internal virtual void OnLayerPropertyOnCurrentValueSet(LayerPropertyEventArgs e)
{
+ Parent?.OnLayerPropertyOnCurrentValueSet(e);
LayerPropertyOnCurrentValueSet?.Invoke(this, e);
}
diff --git a/src/Artemis.Core/Models/Profile/Profile.cs b/src/Artemis.Core/Models/Profile/Profile.cs
index 786ed8369..e92a80027 100644
--- a/src/Artemis.Core/Models/Profile/Profile.cs
+++ b/src/Artemis.Core/Models/Profile/Profile.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using Artemis.Core.Modules;
using Artemis.Storage.Entities.Profile;
+using Newtonsoft.Json;
using SkiaSharp;
namespace Artemis.Core
@@ -66,7 +67,7 @@ namespace Artemis.Core
}
}
- public override void Render(SKCanvas canvas, SKImageInfo canvasInfo)
+ public override void Render(SKCanvas canvas)
{
lock (this)
{
@@ -76,7 +77,7 @@ namespace Artemis.Core
throw new ArtemisCoreException($"Cannot render inactive profile: {this}");
foreach (ProfileElement profileElement in Children)
- profileElement.Render(canvas, canvasInfo);
+ profileElement.Render(canvas);
}
}
diff --git a/src/Artemis.Core/Models/Profile/ProfileElement.cs b/src/Artemis.Core/Models/Profile/ProfileElement.cs
index 56b86b240..85cd09919 100644
--- a/src/Artemis.Core/Models/Profile/ProfileElement.cs
+++ b/src/Artemis.Core/Models/Profile/ProfileElement.cs
@@ -15,8 +15,8 @@ namespace Artemis.Core
private int _order;
private ProfileElement _parent;
private Profile _profile;
- protected bool Disposed;
protected List ChildrenList;
+ protected bool Disposed;
protected ProfileElement()
{
@@ -91,7 +91,7 @@ namespace Artemis.Core
///
/// Renders the element
///
- public abstract void Render(SKCanvas canvas, SKImageInfo canvasInfo);
+ public abstract void Render(SKCanvas canvas);
///
/// Resets the internal state of the element
diff --git a/src/Artemis.Core/Models/Profile/Renderer.cs b/src/Artemis.Core/Models/Profile/Renderer.cs
new file mode 100644
index 000000000..a6494c1c8
--- /dev/null
+++ b/src/Artemis.Core/Models/Profile/Renderer.cs
@@ -0,0 +1,83 @@
+using System;
+using SkiaSharp;
+
+namespace Artemis.Core
+{
+ internal class Renderer : IDisposable
+ {
+ private bool _disposed;
+ public SKBitmap? Bitmap { get; private set; }
+ public SKCanvas? Canvas { get; private set; }
+ public SKPaint? Paint { get; private set; }
+ public SKPath? Path { get; private set; }
+ public SKPath? ClipPath { get; private set; }
+ public SKPoint TargetLocation { get; private set; }
+
+ public bool IsOpen { get; private set; }
+
+ ///
+ /// Opens the render context using the dimensions of the provided path
+ ///
+ public void Open(SKPath path, Folder? parent)
+ {
+ if (_disposed)
+ throw new ObjectDisposedException("Renderer");
+
+ if (IsOpen)
+ throw new ArtemisCoreException("Cannot open render context because it is already open");
+
+ int width = (int) path.Bounds.Width;
+ int height = (int) path.Bounds.Height;
+
+ if (Bitmap == null)
+ Bitmap = new SKBitmap(width, height);
+ else if (Bitmap.Info.Width != width || Bitmap.Info.Height != height)
+ Bitmap = new SKBitmap(width, height);
+
+ Path = new SKPath(path);
+ Canvas = new SKCanvas(Bitmap);
+ Paint = new SKPaint();
+
+ Path.Transform(SKMatrix.MakeTranslation(Path.Bounds.Left * -1, Path.Bounds.Top * -1));
+ Canvas.Clear();
+
+ TargetLocation = new SKPoint(path.Bounds.Location.X, path.Bounds.Location.Y);
+ if (parent != null)
+ TargetLocation -= parent.Path.Bounds.Location;
+
+ ClipPath = new SKPath(Path);
+ ClipPath.Transform(SKMatrix.MakeTranslation(TargetLocation.X, TargetLocation.Y));
+
+ IsOpen = true;
+ }
+
+ public void Close()
+ {
+ if (_disposed)
+ throw new ObjectDisposedException("Renderer");
+
+ Canvas?.Dispose();
+ Paint?.Dispose();
+ Path?.Dispose();
+ ClipPath?.Dispose();
+
+ Canvas = null;
+ Paint = null;
+ Path = null;
+ ClipPath = null;
+
+ IsOpen = false;
+ }
+
+ public void Dispose()
+ {
+ if (IsOpen)
+ Close();
+
+ Bitmap?.Dispose();
+ Bitmap = null;
+
+ _disposed = true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Plugins/LayerBrushes/Internal/BaseLayerBrush.cs b/src/Artemis.Core/Plugins/LayerBrushes/Internal/BaseLayerBrush.cs
index 910432692..847d5cd66 100644
--- a/src/Artemis.Core/Plugins/LayerBrushes/Internal/BaseLayerBrush.cs
+++ b/src/Artemis.Core/Plugins/LayerBrushes/Internal/BaseLayerBrush.cs
@@ -107,7 +107,7 @@ namespace Artemis.Core.LayerBrushes
// but LayerBrush and RgbNetLayerBrush outside the core
internal abstract void Initialize();
- internal abstract void InternalRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPath path, SKPaint paint);
+ internal abstract void InternalRender(SKCanvas canvas, SKPath path, SKPaint paint);
internal virtual void Dispose(bool disposing)
{
diff --git a/src/Artemis.Core/Plugins/LayerBrushes/LayerBrush.cs b/src/Artemis.Core/Plugins/LayerBrushes/LayerBrush.cs
index 0ad17ffb2..8ab2bd9cd 100644
--- a/src/Artemis.Core/Plugins/LayerBrushes/LayerBrush.cs
+++ b/src/Artemis.Core/Plugins/LayerBrushes/LayerBrush.cs
@@ -22,14 +22,13 @@ namespace Artemis.Core.LayerBrushes
/// Called during rendering or layer preview, in the order configured on the layer
///
/// The layer canvas
- ///
/// The path to be filled, represents the shape
/// The paint to be used to fill the shape
- public abstract void Render(SKCanvas canvas, SKImageInfo canvasInfo, SKPath path, SKPaint paint);
+ public abstract void Render(SKCanvas canvas, SKPath path, SKPaint paint);
- internal override void InternalRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPath path, SKPaint paint)
+ internal override void InternalRender(SKCanvas canvas, SKPath path, SKPaint paint)
{
- Render(canvas, canvasInfo, path, paint);
+ Render(canvas, path, paint);
}
internal override void Initialize()
diff --git a/src/Artemis.Core/Plugins/LayerBrushes/PerLedLayerBrush.cs b/src/Artemis.Core/Plugins/LayerBrushes/PerLedLayerBrush.cs
index c76ea80b2..368492cc3 100644
--- a/src/Artemis.Core/Plugins/LayerBrushes/PerLedLayerBrush.cs
+++ b/src/Artemis.Core/Plugins/LayerBrushes/PerLedLayerBrush.cs
@@ -28,18 +28,11 @@ namespace Artemis.Core.LayerBrushes
/// The color the LED will receive
public abstract SKColor GetColor(ArtemisLed led, SKPoint renderPoint);
- internal override void InternalRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPath path, SKPaint paint)
+ internal override void InternalRender(SKCanvas canvas, SKPath path, SKPaint paint)
{
- // We don't want translations on this canvas because that'll displace the LEDs, translations are applied to the points of each LED instead
- Layer.ExcludeCanvasFromTranslation(canvas, true);
-
- if (Layer.General.ResizeMode == LayerResizeMode.Normal)
- {
- // Apply a translated version of the shape as the clipping mask
- SKPath shapePath = new SKPath(Layer.LayerShape.Path);
- Layer.IncludePathInTranslation(shapePath, true);
- canvas.ClipPath(shapePath);
- }
+ // We don't want rotation on this canvas because that'll displace the LEDs, translations are applied to the points of each LED instead
+ if (Layer.General.TransformMode.CurrentValue == LayerTransformMode.Normal && SupportsTransformation)
+ canvas.SetMatrix(canvas.TotalMatrix.PreConcat(Layer.GetTransformMatrix(true, false, false, true).Invert()));
using SKPath pointsPath = new SKPath();
using SKPaint ledPaint = new SKPaint();
@@ -52,7 +45,10 @@ namespace Artemis.Core.LayerBrushes
});
}
- Layer.ExcludePathFromTranslation(pointsPath, true);
+ // Apply the translation to the points of each LED instead
+ if (Layer.General.TransformMode.CurrentValue == LayerTransformMode.Normal && SupportsTransformation)
+ pointsPath.Transform(Layer.GetTransformMatrix(true, true, true, true).Invert());
+
SKPoint[] points = pointsPath.Points;
for (int index = 0; index < Layer.Leds.Count; index++)
{
diff --git a/src/Artemis.Core/Plugins/LayerBrushes/RgbNetLayerBrush.cs b/src/Artemis.Core/Plugins/LayerBrushes/RgbNetLayerBrush.cs
index 38acdb123..0856af7cf 100644
--- a/src/Artemis.Core/Plugins/LayerBrushes/RgbNetLayerBrush.cs
+++ b/src/Artemis.Core/Plugins/LayerBrushes/RgbNetLayerBrush.cs
@@ -68,7 +68,7 @@ namespace Artemis.Core.LayerBrushes
}
// Not used in this effect type
- internal override void InternalRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPath path, SKPaint paint)
+ internal override void InternalRender(SKCanvas canvas, SKPath path, SKPaint paint)
{
throw new NotImplementedException("RGB.NET layer effectes do not implement InternalRender");
}
diff --git a/src/Artemis.Core/Plugins/LayerEffects/Internal/BaseLayerEffect.cs b/src/Artemis.Core/Plugins/LayerEffects/Internal/BaseLayerEffect.cs
index e4ec96c9e..7e5778301 100644
--- a/src/Artemis.Core/Plugins/LayerEffects/Internal/BaseLayerEffect.cs
+++ b/src/Artemis.Core/Plugins/LayerEffects/Internal/BaseLayerEffect.cs
@@ -131,19 +131,17 @@ namespace Artemis.Core.LayerEffects
/// Called before the layer or folder will be rendered
///
/// The canvas used to render the frame
- /// Info on the canvas size and pixel type
/// The bounds this layer/folder will render in
/// The paint this layer/folder will use to render
- public abstract void PreProcess(SKCanvas canvas, SKImageInfo canvasInfo, SKPath renderBounds, SKPaint paint);
+ public abstract void PreProcess(SKCanvas canvas, SKPath renderBounds, SKPaint paint);
///
/// Called after the layer of folder has been rendered
///
/// The canvas used to render the frame
- /// Info on the canvas size and pixel type
/// The bounds this layer/folder rendered in
/// The paint this layer/folder used to render
- public abstract void PostProcess(SKCanvas canvas, SKImageInfo canvasInfo, SKPath renderBounds, SKPaint paint);
+ public abstract void PostProcess(SKCanvas canvas, SKPath renderBounds, SKPaint paint);
// Not only is this needed to initialize properties on the layer effects, it also prevents implementing anything
// but LayerEffect outside the core
diff --git a/src/Artemis.Core/Plugins/LayerEffects/Placeholder/PlaceholderLayerEffect.cs b/src/Artemis.Core/Plugins/LayerEffects/Placeholder/PlaceholderLayerEffect.cs
index 995b244e9..6447679d0 100644
--- a/src/Artemis.Core/Plugins/LayerEffects/Placeholder/PlaceholderLayerEffect.cs
+++ b/src/Artemis.Core/Plugins/LayerEffects/Placeholder/PlaceholderLayerEffect.cs
@@ -40,12 +40,12 @@ namespace Artemis.Core.LayerEffects.Placeholder
}
///
- public override void PreProcess(SKCanvas canvas, SKImageInfo canvasInfo, SKPath renderBounds, SKPaint paint)
+ public override void PreProcess(SKCanvas canvas, SKPath renderBounds, SKPaint paint)
{
}
///
- public override void PostProcess(SKCanvas canvas, SKImageInfo canvasInfo, SKPath renderBounds, SKPaint paint)
+ public override void PostProcess(SKCanvas canvas, SKPath renderBounds, SKPaint paint)
{
}
diff --git a/src/Artemis.Core/Plugins/Modules/ProfileModule.cs b/src/Artemis.Core/Plugins/Modules/ProfileModule.cs
index d72015308..6b5b452e8 100644
--- a/src/Artemis.Core/Plugins/Modules/ProfileModule.cs
+++ b/src/Artemis.Core/Plugins/Modules/ProfileModule.cs
@@ -175,7 +175,7 @@ namespace Artemis.Core.Modules
lock (this)
{
// Render the profile
- ActiveProfile?.Render(canvas, canvasInfo);
+ ActiveProfile?.Render(canvas);
}
ProfileRendered(deltaTime, surface, canvas, canvasInfo);
diff --git a/src/Artemis.Core/Resources/intro-profile.json b/src/Artemis.Core/Resources/intro-profile.json
index 972e89c7c..25fd1d594 100644
--- a/src/Artemis.Core/Resources/intro-profile.json
+++ b/src/Artemis.Core/Resources/intro-profile.json
@@ -1,8 +1,8 @@
{
"$type": "Artemis.Storage.Entities.Profile.ProfileEntity, Artemis.Storage",
- "Id": "824a235d-da46-4c82-a16b-13efe347f492",
+ "Id": "ebf09eb7-77c2-4af5-9695-4db0615a42ad",
"PluginGuid": "0de2991a-d7b8-4f61-ae4e-6623849215b5",
- "Name": "Intro animation - Imported",
+ "Name": "Intro animation",
"IsActive": true,
"Folders": {
"$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.FolderEntity, Artemis.Storage]], System.Private.CoreLib",
@@ -10,12 +10,12 @@
{
"$type": "Artemis.Storage.Entities.Profile.FolderEntity, Artemis.Storage",
"Id": "cc21b67c-3485-4dc6-b2af-105fda42a915",
- "ParentId": "824a235d-da46-4c82-a16b-13efe347f492",
+ "ParentId": "ebf09eb7-77c2-4af5-9695-4db0615a42ad",
"Order": 1,
"Name": "Root folder",
"Enabled": true,
"Profile": null,
- "ProfileId": "824a235d-da46-4c82-a16b-13efe347f492",
+ "ProfileId": "ebf09eb7-77c2-4af5-9695-4db0615a42ad",
"LayerEffects": {
"$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.LayerEffectEntity, Artemis.Storage]], System.Private.CoreLib",
"$values": []
@@ -63,7 +63,7 @@
"$values": []
},
"Profile": null,
- "ProfileId": "824a235d-da46-4c82-a16b-13efe347f492",
+ "ProfileId": "ebf09eb7-77c2-4af5-9695-4db0615a42ad",
"LayerEffects": {
"$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.LayerEffectEntity, Artemis.Storage]], System.Private.CoreLib",
"$values": []
@@ -307,7 +307,7 @@
"$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
"PluginGuid": "61cbbf01-8d69-4ede-a972-f3f269da66d9",
"Path": "LayerBrush.Scale",
- "Value": "{\"IsEmpty\":false,\"Width\":44.9,\"Height\":31.0}",
+ "Value": "{\"IsEmpty\":false,\"Width\":31.5,\"Height\":31.9}",
"KeyframesEnabled": false,
"KeyframeEntities": {
"$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
@@ -362,6 +362,21 @@
"$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.DataBindings.DataBindingEntity, Artemis.Storage]], System.Private.CoreLib",
"$values": []
}
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "General.TransformMode",
+ "Value": "1",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ },
+ "DataBindingEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.DataBindings.DataBindingEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
}
]
},
@@ -401,7 +416,7 @@
"$values": []
},
"Profile": null,
- "ProfileId": "824a235d-da46-4c82-a16b-13efe347f492",
+ "ProfileId": "ebf09eb7-77c2-4af5-9695-4db0615a42ad",
"LayerEffects": {
"$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.LayerEffectEntity, Artemis.Storage]], System.Private.CoreLib",
"$values": []
@@ -608,7 +623,7 @@
"$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
"PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a",
"Path": "LayerBrush.Colors",
- "Value": "{\"Stops\":[{\"Color\":\"#00ff0000\",\"Position\":0.0},{\"Color\":\"#ffff8800\",\"Position\":0.492},{\"Color\":\"#ffedff00\",\"Position\":0.905},{\"Color\":\"#00ff0000\",\"Position\":1.0}]}",
+ "Value": "{\"Stops\":[{\"Color\":\"#00ff0000\",\"Position\":0.0},{\"Color\":\"#ffffdf00\",\"Position\":0.916},{\"Color\":\"#00ffcd00\",\"Position\":1.0},{\"Color\":\"#fff31900\",\"Position\":0.636},{\"Color\":\"#dbf41500\",\"Position\":0.5461956},{\"Color\":\"#00f60f00\",\"Position\":0.462},{\"Color\":\"#93f60d00\",\"Position\":0.3668478}]}",
"KeyframesEnabled": false,
"KeyframeEntities": {
"$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
@@ -678,12 +693,29 @@
"$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.DataBindings.DataBindingEntity, Artemis.Storage]], System.Private.CoreLib",
"$values": []
}
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "General.TransformMode",
+ "Value": "0",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ },
+ "DataBindingEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.DataBindings.DataBindingEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
}
]
},
"ExpandedPropertyGroups": {
"$type": "System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib",
- "$values": []
+ "$values": [
+ "LayerBrush"
+ ]
},
"DisplayCondition": {
"$type": "Artemis.Storage.Entities.Profile.Conditions.DataModelConditionGroupEntity, Artemis.Storage",
@@ -715,7 +747,7 @@
"$values": []
},
"Profile": null,
- "ProfileId": "824a235d-da46-4c82-a16b-13efe347f492",
+ "ProfileId": "ebf09eb7-77c2-4af5-9695-4db0615a42ad",
"LayerEffects": {
"$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.LayerEffectEntity, Artemis.Storage]], System.Private.CoreLib",
"$values": []
@@ -977,13 +1009,29 @@
"$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.DataBindings.DataBindingEntity, Artemis.Storage]], System.Private.CoreLib",
"$values": []
}
+ },
+ {
+ "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage",
+ "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff",
+ "Path": "General.TransformMode",
+ "Value": "0",
+ "KeyframesEnabled": false,
+ "KeyframeEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ },
+ "DataBindingEntities": {
+ "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.DataBindings.DataBindingEntity, Artemis.Storage]], System.Private.CoreLib",
+ "$values": []
+ }
}
]
},
"ExpandedPropertyGroups": {
"$type": "System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib",
"$values": [
- "LayerBrush"
+ "LayerBrush",
+ "General"
]
},
"DisplayCondition": {
@@ -997,9 +1045,9 @@
"Timeline": {
"$type": "Artemis.Storage.Entities.Profile.TimelineEntity, Artemis.Storage",
"StartSegmentLength": "00:00:00",
- "MainSegmentLength": "00:00:05",
+ "MainSegmentLength": "00:00:03",
"EndSegmentLength": "00:00:00",
- "PlayMode": 0,
+ "PlayMode": 1,
"StopMode": 0,
"EventOverlapMode": 0
}
diff --git a/src/Artemis.Core/Services/CoreService.cs b/src/Artemis.Core/Services/CoreService.cs
index 7901cb0a0..7cb158c7e 100644
--- a/src/Artemis.Core/Services/CoreService.cs
+++ b/src/Artemis.Core/Services/CoreService.cs
@@ -33,7 +33,6 @@ namespace Artemis.Core.Services
private readonly IRgbService _rgbService;
private readonly ISurfaceService _surfaceService;
private List _dataModelExpansions;
- private IntroAnimation _introAnimation;
private List _modules;
// ReSharper disable once UnusedParameter.Local - Storage migration service is injected early to ensure it runs before anything else
@@ -111,28 +110,24 @@ namespace Artemis.Core.Services
private void PlayIntroAnimation()
{
- FrameRendering += DrawOverlay;
- _introAnimation = new IntroAnimation(_logger, _profileService, _surfaceService);
+ // The intro is cool and all, but sometimes you just wanna see what you're working on straight away ^^
+ if (Debugger.IsAttached)
+ return;
+
+ IntroAnimation intro = new IntroAnimation(_logger, _profileService, _surfaceService);
// Draw a white overlay over the device
- void DrawOverlay(object sender, FrameRenderingEventArgs args)
- {
- if (_introAnimation == null)
- args.Canvas.Clear(new SKColor(0, 0, 0));
- else
- _introAnimation.Render(args.DeltaTime, args.Canvas, _rgbService.BitmapBrush.Bitmap.Info);
- }
-
- TimeSpan introLength = _introAnimation.AnimationProfile.GetAllLayers().Max(l => l.Timeline.Length);
+ void DrawOverlay(object? sender, FrameRenderingEventArgs args) => intro.Render(args.DeltaTime, args.Canvas);
+ FrameRendering += DrawOverlay;
// Stop rendering after the profile finishes (take 1 second extra in case of slow updates)
+ TimeSpan introLength = intro.AnimationProfile.GetAllLayers().Max(l => l.Timeline.Length);
Task.Run(async () =>
{
await Task.Delay(introLength.Add(TimeSpan.FromSeconds(1)));
FrameRendering -= DrawOverlay;
- _introAnimation.AnimationProfile?.Dispose();
- _introAnimation = null;
+ intro.AnimationProfile.Dispose();
});
}
@@ -167,7 +162,7 @@ namespace Artemis.Core.Services
lock (_dataModelExpansions)
{
// Update all active modules, check Enabled status because it may go false before before the _dataModelExpansions list is updated
- foreach (BaseDataModelExpansion dataModelExpansion in _dataModelExpansions.Where(e => e.Enabled))
+ foreach (BaseDataModelExpansion dataModelExpansion in _dataModelExpansions.Where(e => e.Enabled))
dataModelExpansion.Update(args.DeltaTime);
}
diff --git a/src/Artemis.Core/Utilities/IntroAnimation.cs b/src/Artemis.Core/Utilities/IntroAnimation.cs
index 3e4b90eb8..0529bfb90 100644
--- a/src/Artemis.Core/Utilities/IntroAnimation.cs
+++ b/src/Artemis.Core/Utilities/IntroAnimation.cs
@@ -26,13 +26,13 @@ namespace Artemis.Core
public Profile AnimationProfile { get; set; }
- public void Render(double deltaTime, SKCanvas canvas, SKImageInfo bitmapInfo)
+ public void Render(double deltaTime, SKCanvas canvas)
{
if (AnimationProfile == null)
return;
AnimationProfile.Update(deltaTime);
- AnimationProfile.Render(canvas, bitmapInfo);
+ AnimationProfile.Render(canvas);
}
private void CreateIntroProfile()
diff --git a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs
index 41401ab7e..f687f8eb9 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs
@@ -289,7 +289,6 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties
bool hideRenderRelatedProperties = SelectedLayer?.LayerBrush != null && !SelectedLayer.LayerBrush.SupportsTransformation;
SelectedLayer.General.ShapeType.IsHidden = hideRenderRelatedProperties;
- SelectedLayer.General.ResizeMode.IsHidden = hideRenderRelatedProperties;
SelectedLayer.General.BlendMode.IsHidden = hideRenderRelatedProperties;
SelectedLayer.Transform.IsHidden = hideRenderRelatedProperties;
diff --git a/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/ProfileTreeViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/ProfileTreeViewModel.cs
index 4c1fbc54b..72db66f48 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/ProfileTreeViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/ProfileTreeViewModel.cs
@@ -122,11 +122,11 @@ namespace Artemis.UI.Screens.ProfileEditor.ProfileTree
_updatingTree = false;
// Auto-select the first layer
- if (_profileEditorService.SelectedProfile != null && SelectedTreeItem == null)
- {
- if (_profileEditorService.SelectedProfile.GetRootFolder().Children.FirstOrDefault() is RenderProfileElement firstElement)
- Execute.PostToUIThread(() => _profileEditorService.ChangeSelectedProfileElement(firstElement));
- }
+ // if (_profileEditorService.SelectedProfile != null && SelectedTreeItem == null)
+ // {
+ // if (_profileEditorService.SelectedProfile.GetRootFolder().Children.FirstOrDefault() is RenderProfileElement firstElement)
+ // Execute.PostToUIThread(() => _profileEditorService.ChangeSelectedProfileElement(firstElement));
+ // }
}
private static DragDropType GetDragDropType(IDropInfo dropInfo)
diff --git a/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/TreeItem/LayerView.xaml b/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/TreeItem/LayerView.xaml
index b8036acc8..846634132 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/TreeItem/LayerView.xaml
+++ b/src/Artemis.UI/Screens/ProfileEditor/ProfileTree/TreeItem/LayerView.xaml
@@ -17,6 +17,11 @@
+
+