mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-11 03:19:56 +00:00
Core - Fix profile rendering and timeline bugs found by the tests (#909)
* Core - Keep the name of renamed layer effects when loading BaseLayerEffect.Load checked HasBeenRenamed before reading it from the entity, so renamed effects always fell back to the descriptor's display name. * Core - Apply layer effect filters once on layers Layer.DelegateRendering composited the layer with the pre-processed paint and also handed that paint to the brush, so color and image filters of effects applied to the brush output and again when compositing. The brush now gets a copy of the paint without those filters, like folders which apply them once. * Core - Disable folders that get suspended while enabled Layer.Update disables a suspended layer but Folder.Update only disabled a folder once its timeline finished, so a folder suspended outside of the editor kept rendering its children. * Core - Update device LEDs when rotating or scaling a device The Rotation and Scale setters only updated the RGB.NET device, the Artemis LED rectangles and the device path kept their old positions and DeviceUpdated wasn't raised until X or Y was set. * Core - Correct the docs of the Layer and Folder constructors They claimed the new element adds itself to the child collection of the parent, it doesn't and callers rely on adding it themselves. * Core - Apply layer opacity once for brushes that draw with the paint Brushes drawing with a shader or image keep the alpha of the paint they are given, so the layer opacity and opacity effects applied to the brush output and again when compositing, 50% opacity rendered at 25%. The brush now gets an opaque paint, per-LED brushes already overrode the color. The solid color test brush now draws with a shader like the real solid brush so the opacity tests cover this. * Tests - Describe expected faded colors by their opacity Adds an AtOpacity helper so assertions read as red at 50% over black instead of a hand-computed 127, and uses it for layer opacity, opacity effects, folder fades, the profile fade and semi-transparent drawing. * Core - Wrap timelines around by how far they went past the main segment Repeating timelines wrapped around by the frame time rather than by how far they went past the end of the main segment, so wrapping could be off by up to a frame. A timeline that was already past the end, like the finished timeline new layers start with, still starts the main segment over. * Tests - Mention the defaults the timeline wrap-around tests depend on
This commit is contained in:
1 parent
504845c7d2
commit
a25d0f0c9c
18 files changed
+174
-67
No files matched your search
@@ -16,8 +16,11 @@ public sealed class Folder : RenderProfileElement
|
||||
private bool _isExpanded;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="Folder" /> class and adds itself to the child collection of the provided
|
||||
/// <paramref name="parent" />
|
||||
/// Creates a new instance of the <see cref="Folder" /> class with the provided <paramref name="parent" />.
|
||||
/// <para>
|
||||
/// The folder is not added to the child collection of the parent, use <see cref="ProfileElement.AddChild" /> to
|
||||
/// add it.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="parent">The parent of the folder</param>
|
||||
/// <param name="name">The name of the folder</param>
|
||||
@@ -106,7 +109,7 @@ public sealed class Folder : RenderProfileElement
|
||||
|
||||
if (ShouldBeEnabled)
|
||||
Enable();
|
||||
else if (Timeline.IsFinished)
|
||||
else if (Suspended || Timeline.IsFinished)
|
||||
Disable();
|
||||
|
||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => !e.Suspended))
|
||||
|
||||
@@ -28,8 +28,11 @@ public sealed class Layer : RenderProfileElement
|
||||
private List<LedEntity> _missingLeds = [];
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="Layer" /> class and adds itself to the child collection of the provided
|
||||
/// <paramref name="parent" />
|
||||
/// Creates a new instance of the <see cref="Layer" /> class with the provided <paramref name="parent" />.
|
||||
/// <para>
|
||||
/// The layer is not added to the child collection of the parent, use <see cref="ProfileElement.AddChild" /> to
|
||||
/// add it.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="parent">The parent of the layer</param>
|
||||
/// <param name="name">The name of the layer</param>
|
||||
@@ -663,7 +666,14 @@ public sealed class Layer : RenderProfileElement
|
||||
// Restore the blend mode before doing the actual render
|
||||
layerPaint.BlendMode = SKBlendMode.SrcOver;
|
||||
|
||||
LayerBrush.InternalRender(canvas, bounds, layerPaint);
|
||||
// The opacity and the filters of the effects are applied when compositing the layer, the brush gets an opaque
|
||||
// paint without filters so they don't apply to the brush output a second time
|
||||
using SKPaint brushPaint = layerPaint.Clone();
|
||||
brushPaint.Color = brushPaint.Color.WithAlpha(255);
|
||||
brushPaint.ColorFilter = null;
|
||||
brushPaint.ImageFilter = null;
|
||||
|
||||
LayerBrush.InternalRender(canvas, bounds, brushPaint);
|
||||
|
||||
foreach (BaseLayerEffect baseLayerEffect in LayerEffects)
|
||||
{
|
||||
|
||||
@@ -243,6 +243,7 @@ public class Timeline : CorePropertyChanged, IStorageModel
|
||||
if (IsOverridden)
|
||||
throw new ArtemisCoreException("Can't update an overridden timeline, call ClearOverride first.");
|
||||
|
||||
TimeSpan previousPosition = Position;
|
||||
Delta += delta;
|
||||
Position += delta;
|
||||
|
||||
@@ -251,10 +252,15 @@ public class Timeline : CorePropertyChanged, IStorageModel
|
||||
|
||||
// If the main segment has no length, simply stick to the start of the segment
|
||||
if (MainSegmentLength == TimeSpan.Zero)
|
||||
{
|
||||
Position = MainSegmentStartPosition;
|
||||
// Ensure wrapping back around retains the delta time
|
||||
else
|
||||
Position = MainSegmentStartPosition + TimeSpan.FromMilliseconds(delta.TotalMilliseconds % MainSegmentLength.TotalMilliseconds);
|
||||
return;
|
||||
}
|
||||
|
||||
// Wrap around by as much as the timeline went past the end of the main segment. If it was already past the end
|
||||
// (for example a timeline that had finished), start the main segment over instead
|
||||
TimeSpan overshoot = previousPosition <= MainSegmentEndPosition ? Position - MainSegmentEndPosition : delta;
|
||||
Position = MainSegmentStartPosition + TimeSpan.FromTicks(overshoot.Ticks % MainSegmentLength.Ticks);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user