Browse Source

Merge pull request #643 from PixiEditor/universal-scene-renderer

Universal scene renderer
Krzysztof Krysiński 9 months ago
parent
commit
e18adb4478
100 changed files with 1180 additions and 761 deletions
  1. 3 0
      .gitmodules
  2. 5 5
      src/ChunkyImageLib/Chunk.cs
  3. 71 15
      src/ChunkyImageLib/ChunkyImage.cs
  4. 4 4
      src/ChunkyImageLib/ChunkyImageEx.cs
  5. 2 2
      src/ChunkyImageLib/ChunkyImageLib.csproj
  6. 1 1
      src/ChunkyImageLib/ColorEx.cs
  7. 4 4
      src/ChunkyImageLib/CommittedChunkStorage.cs
  8. 2 2
      src/ChunkyImageLib/DataHolders/AffectedArea.cs
  9. 1 1
      src/ChunkyImageLib/DataHolders/ColorBounds.cs
  10. 2 2
      src/ChunkyImageLib/DataHolders/ShapeCorners.cs
  11. 4 4
      src/ChunkyImageLib/DataHolders/ShapeData.cs
  12. 5 5
      src/ChunkyImageLib/IReadOnlyChunkyImage.cs
  13. 4 4
      src/ChunkyImageLib/Operations/ApplyMaskOperation.cs
  14. 10 10
      src/ChunkyImageLib/Operations/BresenhamLineHelper.cs
  15. 6 6
      src/ChunkyImageLib/Operations/BresenhamLineOperation.cs
  16. 4 4
      src/ChunkyImageLib/Operations/ChunkyImageOperation.cs
  17. 3 3
      src/ChunkyImageLib/Operations/ClearPathOperation.cs
  18. 2 2
      src/ChunkyImageLib/Operations/ClearRegionOperation.cs
  19. 5 5
      src/ChunkyImageLib/Operations/DrawingSurfaceLineOperation.cs
  20. 3 3
      src/ChunkyImageLib/Operations/EllipseHelper.cs
  21. 11 11
      src/ChunkyImageLib/Operations/EllipseOperation.cs
  22. 2 2
      src/ChunkyImageLib/Operations/IDrawOperation.cs
  23. 4 6
      src/ChunkyImageLib/Operations/ImageOperation.cs
  24. 5 5
      src/ChunkyImageLib/Operations/OperationHelper.cs
  25. 2 2
      src/ChunkyImageLib/Operations/PaintOperation.cs
  26. 6 6
      src/ChunkyImageLib/Operations/PathOperation.cs
  27. 5 5
      src/ChunkyImageLib/Operations/PixelOperation.cs
  28. 7 7
      src/ChunkyImageLib/Operations/PixelsOperation.cs
  29. 3 3
      src/ChunkyImageLib/Operations/RectangleOperation.cs
  30. 4 4
      src/ChunkyImageLib/Operations/ReplaceColorOperation.cs
  31. 2 2
      src/ChunkyImageLib/Operations/ResizeOperation.cs
  32. 127 0
      src/ChunkyImageLib/Operations/TextureOperation.cs
  33. 2 2
      src/ChunkyImageLibVis/MainWindow.xaml.cs
  34. 1 1
      src/Directory.Build.props
  35. 1 0
      src/Drawie
  36. 1 1
      src/PixiDocks
  37. 1 1
      src/PixiEditor.AnimationRenderer.Core/IAnimationRenderer.cs
  38. 1 1
      src/PixiEditor.AnimationRenderer.Core/PixiEditor.AnimationRenderer.Core.csproj
  39. 2 2
      src/PixiEditor.AnimationRenderer.FFmpeg/FFMpegRenderer.cs
  40. 2 2
      src/PixiEditor.AnimationRenderer.FFmpeg/ImgFrame.cs
  41. 2 1
      src/PixiEditor.AnimationRenderer.FFmpeg/PixiEditor.AnimationRenderer.FFmpeg.csproj
  42. 2 2
      src/PixiEditor.ChangeableDocument.Gen/Helpers.cs
  43. 11 8
      src/PixiEditor.ChangeableDocument.Gen/UpdateableChangeActionGenerator.cs
  44. 5 0
      src/PixiEditor.ChangeableDocument/Actions/ICancelableAction.cs
  45. 1 1
      src/PixiEditor.ChangeableDocument/ChangeInfos/Drawing/LayerImageArea_ChangeInfo.cs
  46. 1 1
      src/PixiEditor.ChangeableDocument/ChangeInfos/Drawing/MaskArea_ChangeInfo.cs
  47. 1 1
      src/PixiEditor.ChangeableDocument/ChangeInfos/Drawing/Selection_ChangeInfo.cs
  48. 1 1
      src/PixiEditor.ChangeableDocument/ChangeInfos/NodeGraph/CreateNode_ChangeInfo.cs
  49. 1 1
      src/PixiEditor.ChangeableDocument/ChangeInfos/NodeGraph/NodePosition_ChangeInfo.cs
  50. 1 1
      src/PixiEditor.ChangeableDocument/ChangeInfos/Objects/TransformObject_ChangeInfo.cs
  51. 2 2
      src/PixiEditor.ChangeableDocument/ChangeInfos/Root/ReferenceLayerChangeInfos/SetReferenceLayer_ChangeInfo.cs
  52. 2 2
      src/PixiEditor.ChangeableDocument/ChangeInfos/Root/Size_ChangeInfo.cs
  53. 1 1
      src/PixiEditor.ChangeableDocument/ChangeInfos/Structure/CreateStructureMember_ChangeInfo.cs
  54. 1 1
      src/PixiEditor.ChangeableDocument/ChangeInfos/Vectors/VectorShape_ChangeInfo.cs
  55. 2 0
      src/PixiEditor.ChangeableDocument/Changeables/Animations/KeyFrame.cs
  56. 2 0
      src/PixiEditor.ChangeableDocument/Changeables/Animations/RasterKeyFrame.cs
  57. 5 5
      src/PixiEditor.ChangeableDocument/Changeables/Document.cs
  58. 12 11
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Context/FuncContext.cs
  59. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Filter.cs
  60. 3 3
      src/PixiEditor.ChangeableDocument/Changeables/Graph/FuncInputProperty.cs
  61. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/FuncOutputProperty.cs
  62. 62 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/GraphUtils.cs
  63. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/InputProperty.cs
  64. 9 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IChunkRenderable.cs
  65. 8 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IClipSource.cs
  66. 6 3
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IPreviewRenderable.cs
  67. 2 2
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyFolderNode.cs
  68. 3 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyImageNode.cs
  69. 3 4
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyNode.cs
  70. 2 2
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyNodeGraph.cs
  71. 3 3
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyShapeVectorData.cs
  72. 8 5
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyStructureNode.cs
  73. 4 3
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IRenderInput.cs
  74. 14 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/ISceneObject.cs
  75. 2 2
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/ITransformableObject.cs
  76. 21 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/SceneObjectRenderContext.cs
  77. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/Shapes/IReadOnlyEllipseData.cs
  78. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/Shapes/IReadOnlyLineData.cs
  79. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/Shapes/IReadOnlyRectangleData.cs
  80. 4 57
      src/PixiEditor.ChangeableDocument/Changeables/Graph/NodeGraph.cs
  81. 2 5
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Animable/TimeNode.cs
  82. 73 46
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineChannelsNode.cs
  83. 4 5
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineColorNode.cs
  84. 5 6
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineVecDNode.cs
  85. 4 5
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineVecINode.cs
  86. 103 48
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateChannelsNode.cs
  87. 4 5
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateColorNode.cs
  88. 4 5
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateVecDNode.cs
  89. 4 5
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateVecINode.cs
  90. 34 18
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CreateImageNode.cs
  91. 14 9
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/DebugBlendModeNode.cs
  92. 38 25
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/ApplyFilterNode.cs
  93. 2 2
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/ColorMatrixFilterNode.cs
  94. 4 6
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/FilterNode.cs
  95. 7 7
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/GrayscaleNode.cs
  96. 4 4
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/KernelFilterNode.cs
  97. 4 2
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Filters.cs
  98. 140 69
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FolderNode.cs
  99. 109 144
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageLayerNode.cs
  100. 78 70
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/LayerNode.cs

+ 3 - 0
.gitmodules

@@ -4,3 +4,6 @@
 [submodule "src/PixiParser"]
 	path = src/PixiParser
 	url = https://github.com/PixiEditor/PixiParser.git
+[submodule "src/Drawie"]
+	path = src/Drawie
+	url = https://github.com/PixiEditor/Drawie.git

+ 5 - 5
src/ChunkyImageLib/Chunk.cs

@@ -1,9 +1,9 @@
 using ChunkyImageLib.DataHolders;
-using PixiEditor.DrawingApi.Core;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.DrawingApi.Core.Surfaces;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Numerics;
 
 namespace ChunkyImageLib;
 

+ 71 - 15
src/ChunkyImageLib/ChunkyImage.cs

@@ -5,14 +5,14 @@ using ChunkyImageLib.Operations;
 using OneOf;
 using OneOf.Types;
 using PixiEditor.Common;
-using PixiEditor.DrawingApi.Core;
-using PixiEditor.DrawingApi.Core.ColorsImpl;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.DrawingApi.Core.Surfaces;
-using PixiEditor.DrawingApi.Core.Surfaces.ImageData;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
-using PixiEditor.DrawingApi.Core.Surfaces.Vector;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.ColorsImpl;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.ImageData;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Backend.Core.Surfaces.Vector;
+using Drawie.Numerics;
 
 [assembly: InternalsVisibleTo("ChunkyImageLibTest")]
 
@@ -61,6 +61,9 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
     private readonly object lockObject = new();
     private int commitCounter = 0;
 
+    private RectI cachedPreciseBounds = RectI.Empty;
+    private int lastBoundsCacheHash = -1;
+
     public const int FullChunkSize = ChunkPool.FullChunkSize;
     private static Paint ClippingPaint { get; } = new Paint() { BlendMode = BlendMode.DstIn };
     private static Paint InverseClippingPaint { get; } = new Paint() { BlendMode = BlendMode.DstOut };
@@ -93,7 +96,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
     private VectorPath? clippingPath;
     private double? horizontalSymmetryAxis = null;
     private double? verticalSymmetryAxis = null;
-    
+
     private int operationCounter = 0;
 
     private readonly Dictionary<ChunkResolution, Dictionary<VecI, Chunk>> committedChunks;
@@ -189,11 +192,17 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
         {
             ThrowIfDisposed();
 
+            if (lastBoundsCacheHash == GetCacheHash())
+            {
+                return cachedPreciseBounds;
+            }
+
             var chunkSize = suggestedResolution.PixelSize();
             var multiplier = suggestedResolution.Multiplier();
             RectI scaledCommittedSize = (RectI)(new RectD(VecI.Zero, CommittedSize * multiplier)).RoundOutwards();
 
             RectI? preciseBounds = null;
+
             foreach (var (chunkPos, fullResChunk) in committedChunks[ChunkResolution.Full])
             {
                 if (committedChunks[suggestedResolution].TryGetValue(chunkPos, out Chunk? requestedResChunk))
@@ -228,6 +237,9 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
             preciseBounds = (RectI?)preciseBounds?.Scale(suggestedResolution.InvertedMultiplier()).RoundOutwards();
             preciseBounds = preciseBounds?.Intersect(new RectI(preciseBounds.Value.Pos, CommittedSize));
 
+            cachedPreciseBounds = preciseBounds.GetValueOrDefault();
+            lastBoundsCacheHash = GetCacheHash();
+
             return preciseBounds;
         }
     }
@@ -346,7 +358,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
             }
 
             var committedChunk = GetCommittedChunk(chunkPos, resolution);
-            
+
             // draw committed directly
             if (latestChunk.IsT0 || latestChunk.IsT1 && committedChunk is not null && blendMode != BlendMode.Src)
             {
@@ -458,7 +470,6 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
 
     /// <summary>
     /// Tries it's best to return a committed chunk, either if it exists or if it can be created from it's high res version. Returns null if it can't.
-    /// </summary>
     private Chunk? GetCommittedChunk(VecI pos, ChunkResolution resolution)
     {
         var maybeSameRes = MaybeGetCommittedChunk(pos, resolution);
@@ -636,6 +647,51 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
         }
     }
 
+    /// <summary>
+    /// Be careful about the copyImage argument. The default is true, and this is a thread safe version without any side effects. 
+    /// It will however copy the surface right away which can be slow (in updateable changes especially). 
+    /// If copyImage is set to false, the image won't be copied and instead a reference will be stored.
+    /// Texture is NOT THREAD SAFE, so if you pass a Texture here with copyImage == false you must not do anything with that texture anywhere (not even read) until CommitChanges/CancelChanges is called.
+    /// </summary>
+    /// <exception cref="ObjectDisposedException">This image is disposed</exception>
+    public void EnqueueDrawTexture(Matrix3X3 transformMatrix, Texture image, Paint? paint = null, bool copyImage = true)
+    {
+        lock (lockObject)
+        {
+            ThrowIfDisposed();
+            TextureOperation operation = new(transformMatrix, image, paint, copyImage);
+            EnqueueOperation(operation);
+        }
+    }
+
+    /// <summary>
+    /// Be careful about the copyImage argument, see other overload for details
+    /// </summary>
+    /// <exception cref="ObjectDisposedException">This image is disposed</exception>
+    public void EnqueueDrawTexture(ShapeCorners corners, Texture image, Paint? paint = null, bool copyImage = true)
+    {
+        lock (lockObject)
+        {
+            ThrowIfDisposed();
+            TextureOperation operation = new(corners, image, paint, copyImage);
+            EnqueueOperation(operation);
+        }
+    }
+
+    /// <summary>
+    /// Be careful about the copyImage argument, see other overload for details
+    /// </summary>
+    /// <exception cref="ObjectDisposedException">This image is disposed</exception>
+    public void EnqueueDrawTexture(VecI pos, Texture image, Paint? paint = null, bool copyImage = true)
+    {
+        lock (lockObject)
+        {
+            ThrowIfDisposed();
+            TextureOperation operation = new(pos, image, paint, copyImage);
+            EnqueueOperation(operation);
+        }
+    }
+
     public void EnqueueApplyMask(ChunkyImage mask)
     {
         lock (lockObject)
@@ -1424,9 +1480,9 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
     public int GetCacheHash()
     {
         return commitCounter + queuedOperations.Count + operationCounter + activeClips.Count
-            + (int)blendMode + (lockTransparency ? 1 : 0) 
-            + (horizontalSymmetryAxis is not null ? (int)(horizontalSymmetryAxis * 100) : 0) 
-            + (verticalSymmetryAxis is not null ? (int)(verticalSymmetryAxis * 100) : 0) 
-            + (clippingPath is not null ? 1 : 0);
+               + (int)blendMode + (lockTransparency ? 1 : 0)
+               + (horizontalSymmetryAxis is not null ? (int)(horizontalSymmetryAxis * 100) : 0)
+               + (verticalSymmetryAxis is not null ? (int)(verticalSymmetryAxis * 100) : 0)
+               + (clippingPath is not null ? 1 : 0);
     }
 }

+ 4 - 4
src/ChunkyImageLib/ChunkyImageEx.cs

@@ -1,9 +1,9 @@
 using ChunkyImageLib.DataHolders;
 using ChunkyImageLib.Operations;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.DrawingApi.Core.Surfaces;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Numerics;
 
 namespace ChunkyImageLib;
 public static class IReadOnlyChunkyImageEx

+ 2 - 2
src/ChunkyImageLib/ChunkyImageLib.csproj

@@ -4,7 +4,6 @@
     <TargetFramework>net8.0</TargetFramework>
     <ImplicitUsings>enable</ImplicitUsings>
     <Nullable>enable</Nullable>
-    <WarningsAsErrors>Nullable</WarningsAsErrors>
     <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
   </PropertyGroup>
   
@@ -18,8 +17,9 @@
   </ItemGroup>
 
   <ItemGroup>
+    <ProjectReference Include="..\Drawie\src\Drawie.Backend.Core\Drawie.Backend.Core.csproj" />
+    <ProjectReference Include="..\Drawie\src\Drawie.Numerics\Drawie.Numerics.csproj" />
     <ProjectReference Include="..\PixiEditor.Common\PixiEditor.Common.csproj" />
-    <ProjectReference Include="..\PixiEditor.DrawingApi.Core\PixiEditor.DrawingApi.Core.csproj" />
   </ItemGroup>
 
 </Project>

+ 1 - 1
src/ChunkyImageLib/ColorEx.cs

@@ -1,4 +1,4 @@
-using PixiEditor.DrawingApi.Core.ColorsImpl;
+using Drawie.Backend.Core.ColorsImpl;
 
 namespace ChunkyImageLib;
 public static class ColorEx

+ 4 - 4
src/ChunkyImageLib/CommittedChunkStorage.cs

@@ -1,8 +1,8 @@
 using ChunkyImageLib.DataHolders;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.DrawingApi.Core.Surfaces;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Numerics;
 
 namespace ChunkyImageLib;
 

+ 2 - 2
src/ChunkyImageLib/DataHolders/AffectedArea.cs

@@ -3,8 +3,8 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Numerics;
 
 namespace ChunkyImageLib.DataHolders;
 

+ 1 - 1
src/ChunkyImageLib/DataHolders/ColorBounds.cs

@@ -1,5 +1,5 @@
 using System.Runtime.CompilerServices;
-using PixiEditor.DrawingApi.Core.ColorsImpl;
+using Drawie.Backend.Core.ColorsImpl;
 
 namespace ChunkyImageLib.DataHolders;
 

+ 2 - 2
src/ChunkyImageLib/DataHolders/ShapeCorners.cs

@@ -1,6 +1,6 @@
 using System.Diagnostics;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Numerics;
 
 namespace ChunkyImageLib.DataHolders;
 

+ 4 - 4
src/ChunkyImageLib/DataHolders/ShapeData.cs

@@ -1,7 +1,7 @@
-using PixiEditor.DrawingApi.Core.ColorsImpl;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.DrawingApi.Core.Surfaces;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.ColorsImpl;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Numerics;
 
 namespace ChunkyImageLib.DataHolders;
 

+ 5 - 5
src/ChunkyImageLib/IReadOnlyChunkyImage.cs

@@ -1,9 +1,9 @@
 using ChunkyImageLib.DataHolders;
-using PixiEditor.DrawingApi.Core.ColorsImpl;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.DrawingApi.Core.Surfaces;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.ColorsImpl;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Numerics;
 
 namespace ChunkyImageLib;
 

+ 4 - 4
src/ChunkyImageLib/Operations/ApplyMaskOperation.cs

@@ -1,8 +1,8 @@
 using ChunkyImageLib.DataHolders;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.DrawingApi.Core.Surfaces;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Numerics;
 
 namespace ChunkyImageLib.Operations;
 

+ 10 - 10
src/ChunkyImageLib/Operations/BresenhamLineHelper.cs

@@ -1,20 +1,20 @@
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Numerics;
 
 namespace ChunkyImageLib.Operations;
 public static class BresenhamLineHelper
 {
-    public static Point[] GetBresenhamLine(VecI start, VecI end)
+    public static VecF[] GetBresenhamLine(VecI start, VecI end)
     {
         int count = Math.Abs((start - end).LongestAxis) + 1;
         if (count > 100000)
-            return Array.Empty<Point>();
-        Point[] output = new Point[count];
+            return Array.Empty<VecF>();
+        VecF[] output = new VecF[count];
         CalculateBresenhamLine(start, end, output);
         return output;
     }
 
-    private static void CalculateBresenhamLine(VecI start, VecI end, Point[] output)
+    private static void CalculateBresenhamLine(VecI start, VecI end, VecF[] output)
     {
         int index = 0;
 
@@ -25,7 +25,7 @@ public static class BresenhamLineHelper
 
         if (x1 == x2 && y1 == y2)
         {
-            output[index] = new Point(start);
+            output[index] = new VecF(start);
             return;
         }
 
@@ -54,7 +54,7 @@ public static class BresenhamLineHelper
             dy = y1 - y2;
         }
 
-        output[index] = new Point(x, y);
+        output[index] = new VecF(x, y);
         index++;
 
         if (dx > dy)
@@ -77,7 +77,7 @@ public static class BresenhamLineHelper
                     x += xi;
                 }
 
-                output[index] = new Point(x, y);
+                output[index] = new VecF(x, y);
                 index++;
             }
         }
@@ -101,7 +101,7 @@ public static class BresenhamLineHelper
                     y += yi;
                 }
 
-                output[index] = new Point(x, y);
+                output[index] = new VecF(x, y);
                 index++;
             }
         }

+ 6 - 6
src/ChunkyImageLib/Operations/BresenhamLineOperation.cs

@@ -1,9 +1,9 @@
 using ChunkyImageLib.DataHolders;
-using PixiEditor.DrawingApi.Core.ColorsImpl;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.DrawingApi.Core.Surfaces;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.ColorsImpl;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Numerics;
 
 namespace ChunkyImageLib.Operations;
 internal class BresenhamLineOperation : IMirroredDrawOperation
@@ -13,7 +13,7 @@ internal class BresenhamLineOperation : IMirroredDrawOperation
     private readonly VecI to;
     private readonly Color color;
     private readonly BlendMode blendMode;
-    private readonly Point[] points;
+    private readonly VecF[] points;
     private Paint paint;
 
     public BresenhamLineOperation(VecI from, VecI to, Color color, BlendMode blendMode)

+ 4 - 4
src/ChunkyImageLib/Operations/ChunkyImageOperation.cs

@@ -1,8 +1,8 @@
 using ChunkyImageLib.DataHolders;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.DrawingApi.Core.Surfaces;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Numerics;
 
 namespace ChunkyImageLib.Operations;
 internal class ChunkyImageOperation : IMirroredDrawOperation

+ 3 - 3
src/ChunkyImageLib/Operations/ClearPathOperation.cs

@@ -1,7 +1,7 @@
 using ChunkyImageLib.DataHolders;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.DrawingApi.Core.Surfaces.Vector;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces.Vector;
+using Drawie.Numerics;
 
 namespace ChunkyImageLib.Operations;
 internal class ClearPathOperation : IMirroredDrawOperation

+ 2 - 2
src/ChunkyImageLib/Operations/ClearRegionOperation.cs

@@ -1,6 +1,6 @@
 using ChunkyImageLib.DataHolders;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Numerics;
 
 namespace ChunkyImageLib.Operations;
 

+ 5 - 5
src/ChunkyImageLib/Operations/DrawingSurfaceLineOperation.cs

@@ -1,9 +1,9 @@
 using ChunkyImageLib.DataHolders;
-using PixiEditor.DrawingApi.Core.ColorsImpl;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.DrawingApi.Core.Surfaces;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.ColorsImpl;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Numerics;
 
 namespace ChunkyImageLib.Operations;
 internal class DrawingSurfaceLineOperation : IMirroredDrawOperation

+ 3 - 3
src/ChunkyImageLib/Operations/EllipseHelper.cs

@@ -1,7 +1,7 @@
 using ChunkyImageLib.DataHolders;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.DrawingApi.Core.Surfaces.Vector;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces.Vector;
+using Drawie.Numerics;
 
 namespace ChunkyImageLib.Operations;
 

+ 11 - 11
src/ChunkyImageLib/Operations/EllipseOperation.cs

@@ -1,10 +1,10 @@
 using ChunkyImageLib.DataHolders;
-using PixiEditor.DrawingApi.Core.ColorsImpl;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.DrawingApi.Core.Surfaces;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
-using PixiEditor.DrawingApi.Core.Surfaces.Vector;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.ColorsImpl;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Backend.Core.Surfaces.Vector;
+using Drawie.Numerics;
 
 namespace ChunkyImageLib.Operations;
 internal class EllipseOperation : IMirroredDrawOperation
@@ -22,8 +22,8 @@ internal class EllipseOperation : IMirroredDrawOperation
     private VectorPath? innerPath;
     
     private VectorPath? ellipseOutline;
-    private Point[]? ellipse;
-    private Point[]? ellipseFill;
+    private VecF[]? ellipse;
+    private VecF[]? ellipseFill;
     private RectI? ellipseFillRect;
 
     public EllipseOperation(RectI location, Color strokeColor, Color fillColor, int strokeWidth, double rotationRad, Paint? paint = null)
@@ -45,12 +45,12 @@ internal class EllipseOperation : IMirroredDrawOperation
             {
                 var ellipseList = EllipseHelper.GenerateEllipseFromRect(location);
 
-                ellipse = ellipseList.Select(a => new Point(a)).ToArray();
+                ellipse = ellipseList.Select(a => new VecF(a)).ToArray();
 
                 if (fillColor.A > 0 || paint.BlendMode != BlendMode.SrcOver)
                 {
                     (var fill, ellipseFillRect) = EllipseHelper.SplitEllipseFillIntoRegions(ellipseList.ToList(), location);
-                    ellipseFill = fill.Select(a => new Point(a)).ToArray();
+                    ellipseFill = fill.Select(a => new VecF(a)).ToArray();
                 }
             }
             else
@@ -86,7 +86,7 @@ internal class EllipseOperation : IMirroredDrawOperation
                 {
                     paint.Color = fillColor;
                     surf.Canvas.DrawPoints(PointMode.Lines, ellipseFill!, paint);
-                    surf.Canvas.DrawRect(ellipseFillRect!.Value, paint);
+                    surf.Canvas.DrawRect((RectD)ellipseFillRect!.Value, paint);
                 }
                 
                 paint.Color = strokeColor;

+ 2 - 2
src/ChunkyImageLib/Operations/IDrawOperation.cs

@@ -1,6 +1,6 @@
 using ChunkyImageLib.DataHolders;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Numerics;
 
 namespace ChunkyImageLib.Operations;
 

+ 4 - 6
src/ChunkyImageLib/Operations/ImageOperation.cs

@@ -1,8 +1,8 @@
 using ChunkyImageLib.DataHolders;
-using PixiEditor.DrawingApi.Core;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Numerics;
 
 namespace ChunkyImageLib.Operations;
 
@@ -78,8 +78,6 @@ internal class ImageOperation : IMirroredDrawOperation
         imageWasCopied = copyImage;
     }
 
-
-
     public void DrawOnChunk(Chunk targetChunk, VecI chunkPos)
     {
         //customPaint.FilterQuality = chunk.Resolution != ChunkResolution.Full;

+ 5 - 5
src/ChunkyImageLib/Operations/OperationHelper.cs

@@ -1,7 +1,7 @@
 using ChunkyImageLib.DataHolders;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.DrawingApi.Core.Surfaces;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Numerics;
 
 namespace ChunkyImageLib.Operations;
 
@@ -118,10 +118,10 @@ public static class OperationHelper
     }
 
     public static Matrix3X3 CreateMatrixFromPoints(ShapeCorners corners, VecD size)
-        => CreateMatrixFromPoints((Point)corners.TopLeft, (Point)corners.TopRight, (Point)corners.BottomRight, (Point)corners.BottomLeft, (float)size.X, (float)size.Y);
+        => CreateMatrixFromPoints((VecF)corners.TopLeft, (VecF)corners.TopRight, (VecF)corners.BottomRight, (VecF)corners.BottomLeft, (float)size.X, (float)size.Y);
 
     // see https://stackoverflow.com/questions/48416118/perspective-transform-in-skia/72364829#72364829
-    public static Matrix3X3 CreateMatrixFromPoints(Point topLeft, Point topRight, Point botRight, Point botLeft, double width, double height)
+    public static Matrix3X3 CreateMatrixFromPoints(VecF topLeft, VecF topRight, VecF botRight, VecF botLeft, double width, double height)
     {
         (double x1, double y1) = (topLeft.X, topLeft.Y);
         (double x2, double y2) = (topRight.X, topRight.Y);

+ 2 - 2
src/ChunkyImageLib/Operations/PaintOperation.cs

@@ -1,6 +1,6 @@
 using ChunkyImageLib.DataHolders;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Numerics;
 
 namespace ChunkyImageLib.Operations;
 

+ 6 - 6
src/ChunkyImageLib/Operations/PathOperation.cs

@@ -1,10 +1,10 @@
 using ChunkyImageLib.DataHolders;
-using PixiEditor.DrawingApi.Core.ColorsImpl;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.DrawingApi.Core.Surfaces;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
-using PixiEditor.DrawingApi.Core.Surfaces.Vector;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.ColorsImpl;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Backend.Core.Surfaces.Vector;
+using Drawie.Numerics;
 
 namespace ChunkyImageLib.Operations;
 internal class PathOperation : IMirroredDrawOperation

+ 5 - 5
src/ChunkyImageLib/Operations/PixelOperation.cs

@@ -1,9 +1,9 @@
 using ChunkyImageLib.DataHolders;
-using PixiEditor.DrawingApi.Core.ColorsImpl;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.DrawingApi.Core.Surfaces;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.ColorsImpl;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Numerics;
 
 namespace ChunkyImageLib.Operations;
 

+ 7 - 7
src/ChunkyImageLib/Operations/PixelsOperation.cs

@@ -1,25 +1,25 @@
 using System.ComponentModel.DataAnnotations.Schema;
 using System.Linq;
 using ChunkyImageLib.DataHolders;
-using PixiEditor.DrawingApi.Core.ColorsImpl;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.DrawingApi.Core.Surfaces;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.ColorsImpl;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Numerics;
 
 namespace ChunkyImageLib.Operations;
 
 internal class PixelsOperation : IMirroredDrawOperation
 {
     public bool IgnoreEmptyChunks => false;
-    private readonly Point[] pixels;
+    private readonly VecF[] pixels;
     private readonly Color color;
     private readonly BlendMode blendMode;
     private readonly Paint paint;
 
     public PixelsOperation(IEnumerable<VecI> pixels, Color color, BlendMode blendMode)
     {
-        this.pixels = pixels.Select(pixel => new Point(pixel.X, pixel.Y)).ToArray();
+        this.pixels = pixels.Select(pixel => new VecF(pixel.X, pixel.Y)).ToArray();
         this.color = color;
         this.blendMode = blendMode;
         paint = new Paint() { BlendMode = blendMode };

+ 3 - 3
src/ChunkyImageLib/Operations/RectangleOperation.cs

@@ -1,7 +1,7 @@
 using ChunkyImageLib.DataHolders;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.DrawingApi.Core.Surfaces;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Numerics;
 
 namespace ChunkyImageLib.Operations;
 

+ 4 - 4
src/ChunkyImageLib/Operations/ReplaceColorOperation.cs

@@ -1,8 +1,8 @@
 using ChunkyImageLib.DataHolders;
-using PixiEditor.DrawingApi.Core.ColorsImpl;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.DrawingApi.Core.Surfaces;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.ColorsImpl;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Numerics;
 
 namespace ChunkyImageLib.Operations;
 internal class ReplaceColorOperation : IDrawOperation

+ 2 - 2
src/ChunkyImageLib/Operations/ResizeOperation.cs

@@ -1,6 +1,6 @@
 using ChunkyImageLib.DataHolders;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Numerics;
 
 namespace ChunkyImageLib.Operations;
 

+ 127 - 0
src/ChunkyImageLib/Operations/TextureOperation.cs

@@ -0,0 +1,127 @@
+using ChunkyImageLib.DataHolders;
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Numerics;
+
+namespace ChunkyImageLib.Operations;
+
+internal class TextureOperation : IMirroredDrawOperation
+{
+    private Matrix3X3 transformMatrix;
+    private ShapeCorners corners;
+    private Texture toPaint;
+    private bool imageWasCopied = false;
+    private readonly Paint? customPaint;
+
+    public bool IgnoreEmptyChunks => false;
+
+    public TextureOperation(VecI pos, Texture image, Paint? paint = null, bool copyImage = true)
+    {
+        if (paint is not null)
+            customPaint = paint.Clone();
+
+        corners = new()
+        {
+            TopLeft = pos,
+            TopRight = new(pos.X + image.Size.X, pos.Y),
+            BottomRight = pos + image.Size,
+            BottomLeft = new VecD(pos.X, pos.Y + image.Size.Y)
+        };
+        transformMatrix = Matrix3X3.CreateIdentity();
+        transformMatrix.TransX = pos.X;
+        transformMatrix.TransY = pos.Y;
+
+        // copying is needed for thread safety
+        if (copyImage)
+            toPaint = new Texture(image);
+        else
+            toPaint = image;
+        imageWasCopied = copyImage;
+    }
+
+    public TextureOperation(ShapeCorners corners, Texture image, Paint? paint = null, bool copyImage = true)
+    {
+        if (paint is not null)
+            customPaint = paint.Clone();
+
+        this.corners = corners;
+        transformMatrix = OperationHelper.CreateMatrixFromPoints(corners, image.Size);
+
+        // copying is needed for thread safety
+        if (copyImage)
+            toPaint = new Texture(image);
+        else
+            toPaint = image;
+        imageWasCopied = copyImage;
+    }
+
+    public TextureOperation(Matrix3X3 transformMatrix, Texture image, Paint? paint = null, bool copyImage = true)
+    {
+        if (paint is not null)
+            customPaint = paint.Clone();
+
+        this.corners = new ShapeCorners()
+        {
+            TopLeft = transformMatrix.MapPoint(0, 0),
+            TopRight = transformMatrix.MapPoint(image.Size.X, 0),
+            BottomLeft = transformMatrix.MapPoint(0, image.Size.Y),
+            BottomRight = transformMatrix.MapPoint(image.Size),
+        };
+        this.transformMatrix = transformMatrix;
+
+        // copying is needed for thread safety
+        if (copyImage)
+            toPaint = new Texture(image);
+        else
+            toPaint = image;
+        imageWasCopied = copyImage;
+    }
+
+    public void DrawOnChunk(Chunk targetChunk, VecI chunkPos)
+    {
+        //customPaint.FilterQuality = chunk.Resolution != ChunkResolution.Full;
+        float scaleMult = (float)targetChunk.Resolution.Multiplier();
+        VecD trans = -chunkPos * ChunkPool.FullChunkSize;
+
+        var scaleTrans = Matrix3X3.CreateScaleTranslation(scaleMult, scaleMult, (float)trans.X * scaleMult, (float)trans.Y * scaleMult);
+        var finalMatrix = Matrix3X3.Concat(scaleTrans, transformMatrix);
+
+        targetChunk.Surface.DrawingSurface.Canvas.Save();
+        targetChunk.Surface.DrawingSurface.Canvas.SetMatrix(finalMatrix);
+        targetChunk.Surface.DrawingSurface.Canvas.DrawImage(toPaint.DrawingSurface.Snapshot(), 0, 0, customPaint);
+        targetChunk.Surface.DrawingSurface.Canvas.Restore();
+    }
+
+    public AffectedArea FindAffectedArea(VecI imageSize)
+    {
+        return new AffectedArea(OperationHelper.FindChunksTouchingQuadrilateral(corners, ChunkPool.FullChunkSize), (RectI)corners.AABBBounds.RoundOutwards());
+    }
+
+    public void Dispose()
+    {
+        if (imageWasCopied)
+            toPaint.Dispose();
+        customPaint?.Dispose();
+    }
+
+    public IDrawOperation AsMirrored(double? verAxisX, double? horAxisY)
+    {
+        if (verAxisX is not null && horAxisY is not null)
+        {
+            return new TextureOperation
+                (corners.AsMirroredAcrossVerAxis((double)verAxisX).AsMirroredAcrossHorAxis((double)horAxisY), toPaint, customPaint, imageWasCopied);
+        }
+        if (verAxisX is not null)
+        {
+            return new TextureOperation
+                (corners.AsMirroredAcrossVerAxis((double)verAxisX), toPaint, customPaint, imageWasCopied);
+        }
+        if (horAxisY is not null)
+        {
+            return new TextureOperation
+                (corners.AsMirroredAcrossHorAxis((double)horAxisY), toPaint, customPaint, imageWasCopied);
+        }
+        return new TextureOperation(corners, toPaint, customPaint, imageWasCopied);
+    }
+}

+ 2 - 2
src/ChunkyImageLibVis/MainWindow.xaml.cs

@@ -8,8 +8,8 @@ using System.Windows.Controls;
 using System.Windows.Input;
 using System.Windows.Media;
 using System.Windows.Shapes;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Numerics;
 
 namespace ChunkyImageLibVis;
 

+ 1 - 1
src/Directory.Build.props

@@ -1,7 +1,7 @@
 <Project>
     <PropertyGroup>
         <CodeAnalysisRuleSet>../Custom.ruleset</CodeAnalysisRuleSet>
-		    <AvaloniaVersion>11.1.3</AvaloniaVersion>
+		    <AvaloniaVersion>11.1.4</AvaloniaVersion>
     </PropertyGroup>
     <ItemGroup>
         <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />

+ 1 - 0
src/Drawie

@@ -0,0 +1 @@
+Subproject commit a3a2b919c9049bf7cde1324d9bc8afa81c881e1b

+ 1 - 1
src/PixiDocks

@@ -1 +1 @@
-Subproject commit d55a7e27bbed73d19cc59d96b4b20ffdfee35634
+Subproject commit cf516edcce55f6cf0028c6446a4ecf2475606ce1

+ 1 - 1
src/PixiEditor.AnimationRenderer.Core/IAnimationRenderer.cs

@@ -1,4 +1,4 @@
-using PixiEditor.DrawingApi.Core.Surfaces.ImageData;
+using Drawie.Backend.Core.Surfaces.ImageData;
 
 namespace PixiEditor.AnimationRenderer.Core;
 

+ 1 - 1
src/PixiEditor.AnimationRenderer.Core/PixiEditor.AnimationRenderer.Core.csproj

@@ -7,7 +7,7 @@
     </PropertyGroup>
 
     <ItemGroup>
-      <ProjectReference Include="..\PixiEditor.DrawingApi.Core\PixiEditor.DrawingApi.Core.csproj" />
+      <ProjectReference Include="..\Drawie\src\Drawie.Backend.Core\Drawie.Backend.Core.csproj" />
     </ItemGroup>
 
 </Project>

+ 2 - 2
src/PixiEditor.AnimationRenderer.FFmpeg/FFMpegRenderer.cs

@@ -5,8 +5,8 @@ using FFMpegCore.Arguments;
 using FFMpegCore.Enums;
 using FFMpegCore.Pipes;
 using PixiEditor.AnimationRenderer.Core;
-using PixiEditor.DrawingApi.Core.Surfaces.ImageData;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.Surfaces.ImageData;
+using Drawie.Numerics;
 
 namespace PixiEditor.AnimationRenderer.FFmpeg;
 

+ 2 - 2
src/PixiEditor.AnimationRenderer.FFmpeg/ImgFrame.cs

@@ -1,6 +1,6 @@
 using FFMpegCore.Pipes;
-using PixiEditor.DrawingApi.Core.Surfaces;
-using PixiEditor.DrawingApi.Core.Surfaces.ImageData;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.ImageData;
 
 namespace PixiEditor.AnimationRenderer.FFmpeg;
 

+ 2 - 1
src/PixiEditor.AnimationRenderer.FFmpeg/PixiEditor.AnimationRenderer.FFmpeg.csproj

@@ -4,11 +4,12 @@
         <TargetFramework>net8.0</TargetFramework>
         <ImplicitUsings>enable</ImplicitUsings>
         <Nullable>enable</Nullable>
+        <Configurations>Release;Debug</Configurations>
+        <Platforms>arm64;x64</Platforms>
     </PropertyGroup>
 
     <ItemGroup>
       <ProjectReference Include="..\PixiEditor.AnimationRenderer.Core\PixiEditor.AnimationRenderer.Core.csproj" />
-      <ProjectReference Include="..\PixiEditor.Numerics\PixiEditor.Numerics.csproj" />
     </ItemGroup>
 
     <ItemGroup>

+ 2 - 2
src/PixiEditor.ChangeableDocument.Gen/Helpers.cs

@@ -38,7 +38,7 @@ internal static class Helpers
     }
 
     public static Result<string> CreateStartUpdateChangeAction
-        (MethodInfo changeConstructorInfo, MethodInfo updateMethodInfo, ClassDeclarationSyntax containingClass)
+        (MethodInfo changeConstructorInfo, MethodInfo updateMethodInfo, ClassDeclarationSyntax containingClass, bool isCancelable)
     {
         string actionName = changeConstructorInfo.ContainingClass.Name.Split('_')[0] + "_Action";
         List<TypeWithName> constructorArgs = changeConstructorInfo.Arguments;
@@ -55,7 +55,7 @@ internal static class Helpers
         StringBuilder sb = new();
 
         sb.AppendLine("namespace PixiEditor.ChangeableDocument.Actions.Generated;");
-        sb.AppendLine($"public record class {actionName} : PixiEditor.ChangeableDocument.Actions.IStartOrUpdateChangeAction");
+        sb.AppendLine($"public record class {actionName} : PixiEditor.ChangeableDocument.Actions.IStartOrUpdateChangeAction" + (isCancelable ? ", PixiEditor.ChangeableDocument.Actions.ICancelableAction" : ""));
         sb.AppendLine("{");
         sb.Append($"public {actionName}");
         AppendArgumentList(sb, constructorArgs);

+ 11 - 8
src/PixiEditor.ChangeableDocument.Gen/UpdateableChangeActionGenerator.cs

@@ -12,7 +12,7 @@ public class UpdateableChangeActionGenerator : IIncrementalGenerator
     private static NamespacedType constructorAttributeType = new NamespacedType(ConstructorAttribute, AttributesNamespace);
     private static NamespacedType updateMethodAttributeType = new NamespacedType(UpdateMethodAttribute, AttributesNamespace);
 
-    private static Result<(IMethodSymbol, IMethodSymbol, ClassDeclarationSyntax)>? TransformSyntax
+    private static Result<(IMethodSymbol, IMethodSymbol, ClassDeclarationSyntax, bool)>? TransformSyntax
         (GeneratorSyntaxContext context, CancellationToken cancelToken)
     {
         ClassDeclarationSyntax containingClass;
@@ -33,9 +33,11 @@ public class UpdateableChangeActionGenerator : IIncrementalGenerator
         var classSymbol = (INamedTypeSymbol)context.SemanticModel.GetDeclaredSymbol(containingClass)!;
         if (!Helpers.IsInheritedFrom(classSymbol, new("UpdateableChange", "PixiEditor.ChangeableDocument.Changes")))
         {
-            return Result<(IMethodSymbol, IMethodSymbol, ClassDeclarationSyntax)>.Error
+            return Result<(IMethodSymbol, IMethodSymbol, ClassDeclarationSyntax, bool)>.Error
                 ("The GenerateUpdateableChangeActions and UpdateChangeMethodAttribute can only be used inside UpdateableChanges", containingClass.SyntaxTree, containingClass.Span);
         }
+        
+        bool isCancelable = Helpers.IsInheritedFrom(classSymbol, new("CancelableUpdateableChange", "PixiEditor.ChangeableDocument.Changes"));
 
         // here we are sure we are inside an updateable change, time to find the update method
         MethodDeclarationSyntax? methodSyntax = null;
@@ -43,7 +45,7 @@ public class UpdateableChangeActionGenerator : IIncrementalGenerator
         const string errorMessage = $"Update method isn't marked with {UpdateMethodAttribute}";
         if (!members.Any())
         {
-            return Result<(IMethodSymbol, IMethodSymbol, ClassDeclarationSyntax)>.Error
+            return Result<(IMethodSymbol, IMethodSymbol, ClassDeclarationSyntax, bool)>.Error
                 (errorMessage, containingClass.SyntaxTree, containingClass.Span);
         }
         foreach (var member in members)
@@ -59,7 +61,7 @@ public class UpdateableChangeActionGenerator : IIncrementalGenerator
         }
         if (methodSyntax is null)
         {
-            return Result<(IMethodSymbol, IMethodSymbol, ClassDeclarationSyntax)>.Error
+            return Result<(IMethodSymbol, IMethodSymbol, ClassDeclarationSyntax, bool)>.Error
                 (errorMessage, containingClass.SyntaxTree, containingClass.Span);
         }
 
@@ -68,23 +70,24 @@ public class UpdateableChangeActionGenerator : IIncrementalGenerator
         var constructorSymbol = context.SemanticModel.GetDeclaredSymbol(constructorSyntax, cancelToken);
         if (constructorSymbol is not IMethodSymbol || methodSymbol is not IMethodSymbol)
             return null;
-        return ((IMethodSymbol)constructorSymbol, (IMethodSymbol)methodSymbol, containingClass);
+        
+        return ((IMethodSymbol)constructorSymbol, (IMethodSymbol)methodSymbol, containingClass, isCancelable);
     }
 
     private static Result<(NamedSourceCode, NamedSourceCode)> GenerateActions
-        (Result<(IMethodSymbol, IMethodSymbol, ClassDeclarationSyntax)>? prevResult, CancellationToken cancelToken)
+        (Result<(IMethodSymbol, IMethodSymbol, ClassDeclarationSyntax, bool)>? prevResult, CancellationToken cancelToken)
     {
         if (prevResult!.Value.ErrorText is not null)
         {
             return Result<(NamedSourceCode, NamedSourceCode)>.Error
                 (prevResult.Value.ErrorText, prevResult.Value.SyntaxTree!, (TextSpan)prevResult.Value.Span!);
         }
-        var (constructor, update, containingClass) = prevResult.Value.Value;
+        var (constructor, update, containingClass, isCancelable) = prevResult.Value.Value;
 
         var constructorInfo = Helpers.ExtractMethodInfo(constructor!);
         var updateInfo = Helpers.ExtractMethodInfo(update!);
 
-        var maybeStartUpdateAction = Helpers.CreateStartUpdateChangeAction(constructorInfo, updateInfo, containingClass);
+        var maybeStartUpdateAction = Helpers.CreateStartUpdateChangeAction(constructorInfo, updateInfo, containingClass, isCancelable);
         if (maybeStartUpdateAction.ErrorText is not null)
         {
             return Result<(NamedSourceCode, NamedSourceCode)>.Error

+ 5 - 0
src/PixiEditor.ChangeableDocument/Actions/ICancelableAction.cs

@@ -0,0 +1,5 @@
+namespace PixiEditor.ChangeableDocument.Actions;
+
+public interface ICancelableAction : IAction
+{
+}

+ 1 - 1
src/PixiEditor.ChangeableDocument/ChangeInfos/Drawing/LayerImageArea_ChangeInfo.cs

@@ -1,4 +1,4 @@
-using PixiEditor.DrawingApi.Core.Numerics;
+using Drawie.Backend.Core.Numerics;
 
 namespace PixiEditor.ChangeableDocument.ChangeInfos.Drawing;
 

+ 1 - 1
src/PixiEditor.ChangeableDocument/ChangeInfos/Drawing/MaskArea_ChangeInfo.cs

@@ -1,4 +1,4 @@
-using PixiEditor.DrawingApi.Core.Numerics;
+using Drawie.Backend.Core.Numerics;
 
 namespace PixiEditor.ChangeableDocument.ChangeInfos.Drawing;
 

+ 1 - 1
src/PixiEditor.ChangeableDocument/ChangeInfos/Drawing/Selection_ChangeInfo.cs

@@ -1,4 +1,4 @@
-using PixiEditor.DrawingApi.Core.Surfaces.Vector;
+using Drawie.Backend.Core.Surfaces.Vector;
 
 namespace PixiEditor.ChangeableDocument.ChangeInfos.Drawing;
 

+ 1 - 1
src/PixiEditor.ChangeableDocument/ChangeInfos/NodeGraph/CreateNode_ChangeInfo.cs

@@ -6,7 +6,7 @@ using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.ChangeableDocument.ChangeInfos.Structure;
 using PixiEditor.ChangeableDocument.Changes.Structure;
-using PixiEditor.Numerics;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.ChangeInfos.NodeGraph;
 

+ 1 - 1
src/PixiEditor.ChangeableDocument/ChangeInfos/NodeGraph/NodePosition_ChangeInfo.cs

@@ -1,4 +1,4 @@
-using PixiEditor.Numerics;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.ChangeInfos.NodeGraph;
 

+ 1 - 1
src/PixiEditor.ChangeableDocument/ChangeInfos/Objects/TransformObject_ChangeInfo.cs

@@ -1,4 +1,4 @@
-using PixiEditor.Numerics;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.ChangeInfos.Objects;
 

+ 2 - 2
src/PixiEditor.ChangeableDocument/ChangeInfos/Root/ReferenceLayerChangeInfos/SetReferenceLayer_ChangeInfo.cs

@@ -1,6 +1,6 @@
 using System.Collections.Immutable;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.ChangeInfos.Structure;
 

+ 2 - 2
src/PixiEditor.ChangeableDocument/ChangeInfos/Root/Size_ChangeInfo.cs

@@ -1,5 +1,5 @@
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.ChangeInfos.Root;
 

+ 1 - 1
src/PixiEditor.ChangeableDocument/ChangeInfos/Structure/CreateStructureMember_ChangeInfo.cs

@@ -2,7 +2,7 @@
 using System.Reflection;
 using PixiEditor.ChangeableDocument.ChangeInfos.NodeGraph;
 using PixiEditor.ChangeableDocument.Enums;
-using PixiEditor.Numerics;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.ChangeInfos.Structure;
 

+ 1 - 1
src/PixiEditor.ChangeableDocument/ChangeInfos/Vectors/VectorShape_ChangeInfo.cs

@@ -1,5 +1,5 @@
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
-using PixiEditor.Numerics;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.ChangeInfos.Vectors;
 

+ 2 - 0
src/PixiEditor.ChangeableDocument/Changeables/Animations/KeyFrame.cs

@@ -1,6 +1,8 @@
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Animations;
 

+ 2 - 0
src/PixiEditor.ChangeableDocument/Changeables/Animations/RasterKeyFrame.cs

@@ -1,6 +1,8 @@
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Animations;
 

+ 5 - 5
src/PixiEditor.ChangeableDocument/Changeables/Document.cs

@@ -5,11 +5,11 @@ using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using PixiEditor.ChangeableDocument.Rendering;
-using PixiEditor.DrawingApi.Core;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.DrawingApi.Core.Surfaces.ImageData;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces.ImageData;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables;
 

+ 12 - 11
src/PixiEditor.ChangeableDocument/Changeables/Graph/Context/FuncContext.cs

@@ -2,12 +2,13 @@
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.ChangeableDocument.Rendering;
-using PixiEditor.DrawingApi.Core;
-using PixiEditor.DrawingApi.Core.ColorsImpl;
-using PixiEditor.DrawingApi.Core.Shaders.Generation;
-using PixiEditor.DrawingApi.Core.Shaders.Generation.Expressions;
-using PixiEditor.Numerics;
-using Expression = PixiEditor.DrawingApi.Core.Shaders.Generation.Expressions.Expression;
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.ColorsImpl;
+using Drawie.Backend.Core.Shaders.Generation;
+using Drawie.Backend.Core.Shaders.Generation.Expressions;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Numerics;
+using Expression = Drawie.Backend.Core.Shaders.Generation.Expressions.Expression;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Context;
 
@@ -26,7 +27,7 @@ public class FuncContext
     public Float2 SamplePosition { get; private set; }
     public VecI Size { get; private set; }
     public bool HasContext { get; private set; }
-    public RenderingContext RenderingContext { get; set; }
+    public RenderContext RenderContext { get; set; }
 
     public ShaderBuilder Builder { get; set; }
 
@@ -42,18 +43,18 @@ public class FuncContext
     {
     }
 
-    public FuncContext(RenderingContext renderingContext, ShaderBuilder builder)
+    public FuncContext(RenderContext renderContext, ShaderBuilder builder)
     {
-        RenderingContext = renderingContext;
+        RenderContext = renderContext;
         Builder = builder;
         HasContext = true;
         OriginalPosition = new Float2("coords"); // input argument 'half4 main(float2 coords)'
         SamplePosition = Builder.ConstructFloat2(OriginalPosition.X, OriginalPosition.Y); 
     }
 
-    public Half4 SampleTexture(Texture imageValue, Float2 pos)
+    public Half4 SampleSurface(DrawingSurface surface, Float2 pos)
     {
-        TextureSampler texName = Builder.AddOrGetTexture(imageValue);
+        SurfaceSampler texName = Builder.AddOrGetSurface(surface);
         return Builder.Sample(texName, pos);
     }
 

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Filter.cs

@@ -1,5 +1,5 @@
 using System.Diagnostics.Contracts;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph;
 

+ 3 - 3
src/PixiEditor.ChangeableDocument/Changeables/Graph/FuncInputProperty.cs

@@ -2,9 +2,9 @@
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.ChangeableDocument.Changes.NodeGraph;
-using PixiEditor.DrawingApi.Core.Shaders;
-using PixiEditor.DrawingApi.Core.Shaders.Generation;
-using PixiEditor.DrawingApi.Core.Shaders.Generation.Expressions;
+using Drawie.Backend.Core.Shaders;
+using Drawie.Backend.Core.Shaders.Generation;
+using Drawie.Backend.Core.Shaders.Generation.Expressions;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph;
 

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/FuncOutputProperty.cs

@@ -1,7 +1,7 @@
 using PixiEditor.ChangeableDocument.Changeables.Graph.Context;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
-using PixiEditor.DrawingApi.Core.Shaders;
+using Drawie.Backend.Core.Shaders;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph;
 

+ 62 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/GraphUtils.cs

@@ -0,0 +1,62 @@
+using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph;
+
+public static class GraphUtils
+{
+    public static Queue<IReadOnlyNode> CalculateExecutionQueue(IReadOnlyNode outputNode)
+    {
+        var finalQueue = new HashSet<IReadOnlyNode>();
+        var queueNodes = new Queue<IReadOnlyNode>();
+        queueNodes.Enqueue(outputNode);
+
+        while (queueNodes.Count > 0)
+        {
+            var node = queueNodes.Dequeue();
+            
+            if (finalQueue.Contains(node))
+            {
+                continue;
+            }
+            
+            bool canAdd = true;
+
+            foreach (var input in node.InputProperties)
+            {
+                if (input.Connection == null)
+                {
+                    continue;
+                }
+
+                if (finalQueue.Contains(input.Connection.Node))
+                {
+                    continue;
+                }
+
+                canAdd = false;
+                
+                if (finalQueue.Contains(input.Connection.Node))
+                {
+                    finalQueue.Remove(input.Connection.Node);
+                    finalQueue.Add(input.Connection.Node);
+                }
+
+                if (!queueNodes.Contains(input.Connection.Node))
+                {
+                    queueNodes.Enqueue(input.Connection.Node);
+                }
+            }
+            
+            if (canAdd)
+            {
+                finalQueue.Add(node);
+            }
+            else
+            {
+                queueNodes.Enqueue(node);
+            }
+        }
+
+        return new Queue<IReadOnlyNode>(finalQueue);
+    }
+}

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/InputProperty.cs

@@ -3,7 +3,7 @@ using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.ChangeableDocument.Changes.NodeGraph;
 using PixiEditor.Common;
-using PixiEditor.DrawingApi.Core.Shaders.Generation;
+using Drawie.Backend.Core.Shaders.Generation;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph;
 

+ 9 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IChunkRenderable.cs

@@ -0,0 +1,9 @@
+using PixiEditor.ChangeableDocument.Changeables.Animations;
+using Drawie.Numerics;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+
+public interface IChunkRenderable
+{
+    public void RenderChunk(VecI chunkPos, ChunkResolution resolution, KeyFrameTime frameTime);
+}

+ 8 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IClipSource.cs

@@ -0,0 +1,8 @@
+using Drawie.Backend.Core.Surfaces;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+
+public interface IClipSource
+{
+    public void DrawOnTexture(SceneObjectRenderContext context, DrawingSurface drawOnto);
+}

+ 6 - 3
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IPreviewRenderable.cs

@@ -1,9 +1,12 @@
-using PixiEditor.DrawingApi.Core;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 
 public interface IPreviewRenderable
 {
-    public bool RenderPreview(Texture renderOn, VecI chunk, ChunkResolution resolution, int frame);
+    public RectD? GetPreviewBounds(int frame, string elementToRenderName = ""); 
+    public bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame,
+        string elementToRenderName);
 }

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyFolderNode.cs

@@ -1,5 +1,5 @@
-using PixiEditor.DrawingApi.Core;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 

+ 3 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyImageNode.cs

@@ -1,4 +1,6 @@
-using PixiEditor.ChangeableDocument.Changeables.Interfaces;
+using PixiEditor.ChangeableDocument.Changeables.Animations;
+using PixiEditor.ChangeableDocument.Changeables.Interfaces;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 

+ 3 - 4
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyNode.cs

@@ -1,8 +1,8 @@
 using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using PixiEditor.ChangeableDocument.Rendering;
-using PixiEditor.DrawingApi.Core;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 
@@ -13,10 +13,9 @@ public interface IReadOnlyNode
     public IReadOnlyList<IOutputProperty> OutputProperties { get; }
     public IReadOnlyList<IReadOnlyKeyFrameData> KeyFrames { get; }
     public VecD Position { get; }
-    public Texture? CachedResult { get; }
     string DisplayName { get; }
 
-    public Texture? Execute(RenderingContext context);
+    public void Execute(RenderContext context);
     
     /// <summary>
     ///     Checks if the inputs are legal. If they are not, the node should not be executed.

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyNodeGraph.cs

@@ -1,5 +1,5 @@
 using PixiEditor.ChangeableDocument.Rendering;
-using PixiEditor.DrawingApi.Core;
+using Drawie.Backend.Core;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 
@@ -10,6 +10,6 @@ public interface IReadOnlyNodeGraph
     public void AddNode(IReadOnlyNode node);
     public void RemoveNode(IReadOnlyNode node);
     public bool TryTraverse(Action<IReadOnlyNode> action);
-    public Texture? Execute(RenderingContext context);
+    public void Execute(RenderContext context);
     Queue<IReadOnlyNode> CalculateExecutionQueue(IReadOnlyNode endNode);
 }

+ 3 - 3
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyShapeVectorData.cs

@@ -1,6 +1,6 @@
-using PixiEditor.DrawingApi.Core.ColorsImpl;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.ColorsImpl;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 

+ 8 - 5
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyStructureNode.cs

@@ -1,20 +1,23 @@
 using PixiEditor.ChangeableDocument.Changeables.Animations;
-using PixiEditor.ChangeableDocument.Enums;
-using PixiEditor.DrawingApi.Core;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Numerics;
+using PixiEditor.ChangeableDocument.Rendering;
+using BlendMode = PixiEditor.ChangeableDocument.Enums.BlendMode;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 
-public interface IReadOnlyStructureNode : IReadOnlyNode
+public interface IReadOnlyStructureNode : IReadOnlyNode, ISceneObject, IChunkRenderable
 {
     public InputProperty<float> Opacity { get; }
     public InputProperty<bool> IsVisible { get; }
     public bool ClipToPreviousMember { get; }
     public InputProperty<BlendMode> BlendMode { get; }
-    public InputProperty<Texture?> CustomMask { get; }
+    public RenderInputProperty CustomMask { get; }
     public InputProperty<bool> MaskIsVisible { get; }
     public string MemberName { get; set; }
     public RectD? GetTightBounds(KeyFrameTime frameTime);
     public ChunkyImage? EmbeddedMask { get; }
     public ShapeCorners GetTransformationCorners(KeyFrameTime frameTime);
+    public void RenderForOutput(RenderContext context, DrawingSurface renderTarget, RenderOutputProperty output);
 }

+ 4 - 3
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IBackgroundInput.cs → src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IRenderInput.cs

@@ -1,9 +1,10 @@
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
-using PixiEditor.DrawingApi.Core;
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.Surfaces;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 
-public interface IBackgroundInput
+public interface IRenderInput
 {
-    InputProperty<Texture?> Background { get; }
+    RenderInputProperty Background { get; }
 }

+ 14 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/ISceneObject.cs

@@ -0,0 +1,14 @@
+using PixiEditor.ChangeableDocument.Changeables.Animations;
+using Drawie.Numerics;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+
+public interface ISceneObject
+{
+    public VecD GetScenePosition(KeyFrameTime atTime);
+    public VecD GetSceneSize(KeyFrameTime atTime);
+    
+    public RectD GetGlobalBounds(KeyFrameTime atTime) => new RectD(GetScenePosition(atTime), GetSceneSize(atTime));
+    
+    public void Render(SceneObjectRenderContext context);
+}

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/ITransformableObject.cs

@@ -1,5 +1,5 @@
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.Numerics;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 

+ 21 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/SceneObjectRenderContext.cs

@@ -0,0 +1,21 @@
+using PixiEditor.ChangeableDocument.Changeables.Animations;
+using PixiEditor.ChangeableDocument.Rendering;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Numerics;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+
+public class SceneObjectRenderContext : RenderContext
+{
+    public RectD LocalBounds { get; }
+    public bool RenderSurfaceIsScene { get; }
+    public RenderOutputProperty TargetPropertyOutput { get; }
+
+    public SceneObjectRenderContext(RenderOutputProperty targetPropertyOutput, DrawingSurface surface, RectD localBounds, KeyFrameTime frameTime,
+        ChunkResolution chunkResolution, VecI docSize, bool renderSurfaceIsScene, double opacity) : base(surface, frameTime, chunkResolution, docSize, opacity)
+    {
+        TargetPropertyOutput = targetPropertyOutput;
+        LocalBounds = localBounds;
+        RenderSurfaceIsScene = renderSurfaceIsScene;
+    }
+}

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/Shapes/IReadOnlyEllipseData.cs

@@ -1,4 +1,4 @@
-using PixiEditor.Numerics;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces.Shapes;
 

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/Shapes/IReadOnlyLineData.cs

@@ -1,4 +1,4 @@
-using PixiEditor.Numerics;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces.Shapes;
 

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/Shapes/IReadOnlyRectangleData.cs

@@ -1,4 +1,4 @@
-using PixiEditor.Numerics;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces.Shapes;
 

+ 4 - 57
src/PixiEditor.ChangeableDocument/Changeables/Graph/NodeGraph.cs

@@ -3,7 +3,7 @@ using System.Diagnostics;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.ChangeableDocument.Rendering;
-using PixiEditor.DrawingApi.Core;
+using Drawie.Backend.Core;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph;
 
@@ -38,58 +38,7 @@ public class NodeGraph : IReadOnlyNodeGraph, IDisposable
 
     public Queue<IReadOnlyNode> CalculateExecutionQueue(IReadOnlyNode outputNode)
     {
-        var finalQueue = new HashSet<IReadOnlyNode>();
-        var queueNodes = new Queue<IReadOnlyNode>();
-        queueNodes.Enqueue(outputNode);
-
-        while (queueNodes.Count > 0)
-        {
-            var node = queueNodes.Dequeue();
-            
-            if (finalQueue.Contains(node))
-            {
-                continue;
-            }
-            
-            bool canAdd = true;
-
-            foreach (var input in node.InputProperties)
-            {
-                if (input.Connection == null)
-                {
-                    continue;
-                }
-
-                if (finalQueue.Contains(input.Connection.Node))
-                {
-                    continue;
-                }
-
-                canAdd = false;
-                
-                if (finalQueue.Contains(input.Connection.Node))
-                {
-                    finalQueue.Remove(input.Connection.Node);
-                    finalQueue.Add(input.Connection.Node);
-                }
-
-                if (!queueNodes.Contains(input.Connection.Node))
-                {
-                    queueNodes.Enqueue(input.Connection.Node);
-                }
-            }
-            
-            if (canAdd)
-            {
-                finalQueue.Add(node);
-            }
-            else
-            {
-                queueNodes.Enqueue(node);
-            }
-        }
-
-        return new Queue<IReadOnlyNode>(finalQueue);
+        return GraphUtils.CalculateExecutionQueue(outputNode);
     }
 
     void IReadOnlyNodeGraph.AddNode(IReadOnlyNode node) => AddNode((Node)node);
@@ -119,9 +68,9 @@ public class NodeGraph : IReadOnlyNodeGraph, IDisposable
         return true;
     }
 
-    public Texture? Execute(RenderingContext context)
+    public void Execute(RenderContext context)
     {
-        if (OutputNode == null) return null;
+        if (OutputNode == null) return;
 
         var queue = CalculateExecutionQueue(OutputNode);
         
@@ -138,7 +87,5 @@ public class NodeGraph : IReadOnlyNodeGraph, IDisposable
                 node.Execute(context);
             }
         }
-
-        return OutputNode.Input.Value;
     }
 }

+ 2 - 5
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Animable/TimeNode.cs

@@ -1,5 +1,5 @@
 using PixiEditor.ChangeableDocument.Rendering;
-using PixiEditor.DrawingApi.Core;
+using Drawie.Backend.Core;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Animable;
 
@@ -9,7 +9,6 @@ public class TimeNode : Node
     public OutputProperty<int> ActiveFrame { get; set; }
     public OutputProperty<double> NormalizedTime { get; set; }
 
-    protected override bool AffectedByAnimation => true;
 
     public TimeNode()
     {
@@ -17,12 +16,10 @@ public class TimeNode : Node
         NormalizedTime = CreateOutput("NormalizedTime", "NORMALIZED_TIME", 0.0);
     }
     
-    protected override Texture? OnExecute(RenderingContext context)
+    protected override void OnExecute(RenderContext context)
     {
         ActiveFrame.Value = context.FrameTime.Frame;
         NormalizedTime.Value = context.FrameTime.NormalizedTime;
-        
-        return null;
     }
 
     public override Node CreateCopy()

+ 73 - 46
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineChannelsNode.cs

@@ -1,13 +1,14 @@
-using PixiEditor.ChangeableDocument.Rendering;
-using PixiEditor.DrawingApi.Core;
-using PixiEditor.DrawingApi.Core.Surfaces;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
-using PixiEditor.Numerics;
+using PixiEditor.ChangeableDocument.Helpers;
+using PixiEditor.ChangeableDocument.Rendering;
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.CombineSeparate;
 
 [NodeInfo("CombineChannels")]
-public class CombineChannelsNode : Node
+public class CombineChannelsNode : RenderNode
 {
     private readonly Paint _screenPaint = new() { BlendMode = BlendMode.Screen };
     private readonly Paint _clearPaint = new() { BlendMode = BlendMode.DstIn };
@@ -16,94 +17,120 @@ public class CombineChannelsNode : Node
     private readonly ColorFilter _greenFilter = ColorFilter.CreateColorMatrix(ColorMatrix.UseGreen + ColorMatrix.OpaqueAlphaOffset);
     private readonly ColorFilter _blueFilter = ColorFilter.CreateColorMatrix(ColorMatrix.UseBlue + ColorMatrix.OpaqueAlphaOffset);
 
-    public InputProperty<Texture> Red { get; }
+    public RenderInputProperty Red { get; }
     
-    public InputProperty<Texture> Green { get; }
+    public RenderInputProperty Green { get; }
     
-    public InputProperty<Texture> Blue { get; }
+    public RenderInputProperty Blue { get; }
     
-    public InputProperty<Texture> Alpha { get; }
+    public RenderInputProperty Alpha { get; }
 
-    public OutputProperty<Texture> Image { get; }
-    
     // TODO: Either use a shader to combine each, or find a way to automatically "detect" if alpha channel is grayscale or not, oooor find an even better solution
     public InputProperty<bool> Grayscale { get; }
 
     public CombineChannelsNode()
     {
-        Red = CreateInput<Texture>(nameof(Red), "RED", null);
-        Green = CreateInput<Texture>(nameof(Green), "GREEN", null);
-        Blue = CreateInput<Texture>(nameof(Blue), "BLUE", null);
-        Alpha = CreateInput<Texture>(nameof(Alpha), "ALPHA", null);
+        Red = CreateRenderInput("Red", "RED");
+        Green = CreateRenderInput("Green","GREEN");
+        Blue = CreateRenderInput("Blue", "BLUE");
+        Alpha = CreateRenderInput("Alpha", "ALPHA");
         
-        Image = CreateOutput<Texture>(nameof(Image), "IMAGE", null);
         Grayscale = CreateInput(nameof(Grayscale), "GRAYSCALE", false);
     }
 
-    protected override Texture? OnExecute(RenderingContext context)
+    
+    protected override void OnPaint(RenderContext context, DrawingSurface surface)
     {
-        var size = GetSize();
-
-        if (size == VecI.Zero)
-            return null;
-        
-        var workingSurface = RequestTexture(0, size); 
-
+        int saved = surface.Canvas.SaveLayer();
         if (Red.Value is { } red)
         {
             _screenPaint.ColorFilter = _redFilter;
-            workingSurface.DrawingSurface.Canvas.DrawSurface(red.DrawingSurface, 0, 0, _screenPaint);
+            
+            int savedRed = surface.Canvas.SaveLayer(_screenPaint);
+            red.Paint(context, surface);
+            
+            surface.Canvas.RestoreToCount(savedRed);
         }
 
         if (Green.Value is { } green)
         {
             _screenPaint.ColorFilter = _greenFilter;
-            workingSurface.DrawingSurface.Canvas.DrawSurface(green.DrawingSurface, 0, 0, _screenPaint);
+            int savedGreen = surface.Canvas.SaveLayer(_screenPaint);
+            green.Paint(context, surface);
+            
+            surface.Canvas.RestoreToCount(savedGreen);
         }
 
         if (Blue.Value is { } blue)
         {
             _screenPaint.ColorFilter = _blueFilter;
-            workingSurface.DrawingSurface.Canvas.DrawSurface(blue.DrawingSurface, 0, 0, _screenPaint);
+            int savedBlue = surface.Canvas.SaveLayer(_screenPaint);
+            blue.Paint(context, surface);
+            
+            surface.Canvas.RestoreToCount(savedBlue);
         }
 
         if (Alpha.Value is { } alpha)
         {
             _clearPaint.ColorFilter = Grayscale.Value ? Filters.AlphaGrayscaleFilter : null;
-
-            workingSurface.DrawingSurface.Canvas.DrawSurface(alpha.DrawingSurface, 0, 0, _clearPaint);
+            int savedAlpha = surface.Canvas.SaveLayer(_clearPaint);
+            alpha.Paint(context, surface);
+            
+            surface.Canvas.RestoreToCount(savedAlpha);
         }
-
-        Image.Value = workingSurface;
-
-        return workingSurface;
+            
+        surface.Canvas.RestoreToCount(saved);
     }
 
-    private VecI GetSize()
+    public override RectD? GetPreviewBounds(int frame, string elementToRenderName = "")
     {
-        var final = new RectI();
+        RectD? redBounds = PreviewUtils.FindPreviewBounds(Red.Connection, frame, elementToRenderName);
+        RectD? greenBounds = PreviewUtils.FindPreviewBounds(Green.Connection, frame, elementToRenderName);
+        RectD? blueBounds = PreviewUtils.FindPreviewBounds(Blue.Connection, frame, elementToRenderName);
+        RectD? alphaBounds = PreviewUtils.FindPreviewBounds(Alpha.Connection, frame, elementToRenderName);
 
-        if (Red.Value is { } red)
+        RectD? finalBounds = null;
+        
+        if (redBounds == null && greenBounds == null && blueBounds == null && alphaBounds == null)
         {
-            final = final.Union(new RectI(VecI.Zero, red.Size));
+            return null;
         }
 
-        if (Green.Value is { } green)
+        if (redBounds.HasValue)
         {
-            final = final.Union(new RectI(VecI.Zero, green.Size));
+            finalBounds = redBounds.Value;
         }
-
-        if (Blue.Value is { } blue)
+        
+        if (greenBounds.HasValue)
+        {
+            finalBounds = finalBounds?.Union(greenBounds.Value) ?? greenBounds.Value;
+        }
+        
+        if (blueBounds.HasValue)
+        {
+            finalBounds = finalBounds?.Union(blueBounds.Value) ?? blueBounds.Value;
+        }
+        
+        if (alphaBounds.HasValue)
         {
-            final = final.Union(new RectI(VecI.Zero, blue.Size));
+            finalBounds = finalBounds?.Union(alphaBounds.Value) ?? alphaBounds.Value;
         }
+        
+        return finalBounds;
+    }
 
-        if (Alpha.Value is { } alpha)
+    public override bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame, string elementToRenderName)
+    {
+        if (Red.Value == null && Green.Value == null && Blue.Value == null && Alpha.Value == null)
         {
-            final = final.Union(new RectI(VecI.Zero, alpha.Size));
+            return false;
         }
 
-        return final.Size;
+        RenderContext context = new(renderOn, frame, resolution, VecI.One);
+        
+        OnPaint(context, renderOn); 
+        
+        return true;
     }
 
     public override Node CreateCopy() => new CombineChannelsNode();

+ 4 - 5
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineColorNode.cs

@@ -1,8 +1,8 @@
 using PixiEditor.ChangeableDocument.Changeables.Graph.Context;
 using PixiEditor.ChangeableDocument.Rendering;
-using PixiEditor.DrawingApi.Core;
-using PixiEditor.DrawingApi.Core.ColorsImpl;
-using PixiEditor.DrawingApi.Core.Shaders.Generation.Expressions;
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.ColorsImpl;
+using Drawie.Backend.Core.Shaders.Generation.Expressions;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.CombineSeparate;
 
@@ -79,9 +79,8 @@ public class CombineColorNode : Node
         return ctx.HslaToRgba(h, s, l, a);
     }
 
-    protected override Texture? OnExecute(RenderingContext context)
+    protected override void OnExecute(RenderContext context)
     {
-        return null;
     }
 
 

+ 5 - 6
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineVecDNode.cs

@@ -1,9 +1,9 @@
 using PixiEditor.ChangeableDocument.Changeables.Graph.Context;
 using PixiEditor.ChangeableDocument.Rendering;
-using PixiEditor.DrawingApi.Core;
-using PixiEditor.DrawingApi.Core.ColorsImpl;
-using PixiEditor.DrawingApi.Core.Shaders.Generation.Expressions;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.ColorsImpl;
+using Drawie.Backend.Core.Shaders.Generation.Expressions;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.CombineSeparate;
 
@@ -32,9 +32,8 @@ public class CombineVecDNode : Node
         return ctx.NewFloat2(x, y); 
     }
 
-    protected override Texture? OnExecute(RenderingContext context)
+    protected override void OnExecute(RenderContext context)
     {
-        return null;
     }
 
 

+ 4 - 5
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineVecINode.cs

@@ -1,8 +1,8 @@
 using PixiEditor.ChangeableDocument.Changeables.Graph.Context;
 using PixiEditor.ChangeableDocument.Rendering;
-using PixiEditor.DrawingApi.Core;
-using PixiEditor.DrawingApi.Core.Shaders.Generation.Expressions;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.Shaders.Generation.Expressions;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.CombineSeparate;
 
@@ -32,9 +32,8 @@ public class CombineVecINode : Node
     }
 
 
-    protected override Texture? OnExecute(RenderingContext context)
+    protected override void OnExecute(RenderContext context)
     {
-        return null;
     }
 
 

+ 103 - 48
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateChannelsNode.cs

@@ -1,12 +1,16 @@
-using PixiEditor.ChangeableDocument.Rendering;
-using PixiEditor.DrawingApi.Core;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
-using PixiEditor.Numerics;
+using System.Diagnostics.CodeAnalysis;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+using PixiEditor.ChangeableDocument.Helpers;
+using PixiEditor.ChangeableDocument.Rendering;
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.CombineSeparate;
 
 [NodeInfo("SeparateChannels")]
-public class SeparateChannelsNode : Node
+public class SeparateChannelsNode : Node, IRenderInput, IPreviewRenderable
 {
     private readonly Paint _paint = new();
     
@@ -20,75 +24,126 @@ public class SeparateChannelsNode : Node
     private readonly ColorFilter _blueGrayscaleFilter = ColorFilter.CreateColorMatrix(ColorMatrix.UseBlue + ColorMatrix.MapBlueToRedGreen + ColorMatrix.OpaqueAlphaOffset);
     private readonly ColorFilter _alphaGrayscaleFilter = ColorFilter.CreateColorMatrix(ColorMatrix.MapAlphaToRedGreenBlue + ColorMatrix.OpaqueAlphaOffset);
 
-    public OutputProperty<Texture?> Red { get; }
+    public RenderOutputProperty Red { get; }
     
-    public OutputProperty<Texture?> Green { get; }
+    public RenderOutputProperty Green { get; }
     
-    public OutputProperty<Texture?> Blue { get; }
+    public RenderOutputProperty Blue { get; }
 
-    public OutputProperty<Texture?> Alpha { get; }
+    public RenderOutputProperty Alpha { get; }
     
-    public InputProperty<Texture?> Image { get; }
+    public RenderInputProperty Image { get; } 
     
     public InputProperty<bool> Grayscale { get; }
 
     public SeparateChannelsNode()
     {
-        Red = CreateOutput<Texture>(nameof(Red), "RED", null);
-        Green = CreateOutput<Texture>(nameof(Green), "GREEN", null);
-        Blue = CreateOutput<Texture>(nameof(Blue), "BLUE", null);
-        Alpha = CreateOutput<Texture>(nameof(Alpha), "ALPHA", null);
+        Red = CreateRenderOutput("Red", "RED", () => new Painter(PaintRed));
+        Green = CreateRenderOutput("Green","GREEN", () => new Painter(PaintGreen)); 
+        Blue = CreateRenderOutput("Blue", "BLUE", () => new Painter(PaintBlue));
+        Alpha = CreateRenderOutput("Alpha", "ALPHA", () => new Painter(PaintAlpha));
         
-        Image = CreateInput<Texture>(nameof(Image), "IMAGE", null);
+        Image = CreateRenderInput("Image", "IMAGE");
         Grayscale = CreateInput(nameof(Grayscale), "GRAYSCALE", false);
     }
     
-    protected override Texture? OnExecute(RenderingContext context)
+    private void PaintRed(RenderContext context, DrawingSurface drawingSurface)
     {
-        var image = Image.Value;
+        Paint(context, drawingSurface, _redFilter, _redGrayscaleFilter);
+    }
+    
+    private void PaintGreen(RenderContext context, DrawingSurface drawingSurface)
+    {
+        Paint(context, drawingSurface, _greenFilter, _greenGrayscaleFilter);
+    }
+    
+    private void PaintBlue(RenderContext context, DrawingSurface drawingSurface)
+    {
+        Paint(context, drawingSurface, _blueFilter, _blueGrayscaleFilter);
+    }
+    
+    private void PaintAlpha(RenderContext context, DrawingSurface drawingSurface)
+    {
+        Paint(context, drawingSurface, _alphaFilter, _alphaGrayscaleFilter);
+    }
 
-        if (image == null)
-            return null;
+    private void Paint(RenderContext context, DrawingSurface drawingSurface, ColorFilter colorFilter, ColorFilter grayscaleFilter)
+    {
+        bool grayscale = Grayscale.Value;
+        
+        ColorFilter filter = grayscale ? grayscaleFilter : colorFilter; 
+        _paint.ColorFilter = filter;
+        
+        int saved = drawingSurface.Canvas.SaveLayer(_paint);
         
-        var grayscale = Grayscale.Value;
+        Image.Value.Paint(context, drawingSurface);
+        
+        drawingSurface.Canvas.RestoreToCount(saved);
+    }
+
+    protected override void OnExecute(RenderContext context)
+    {
+        Red.ChainToPainterValue();
+        Green.ChainToPainterValue();
+        Blue.ChainToPainterValue();
+        Alpha.ChainToPainterValue();
+    }
+
+    public override Node CreateCopy() => new SeparateChannelsNode();
+    RenderInputProperty IRenderInput.Background => Image;
+    public RectD? GetPreviewBounds(int frame, string elementToRenderName = "")
+    {
+        RectD? bounds = PreviewUtils.FindPreviewBounds(Image.Connection, frame, elementToRenderName);
+        return bounds;
+    }
 
-        var red = !grayscale ? _redFilter : _redGrayscaleFilter;
-        var green = !grayscale ? _greenFilter : _greenGrayscaleFilter;
-        var blue = !grayscale ? _blueFilter : _blueGrayscaleFilter;
-        var alpha = !grayscale ? _alphaFilter : _alphaGrayscaleFilter;
+    public bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame, string elementToRenderName)
+    {
+        if (Image.Value == null)
+            return false;
+
+        RectD? bounds = GetPreviewBounds(frame, elementToRenderName);
+        
+        if (bounds == null)
+            return false;
+        
+        RenderContext context = new(renderOn, frame, resolution, VecI.One);
 
-        Red.Value = GetImage(image, red, 0);
-        Green.Value = GetImage(image, green, 1);
-        Blue.Value = GetImage(image, blue, 2);
-        Alpha.Value = GetImage(image, alpha, 3);
+        renderOn.Canvas.Save();
 
-        var previewSurface = RequestTexture(4, image.Size * 2);
+        _paint.ColorFilter = Grayscale.Value ? _redGrayscaleFilter : _redFilter;
+        RectD localBounds = new(bounds.Value.X, bounds.Value.Y, bounds.Value.Width / 2, bounds.Value.Height / 2);
+        PaintPreview(renderOn, localBounds, bounds.Value.Pos, context);
 
-        var size = image.Size;
+        _paint.ColorFilter = Grayscale.Value ? _greenGrayscaleFilter : _greenFilter;
+        localBounds = new(bounds.Value.X + bounds.Value.Width / 2, bounds.Value.Y, bounds.Value.Width / 2, bounds.Value.Height / 2);
+        PaintPreview(renderOn, localBounds, new VecD(bounds.Value.X + bounds.Value.Width, bounds.Value.Y), context);
         
-        var redPos = new VecI();
-        var greenPos = new VecI(size.X, 0);
-        var bluePos = new VecI(0, size.Y);
-        var alphaPos = new VecI(size.X, size.Y);
+        _paint.ColorFilter = Grayscale.Value ? _blueGrayscaleFilter : _blueFilter;
+        localBounds = new(bounds.Value.X, bounds.Value.Y + bounds.Value.Height / 2, bounds.Value.Width / 2, bounds.Value.Height / 2);
+        PaintPreview(renderOn, localBounds, new VecD(bounds.Value.X, bounds.Value.Y + bounds.Value.Height), context);
         
-        previewSurface.DrawingSurface.Canvas.DrawSurface(Red.Value.DrawingSurface, redPos, context.ReplacingPaintWithOpacity);
-        previewSurface.DrawingSurface.Canvas.DrawSurface(Green.Value.DrawingSurface, greenPos, context.ReplacingPaintWithOpacity);
-        previewSurface.DrawingSurface.Canvas.DrawSurface(Blue.Value.DrawingSurface, bluePos, context.ReplacingPaintWithOpacity);
-        previewSurface.DrawingSurface.Canvas.DrawSurface(Alpha.Value.DrawingSurface, alphaPos, context.ReplacingPaintWithOpacity);
+        _paint.ColorFilter = Grayscale.Value ? _alphaGrayscaleFilter : _alphaFilter;
+        localBounds = new(bounds.Value.X + bounds.Value.Width / 2, bounds.Value.Y + bounds.Value.Height / 2, bounds.Value.Width / 2, bounds.Value.Height / 2);
+        PaintPreview(renderOn, localBounds, new VecD(bounds.Value.X + bounds.Value.Width, bounds.Value.Y + bounds.Value.Height), context);
         
-        return previewSurface;
+        renderOn.Canvas.Restore();
+
+        return true;
     }
 
-    private Texture GetImage(Texture image, ColorFilter filter, int id)
+    private void PaintPreview(DrawingSurface renderOn, RectD localBounds, VecD translation, RenderContext context)
     {
-        var imageSurface = RequestTexture(id, image.Size);
-
-        _paint.ColorFilter = filter;
-        imageSurface.DrawingSurface.Canvas.DrawSurface(image.DrawingSurface, 0, 0, _paint);
+        int saved = renderOn.Canvas.Save();
+        
+        renderOn.Canvas.ClipRect(localBounds);
+        renderOn.Canvas.SaveLayer(_paint, localBounds);
 
-        return imageSurface;
+        renderOn.Canvas.Scale(0.5f);
+        renderOn.Canvas.Translate((float)translation.X, (float)translation.Y);
+        
+        Image.Value.Paint(context, renderOn);
+        
+        renderOn.Canvas.RestoreToCount(saved);
     }
-
-
-    public override Node CreateCopy() => new SeparateChannelsNode();
 }

+ 4 - 5
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateColorNode.cs

@@ -1,9 +1,9 @@
 using PixiEditor.ChangeableDocument.Changeables.Graph.Context;
 using PixiEditor.ChangeableDocument.Enums;
 using PixiEditor.ChangeableDocument.Rendering;
-using PixiEditor.DrawingApi.Core;
-using PixiEditor.DrawingApi.Core.ColorsImpl;
-using PixiEditor.DrawingApi.Core.Shaders.Generation.Expressions;
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.ColorsImpl;
+using Drawie.Backend.Core.Shaders.Generation.Expressions;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.CombineSeparate;
 
@@ -43,9 +43,8 @@ public class SeparateColorNode : Node
         Color = CreateFuncInput<Half4>(nameof(Color), "COLOR", new Color());
     }
 
-    protected override Texture? OnExecute(RenderingContext context)
+    protected override void OnExecute(RenderContext context)
     {
-        return null;
     }
     
     private Half4 GetColor(FuncContext ctx) =>

+ 4 - 5
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateVecDNode.cs

@@ -1,8 +1,8 @@
 using PixiEditor.ChangeableDocument.Changeables.Graph.Context;
 using PixiEditor.ChangeableDocument.Rendering;
-using PixiEditor.DrawingApi.Core;
-using PixiEditor.DrawingApi.Core.Shaders.Generation.Expressions;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.Shaders.Generation.Expressions;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.CombineSeparate;
 
@@ -22,9 +22,8 @@ public class SeparateVecDNode : Node
         Vector = CreateFuncInput<Float2>("Vector", "VECTOR", VecD.Zero);
     }
 
-    protected override Texture? OnExecute(RenderingContext context)
+    protected override void OnExecute(RenderContext context)
     {
-        return null;
     }
 
 

+ 4 - 5
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateVecINode.cs

@@ -1,7 +1,7 @@
 using PixiEditor.ChangeableDocument.Rendering;
-using PixiEditor.DrawingApi.Core;
-using PixiEditor.DrawingApi.Core.Shaders.Generation.Expressions;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.Shaders.Generation.Expressions;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.CombineSeparate;
 
@@ -21,9 +21,8 @@ public class SeparateVecINode : Node
         Vector = CreateFuncInput<Int2>("Vector", "VECTOR", new VecI(0, 0));
     }
 
-    protected override Texture? OnExecute(RenderingContext context)
+    protected override void OnExecute(RenderContext context)
     {
-        return null;
     }
 
 

+ 34 - 18
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CreateImageNode.cs

@@ -1,46 +1,62 @@
-using PixiEditor.ChangeableDocument.Changeables.Animations;
-using PixiEditor.ChangeableDocument.Rendering;
-using PixiEditor.DrawingApi.Core;
-using PixiEditor.DrawingApi.Core.ColorsImpl;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
-using PixiEditor.Numerics;
+using PixiEditor.ChangeableDocument.Rendering;
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.Bridge;
+using Drawie.Backend.Core.ColorsImpl;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 
 [NodeInfo("CreateImage")]
 public class CreateImageNode : Node
 {
-    private Paint _paint = new();
-    
     public OutputProperty<Texture> Output { get; }
 
     public InputProperty<VecI> Size { get; }
-    
+
     public InputProperty<Color> Fill { get; }
 
+    public RenderInputProperty Content { get; }
+
+    public RenderOutputProperty RenderOutput { get; }
+
     public CreateImageNode()
     {
-        Output = CreateOutput<Texture>(nameof(Output), "EMPTY_IMAGE", null);
+        Output = CreateOutput<Texture>(nameof(Output), "IMAGE", null);
         Size = CreateInput(nameof(Size), "SIZE", new VecI(32, 32)).WithRules(v => v.Min(VecI.One));
-        Fill = CreateInput(nameof(Fill), "FILL", new Color(0, 0, 0, 255));
+        Fill = CreateInput(nameof(Fill), "FILL", Colors.Transparent);
+        Content = CreateRenderInput(nameof(Content), "CONTENT");
+        RenderOutput = CreateRenderOutput("RenderOutput", "RENDER_OUTPUT", () => new Painter(OnPaint));
     }
 
-    protected override Texture? OnExecute(RenderingContext context)
+    protected override void OnExecute(RenderContext context)
     {
         if (Size.Value.X <= 0 || Size.Value.Y <= 0)
         {
-            return null;
+            return;
         }
 
-        var surface = RequestTexture(0, Size.Value); 
+        var surface = RequestTexture(0, Size.Value, false);
+
+        surface.DrawingSurface.Canvas.Clear(Fill.Value);
+
+        int saved = surface.DrawingSurface.Canvas.Save();
 
-        _paint.Color = Fill.Value;
-        surface.DrawingSurface.Canvas.DrawPaint(_paint);
+        RenderContext ctx = new RenderContext(surface.DrawingSurface, context.FrameTime, context.ChunkResolution,
+            context.DocumentSize);
 
+        Content.Value?.Paint(ctx, surface.DrawingSurface);
+
+        surface.DrawingSurface.Canvas.RestoreToCount(saved);
         Output.Value = surface;
 
-        return Output.Value;
+        RenderOutput.ChainToPainterValue();
     }
- 
+
+    private void OnPaint(RenderContext context, DrawingSurface surface)
+    {
+        surface.Canvas.DrawSurface(Output.Value.DrawingSurface, 0, 0);
+    }
+
     public override Node CreateCopy() => new CreateImageNode();
 }

+ 14 - 9
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/DebugBlendModeNode.cs

@@ -1,8 +1,8 @@
 using PixiEditor.ChangeableDocument.Rendering;
-using PixiEditor.DrawingApi.Core;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
-using DrawingApiBlendMode = PixiEditor.DrawingApi.Core.Surfaces.BlendMode;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using DrawingApiBlendMode = Drawie.Backend.Core.Surfaces.BlendMode;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 
@@ -20,6 +20,7 @@ public class DebugBlendModeNode : Node
 
     public OutputProperty<Texture> Result { get; }
 
+    private Paint blendModeOpacityPaint => new() { BlendMode = DrawingApiBlendMode.SrcOver }; 
     public DebugBlendModeNode()
     {
         Dst = CreateInput<Texture?>(nameof(Dst), "Dst", null);
@@ -29,24 +30,28 @@ public class DebugBlendModeNode : Node
         Result = CreateOutput<Texture>(nameof(Result), "Result", null);
     }
 
-    protected override Texture? OnExecute(RenderingContext context)
+    protected override void OnExecute(RenderContext context)
     {
         if (Dst.Value is not { } dst || Src.Value is not { } src)
-            return null;
+            return;
 
         var size = new VecI(Math.Max(src.Size.X, dst.Size.X), int.Max(src.Size.Y, dst.Size.Y));
         var workingSurface = RequestTexture(0, size);
 
-        workingSurface.DrawingSurface.Canvas.DrawSurface(dst.DrawingSurface, 0, 0, context.BlendModeOpacityPaint);
+        workingSurface.DrawingSurface.Canvas.DrawSurface(dst.DrawingSurface, 0, 0, blendModeOpacityPaint);
 
         _paint.BlendMode = BlendMode.Value;
         workingSurface.DrawingSurface.Canvas.DrawSurface(src.DrawingSurface, 0, 0, _paint);
         
         Result.Value = workingSurface;
-
-        return workingSurface;
     }
 
 
     public override Node CreateCopy() => new DebugBlendModeNode();
+
+    public override void Dispose()
+    {
+        base.Dispose();
+        _paint.Dispose();
+    }
 }

+ 38 - 25
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/ApplyFilterNode.cs

@@ -1,46 +1,59 @@
-using PixiEditor.ChangeableDocument.Helpers;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+using PixiEditor.ChangeableDocument.Helpers;
 using PixiEditor.ChangeableDocument.Rendering;
-using PixiEditor.DrawingApi.Core;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.FilterNodes;
 
 [NodeInfo("ApplyFilter")]
-public class ApplyFilterNode : Node
+public class ApplyFilterNode : RenderNode, IRenderInput
 {
     private Paint _paint = new();
-    
-    
-    public OutputProperty<Texture?> Output { get; }
-
-    public InputProperty<Texture?> Input { get; }
-    
     public InputProperty<Filter?> Filter { get; }
-    
-    private Texture _workingSurface;
+
+    public RenderInputProperty Background { get; }
 
     public ApplyFilterNode()
     {
-        Output = CreateOutput<Texture>(nameof(Output), "IMAGE", null);
-        Input = CreateInput<Texture>(nameof(Input), "IMAGE", null);
-        Filter = CreateInput<Filter>(nameof(Filter), "FILTER", null);
+        Background = CreateRenderInput("Input", "IMAGE");
+        Filter = CreateInput<Filter>("Filter", "FILTER", null);
+        Output.FirstInChain = null;
     }
-    
-    protected override Texture? OnExecute(RenderingContext context)
+
+    protected override void OnPaint(RenderContext context, DrawingSurface surface)
     {
-        if (Input.Value is not { } input)
-        {
-            return null;
-        }
+        if (Background.Value == null || Filter.Value == null)
+            return;
         
         _paint.SetFilters(Filter.Value);
+        var layer = surface.Canvas.SaveLayer(_paint);
         
-        _workingSurface = RequestTexture(0, input.Size, true);
+        Background.Value.Paint(context, surface);
+        
+        surface.Canvas.RestoreToCount(layer);
+    }
+
+    public override RectD? GetPreviewBounds(int frame, string elementToRenderName = "")
+    {
+        return PreviewUtils.FindPreviewBounds(Background.Connection, frame, elementToRenderName); 
+    }
+
+    public override bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame,
+        string elementToRenderName)
+    {
+        if(Background.Value == null)
+            return false;
+
+        RenderContext context = new(renderOn, frame, ChunkResolution.Full, VecI.One);
         
-        _workingSurface.DrawingSurface.Canvas.DrawSurface(input.DrawingSurface, 0, 0, _paint);
+        int layer = renderOn.Canvas.SaveLayer(_paint);
+        Background.Value.Paint(context, renderOn);
+        renderOn.Canvas.RestoreToCount(layer);
 
-        Output.Value = _workingSurface;
-        return _workingSurface;
+        return true;
     }
 
     public override Node CreateCopy() => new ApplyFilterNode();

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/ColorMatrixFilterNode.cs

@@ -1,5 +1,5 @@
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.FilterNodes;
 

+ 4 - 6
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/FilterNode.cs

@@ -1,6 +1,6 @@
 using PixiEditor.ChangeableDocument.Rendering;
-using PixiEditor.DrawingApi.Core;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.FilterNodes;
 
@@ -16,7 +16,7 @@ public abstract class FilterNode : Node
         Input = CreateInput<Filter>(nameof(Input), "PREVIOUS", null);
     }
     
-    protected override Texture? OnExecute(RenderingContext context)
+    protected override void OnExecute(RenderContext context)
     {
         var colorFilter = GetColorFilter();
         var imageFilter = GetImageFilter();
@@ -24,14 +24,12 @@ public abstract class FilterNode : Node
         if (colorFilter == null && imageFilter == null)
         {
             Output.Value = Input.Value;
-            return null;
+            return;
         }
 
         var filter = Input.Value;
 
         Output.Value = filter == null ? new Filter(colorFilter, imageFilter) : filter.Add(colorFilter, imageFilter);
-        
-        return null;
     }
 
     protected virtual ColorFilter? GetColorFilter() => null;

+ 7 - 7
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/GrayscaleNode.cs

@@ -1,5 +1,5 @@
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.FilterNodes;
 
@@ -16,12 +16,12 @@ public class GrayscaleNode : FilterNode
     public InputProperty<bool> Normalize { get; }
 
     // TODO: Hide when Mode != Custom
-    public InputProperty<VecD3> CustomWeight { get; }
+    public InputProperty<Vec3D> CustomWeight { get; }
     
     private GrayscaleMode lastMode;
     private double lastFactor;
     private bool lastNormalize;
-    private VecD3 lastCustomWeight;
+    private Vec3D lastCustomWeight;
     
     private ColorFilter? filter;
     
@@ -31,7 +31,7 @@ public class GrayscaleNode : FilterNode
         // TODO: Clamp 0 - 1 in UI
         Factor = CreateInput("Factor", "FACTOR", 1d);
         Normalize = CreateInput("Normalize", "NORMALIZE", true);
-        CustomWeight = CreateInput("CustomWeight", "WEIGHT_FACTOR", new VecD3(1, 1, 1));
+        CustomWeight = CreateInput("CustomWeight", "WEIGHT_FACTOR", new Vec3D(1, 1, 1));
     }
 
     protected override ColorFilter GetColorFilter()
@@ -74,7 +74,7 @@ public class GrayscaleNode : FilterNode
         };
     }
 
-    private VecD3 GetAdjustedCustomWeight()
+    private Vec3D GetAdjustedCustomWeight()
     {
         var weight = CustomWeight.Value;
         var normalize = Normalize.Value;
@@ -88,7 +88,7 @@ public class GrayscaleNode : FilterNode
 
         if (sum == 0)
         {
-            return VecD3.Zero;
+            return Vec3D.Zero;
         }
             
         return weight / weight.Sum();

+ 4 - 4
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/KernelFilterNode.cs

@@ -1,6 +1,6 @@
-using PixiEditor.DrawingApi.Core.Surfaces;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.FilterNodes;
 
@@ -27,7 +27,7 @@ public class KernelFilterNode : FilterNode
 
     public KernelFilterNode()
     {
-        Kernel = CreateInput(nameof(Kernel), "KERNEL", Numerics.Kernel.Identity(3, 3));
+        Kernel = CreateInput(nameof(Kernel), "KERNEL", Drawie.Numerics.Kernel.Identity(3, 3));
         Gain = CreateInput(nameof(Gain), "GAIN", 1d);
         Bias = CreateInput(nameof(Bias), "BIAS", 0d);
         Tile = CreateInput(nameof(Tile), "TILE_MODE", TileMode.Clamp);

+ 4 - 2
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Filters.cs

@@ -1,5 +1,5 @@
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 
@@ -37,4 +37,6 @@ public static class Filters
     /// </summary>
     public static readonly ColorFilter AverageGrayscaleFilter =
         ColorFilter.CreateColorMatrix(ColorMatrix.AverageGrayscale + ColorMatrix.OpaqueAlphaOffset);
+
+    public static ColorFilter MaskFilter => ColorFilter.CreateColorMatrix(ColorMatrix.WeightedWavelengthAlphaGrayscale);
 }

+ 140 - 69
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FolderNode.cs

@@ -1,111 +1,130 @@
 using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Rendering;
-using PixiEditor.DrawingApi.Core;
-using PixiEditor.DrawingApi.Core.ColorsImpl;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.ColorsImpl;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 
 [NodeInfo("Folder")]
-public class FolderNode : StructureNode, IReadOnlyFolderNode
+public class FolderNode : StructureNode, IReadOnlyFolderNode, IClipSource, IPreviewRenderable
 {
-    public InputProperty<Texture?> Content { get; }
+    private VecI documentSize;
+    public RenderInputProperty Content { get; }
 
     public FolderNode()
     {
-        Content = CreateInput<Texture?>("Content", "CONTENT", null);
+        Content = CreateRenderInput("Content", "CONTENT");
     }
 
     public override Node CreateCopy() => new FolderNode { MemberName = MemberName };
 
+    public override VecD GetScenePosition(KeyFrameTime time) =>
+        documentSize / 2f; //GetTightBounds(time).GetValueOrDefault().Center;
 
-    protected override Texture? OnExecute(RenderingContext context)
+    public override VecD GetSceneSize(KeyFrameTime time) =>
+        documentSize; //GetTightBounds(time).GetValueOrDefault().Size;
+
+    protected override void OnExecute(RenderContext context)
+    {
+        base.OnExecute(context);
+        documentSize = context.DocumentSize;
+    }
+
+    public override void Render(SceneObjectRenderContext sceneContext)
     {
-        if(Background.Value == null && Content.Value == null)
-        {
-            Output.Value = null;
-            return null;
-        }
-        
         if (!IsVisible.Value || Opacity.Value <= 0 || IsEmptyMask())
         {
             Output.Value = Background.Value;
-            return Output.Value;
-        }
-        
-        blendPaint.Color = new Color(255, 255, 255, 255);
-        blendPaint.BlendMode = DrawingApi.Core.Surfaces.BlendMode.Src;
-
-        VecI size = Content.Value?.Size ?? Background.Value?.Size ?? VecI.Zero;
-        
-        var outputWorkingSurface = RequestTexture(0, size); 
-        var filterlessWorkingSurface = RequestTexture(1, size); 
-        
-        if (Background.Value != null)
-        {
-            DrawBackground(filterlessWorkingSurface, context);
-            blendPaint.BlendMode = RenderingContext.GetDrawingBlendMode(BlendMode.Value);
+            return;
         }
 
-        if (Content.Value != null)
+        if (Content.Connection == null || (!HasOperations() && BlendMode.Value == Enums.BlendMode.Normal))
         {
-            blendPaint.Color = blendPaint.Color.WithAlpha((byte)Math.Round(Opacity.Value * 255)); 
-            DrawSurface(filterlessWorkingSurface, Content.Value, context, null);
+            using Paint paint = new();
+            paint.Color = Colors.White.WithAlpha((byte)Math.Round(Opacity.Value * 255f));
+
+            if (sceneContext.TargetPropertyOutput == Output)
+            {
+                paint.ColorFilter = Filters.Value?.ColorFilter;
+                paint.ImageFilter = Filters.Value?.ImageFilter;
+            }
+
+            int saved = sceneContext.RenderSurface.Canvas.SaveLayer(paint);
+            Content.Value?.Paint(sceneContext, sceneContext.RenderSurface);
+
+            sceneContext.RenderSurface.Canvas.RestoreToCount(saved);
+            return;
         }
 
-        FilterlessOutput.Value = filterlessWorkingSurface;
+        RectD bounds = RectD.Create(VecI.Zero, sceneContext.DocumentSize);
 
-        if (!HasOperations())
+        if (sceneContext.TargetPropertyOutput == Output)
         {
             if (Background.Value != null)
             {
-                blendPaint.Color = new Color(255, 255, 255, 255);
-                blendPaint.BlendMode = DrawingApi.Core.Surfaces.BlendMode.Src;
-                DrawBackground(outputWorkingSurface, context);
-                blendPaint.BlendMode = RenderingContext.GetDrawingBlendMode(BlendMode.Value);
+                blendPaint.BlendMode = RenderContext.GetDrawingBlendMode(BlendMode.Value);
             }
-            
-            if (Content.Value != null)
+
+            RenderFolderContent(sceneContext, bounds, true);
+        }
+        else if (sceneContext.TargetPropertyOutput == FilterlessOutput ||
+                 sceneContext.TargetPropertyOutput == RawOutput)
+        {
+            RenderFolderContent(sceneContext, bounds, false);
+        }
+    }
+
+    private void RenderFolderContent(SceneObjectRenderContext sceneContext, RectD bounds, bool useFilters)
+    {
+        VecI size = (VecI)bounds.Size;
+        var outputWorkingSurface = RequestTexture(0, size, true);
+
+        blendPaint.ImageFilter = null;
+        blendPaint.ColorFilter = null;
+
+        Content.Value?.Paint(sceneContext, outputWorkingSurface.DrawingSurface);
+
+        ApplyMaskIfPresent(outputWorkingSurface.DrawingSurface, sceneContext);
+
+        if (Background.Value != null && sceneContext.TargetPropertyOutput != RawOutput)
+        {
+            Texture tempSurface = RequestTexture(1, outputWorkingSurface.Size);
+            if (Background.Connection.Node is IClipSource clipSource && ClipToPreviousMember)
             {
-                blendPaint.Color = blendPaint.Color.WithAlpha((byte)Math.Round(Opacity.Value * 255)); 
-                DrawSurface(outputWorkingSurface, Content.Value, context, Filters.Value);
+                DrawClipSource(tempSurface.DrawingSurface, clipSource, sceneContext);
             }
-            
-            Output.Value = outputWorkingSurface;
-            return Output.Value;
+
+            ApplyRasterClip(outputWorkingSurface.DrawingSurface, tempSurface.DrawingSurface);
         }
-        
-        if (Content.Value != null)
+
+        AdjustPaint(useFilters);
+
+        sceneContext.RenderSurface.Canvas.DrawSurface(outputWorkingSurface.DrawingSurface, 0, 0, blendPaint);
+    }
+
+    private void AdjustPaint(bool useFilters)
+    {
+        blendPaint.Color = Colors.White.WithAlpha((byte)Math.Round(Opacity.Value * 255f));
+        if (useFilters)
         {
-            DrawSurface(outputWorkingSurface, Content.Value, context, Filters.Value);
-            
-            ApplyMaskIfPresent(outputWorkingSurface, context);
+            blendPaint.ColorFilter = Filters.Value?.ColorFilter;
+            blendPaint.ImageFilter = Filters.Value?.ImageFilter;
         }
-        
-        if (Background.Value != null)
+        else
         {
-            Texture tempSurface = RequestTexture(2, outputWorkingSurface.Size);
-            DrawBackground(tempSurface, context);
-            
-            ApplyRasterClip(outputWorkingSurface, tempSurface);
-            
-            blendPaint.Color = blendPaint.Color.WithAlpha((byte)Math.Round(Opacity.Value * 255));
-            blendPaint.BlendMode = RenderingContext.GetDrawingBlendMode(BlendMode.Value);
-            tempSurface.DrawingSurface.Canvas.DrawSurface(outputWorkingSurface.DrawingSurface, 0, 0, blendPaint);
-
-            Output.Value = tempSurface;
-            return tempSurface;
+            blendPaint.ColorFilter = null;
+            blendPaint.ImageFilter = null;
         }
-
-        Output.Value = outputWorkingSurface;
-        return Output.Value;
     }
 
     public override RectD? GetTightBounds(KeyFrameTime frameTime)
     {
         RectI bounds = new RectI();
-        if(Content.Connection != null)
+        if (Content.Connection != null)
         {
             Content.Connection.Node.TraverseBackwards((n) =>
             {
@@ -120,11 +139,11 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode
 
                 return true;
             });
-            
+
             return (RectD)bounds;
         }
-        
-        return (RectD)RectI.Create(0, 0, Content.Value?.Size.X ?? 0, Content.Value?.Size.Y ?? 0);
+
+        return null;
     }
 
     public HashSet<Guid> GetLayerNodeGuids()
@@ -168,4 +187,56 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode
             MaskIsVisible = MaskIsVisible
         };
     }*/
+    public void DrawOnTexture(SceneObjectRenderContext context, DrawingSurface drawOnto)
+    {
+        if (Content.Connection != null)
+        {
+            var executionQueue = GraphUtils.CalculateExecutionQueue(Content.Connection.Node);
+
+            while (executionQueue.Count > 0)
+            {
+                IReadOnlyNode node = executionQueue.Dequeue();
+                if (node is IClipSource clipSource)
+                {
+                    clipSource.DrawOnTexture(context, drawOnto);
+                }
+            }
+        }
+    }
+
+    public override RectD? GetPreviewBounds(int frame, string elementFor = "")
+    {
+        if (elementFor == nameof(EmbeddedMask))
+        {
+            return base.GetPreviewBounds(frame, elementFor);
+        }
+
+        return GetTightBounds(frame);
+    }
+
+    public override bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame,
+        string elementToRenderName)
+    {
+        if (elementToRenderName == nameof(EmbeddedMask))
+        {
+            return base.RenderPreview(renderOn, resolution, frame, elementToRenderName);
+        }
+
+        // TODO: Make preview better, with filters, clips and stuff
+
+        if (Content.Connection != null)
+        {
+            var executionQueue = GraphUtils.CalculateExecutionQueue(Content.Connection.Node);
+            while (executionQueue.Count > 0)
+            {
+                IReadOnlyNode node = executionQueue.Dequeue();
+                if (node is IPreviewRenderable previewRenderable)
+                {
+                    previewRenderable.RenderPreview(renderOn, resolution, frame, elementToRenderName);
+                }
+            }
+        }
+
+        return true;
+    }
 }

+ 109 - 144
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageLayerNode.cs

@@ -3,10 +3,11 @@ using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using PixiEditor.ChangeableDocument.Helpers;
 using PixiEditor.ChangeableDocument.Rendering;
-using PixiEditor.DrawingApi.Core;
-using PixiEditor.DrawingApi.Core.ColorsImpl;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.ColorsImpl;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 
@@ -15,214 +16,169 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
 {
     public const string ImageFramesKey = "Frames";
     public const string ImageLayerKey = "LayerImage";
-    public OutputProperty<Texture> RawOutput { get; }
+
+    public override VecD GetScenePosition(KeyFrameTime time) => layerImage.CommittedSize / 2f;
+    public override VecD GetSceneSize(KeyFrameTime time) => layerImage.CommittedSize;
 
     public bool LockTransparency { get; set; }
 
-    private VecI size;
+    private VecI startSize;
     private ChunkyImage layerImage => keyFrames[0]?.Data as ChunkyImage;
 
-
-    protected Dictionary<(ChunkResolution, int), Texture> workingSurfaces =
-        new Dictionary<(ChunkResolution, int), Texture>();
-
-    private static readonly Paint clearPaint = new()
-    {
-        BlendMode = DrawingApi.Core.Surfaces.BlendMode.Src,
-        Color = PixiEditor.DrawingApi.Core.ColorsImpl.Colors.Transparent
-    };
-
-    // Handled by overriden CacheChanged
-    protected override bool AffectedByAnimation => true;
-
-    protected override bool AffectedByChunkResolution => true;
-
-    protected override bool AffectedByChunkToUpdate => true;
+    private Texture fullResrenderedSurface;
+    private int renderedSurfaceFrame = -1;
 
     public ImageLayerNode(VecI size)
     {
-        RawOutput = CreateOutput<Texture>(nameof(RawOutput), "RAW_LAYER_OUTPUT", null);
-
         if (keyFrames.Count == 0)
         {
             keyFrames.Add(new KeyFrameData(Guid.NewGuid(), 0, 0, ImageLayerKey) { Data = new ChunkyImage(size) });
         }
 
-        this.size = size;
+        this.startSize = size;
     }
 
-
     public override RectD? GetTightBounds(KeyFrameTime frameTime)
     {
         return (RectD?)GetLayerImageAtFrame(frameTime.Frame).FindTightCommittedBounds();
     }
 
-    protected override Texture? OnExecute(RenderingContext context)
+    protected override VecI GetTargetSize(RenderContext ctx)
     {
-        var rendered = base.OnExecute(context);
-
-        if (RawOutput.Connections.Count > 0)
-        {
-            var rawWorkingSurface = TryInitWorkingSurface(GetTargetSize(context), context, 2);
-            DrawLayer(context, rawWorkingSurface, true, useFilters: false);
+        return (GetFrameWithImage(ctx.FrameTime).Data as ChunkyImage).LatestSize;
+    }
 
-            RawOutput.Value = rawWorkingSurface;
-        }
+    protected internal override void DrawLayerInScene(SceneObjectRenderContext ctx, DrawingSurface workingSurface,
+        bool useFilters = true)
+    {
+        int scaled = workingSurface.Canvas.Save();
+        float multiplier = (float)ctx.ChunkResolution.InvertedMultiplier();
+        workingSurface.Canvas.Translate(GetScenePosition(ctx.FrameTime));
+        base.DrawLayerInScene(ctx, workingSurface, useFilters);
 
-        return rendered;
+        workingSurface.Canvas.RestoreToCount(scaled);
     }
 
-    protected override VecI GetTargetSize(RenderingContext ctx)
+    protected internal override void DrawLayerOnTexture(SceneObjectRenderContext ctx, DrawingSurface workingSurface,
+        bool useFilters)
     {
-        return (GetFrameWithImage(ctx.FrameTime).Data as ChunkyImage).LatestSize;
+        int scaled = workingSurface.Canvas.Save();
+        workingSurface.Canvas.Translate(GetScenePosition(ctx.FrameTime) * ctx.ChunkResolution.Multiplier());
+        workingSurface.Canvas.Scale((float)ctx.ChunkResolution.Multiplier());
+
+        DrawLayerOnto(ctx, workingSurface, useFilters);
+
+        workingSurface.Canvas.RestoreToCount(scaled);
     }
 
-    protected override void DrawWithoutFilters(RenderingContext ctx, Texture workingSurface, bool shouldClear,
+    protected override void DrawWithoutFilters(SceneObjectRenderContext ctx, DrawingSurface workingSurface,
         Paint paint)
     {
-        var frameImage = GetFrameWithImage(ctx.FrameTime).Data as ChunkyImage;
-        if (!frameImage.DrawMostUpToDateChunkOn(
-                ctx.ChunkToUpdate,
-                ctx.ChunkResolution,
-                workingSurface.DrawingSurface,
-                ctx.ChunkToUpdate * ctx.ChunkResolution.PixelSize(),
-                blendPaint) && shouldClear)
-        {
-            workingSurface.DrawingSurface.Canvas.DrawRect(CalculateDestinationRect(ctx), clearPaint);
-        }
+        DrawLayer(workingSurface, paint, ctx);
     }
 
-    // Draw with filters is a bit tricky since some filters sample data from chunks surrounding the chunk being drawn,
-    // this is why we need to do intermediate drawing to a temporary surface and then apply filters to that surface
-    protected override void DrawWithFilters(RenderingContext context, Texture workingSurface,
-        bool shouldClear, Paint paint)
+    protected override void DrawWithFilters(SceneObjectRenderContext context, DrawingSurface workingSurface,
+        Paint paint)
     {
-        var frameImage = GetFrameWithImage(context.FrameTime).Data as ChunkyImage;
-        
-        VecI imageChunksSize = frameImage.LatestSize / context.ChunkResolution.PixelSize();
-        bool requiresTopLeft = context.ChunkToUpdate.X > 0 || context.ChunkToUpdate.Y > 0;
-        bool requiresTop = context.ChunkToUpdate.Y > 0;
-        bool requiresLeft = context.ChunkToUpdate.X > 0;
-        bool requiresTopRight = context.ChunkToUpdate.X < imageChunksSize.X - 1 && context.ChunkToUpdate.Y > 0;
-        bool requiresRight = context.ChunkToUpdate.X < imageChunksSize.X - 1;
-        bool requiresBottomRight = context.ChunkToUpdate.X < imageChunksSize.X - 1 &&
-                                   context.ChunkToUpdate.Y < imageChunksSize.Y - 1;
-        bool requiresBottom = context.ChunkToUpdate.Y < imageChunksSize.Y - 1;
-        bool requiresBottomLeft = context.ChunkToUpdate.X > 0 && context.ChunkToUpdate.Y < imageChunksSize.Y - 1;
-
-        VecI tempSizeInChunks = new VecI(1, 1);
-        if (requiresLeft)
-        {
-            tempSizeInChunks.X++;
-        }
+        DrawLayer(workingSurface, paint, context);
+    }
 
-        if (requiresRight)
-        {
-            tempSizeInChunks.X++;
-        }
+    private void DrawLayer(DrawingSurface workingSurface, Paint paint, SceneObjectRenderContext ctx)
+    {
+        int saved = workingSurface.Canvas.Save();
 
-        if (requiresTop)
+        var sceneSize = GetSceneSize(ctx.FrameTime);
+        VecD topLeft = sceneSize / 2f;
+        if (renderedSurfaceFrame == null || ctx.FullRerender || ctx.FrameTime.Frame != renderedSurfaceFrame)
         {
-            tempSizeInChunks.Y++;
+            GetLayerImageAtFrame(ctx.FrameTime.Frame).DrawMostUpToDateRegionOn(
+                new RectI(0, 0, layerImage.LatestSize.X, layerImage.LatestSize.Y),
+                ChunkResolution.Full,
+                workingSurface, -(VecI)topLeft, paint);
         }
-
-        if (requiresBottom)
+        else
         {
-            tempSizeInChunks.Y++;
+            workingSurface.Canvas.DrawSurface(fullResrenderedSurface.DrawingSurface, -(VecI)topLeft, paint);
         }
 
-        VecI tempSize = tempSizeInChunks * context.ChunkResolution.PixelSize();
-        tempSize = new VecI(Math.Min(tempSize.X, workingSurface.Size.X), Math.Min(tempSize.Y, workingSurface.Size.Y));
+        workingSurface.Canvas.RestoreToCount(saved);
+    }
 
-        if (shouldClear)
+    public override RectD? GetPreviewBounds(int frame, string elementFor = "")
+    {
+        if (IsDisposed)
         {
-            workingSurface.DrawingSurface.Canvas.DrawRect(
-                new RectI(
-                    VecI.Zero,
-                    tempSize),
-                clearPaint);
+            return null;
         }
 
-        using Texture tempSurface = new Texture(tempSize);
-
-        if (requiresTopLeft)
+        if (elementFor == nameof(EmbeddedMask))
         {
-            DrawChunk(frameImage, context, tempSurface, new VecI(-1, -1), paint);
+            return base.GetPreviewBounds(frame, elementFor);
         }
 
-        if (requiresTop)
+        if (Guid.TryParse(elementFor, out Guid guid))
         {
-            DrawChunk(frameImage, context, tempSurface, new VecI(0, -1), paint);
-        }
+            var keyFrame = keyFrames.FirstOrDefault(x => x.KeyFrameGuid == guid);
 
-        if (requiresLeft)
-        {
-            DrawChunk(frameImage, context, tempSurface, new VecI(-1, 0), paint);
+            if (keyFrame != null)
+            {
+                return (RectD?)GetLayerImageByKeyFrameGuid(keyFrame.KeyFrameGuid).FindTightCommittedBounds();
+            }
         }
 
-        if (requiresTopRight)
+        try
         {
-            DrawChunk(frameImage, context, tempSurface, new VecI(1, -1), paint);
+            return (RectD?)GetLayerImageAtFrame(frame).FindTightCommittedBounds();
         }
-
-        if (requiresRight)
+        catch (ObjectDisposedException)
         {
-            DrawChunk(frameImage, context, tempSurface, new VecI(1, 0), paint);
+            return null;
         }
+    }
 
-        if (requiresBottomRight)
+    public override bool RenderPreview(DrawingSurface renderOnto, ChunkResolution resolution, int frame,
+        string elementToRenderName)
+    {
+        if (IsDisposed)
         {
-            DrawChunk(frameImage, context, tempSurface, new VecI(1, 1), paint);
+            return false;
         }
 
-        if (requiresBottom)
+        if (elementToRenderName == nameof(EmbeddedMask))
         {
-            DrawChunk(frameImage, context, tempSurface, new VecI(0, 1), paint);
+            return base.RenderPreview(renderOnto, resolution, frame, elementToRenderName);
         }
 
-        if (requiresBottomLeft)
-        {
-            DrawChunk(frameImage, context, tempSurface, new VecI(-1, 1), paint);
-        }
+        var img = GetLayerImageAtFrame(frame);
 
-        DrawChunk(frameImage, context, tempSurface, new VecI(0, 0), paint);
+        if (Guid.TryParse(elementToRenderName, out Guid guid))
+        {
+            var keyFrame = keyFrames.FirstOrDefault(x => x.KeyFrameGuid == guid);
 
-        workingSurface.DrawingSurface.Canvas.DrawSurface(tempSurface.DrawingSurface, VecI.Zero, paint);
-    }
+            if (keyFrame != null)
+            {
+                img = GetLayerImageByKeyFrameGuid(keyFrame.KeyFrameGuid);
+            }
+        }
 
-    public override bool RenderPreview(Texture renderOn, VecI chunk, ChunkResolution resolution, int frame)
-    {
-        var img = GetLayerImageAtFrame(frame);
-        
         if (img is null)
         {
             return false;
         }
-        
-        img.DrawMostUpToDateChunkOn(
-            chunk,
-            resolution,
-            renderOn.DrawingSurface,
-            chunk * resolution.PixelSize(),
-            blendPaint);
-        
-        return true;
-    }
-
 
-    private void DrawChunk(ChunkyImage frameImage, RenderingContext context, Texture tempSurface, VecI vecI,
-        Paint paint)
-    {
-        VecI chunkPos = context.ChunkToUpdate + vecI;
-        if (frameImage.LatestOrCommittedChunkExists(chunkPos))
+        if (renderedSurfaceFrame == frame)
+        {
+            renderOnto.Canvas.DrawSurface(fullResrenderedSurface.DrawingSurface, VecI.Zero, blendPaint);
+        }
+        else
         {
-            frameImage.DrawMostUpToDateChunkOn(
-                chunkPos,
-                context.ChunkResolution,
-                tempSurface.DrawingSurface,
-                chunkPos * context.ChunkResolution.PixelSize(),
-                paint);
+            img.DrawMostUpToDateRegionOn(
+                new RectI(0, 0, img.LatestSize.X, img.LatestSize.Y),
+                resolution,
+                renderOnto, VecI.Zero, blendPaint);
         }
+
+        return true;
     }
 
     private KeyFrameData GetFrameWithImage(KeyFrameTime frame)
@@ -237,14 +193,14 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         return frameImage;
     }
 
-    protected override bool CacheChanged(RenderingContext context)
+    protected override bool CacheChanged(RenderContext context)
     {
         var frame = GetFrameWithImage(context.FrameTime);
 
         return base.CacheChanged(context) || frame?.RequiresUpdate == true;
     }
 
-    protected override void UpdateCache(RenderingContext context)
+    protected override void UpdateCache(RenderContext context)
     {
         base.UpdateCache(context);
         var imageFrame = GetFrameWithImage(context.FrameTime);
@@ -256,7 +212,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
 
     public override Node CreateCopy()
     {
-        var image = new ImageLayerNode(size) { MemberName = this.MemberName, };
+        var image = new ImageLayerNode(startSize) { MemberName = this.MemberName, };
 
         image.keyFrames.Clear();
 
@@ -273,7 +229,6 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         }
     }
 
-
     IReadOnlyChunkyImage IReadOnlyImageNode.GetLayerImageAtFrame(int frame) => GetLayerImageAtFrame(frame);
 
     IReadOnlyChunkyImage IReadOnlyImageNode.GetLayerImageByKeyFrameGuid(Guid keyFrameGuid) =>
@@ -284,6 +239,16 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
 
     void IReadOnlyImageNode.ForEveryFrame(Action<IReadOnlyChunkyImage> action) => ForEveryFrame(action);
 
+    public override void RenderChunk(VecI chunkPos, ChunkResolution resolution, KeyFrameTime frameTime)
+    {
+        base.RenderChunk(chunkPos, resolution, frameTime);
+
+        var img = GetLayerImageAtFrame(frameTime.Frame);
+
+        RenderChunkyImageChunk(chunkPos, resolution, img, 85, ref fullResrenderedSurface);
+        renderedSurfaceFrame = frameTime.Frame;
+    }
+
     public void ForEveryFrame(Action<ChunkyImage> action)
     {
         foreach (var frame in keyFrames)

+ 78 - 70
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/LayerNode.cs

@@ -1,128 +1,133 @@
-using PixiEditor.ChangeableDocument.Changeables.Animations;
-using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Helpers;
 using PixiEditor.ChangeableDocument.Rendering;
-using PixiEditor.DrawingApi.Core;
-using PixiEditor.DrawingApi.Core.ColorsImpl;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
-using PixiEditor.Numerics;
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.ColorsImpl;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 
-public abstract class LayerNode : StructureNode, IReadOnlyLayerNode
+public abstract class LayerNode : StructureNode, IReadOnlyLayerNode, IClipSource
 {
     protected Dictionary<(ChunkResolution, int), Texture> workingSurfaces =
         new Dictionary<(ChunkResolution, int), Texture>();
 
-    protected override Texture? OnExecute(RenderingContext context)
+    public LayerNode()
+    {
+    }
+
+    public override void Render(SceneObjectRenderContext sceneContext)
     {
         if (!IsVisible.Value || Opacity.Value <= 0 || IsEmptyMask())
         {
-            Output.Value = Background.Value;
-            return Output.Value;
+            Output.Value = Background.Value; 
+            return;
         }
 
         blendPaint.Color = new Color(255, 255, 255, 255);
-        blendPaint.BlendMode = DrawingApi.Core.Surfaces.BlendMode.Src;
+        blendPaint.BlendMode = Drawie.Backend.Core.Surfaces.BlendMode.SrcOver;
 
-        VecI targetSize = GetTargetSize(context);
-        bool shouldClear = Background.Value == null;
+        VecI targetSize = GetTargetSize(sceneContext);
 
-        if (FilterlessOutput.Connections.Count > 0)
-        {
-            var filterlessWorkingSurface = TryInitWorkingSurface(targetSize, context, 0);
+        RenderContent(targetSize, sceneContext, sceneContext.RenderSurface,
+            sceneContext.TargetPropertyOutput != FilterlessOutput);
+    }
 
+    private void RenderContent(VecI size, SceneObjectRenderContext context, DrawingSurface renderOnto, bool useFilters)
+    {
+        if (!HasOperations())
+        {
             if (Background.Value != null)
             {
-                DrawBackground(filterlessWorkingSurface, context);
-                blendPaint.BlendMode = RenderingContext.GetDrawingBlendMode(BlendMode.Value);
+                blendPaint.BlendMode = RenderContext.GetDrawingBlendMode(BlendMode.Value);
             }
 
-            DrawLayer(context, filterlessWorkingSurface, shouldClear, useFilters: false);
-            blendPaint.BlendMode = DrawingApi.Core.Surfaces.BlendMode.Src;
-
-            FilterlessOutput.Value = filterlessWorkingSurface;
+            DrawLayerInScene(context, renderOnto, useFilters);
+            return;
         }
 
-        var rendered = RenderImage(targetSize, context, shouldClear);
-        Output.Value = rendered;
+        var outputWorkingSurface = TryInitWorkingSurface(size, context.ChunkResolution, 1);
+        outputWorkingSurface.DrawingSurface.Canvas.Clear();
 
-        return rendered;
-    }
+        DrawLayerOnTexture(context, outputWorkingSurface.DrawingSurface, useFilters);
 
-    private Texture RenderImage(VecI size, RenderingContext context, bool shouldClear)
-    {
-        if (Output.Connections.Count > 0)
-        {
-            var outputWorkingSurface = TryInitWorkingSurface(size, context, 1);
+        ApplyMaskIfPresent(outputWorkingSurface.DrawingSurface, context);
 
-            if (!HasOperations())
+        if (Background.Value != null)
+        {
+            Texture tempSurface = TryInitWorkingSurface(size, context.ChunkResolution, 4);
+            tempSurface.DrawingSurface.Canvas.Clear();
+            if (Background.Connection is { Node: IClipSource clipSource } && ClipToPreviousMember)
             {
-                if (Background.Value != null)
-                {
-                    DrawBackground(outputWorkingSurface, context);
-                    blendPaint.BlendMode = RenderingContext.GetDrawingBlendMode(BlendMode.Value);
-                }
+                DrawClipSource(tempSurface.DrawingSurface, clipSource, context);
+            }
 
-                DrawLayer(context, outputWorkingSurface, shouldClear);
+            ApplyRasterClip(outputWorkingSurface.DrawingSurface, tempSurface.DrawingSurface);
+        }
 
-                return outputWorkingSurface;
-            }
+        DrawWithResolution(outputWorkingSurface.DrawingSurface, renderOnto, context.ChunkResolution, size);
+    }
 
-            DrawLayer(context, outputWorkingSurface, true);
+    protected internal virtual void DrawLayerOnTexture(SceneObjectRenderContext ctx, DrawingSurface workingSurface,
+        bool useFilters)
+    {
+        int scaled = workingSurface.Canvas.Save();
+        workingSurface.Canvas.Scale((float)ctx.ChunkResolution.Multiplier());
 
-            // shit gets downhill with mask on big canvases, TODO: optimize
-            ApplyMaskIfPresent(outputWorkingSurface, context);
+        DrawLayerOnto(ctx, workingSurface, useFilters);
 
-            if (Background.Value != null)
-            {
-                Texture tempSurface = RequestTexture(4, outputWorkingSurface.Size, true);
-                DrawBackground(tempSurface, context);
-                ApplyRasterClip(outputWorkingSurface, tempSurface);
-                blendPaint.BlendMode = RenderingContext.GetDrawingBlendMode(BlendMode.Value);
-                tempSurface.DrawingSurface.Canvas.DrawSurface(outputWorkingSurface.DrawingSurface, 0, 0,
-                    blendPaint);
-
-                return tempSurface;
-            }
+        workingSurface.Canvas.RestoreToCount(scaled);
+    }
 
-            return outputWorkingSurface;
-        }
+    private void DrawWithResolution(DrawingSurface source, DrawingSurface target, ChunkResolution resolution, VecI size)
+    {
+        int scaled = target.Canvas.Save();
+        float multiplier = (float)resolution.InvertedMultiplier();
+        target.Canvas.Scale(multiplier, multiplier);
+
+        target.Canvas.DrawSurface(source, 0, 0, blendPaint);
 
-        return null;
+        target.Canvas.RestoreToCount(scaled);
     }
 
-    protected abstract VecI GetTargetSize(RenderingContext ctx);
+    protected abstract VecI GetTargetSize(RenderContext ctx);
 
-    protected virtual void DrawLayer(RenderingContext ctx, Texture workingSurface, bool shouldClear,
+    protected internal virtual void DrawLayerInScene(SceneObjectRenderContext ctx, DrawingSurface workingSurface,
         bool useFilters = true)
     {
-        blendPaint.Color = blendPaint.Color.WithAlpha((byte)Math.Round(Opacity.Value * 255));
+        DrawLayerOnto(ctx, workingSurface, useFilters);
+    }
+
+    protected void DrawLayerOnto(SceneObjectRenderContext ctx, DrawingSurface workingSurface, bool useFilters)
+    {
+        blendPaint.Color = blendPaint.Color.WithAlpha((byte)Math.Round(Opacity.Value * ctx.Opacity * 255));
 
         if (useFilters && Filters.Value != null)
         {
             blendPaint.SetFilters(Filters.Value);
-            DrawWithFilters(ctx, workingSurface, shouldClear, blendPaint);
+            DrawWithFilters(ctx, workingSurface, blendPaint);
         }
         else
         {
             blendPaint.SetFilters(null);
-            DrawWithoutFilters(ctx, workingSurface, shouldClear, blendPaint);
+            DrawWithoutFilters(ctx, workingSurface, blendPaint);
         }
     }
-    
-    protected abstract void DrawWithoutFilters(RenderingContext ctx, Texture workingSurface, bool shouldClear,
+
+    protected abstract void DrawWithoutFilters(SceneObjectRenderContext ctx, DrawingSurface workingSurface,
         Paint paint);
-    
-    protected abstract void DrawWithFilters(RenderingContext ctx, Texture workingSurface, bool shouldClear,
+
+    protected abstract void DrawWithFilters(SceneObjectRenderContext ctx, DrawingSurface workingSurface,
         Paint paint);
 
-    protected Texture TryInitWorkingSurface(VecI imageSize, RenderingContext context, int id)
+    protected Texture TryInitWorkingSurface(VecI imageSize, ChunkResolution resolution, int id)
     {
-        ChunkResolution targetResolution = context.ChunkResolution;
+        ChunkResolution targetResolution = resolution;
         bool hasSurface = workingSurfaces.TryGetValue((targetResolution, id), out Texture workingSurface);
         VecI targetSize = (VecI)(imageSize * targetResolution.Multiplier());
-        
+
         targetSize = new VecI(Math.Max(1, targetSize.X), Math.Max(1, targetSize.Y));
 
         if (!hasSurface || workingSurface.Size != targetSize || workingSurface.IsDisposed)
@@ -134,5 +139,8 @@ public abstract class LayerNode : StructureNode, IReadOnlyLayerNode
         return workingSurface;
     }
 
-    public abstract bool RenderPreview(Texture renderOn, VecI chunk, ChunkResolution resolution, int frame);
+    void IClipSource.DrawOnTexture(SceneObjectRenderContext context, DrawingSurface drawOnto)
+    {
+        DrawLayerOnTexture(context, drawOnto, false);
+    }
 }

Some files were not shown because too many files changed in this diff