diff --git a/src/PixiEditor.ChangeableDocument/Changeables/Document.cs b/src/PixiEditor.ChangeableDocument/Changeables/Document.cs
index 3ee9853bc..ca3e121c9 100644
--- a/src/PixiEditor.ChangeableDocument/Changeables/Document.cs
+++ b/src/PixiEditor.ChangeableDocument/Changeables/Document.cs
@@ -50,6 +50,7 @@ internal class Document : IChangeable, IReadOnlyDocument
public double VerticalSymmetryAxisX { get; set; }
public bool IsDisposed { get; private set; }
+
public Document()
{
AnimationData = new AnimationData(this);
@@ -279,14 +280,14 @@ internal class Document : IChangeable, IReadOnlyDocument
/// The node with the given or null if it doesn't exist.
public Node? FindNode(Guid guid)
{
- return NodeGraph.Nodes.FirstOrDefault(x => x.Id == guid);
+ return NodeGraph.FindNode(guid);
}
IReadOnlyNode IReadOnlyDocument.FindNode(Guid guid) => FindNodeOrThrow(guid);
public T? FindNode(Guid guid) where T : Node
{
- return NodeGraph.Nodes.FirstOrDefault(x => x.Id == guid && x is T) as T;
+ return NodeGraph.FindNode(guid);
}
///
@@ -298,7 +299,7 @@ internal class Document : IChangeable, IReadOnlyDocument
/// True if the node could be found, otherwise false.
public bool TryFindNode(Guid id, out T node) where T : Node
{
- node = (T?)NodeGraph.Nodes.FirstOrDefault(x => x.Id == id && x is T) ?? default;
+ node = (T?)NodeGraph.FindNode(id) ?? null;
return node != null;
}
diff --git a/src/PixiEditor.ChangeableDocument/Changeables/Graph/NodeGraph.cs b/src/PixiEditor.ChangeableDocument/Changeables/Graph/NodeGraph.cs
index 02683023a..8c631e502 100644
--- a/src/PixiEditor.ChangeableDocument/Changeables/Graph/NodeGraph.cs
+++ b/src/PixiEditor.ChangeableDocument/Changeables/Graph/NodeGraph.cs
@@ -11,12 +11,16 @@ public class NodeGraph : IReadOnlyNodeGraph, IDisposable
private readonly List _nodes = new();
public IReadOnlyCollection Nodes => _nodes;
+ public IReadOnlyDictionary NodeLookup => nodeLookup;
public Node? OutputNode => CustomOutputNode ?? Nodes.OfType().FirstOrDefault();
public Node? CustomOutputNode { get; set; }
+ private Dictionary nodeLookup = new();
+
IReadOnlyCollection IReadOnlyNodeGraph.AllNodes => Nodes;
IReadOnlyNode IReadOnlyNodeGraph.OutputNode => OutputNode;
+
public void AddNode(Node node)
{
if (Nodes.Contains(node))
@@ -26,6 +30,7 @@ public class NodeGraph : IReadOnlyNodeGraph, IDisposable
node.ConnectionsChanged += ResetCache;
_nodes.Add(node);
+ nodeLookup[node.Id] = node;
ResetCache();
}
@@ -38,9 +43,20 @@ public class NodeGraph : IReadOnlyNodeGraph, IDisposable
node.ConnectionsChanged -= ResetCache;
_nodes.Remove(node);
+ nodeLookup.Remove(node.Id);
ResetCache();
}
+ public Node? FindNode(Guid guid)
+ {
+ return nodeLookup.GetValueOrDefault(guid);
+ }
+
+ public T? FindNode(Guid guid) where T : Node
+ {
+ return nodeLookup.TryGetValue(guid, out Node? node) && node is T typedNode ? typedNode : null;
+ }
+
public Queue CalculateExecutionQueue(IReadOnlyNode outputNode)
{
return new Queue(CalculateExecutionQueueInternal(outputNode));
diff --git a/src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/LayerNode.cs b/src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/LayerNode.cs
index 1656716a9..b080cfbb4 100644
--- a/src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/LayerNode.cs
+++ b/src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/LayerNode.cs
@@ -171,12 +171,11 @@ public abstract class LayerNode : StructureNode, IReadOnlyLayerNode, IClipSource
bool useFilters, Paint paint)
{
paint.Color = paint.Color.WithAlpha((byte)Math.Round(Opacity.Value * ctx.Opacity * 255));
- var finalPaint = paint;
var targetSurface = workingSurface;
Texture? tex = null;
int saved = -1;
- if (!ctx.ProcessingColorSpace.IsSrgb)
+ if (!ctx.ProcessingColorSpace.IsSrgb && useFilters && Filters.Value != null)
{
saved = workingSurface.Canvas.Save();
@@ -185,29 +184,22 @@ public abstract class LayerNode : StructureNode, IReadOnlyLayerNode, IClipSource
workingSurface.Canvas.SetMatrix(Matrix3X3.Identity);
targetSurface = tex.DrawingSurface;
-
- finalPaint = new Paint();
}
if (useFilters && Filters.Value != null)
{
paint.SetFilters(Filters.Value);
- DrawWithFilters(ctx, targetSurface, finalPaint);
+ DrawWithFilters(ctx, targetSurface, paint);
}
else
{
paint.SetFilters(null);
- DrawWithoutFilters(ctx, targetSurface, finalPaint);
- }
-
- if (finalPaint != paint)
- {
- finalPaint.Dispose();
+ DrawWithoutFilters(ctx, targetSurface, paint);
}
if (targetSurface != workingSurface)
{
- workingSurface.Canvas.DrawSurface(targetSurface, 0, 0, paint);
+ workingSurface.Canvas.DrawSurface(targetSurface, 0, 0);
tex.Dispose();
workingSurface.Canvas.RestoreToCount(saved);
}
diff --git a/src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/VectorLayerNode.cs b/src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/VectorLayerNode.cs
index 69b07852b..f84d2e261 100644
--- a/src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/VectorLayerNode.cs
+++ b/src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/VectorLayerNode.cs
@@ -50,7 +50,9 @@ public class VectorLayerNode : LayerNode, ITransformableObject, IReadOnlyVectorN
IReadOnlyShapeVectorData IReadOnlyVectorNode.ShapeData => RenderableShapeData;
- public override VecD GetScenePosition(KeyFrameTime time) => RenderableShapeData?.TransformedAABB.Center ?? VecD.Zero;
+ public override VecD GetScenePosition(KeyFrameTime time) =>
+ RenderableShapeData?.TransformedAABB.Center ?? VecD.Zero;
+
public override VecD GetSceneSize(KeyFrameTime time) => RenderableShapeData?.TransformedAABB.Size ?? VecD.Zero;
public VectorLayerNode()
@@ -130,21 +132,7 @@ public class VectorLayerNode : LayerNode, ITransformableObject, IReadOnlyVectorN
return false;
}
- Matrix3X3 matrix = RenderableShapeData.TransformationMatrix;
-
- if (!context.ProcessingColorSpace.IsSrgb)
- {
- int saved = renderOn.Canvas.Save();
- using Texture tex = Texture.ForProcessing(renderOn, ColorSpace.CreateSrgb());
- renderOn.Canvas.SetMatrix(Matrix3X3.Identity);
- Rasterize(tex.DrawingSurface, paint);
- renderOn.Canvas.DrawSurface(tex.DrawingSurface, 0, 0);
- renderOn.Canvas.RestoreToCount(saved);
- }
- else
- {
- Rasterize(renderOn, paint);
- }
+ Rasterize(renderOn, paint);
return true;
}
@@ -222,7 +210,7 @@ public class VectorLayerNode : LayerNode, ITransformableObject, IReadOnlyVectorN
return;
}
- if(EmbeddedShapeData is IScalable resizable)
+ if (EmbeddedShapeData is IScalable resizable)
{
resizable.Resize(multiplier);
}
diff --git a/src/PixiEditor/Models/DocumentModels/ActionAccumulator.cs b/src/PixiEditor/Models/DocumentModels/ActionAccumulator.cs
index d9e0d60af..7fd6952fa 100644
--- a/src/PixiEditor/Models/DocumentModels/ActionAccumulator.cs
+++ b/src/PixiEditor/Models/DocumentModels/ActionAccumulator.cs
@@ -121,6 +121,7 @@ internal class ActionAccumulator
toExecute.Any(static action => action.action is RefreshViewport_PassthroughAction);
bool changeFrameRequest =
toExecute.Any(static action => action.action is SetActiveFrame_PassthroughAction);
+
foreach (IChangeInfo info in optimizedChanges)
{
internals.Updater.ApplyChangeFromChangeInfo(info);
@@ -129,7 +130,6 @@ internal class ActionAccumulator
if (undoBoundaryPassed)
internals.Updater.AfterUndoBoundaryPassed();
- // update the contents of the bitmaps
var affectedAreas = new AffectedAreasGatherer(document.AnimationHandler.ActiveFrameTime,
internals.Tracker,
optimizedChanges);
@@ -145,8 +145,8 @@ internal class ActionAccumulator
}*/
previewUpdater.UpdatePreviews(
- affectedAreas.ImagePreviewAreas.Keys,
- affectedAreas.MaskPreviewAreas.Keys,
+ affectedAreas.ChangedMembers,
+ affectedAreas.ChangedMasks,
affectedAreas.ChangedNodes, affectedAreas.ChangedKeyFrames);
// force refresh viewports for better responsiveness
diff --git a/src/PixiEditor/Models/DocumentModels/DocumentUpdater.cs b/src/PixiEditor/Models/DocumentModels/DocumentUpdater.cs
index cf2a2048c..052956bc3 100644
--- a/src/PixiEditor/Models/DocumentModels/DocumentUpdater.cs
+++ b/src/PixiEditor/Models/DocumentModels/DocumentUpdater.cs
@@ -395,7 +395,9 @@ internal class DocumentUpdater
IStructureMemberHandler memberVM;
if (info is CreateLayer_ChangeInfo layerInfo)
{
- memberVM = doc.NodeGraphHandler.AllNodes.FirstOrDefault(x => x.Id == info.Id) as ILayerHandler;
+ memberVM = doc.NodeGraphHandler.NodeLookup.TryGetValue(layerInfo.Id, out var node)
+ ? node as IStructureMemberHandler
+ : null;
if (memberVM is ITransparencyLockableMember transparencyLockableMember)
{
transparencyLockableMember.SetLockTransparency(layerInfo.LockTransparency);
@@ -403,7 +405,9 @@ internal class DocumentUpdater
}
else if (info is CreateFolder_ChangeInfo)
{
- memberVM = doc.NodeGraphHandler.AllNodes.FirstOrDefault(x => x.Id == info.Id) as IFolderHandler;
+ memberVM = doc.NodeGraphHandler.NodeLookup.TryGetValue(info.Id, out var node)
+ ? node as IFolderHandler
+ : null;
}
else
{
diff --git a/src/PixiEditor/Models/DocumentModels/Public/DocumentStructureModule.cs b/src/PixiEditor/Models/DocumentModels/Public/DocumentStructureModule.cs
index f3eff36a5..8154cadb1 100644
--- a/src/PixiEditor/Models/DocumentModels/Public/DocumentStructureModule.cs
+++ b/src/PixiEditor/Models/DocumentModels/Public/DocumentStructureModule.cs
@@ -1,6 +1,4 @@
-using System.Collections;
-using System.Collections.Generic;
-using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
using PixiEditor.Models.Handlers;
namespace PixiEditor.Models.DocumentModels.Public;
@@ -139,7 +137,7 @@ internal class DocumentStructureModule
/// Returns all layers in the document.
///
/// List of ILayerHandlers. Empty if no layers found.
- public List GetAllLayers(bool includeFoldersWithMask = false)
+ public List GetAllLayers()
{
List layers = new List();
diff --git a/src/PixiEditor/Models/Handlers/INodeGraphHandler.cs b/src/PixiEditor/Models/Handlers/INodeGraphHandler.cs
index 8fde140e2..85cf2425a 100644
--- a/src/PixiEditor/Models/Handlers/INodeGraphHandler.cs
+++ b/src/PixiEditor/Models/Handlers/INodeGraphHandler.cs
@@ -22,4 +22,5 @@ internal interface INodeGraphHandler
public void RemoveConnections(Guid nodeId);
public void UpdateAvailableRenderOutputs();
public void RequestUpdateComputedPropertyValue(INodePropertyHandler property);
+ public IReadOnlyDictionary NodeLookup { get; }
}
diff --git a/src/PixiEditor/Models/Rendering/AffectedAreasGatherer.cs b/src/PixiEditor/Models/Rendering/AffectedAreasGatherer.cs
index e3d9199cd..5368ddf9e 100644
--- a/src/PixiEditor/Models/Rendering/AffectedAreasGatherer.cs
+++ b/src/PixiEditor/Models/Rendering/AffectedAreasGatherer.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System.Diagnostics;
using ChunkyImageLib;
using ChunkyImageLib.DataHolders;
using ChunkyImageLib.Operations;
@@ -28,13 +29,15 @@ internal class AffectedAreasGatherer
private readonly DocumentChangeTracker tracker;
public AffectedArea MainImageArea { get; private set; } = new();
- public Dictionary ImagePreviewAreas { get; private set; } = new();
- public Dictionary MaskPreviewAreas { get; private set; } = new();
- public List ChangedKeyFrames { get; private set; } = new();
-
+ public HashSet ChangedMembers { get; private set; } = new();
+ public HashSet ChangedMasks { get; private set; } = new();
+ public HashSet ChangedKeyFrames { get; private set; } = new();
+
private KeyFrameTime ActiveFrame { get; set; }
- public List ChangedNodes { get; set; } = new();
+ public HashSet ChangedNodes { get; set; } = new();
+
+ private bool alreadyAddedWholeCanvasToEveryImagePreview = false;
public AffectedAreasGatherer(KeyFrameTime activeFrame, DocumentChangeTracker tracker,
IReadOnlyList changes)
@@ -42,11 +45,11 @@ internal class AffectedAreasGatherer
this.tracker = tracker;
ActiveFrame = activeFrame;
ProcessChanges(changes);
-
+
var outputNode = tracker.Document.NodeGraph.OutputNode;
if (outputNode is null)
return;
-
+
if (tracker.Document.NodeGraph.CalculateExecutionQueue(tracker.Document.NodeGraph.OutputNode)
.Any(x => x is ICustomShaderNode))
{
@@ -64,20 +67,20 @@ internal class AffectedAreasGatherer
if (info.Area.Chunks is null)
throw new InvalidOperationException("Chunks must not be null");
AddToMainImage(info.Area);
- AddToImagePreviews(info.Id, info.Area, true);
- AddToMaskPreview(info.Id, info.Area);
+ AddToImagePreviews(info.Id, true);
+ AddToMaskPreview(info.Id);
AddToNodePreviews(info.Id);
break;
case LayerImageArea_ChangeInfo info:
if (info.Area.Chunks is null)
throw new InvalidOperationException("Chunks must not be null");
AddToMainImage(info.Area);
- AddToImagePreviews(info.Id, info.Area);
+ AddToImagePreviews(info.Id);
AddToNodePreviews(info.Id);
break;
case TransformObject_ChangeInfo info:
AddToMainImage(info.Area);
- AddToImagePreviews(info.NodeGuid, info.Area);
+ AddToImagePreviews(info.NodeGuid);
AddToNodePreviews(info.NodeGuid);
break;
case CreateStructureMember_ChangeInfo info:
@@ -104,7 +107,6 @@ internal class AffectedAreasGatherer
break;
case StructureMemberMask_ChangeInfo info:
AddWholeCanvasToMainImage();
- AddWholeCanvasToMaskPreview(info.Id);
AddWholeCanvasToImagePreviews(info.Id, true);
AddToNodePreviews(info.Id);
break;
@@ -172,7 +174,7 @@ internal class AffectedAreasGatherer
{
AddToNodePreviews(info.OutputNodeId.Value);
}
-
+
break;
case PropertyValueUpdated_ChangeInfo info:
AddWholeCanvasToMainImage();
@@ -187,12 +189,13 @@ internal class AffectedAreasGatherer
break;
case VectorShape_ChangeInfo info:
AddToMainImage(info.Affected);
- AddToImagePreviews(info.LayerId, info.Affected);
+ AddToImagePreviews(info.LayerId);
AddToNodePreviews(info.LayerId);
break;
case ProcessingColorSpace_ChangeInfo:
AddWholeCanvasToMainImage();
AddWholeCanvasToEveryImagePreview();
+ AddWholeCanvasToEveryMaskPreview();
break;
}
}
@@ -200,20 +203,20 @@ internal class AffectedAreasGatherer
private void AddKeyFrame(Guid infoKeyFrameId)
{
- ChangedKeyFrames ??= new List();
+ ChangedKeyFrames ??= new HashSet();
if (!ChangedKeyFrames.Contains(infoKeyFrameId))
ChangedKeyFrames.Add(infoKeyFrameId);
}
private void AddToNodePreviews(Guid nodeId)
{
- ChangedNodes ??= new List();
+ ChangedNodes ??= new HashSet();
if (!ChangedNodes.Contains(nodeId))
{
ChangedNodes.Add(nodeId);
}
}
-
+
private void AddAllNodesToImagePreviews()
{
foreach (var node in tracker.Document.NodeGraph.AllNodes)
@@ -234,8 +237,7 @@ internal class AffectedAreasGatherer
return;
}
- var chunks = result.FindAllChunks();
- AddToImagePreviews(memberGuid, new AffectedArea(chunks), ignoreSelf);
+ AddToImagePreviews(member, ignoreSelf);
}
else if (member is IReadOnlyFolderNode folder)
{
@@ -248,9 +250,7 @@ internal class AffectedAreasGatherer
var tightBounds = genericLayerNode.GetTightBounds(frame);
if (tightBounds is not null)
{
- var affectedArea = new AffectedArea(
- OperationHelper.FindChunksTouchingRectangle((RectI)tightBounds.Value, ChunkyImage.FullChunkSize));
- AddToImagePreviews(memberGuid, affectedArea, ignoreSelf);
+ AddToImagePreviews(member, ignoreSelf);
}
else
{
@@ -283,9 +283,9 @@ internal class AffectedAreasGatherer
{
var affectedArea = new AffectedArea(
OperationHelper.FindChunksTouchingRectangle((RectI)tightBounds.Value, ChunkyImage.FullChunkSize));
-
+
var lastArea = new AffectedArea(affectedArea);
-
+
AddToMainImage(affectedArea);
}
else
@@ -312,7 +312,7 @@ internal class AffectedAreasGatherer
if (member.EmbeddedMask is not null)
{
var chunks = member.EmbeddedMask.FindAllChunks();
- AddToMaskPreview(memberGuid, new AffectedArea(chunks));
+ AddToMaskPreview(memberGuid);
}
if (member is IReadOnlyFolderNode folder)
@@ -330,39 +330,34 @@ internal class AffectedAreasGatherer
MainImageArea = temp;
}
- private void AddToImagePreviews(Guid memberGuid, AffectedArea area, bool ignoreSelf = false)
+ private void AddToImagePreviews(Guid memberGuid, bool ignoreSelf = false)
{
- var path = tracker.Document.GetParents(memberGuid);
- path.Insert(0, tracker.Document.FindMember(memberGuid));
+ var sourceMember = tracker.Document.FindMember(memberGuid);
+ if (sourceMember is null)
+ {
+ // If the member is not found, we cannot add it to previews
+ return;
+ }
+
+ AddToImagePreviews(sourceMember, ignoreSelf);
+ }
+
+ private void AddToImagePreviews(IReadOnlyStructureNode sourceMember, bool ignoreSelf)
+ {
+ var path = tracker.Document.GetParents(sourceMember.Id);
+ path.Insert(0, sourceMember);
for (int i = ignoreSelf ? 1 : 0; i < path.Count; i++)
{
var member = path[i];
- if(member == null) continue;
- if (!ImagePreviewAreas.ContainsKey(member.Id))
- {
- ImagePreviewAreas[member.Id] = new AffectedArea(area);
- }
- else
- {
- var temp = ImagePreviewAreas[member.Id];
- temp.UnionWith(area);
- ImagePreviewAreas[member.Id] = temp;
- }
+ if (member == null) continue;
+
+ ChangedMembers.Add(member.Id);
}
}
- private void AddToMaskPreview(Guid memberGuid, AffectedArea area)
+ private void AddToMaskPreview(Guid memberGuid)
{
- if (!MaskPreviewAreas.ContainsKey(memberGuid))
- {
- MaskPreviewAreas[memberGuid] = new AffectedArea(area);
- }
- else
- {
- var temp = MaskPreviewAreas[memberGuid];
- temp.UnionWith(area);
- MaskPreviewAreas[memberGuid] = temp;
- }
+ ChangedMasks.Add(memberGuid);
}
@@ -380,23 +375,19 @@ internal class AffectedAreasGatherer
for (int i = ignoreSelf ? 1 : 0; i < path.Count; i++)
{
var member = path[i];
- if (!ImagePreviewAreas.ContainsKey(member.Id))
- ImagePreviewAreas[member.Id] = new AffectedArea();
- ImagePreviewAreas[member.Id] = AddWholeArea(ImagePreviewAreas[member.Id]);
+ if (member is null) continue;
+
+ ChangedMembers.Add(member.Id);
}
}
- private void AddWholeCanvasToMaskPreview(Guid memberGuid)
- {
- if (!MaskPreviewAreas.ContainsKey(memberGuid))
- MaskPreviewAreas[memberGuid] = new AffectedArea();
- MaskPreviewAreas[memberGuid] = AddWholeArea(MaskPreviewAreas[memberGuid]);
- }
-
-
private void AddWholeCanvasToEveryImagePreview()
{
+ if (alreadyAddedWholeCanvasToEveryImagePreview)
+ return;
+
tracker.Document.ForEveryReadonlyMember((member) => AddWholeCanvasToImagePreviews(member.Id));
+ alreadyAddedWholeCanvasToEveryImagePreview = true;
}
private void AddWholeCanvasToEveryMaskPreview()
@@ -404,7 +395,9 @@ internal class AffectedAreasGatherer
tracker.Document.ForEveryReadonlyMember((member) =>
{
if (member.EmbeddedMask is not null)
- AddWholeCanvasToMaskPreview(member.Id);
+ {
+ ChangedMasks.Add(member.Id);
+ }
});
}
diff --git a/src/PixiEditor/Models/Rendering/MemberPreviewUpdater.cs b/src/PixiEditor/Models/Rendering/MemberPreviewUpdater.cs
index a7b498428..19b3920c0 100644
--- a/src/PixiEditor/Models/Rendering/MemberPreviewUpdater.cs
+++ b/src/PixiEditor/Models/Rendering/MemberPreviewUpdater.cs
@@ -25,8 +25,8 @@ internal class MemberPreviewUpdater
AnimationKeyFramePreviewRenderer = new AnimationKeyFramePreviewRenderer(internals);
}
- public void UpdatePreviews(IEnumerable membersToUpdate,
- IEnumerable masksToUpdate, IEnumerable nodesToUpdate, IEnumerable keyFramesToUpdate)
+ public void UpdatePreviews(HashSet membersToUpdate,
+ HashSet masksToUpdate, HashSet nodesToUpdate, HashSet keyFramesToUpdate)
{
if (!membersToUpdate.Any() && !masksToUpdate.Any() && !nodesToUpdate.Any() &&
!keyFramesToUpdate.Any())
@@ -40,21 +40,16 @@ internal class MemberPreviewUpdater
///
/// Members that should be rendered
/// Masks that should be rendered
- private void UpdatePreviewPainters(IEnumerable members, IEnumerable masksToUpdate,
- IEnumerable nodesToUpdate, IEnumerable keyFramesToUpdate)
+ private void UpdatePreviewPainters(HashSet members, HashSet masksToUpdate,
+ HashSet nodesToUpdate, HashSet keyFramesToUpdate)
{
- Guid[] memberGuids = members as Guid[] ?? members.ToArray();
- Guid[] maskGuids = masksToUpdate as Guid[] ?? masksToUpdate.ToArray();
- Guid[] nodesGuids = nodesToUpdate as Guid[] ?? nodesToUpdate.ToArray();
- Guid[] keyFramesGuids = keyFramesToUpdate as Guid[] ?? keyFramesToUpdate.ToArray();
-
RenderWholeCanvasPreview();
- RenderLayersPreview(memberGuids);
- RenderMaskPreviews(maskGuids);
+ RenderLayersPreview(members);
+ RenderMaskPreviews(masksToUpdate);
- RenderAnimationPreviews(memberGuids, keyFramesGuids);
+ RenderAnimationPreviews(members, keyFramesToUpdate);
- RenderNodePreviews(nodesGuids);
+ RenderNodePreviews(nodesToUpdate);
}
///
@@ -77,7 +72,7 @@ internal class MemberPreviewUpdater
doc.PreviewPainter.Repaint();
}
- private void RenderLayersPreview(Guid[] memberGuids)
+ private void RenderLayersPreview(HashSet memberGuids)
{
foreach (var node in doc.NodeGraphHandler.AllNodes)
{
@@ -111,7 +106,7 @@ internal class MemberPreviewUpdater
}
}
- private void RenderAnimationPreviews(Guid[] memberGuids, Guid[] keyFramesGuids)
+ private void RenderAnimationPreviews(HashSet memberGuids, HashSet keyFramesGuids)
{
foreach (var keyFrame in doc.AnimationHandler.KeyFrames)
{
@@ -191,7 +186,7 @@ internal class MemberPreviewUpdater
}
}
- private void RenderMaskPreviews(Guid[] members)
+ private void RenderMaskPreviews(HashSet members)
{
foreach (var node in doc.NodeGraphHandler.AllNodes)
{
@@ -227,7 +222,7 @@ internal class MemberPreviewUpdater
}
}
- private void RenderNodePreviews(Guid[] nodesGuids)
+ private void RenderNodePreviews(HashSet nodesGuids)
{
var outputNode = internals.Tracker.Document.NodeGraph.OutputNode;
@@ -238,7 +233,7 @@ internal class MemberPreviewUpdater
internals.Tracker.Document.NodeGraph
.AllNodes; //internals.Tracker.Document.NodeGraph.CalculateExecutionQueue(outputNode);
- if (nodesGuids.Length == 0)
+ if (nodesGuids.Count == 0)
return;
List actualRepaintedNodes = new();
diff --git a/src/PixiEditor/ViewModels/Document/AnimationDataViewModel.cs b/src/PixiEditor/ViewModels/Document/AnimationDataViewModel.cs
index 0e371a804..36f55a5a9 100644
--- a/src/PixiEditor/ViewModels/Document/AnimationDataViewModel.cs
+++ b/src/PixiEditor/ViewModels/Document/AnimationDataViewModel.cs
@@ -397,6 +397,9 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
public void SortByLayers()
{
+ if (keyFrames.Count < 2)
+ return;
+
var allLayers = Document.StructureHelper.GetAllLayers();
if (!OrderDifferent(keyFrames, allLayers)) return;
diff --git a/src/PixiEditor/ViewModels/Document/NodeGraphViewModel.cs b/src/PixiEditor/ViewModels/Document/NodeGraphViewModel.cs
index a21393fb3..2913fc2b8 100644
--- a/src/PixiEditor/ViewModels/Document/NodeGraphViewModel.cs
+++ b/src/PixiEditor/ViewModels/Document/NodeGraphViewModel.cs
@@ -26,9 +26,12 @@ internal class NodeGraphViewModel : ViewModelBase, INodeGraphHandler, IDisposabl
public ObservableCollection AvailableRenderOutputs { get; } = new();
public StructureTree StructureTree { get; } = new();
public INodeHandler? OutputNode { get; private set; }
-
public Dictionary CustomRenderOutputs { get; } = new();
+ public Dictionary NodeLookup { get; } = new();
+
+ IReadOnlyDictionary INodeGraphHandler.NodeLookup => NodeLookup;
+
private DocumentInternalParts Internals { get; }
public NodeGraphViewModel(DocumentViewModel documentViewModel, DocumentInternalParts internals)
@@ -47,6 +50,7 @@ internal class NodeGraphViewModel : ViewModelBase, INodeGraphHandler, IDisposabl
AllNodes.Add(node);
StructureTree.Update(this);
+ NodeLookup[node.Id] = node;
UpdateAvailableRenderOutputs();
}
@@ -60,6 +64,7 @@ internal class NodeGraphViewModel : ViewModelBase, INodeGraphHandler, IDisposabl
}
StructureTree.Update(this);
+ NodeLookup.Remove(nodeId);
UpdateAvailableRenderOutputs();
}
diff --git a/src/PixiEditor/ViewModels/SubViewModels/DiscordViewModel.cs b/src/PixiEditor/ViewModels/SubViewModels/DiscordViewModel.cs
index 7cde4e63a..be08227da 100644
--- a/src/PixiEditor/ViewModels/SubViewModels/DiscordViewModel.cs
+++ b/src/PixiEditor/ViewModels/SubViewModels/DiscordViewModel.cs
@@ -10,10 +10,13 @@ namespace PixiEditor.ViewModels.SubViewModels;
internal class DiscordViewModel : SubViewModel, IDisposable
{
+ public const double MinUpdateInterval = 5.0; // seconds
private DiscordRpcClient client;
private string clientId;
private DocumentViewModel currentDocument;
+ private DateTime lastUpdate = DateTime.MinValue;
+
public bool Enabled
{
get => client != null;
@@ -65,6 +68,11 @@ internal class DiscordViewModel : SubViewModel, IDisposable
return;
}
+ if(lastUpdate != DateTime.MinValue && (DateTime.UtcNow - lastUpdate).TotalSeconds < MinUpdateInterval)
+ {
+ return; // Prevent too frequent updates
+ }
+
RichPresence richPresence = NewDefaultRP();
if (document != null)
@@ -96,6 +104,7 @@ internal class DiscordViewModel : SubViewModel, IDisposable
}
client.SetPresence(richPresence);
+ lastUpdate = DateTime.UtcNow;
}
private int CountLayers(NodeGraphViewModel graph)
diff --git a/tests/PixiEditor.Tests/RenderTests.cs b/tests/PixiEditor.Tests/RenderTests.cs
index 75f10cce3..2463f1bb7 100644
--- a/tests/PixiEditor.Tests/RenderTests.cs
+++ b/tests/PixiEditor.Tests/RenderTests.cs
@@ -30,6 +30,11 @@ public class RenderTests : FullPixiEditorTest
[InlineData("VectorRectangleClippedToCircle")]
[InlineData("VectorRectangleClippedToCircleShadowFilter")]
[InlineData("VectorRectangleClippedToCircleMasked")]
+ [InlineData("BlendingLinearSrgb")]
+ [InlineData("BlendingSrgb")]
+ [InlineData("VectorWithSepiaFilter")]
+ [InlineData("VectorWithSepiaFilterSrgb")]
+ [InlineData("VectorWithSepiaFilterChained")]
public void TestThatPixiFilesRenderTheSameResultAsSavedPng(string fileName)
{
if (!DrawingBackendApi.Current.IsHardwareAccelerated)
diff --git a/tests/PixiEditor.Tests/TestFiles/RenderTests/BlendingLinearSrgb.pixi b/tests/PixiEditor.Tests/TestFiles/RenderTests/BlendingLinearSrgb.pixi
new file mode 100644
index 000000000..3b1b22c0b
Binary files /dev/null and b/tests/PixiEditor.Tests/TestFiles/RenderTests/BlendingLinearSrgb.pixi differ
diff --git a/tests/PixiEditor.Tests/TestFiles/RenderTests/BlendingLinearSrgb.png b/tests/PixiEditor.Tests/TestFiles/RenderTests/BlendingLinearSrgb.png
new file mode 100644
index 000000000..1881ca587
Binary files /dev/null and b/tests/PixiEditor.Tests/TestFiles/RenderTests/BlendingLinearSrgb.png differ
diff --git a/tests/PixiEditor.Tests/TestFiles/RenderTests/BlendingSrgb.pixi b/tests/PixiEditor.Tests/TestFiles/RenderTests/BlendingSrgb.pixi
new file mode 100644
index 000000000..d8597d76f
Binary files /dev/null and b/tests/PixiEditor.Tests/TestFiles/RenderTests/BlendingSrgb.pixi differ
diff --git a/tests/PixiEditor.Tests/TestFiles/RenderTests/BlendingSrgb.png b/tests/PixiEditor.Tests/TestFiles/RenderTests/BlendingSrgb.png
new file mode 100644
index 000000000..967ba15c1
Binary files /dev/null and b/tests/PixiEditor.Tests/TestFiles/RenderTests/BlendingSrgb.png differ
diff --git a/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorRectangleClippedToCircle.png b/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorRectangleClippedToCircle.png
index ae43fa6bb..5c7b87be7 100644
Binary files a/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorRectangleClippedToCircle.png and b/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorRectangleClippedToCircle.png differ
diff --git a/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorRectangleClippedToCircleMasked.png b/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorRectangleClippedToCircleMasked.png
index 3046842c3..fb6fadfe2 100644
Binary files a/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorRectangleClippedToCircleMasked.png and b/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorRectangleClippedToCircleMasked.png differ
diff --git a/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorRectangleClippedToCircleShadowFilter.png b/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorRectangleClippedToCircleShadowFilter.png
index 2bd6e95bd..860a35617 100644
Binary files a/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorRectangleClippedToCircleShadowFilter.png and b/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorRectangleClippedToCircleShadowFilter.png differ
diff --git a/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorWithSepiaFilter.pixi b/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorWithSepiaFilter.pixi
new file mode 100644
index 000000000..756c67312
Binary files /dev/null and b/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorWithSepiaFilter.pixi differ
diff --git a/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorWithSepiaFilter.png b/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorWithSepiaFilter.png
new file mode 100644
index 000000000..c5f1056f3
Binary files /dev/null and b/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorWithSepiaFilter.png differ
diff --git a/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorWithSepiaFilterChained.pixi b/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorWithSepiaFilterChained.pixi
new file mode 100644
index 000000000..3c13fec97
Binary files /dev/null and b/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorWithSepiaFilterChained.pixi differ
diff --git a/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorWithSepiaFilterChained.png b/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorWithSepiaFilterChained.png
new file mode 100644
index 000000000..6d1501cff
Binary files /dev/null and b/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorWithSepiaFilterChained.png differ
diff --git a/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorWithSepiaFilterSrgb.pixi b/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorWithSepiaFilterSrgb.pixi
new file mode 100644
index 000000000..b001ba250
Binary files /dev/null and b/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorWithSepiaFilterSrgb.pixi differ
diff --git a/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorWithSepiaFilterSrgb.png b/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorWithSepiaFilterSrgb.png
new file mode 100644
index 000000000..f19efde17
Binary files /dev/null and b/tests/PixiEditor.Tests/TestFiles/RenderTests/VectorWithSepiaFilterSrgb.png differ