Browse Source

Simplify raster clip

Equbuxu 3 years ago
parent
commit
ba921231b3

+ 84 - 86
src/ChunkyImageLib/ChunkyImage.cs

@@ -1,6 +1,8 @@
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
 using ChunkyImageLib.DataHolders;
 using ChunkyImageLib.DataHolders;
 using ChunkyImageLib.Operations;
 using ChunkyImageLib.Operations;
+using OneOf;
+using OneOf.Types;
 using SkiaSharp;
 using SkiaSharp;
 
 
 [assembly: InternalsVisibleTo("ChunkyImageLibTest")]
 [assembly: InternalsVisibleTo("ChunkyImageLibTest")]
@@ -52,6 +54,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
     public Vector2i LatestSize { get; private set; }
     public Vector2i LatestSize { get; private set; }
 
 
     private List<(IOperation operation, HashSet<Vector2i> affectedChunks)> queuedOperations = new();
     private List<(IOperation operation, HashSet<Vector2i> affectedChunks)> queuedOperations = new();
+    private List<ChunkyImage> activeClips = new();
 
 
     private Dictionary<ChunkResolution, Dictionary<Vector2i, Chunk>> committedChunks;
     private Dictionary<ChunkResolution, Dictionary<Vector2i, Chunk>> committedChunks;
     private Dictionary<ChunkResolution, Dictionary<Vector2i, Chunk>> latestChunks;
     private Dictionary<ChunkResolution, Dictionary<Vector2i, Chunk>> latestChunks;
@@ -94,7 +97,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
             {
             {
                 var image = (Chunk?)GetLatestChunk(chunk, ChunkResolution.Full);
                 var image = (Chunk?)GetLatestChunk(chunk, ChunkResolution.Full);
                 if (image is not null)
                 if (image is not null)
-                    output.DrawImage(chunk * ChunkSize, image.Surface);
+                    output.EnqueueDrawImage(chunk * ChunkSize, image.Surface);
             }
             }
             output.CommitChanges();
             output.CommitChanges();
             return output;
             return output;
@@ -197,52 +200,53 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
     private Chunk? MaybeGetLatestChunk(Vector2i pos, ChunkResolution resolution) => latestChunks[resolution].TryGetValue(pos, out Chunk? value) ? value : null;
     private Chunk? MaybeGetLatestChunk(Vector2i pos, ChunkResolution resolution) => latestChunks[resolution].TryGetValue(pos, out Chunk? value) ? value : null;
     private Chunk? MaybeGetCommittedChunk(Vector2i pos, ChunkResolution resolution) => committedChunks[resolution].TryGetValue(pos, out Chunk? value) ? value : null;
     private Chunk? MaybeGetCommittedChunk(Vector2i pos, ChunkResolution resolution) => committedChunks[resolution].TryGetValue(pos, out Chunk? value) ? value : null;
 
 
-    public void DrawRectangle(ShapeData rect)
+    public void AddRasterClip(ChunkyImage clippingMask)
     {
     {
         lock (lockObject)
         lock (lockObject)
         {
         {
-            RectangleOperation operation = new(rect);
-            EnqueueOperation(operation);
+            if (queuedOperations.Count > 0)
+                throw new InvalidOperationException("This function can only be executed when there are no queued operations");
+            activeClips.Add(clippingMask);
         }
         }
     }
     }
 
 
-    public void DrawImage(Vector2i pos, Surface image)
+    public void EnqueueDrawRectangle(ShapeData rect)
     {
     {
         lock (lockObject)
         lock (lockObject)
         {
         {
-            ImageOperation operation = new(pos, image);
+            RectangleOperation operation = new(rect);
             EnqueueOperation(operation);
             EnqueueOperation(operation);
         }
         }
     }
     }
 
 
-    public void ClearRegion(Vector2i pos, Vector2i size)
+    public void EnqueueDrawImage(Vector2i pos, Surface image)
     {
     {
         lock (lockObject)
         lock (lockObject)
         {
         {
-            ClearRegionOperation operation = new(pos, size);
+            ImageOperation operation = new(pos, image);
             EnqueueOperation(operation);
             EnqueueOperation(operation);
         }
         }
     }
     }
 
 
-    public void Clear()
+    public void EnqueueClearRegion(Vector2i pos, Vector2i size)
     {
     {
         lock (lockObject)
         lock (lockObject)
         {
         {
-            ClearOperation operation = new();
-            EnqueueOperation(operation, FindAllChunks());
+            ClearRegionOperation operation = new(pos, size);
+            EnqueueOperation(operation);
         }
         }
     }
     }
 
 
-    public void ApplyRasterClip(ChunkyImage clippingMask)
+    public void EnqueueClear()
     {
     {
         lock (lockObject)
         lock (lockObject)
         {
         {
-            RasterClipOperation operation = new(clippingMask);
-            EnqueueOperation(operation, new());
+            ClearOperation operation = new();
+            EnqueueOperation(operation, FindAllChunks());
         }
         }
     }
     }
 
 
-    public void Resize(Vector2i newSize)
+    public void EnqueueResize(Vector2i newSize)
     {
     {
         lock (lockObject)
         lock (lockObject)
         {
         {
@@ -273,6 +277,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
             foreach (var operation in queuedOperations)
             foreach (var operation in queuedOperations)
                 operation.Item1.Dispose();
                 operation.Item1.Dispose();
             queuedOperations.Clear();
             queuedOperations.Clear();
+            activeClips.Clear();
 
 
             //clear latest chunks
             //clear latest chunks
             foreach (var (_, chunksOfRes) in latestChunks)
             foreach (var (_, chunksOfRes) in latestChunks)
@@ -307,6 +312,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
             CommitLatestChunks();
             CommitLatestChunks();
             CommittedSize = LatestSize;
             CommittedSize = LatestSize;
             queuedOperations.Clear();
             queuedOperations.Clear();
+            activeClips.Clear();
 
 
             commitCounter++;
             commitCounter++;
             if (commitCounter % 30 == 0)
             if (commitCounter % 30 == 0)
@@ -415,41 +421,77 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
             return;
             return;
 
 
         Chunk? targetChunk = null;
         Chunk? targetChunk = null;
-        List<ChunkyImage> activeClips = new();
-        bool isFullyMaskedOut = false;
-        bool somethingWasApplied = false;
+        OneOf<All, None, Chunk> combinedClips = new All();
+
+        bool initialized = false;
+
         for (int i = 0; i < queuedOperations.Count; i++)
         for (int i = 0; i < queuedOperations.Count; i++)
         {
         {
             var (operation, operChunks) = queuedOperations[i];
             var (operation, operChunks) = queuedOperations[i];
-            if (operation is RasterClipOperation clipOperation)
-            {
-                // handle self-clipping as a special case to avoid deadlock
-                bool clippingChunkExists = ReferenceEquals(this, clipOperation.ClippingMask) ?
-                    MaybeGetCommittedChunk(chunkPos, ChunkResolution.Full) != null :
-                    clipOperation.ClippingMask.CommitedChunkExists(chunkPos, resolution);
-                if (clippingChunkExists)
-                    activeClips.Add(clipOperation.ClippingMask);
-                else
-                    isFullyMaskedOut = true;
-            }
-
             if (!operChunks.Contains(chunkPos))
             if (!operChunks.Contains(chunkPos))
                 continue;
                 continue;
-            if (!somethingWasApplied)
+
+            if (!initialized)
             {
             {
-                somethingWasApplied = true;
+                initialized = true;
                 targetChunk = GetOrCreateLatestChunk(chunkPos, resolution);
                 targetChunk = GetOrCreateLatestChunk(chunkPos, resolution);
+                combinedClips = CombineClipsForChunk(chunkPos, resolution);
             }
             }
 
 
             if (chunkData.QueueProgress <= i)
             if (chunkData.QueueProgress <= i)
-                chunkData.IsDeleted = ApplyOperationToChunk(operation, activeClips, isFullyMaskedOut, targetChunk!, chunkPos, resolution, chunkData);
+                chunkData.IsDeleted = ApplyOperationToChunk(operation, combinedClips, targetChunk!, chunkPos, resolution, chunkData);
         }
         }
 
 
-        if (somethingWasApplied)
+        if (initialized)
         {
         {
             chunkData.QueueProgress = queuedOperations.Count;
             chunkData.QueueProgress = queuedOperations.Count;
             latestChunksData[resolution][chunkPos] = chunkData;
             latestChunksData[resolution][chunkPos] = chunkData;
         }
         }
+
+        if (combinedClips.TryPickT2(out Chunk value, out var _))
+            value.Dispose();
+    }
+
+    /// <summary>
+    /// (All) -> All is visible, (None) -> None is visible, (Chunk) -> Combined clip
+    /// </summary>
+    private OneOf<All, None, Chunk> CombineClipsForChunk(Vector2i chunkPos, ChunkResolution resolution)
+    {
+        if (activeClips.Count == 0)
+        {
+            return new All();
+        }
+
+        var intersection = Chunk.Create(resolution);
+        intersection.Surface.SkiaSurface.Canvas.Clear(SKColors.White);
+
+        foreach (var mask in activeClips)
+        {
+            // handle self-clipping as a special case to avoid deadlock
+            if (!ReferenceEquals(this, mask))
+            {
+                if (mask.CommitedChunkExists(chunkPos, resolution))
+                {
+                    mask.DrawCommittedChunkOn(chunkPos, resolution, intersection.Surface.SkiaSurface, new(0, 0), ClippingPaint);
+                }
+                else
+                {
+                    intersection.Dispose();
+                    return new None();
+                }
+            }
+            else
+            {
+                var maskChunk = GetCommittedChunk(chunkPos, resolution);
+                if (maskChunk is null)
+                {
+                    intersection.Dispose();
+                    return new None();
+                }
+                maskChunk.DrawOnSurface(intersection.Surface.SkiaSurface, new(0, 0), ClippingPaint);
+            }
+        }
+        return intersection;
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -457,8 +499,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
     /// </summary>
     /// </summary>
     private bool ApplyOperationToChunk(
     private bool ApplyOperationToChunk(
         IOperation operation,
         IOperation operation,
-        IReadOnlyList<ChunkyImage> activeClips,
-        bool isFullyMaskedOut,
+        OneOf<All, None, Chunk> combinedClips,
         Chunk targetChunk,
         Chunk targetChunk,
         Vector2i chunkPos,
         Vector2i chunkPos,
         ChunkResolution resolution,
         ChunkResolution resolution,
@@ -469,46 +510,29 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
 
 
         if (operation is IDrawOperation chunkOperation)
         if (operation is IDrawOperation chunkOperation)
         {
         {
-            if (isFullyMaskedOut)
+            if (combinedClips.IsT1) //None is visible
                 return chunkData.IsDeleted;
                 return chunkData.IsDeleted;
 
 
             if (chunkData.IsDeleted)
             if (chunkData.IsDeleted)
                 targetChunk.Surface.SkiaSurface.Canvas.Clear();
                 targetChunk.Surface.SkiaSurface.Canvas.Clear();
 
 
             // just regular drawing
             // just regular drawing
-            if (activeClips.Count == 0)
+            if (combinedClips.IsT0) //All is visible
             {
             {
                 chunkOperation.DrawOnChunk(targetChunk, chunkPos);
                 chunkOperation.DrawOnChunk(targetChunk, chunkPos);
                 return false;
                 return false;
             }
             }
 
 
             // drawing with clipping
             // drawing with clipping
+            var clip = combinedClips.AsT2;
+
             using var tempChunk = Chunk.Create(targetChunk.Resolution);
             using var tempChunk = Chunk.Create(targetChunk.Resolution);
             targetChunk.DrawOnSurface(tempChunk.Surface.SkiaSurface, new(0, 0), ReplacingPaint);
             targetChunk.DrawOnSurface(tempChunk.Surface.SkiaSurface, new(0, 0), ReplacingPaint);
+
             chunkOperation.DrawOnChunk(tempChunk, chunkPos);
             chunkOperation.DrawOnChunk(tempChunk, chunkPos);
 
 
-            if (activeClips.Count > 1)
-            {
-                using var intersection = IntersectMasks(chunkPos, activeClips, resolution);
-                intersection.DrawOnSurface(tempChunk.Surface.SkiaSurface, new(0, 0), ClippingPaint);
-                intersection.DrawOnSurface(targetChunk.Surface.SkiaSurface, new(0, 0), InverseClippingPaint);
-            }
-            else
-            {
-                if (!ReferenceEquals(this, activeClips[0]))
-                {
-                    activeClips[0].DrawCommittedChunkOn(chunkPos, resolution, tempChunk.Surface.SkiaSurface, new(0, 0), ClippingPaint);
-                    activeClips[0].DrawCommittedChunkOn(chunkPos, resolution, targetChunk.Surface.SkiaSurface, new(0, 0), InverseClippingPaint);
-                }
-                else
-                {
-                    var maskChunk = GetCommittedChunk(chunkPos, resolution);
-                    if (maskChunk is null)
-                        return true; // this should never happen, there is a check in MaybeCreateAndProcessQueueForChunk
-                    maskChunk.DrawOnSurface(tempChunk.Surface.SkiaSurface, new(0, 0), ClippingPaint);
-                    maskChunk.DrawOnSurface(targetChunk.Surface.SkiaSurface, new(0, 0), InverseClippingPaint);
-                }
-            }
+            clip.DrawOnSurface(tempChunk.Surface.SkiaSurface, new(0, 0), ClippingPaint);
+            clip.DrawOnSurface(targetChunk.Surface.SkiaSurface, new(0, 0), InverseClippingPaint);
 
 
             tempChunk.DrawOnSurface(targetChunk.Surface.SkiaSurface, new(0, 0), AddingPaint);
             tempChunk.DrawOnSurface(targetChunk.Surface.SkiaSurface, new(0, 0), AddingPaint);
             return false;
             return false;
@@ -521,32 +545,6 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
         return chunkData.IsDeleted;
         return chunkData.IsDeleted;
     }
     }
 
 
-    private Chunk IntersectMasks(Vector2i chunkPos, IReadOnlyList<ChunkyImage> activeClips, ChunkResolution resolution)
-    {
-        var maskIntersection = Chunk.Create(resolution);
-        maskIntersection.Surface.SkiaSurface.Canvas.Clear(SKColors.White);
-
-        foreach (var mask in activeClips)
-        {
-            // handle self-clipping as a special case to avoid deadlock
-            if (!ReferenceEquals(this, mask))
-            {
-                mask.DrawCommittedChunkOn(chunkPos, resolution, maskIntersection.Surface.SkiaSurface, new(0, 0), ClippingPaint);
-            }
-            else
-            {
-                var maskChunk = GetCommittedChunk(chunkPos, resolution);
-                if (maskChunk is null)
-                {
-                    maskIntersection.Surface.SkiaSurface.Canvas.Clear();
-                    return maskIntersection;
-                }
-                maskChunk.DrawOnSurface(maskIntersection.Surface.SkiaSurface, new(0, 0), ClippingPaint);
-            }
-        }
-        return maskIntersection;
-    }
-
     /// <summary>
     /// <summary>
     /// Note: this function modifies the internal state, it is not thread safe! (same as all the other functions that change the image in some way)
     /// Note: this function modifies the internal state, it is not thread safe! (same as all the other functions that change the image in some way)
     /// </summary>
     /// </summary>

+ 1 - 0
src/ChunkyImageLib/ChunkyImageLib.csproj

@@ -9,6 +9,7 @@
   </PropertyGroup>
   </PropertyGroup>
 
 
   <ItemGroup>
   <ItemGroup>
+    <PackageReference Include="OneOf" Version="3.0.216" />
     <PackageReference Include="SkiaSharp" Version="2.80.3" />
     <PackageReference Include="SkiaSharp" Version="2.80.3" />
   </ItemGroup>
   </ItemGroup>
 
 

+ 2 - 2
src/ChunkyImageLib/CommittedChunkStorage.cs

@@ -31,9 +31,9 @@ public class CommittedChunkStorage : IDisposable
         foreach (var (pos, chunk) in savedChunks)
         foreach (var (pos, chunk) in savedChunks)
         {
         {
             if (chunk is null)
             if (chunk is null)
-                image.ClearRegion(pos * ChunkPool.FullChunkSize, new(ChunkPool.FullChunkSize, ChunkPool.FullChunkSize));
+                image.EnqueueClearRegion(pos * ChunkPool.FullChunkSize, new(ChunkPool.FullChunkSize, ChunkPool.FullChunkSize));
             else
             else
-                image.DrawImage(pos * ChunkPool.FullChunkSize, chunk.Surface);
+                image.EnqueueDrawImage(pos * ChunkPool.FullChunkSize, chunk.Surface);
         }
         }
     }
     }
 
 

+ 0 - 12
src/ChunkyImageLib/Operations/RasterClipOperation.cs

@@ -1,12 +0,0 @@
-namespace ChunkyImageLib.Operations;
-
-internal class RasterClipOperation : IOperation
-{
-    public ChunkyImage ClippingMask { get; }
-    public RasterClipOperation(ChunkyImage clippingMask)
-    {
-        ClippingMask = clippingMask;
-    }
-
-    public void Dispose() { }
-}

+ 1 - 1
src/ChunkyImageLibBenchmark/Program.cs

@@ -24,7 +24,7 @@ Console.ReadKey();
 (double first, double second) Benchmark()
 (double first, double second) Benchmark()
 {
 {
     using ChunkyImage image = new(new(1024, 1024));
     using ChunkyImage image = new(new(1024, 1024));
-    image.DrawRectangle(new(new(0, 0), new(1024, 1024), 10, SKColors.Black, SKColors.Bisque));
+    image.EnqueueDrawRectangle(new(new(0, 0), new(1024, 1024), 10, SKColors.Black, SKColors.Bisque));
 
 
     Stopwatch sw = Stopwatch.StartNew();
     Stopwatch sw = Stopwatch.StartNew();
     for (int i = 0; i < 4; i++)
     for (int i = 0; i < 4; i++)

+ 4 - 4
src/ChunkyImageLibTest/ChunkyImageTests.cs

@@ -9,16 +9,16 @@ public class ChunkyImageTests
     public void ChunkyImage_Dispose_ReturnsAllChunks()
     public void ChunkyImage_Dispose_ReturnsAllChunks()
     {
     {
         ChunkyImage image = new ChunkyImage(new(ChunkyImage.ChunkSize, ChunkyImage.ChunkSize));
         ChunkyImage image = new ChunkyImage(new(ChunkyImage.ChunkSize, ChunkyImage.ChunkSize));
-        image.DrawRectangle(new(new(5, 5), new(80, 80), 2, SKColors.AliceBlue, SKColors.Snow));
+        image.EnqueueDrawRectangle(new(new(5, 5), new(80, 80), 2, SKColors.AliceBlue, SKColors.Snow));
         using (Chunk target = Chunk.Create())
         using (Chunk target = Chunk.Create())
         {
         {
             image.DrawLatestChunkOn(new(0, 0), ChunkyImageLib.DataHolders.ChunkResolution.Full, target.Surface.SkiaSurface, new(0, 0));
             image.DrawLatestChunkOn(new(0, 0), ChunkyImageLib.DataHolders.ChunkResolution.Full, target.Surface.SkiaSurface, new(0, 0));
             image.CancelChanges();
             image.CancelChanges();
-            image.Resize(new(ChunkyImage.ChunkSize * 4, ChunkyImage.ChunkSize * 4));
+            image.EnqueueResize(new(ChunkyImage.ChunkSize * 4, ChunkyImage.ChunkSize * 4));
             image.CommitChanges();
             image.CommitChanges();
-            image.DrawRectangle(new(new(0, 0), image.CommittedSize, 2, SKColors.AliceBlue, SKColors.Snow));
+            image.EnqueueDrawRectangle(new(new(0, 0), image.CommittedSize, 2, SKColors.AliceBlue, SKColors.Snow));
             image.CommitChanges();
             image.CommitChanges();
-            image.DrawRectangle(new(new(0, 0), image.CommittedSize, 2, SKColors.AliceBlue, SKColors.Snow));
+            image.EnqueueDrawRectangle(new(new(0, 0), image.CommittedSize, 2, SKColors.AliceBlue, SKColors.Snow));
             image.CancelChanges();
             image.CancelChanges();
         }
         }
         image.Dispose();
         image.Dispose();

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changes/Drawing/ClearSelection_Change.cs

@@ -27,7 +27,7 @@ internal class ClearSelection_Change : Change
         target.Selection.IsEmptyAndInactive = true;
         target.Selection.IsEmptyAndInactive = true;
 
 
         target.Selection.SelectionImage.CancelChanges();
         target.Selection.SelectionImage.CancelChanges();
-        target.Selection.SelectionImage.Clear();
+        target.Selection.SelectionImage.EnqueueClear();
         HashSet<Vector2i> affChunks = target.Selection.SelectionImage.FindAffectedChunks();
         HashSet<Vector2i> affChunks = target.Selection.SelectionImage.FindAffectedChunks();
         target.Selection.SelectionImage.CommitChanges();
         target.Selection.SelectionImage.CommitChanges();
 
 

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changes/Drawing/CombineStructureMembersOnto_Change.cs

@@ -56,11 +56,11 @@ internal class CombineStructureMembersOnto_Change : Change
             chunksToCombine.UnionWith(layer.LayerImage.FindAllChunks());
             chunksToCombine.UnionWith(layer.LayerImage.FindAllChunks());
         }
         }
 
 
-        toDrawOn.LayerImage.Clear();
+        toDrawOn.LayerImage.EnqueueClear();
         foreach (var chunk in chunksToCombine)
         foreach (var chunk in chunksToCombine)
         {
         {
             using var combined = ChunkRenderer.RenderSpecificLayers(chunk, ChunkResolution.Full, target.StructureRoot, layersToCombine);
             using var combined = ChunkRenderer.RenderSpecificLayers(chunk, ChunkResolution.Full, target.StructureRoot, layersToCombine);
-            toDrawOn.LayerImage.DrawImage(chunk * ChunkyImage.ChunkSize, combined.Surface);
+            toDrawOn.LayerImage.EnqueueDrawImage(chunk * ChunkyImage.ChunkSize, combined.Surface);
         }
         }
         var affectedChunks = toDrawOn.LayerImage.FindAffectedChunks();
         var affectedChunks = toDrawOn.LayerImage.FindAffectedChunks();
         originalChunks = new CommittedChunkStorage(toDrawOn.LayerImage, affectedChunks);
         originalChunks = new CommittedChunkStorage(toDrawOn.LayerImage, affectedChunks);

+ 3 - 3
src/PixiEditor.ChangeableDocument/Changes/Drawing/DrawRectangle_UpdateableChange.cs

@@ -29,12 +29,12 @@ internal class DrawRectangle_UpdateableChange : UpdateableChange
         targetImage.CancelChanges();
         targetImage.CancelChanges();
 
 
         if (!target.Selection.IsEmptyAndInactive)
         if (!target.Selection.IsEmptyAndInactive)
-            targetImage.ApplyRasterClip(target.Selection.SelectionImage);
+            targetImage.AddRasterClip(target.Selection.SelectionImage);
         var targetMember = target.FindMemberOrThrow(memberGuid);
         var targetMember = target.FindMemberOrThrow(memberGuid);
         if (targetMember is Layer layer && layer.LockTransparency)
         if (targetMember is Layer layer && layer.LockTransparency)
-            targetImage.ApplyRasterClip(targetImage);
+            targetImage.AddRasterClip(targetImage);
 
 
-        targetImage.DrawRectangle(rect);
+        targetImage.EnqueueDrawRectangle(rect);
 
 
         var affectedChunks = targetImage.FindAffectedChunks();
         var affectedChunks = targetImage.FindAffectedChunks();
         affectedChunks.UnionWith(oldAffectedChunks);
         affectedChunks.UnionWith(oldAffectedChunks);

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changes/Drawing/SelectRectangle_UpdateableChange.cs

@@ -33,7 +33,7 @@ internal class SelectRectangle_UpdateableChange : UpdateableChange
         var oldChunks = target.Selection.SelectionImage.FindAffectedChunks();
         var oldChunks = target.Selection.SelectionImage.FindAffectedChunks();
         target.Selection.SelectionImage.CancelChanges();
         target.Selection.SelectionImage.CancelChanges();
         target.Selection.IsEmptyAndInactive = false;
         target.Selection.IsEmptyAndInactive = false;
-        target.Selection.SelectionImage.DrawRectangle(new ShapeData(pos, size, 0, SKColors.Transparent, Selection.SelectionColor));
+        target.Selection.SelectionImage.EnqueueDrawRectangle(new ShapeData(pos, size, 0, SKColors.Transparent, Selection.SelectionColor));
 
 
         oldChunks.UnionWith(target.Selection.SelectionImage.FindAffectedChunks());
         oldChunks.UnionWith(target.Selection.SelectionImage.FindAffectedChunks());
         return new Selection_ChangeInfo() { Chunks = oldChunks };
         return new Selection_ChangeInfo() { Chunks = oldChunks };

+ 6 - 6
src/PixiEditor.ChangeableDocument/Changes/Root/ResizeCanvas_Change.cs

@@ -47,19 +47,19 @@ internal class ResizeCanvas_Change : Change
 
 
         ForEachLayer(target.StructureRoot, (layer) =>
         ForEachLayer(target.StructureRoot, (layer) =>
         {
         {
-            layer.LayerImage.Resize(newSize);
+            layer.LayerImage.EnqueueResize(newSize);
             deletedChunks.Add(layer.GuidValue, new CommittedChunkStorage(layer.LayerImage, layer.LayerImage.FindAffectedChunks()));
             deletedChunks.Add(layer.GuidValue, new CommittedChunkStorage(layer.LayerImage, layer.LayerImage.FindAffectedChunks()));
             layer.LayerImage.CommitChanges();
             layer.LayerImage.CommitChanges();
 
 
             if (layer.Mask is null)
             if (layer.Mask is null)
                 return;
                 return;
 
 
-            layer.Mask.Resize(newSize);
+            layer.Mask.EnqueueResize(newSize);
             deletedMaskChunks.Add(layer.GuidValue, new CommittedChunkStorage(layer.Mask, layer.Mask.FindAffectedChunks()));
             deletedMaskChunks.Add(layer.GuidValue, new CommittedChunkStorage(layer.Mask, layer.Mask.FindAffectedChunks()));
             layer.Mask.CommitChanges();
             layer.Mask.CommitChanges();
         });
         });
 
 
-        target.Selection.SelectionImage.Resize(newSize);
+        target.Selection.SelectionImage.EnqueueResize(newSize);
         selectionChunkStorage = new(target.Selection.SelectionImage, target.Selection.SelectionImage.FindAffectedChunks());
         selectionChunkStorage = new(target.Selection.SelectionImage, target.Selection.SelectionImage.FindAffectedChunks());
         target.Selection.SelectionImage.CommitChanges();
         target.Selection.SelectionImage.CommitChanges();
 
 
@@ -75,19 +75,19 @@ internal class ResizeCanvas_Change : Change
         target.Size = originalSize;
         target.Size = originalSize;
         ForEachLayer(target.StructureRoot, (layer) =>
         ForEachLayer(target.StructureRoot, (layer) =>
         {
         {
-            layer.LayerImage.Resize(originalSize);
+            layer.LayerImage.EnqueueResize(originalSize);
             deletedChunks[layer.GuidValue].ApplyChunksToImage(layer.LayerImage);
             deletedChunks[layer.GuidValue].ApplyChunksToImage(layer.LayerImage);
             layer.LayerImage.CommitChanges();
             layer.LayerImage.CommitChanges();
 
 
             if (layer.Mask is null)
             if (layer.Mask is null)
                 return;
                 return;
 
 
-            layer.Mask.Resize(originalSize);
+            layer.Mask.EnqueueResize(originalSize);
             deletedMaskChunks[layer.GuidValue].ApplyChunksToImage(layer.Mask);
             deletedMaskChunks[layer.GuidValue].ApplyChunksToImage(layer.Mask);
             layer.Mask.CommitChanges();
             layer.Mask.CommitChanges();
         });
         });
 
 
-        target.Selection.SelectionImage.Resize(originalSize);
+        target.Selection.SelectionImage.EnqueueResize(originalSize);
         selectionChunkStorage!.ApplyChunksToImage(target.Selection.SelectionImage);
         selectionChunkStorage!.ApplyChunksToImage(target.Selection.SelectionImage);
         target.Selection.SelectionImage.CommitChanges();
         target.Selection.SelectionImage.CommitChanges();
         selectionChunkStorage.Dispose();
         selectionChunkStorage.Dispose();