浏览代码

uhhh fixed filters chunk bug?

flabbet 11 月之前
父节点
当前提交
5ac92e37e8

+ 1 - 1
src/ChunkyImageLib/ChunkyImage.cs

@@ -346,7 +346,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
             }
             }
 
 
             var committedChunk = GetCommittedChunk(chunkPos, resolution);
             var committedChunk = GetCommittedChunk(chunkPos, resolution);
-
+            
             // draw committed directly
             // draw committed directly
             if (latestChunk.IsT0 || latestChunk.IsT1 && committedChunk is not null && blendMode != BlendMode.Src)
             if (latestChunk.IsT0 || latestChunk.IsT1 && committedChunk is not null && blendMode != BlendMode.Src)
             {
             {

+ 132 - 17
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageLayerNode.cs

@@ -91,14 +91,14 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
 
 
         DrawLayer(frameImage, context, filterlessWorkingSurface, shouldClear, useFilters: false);
         DrawLayer(frameImage, context, filterlessWorkingSurface, shouldClear, useFilters: false);
         blendPaint.BlendMode = DrawingApi.Core.Surfaces.BlendMode.Src;
         blendPaint.BlendMode = DrawingApi.Core.Surfaces.BlendMode.Src;
-        
+
         FilterlessOutput.Value = filterlessWorkingSurface;
         FilterlessOutput.Value = filterlessWorkingSurface;
-        
+
         // Draw raw
         // Draw raw
         DrawLayer(frameImage, context, rawWorkingSurface, true, useFilters: false);
         DrawLayer(frameImage, context, rawWorkingSurface, true, useFilters: false);
 
 
         RawOutput.Value = rawWorkingSurface;
         RawOutput.Value = rawWorkingSurface;
-        
+
         // Draw output
         // Draw output
         if (!HasOperations())
         if (!HasOperations())
         {
         {
@@ -109,9 +109,9 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
             }
             }
 
 
             DrawLayer(frameImage, context, outputWorkingSurface, shouldClear);
             DrawLayer(frameImage, context, outputWorkingSurface, shouldClear);
-            
+
             Output.Value = outputWorkingSurface;
             Output.Value = outputWorkingSurface;
-            
+
             return outputWorkingSurface;
             return outputWorkingSurface;
         }
         }
 
 
@@ -120,7 +120,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         // shit gets downhill with mask on big canvases, TODO: optimize
         // shit gets downhill with mask on big canvases, TODO: optimize
         ApplyMaskIfPresent(outputWorkingSurface, context);
         ApplyMaskIfPresent(outputWorkingSurface, context);
         ApplyRasterClip(outputWorkingSurface, context);
         ApplyRasterClip(outputWorkingSurface, context);
-        
+
         if (Background.Value != null)
         if (Background.Value != null)
         {
         {
             Surface tempSurface = new Surface(outputWorkingSurface.Size);
             Surface tempSurface = new Surface(outputWorkingSurface.Size);
@@ -133,25 +133,140 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         }
         }
 
 
         Output.Value = outputWorkingSurface;
         Output.Value = outputWorkingSurface;
-        
+
         return outputWorkingSurface;
         return outputWorkingSurface;
     }
     }
 
 
-    private void DrawLayer(ChunkyImage frameImage, RenderingContext context, Surface workingSurface, bool shouldClear, bool useFilters = true)
+    private void DrawLayer(ChunkyImage frameImage, RenderingContext context, Surface workingSurface, bool shouldClear,
+        bool useFilters = true)
     {
     {
         blendPaint.Color = blendPaint.Color.WithAlpha((byte)Math.Round(Opacity.Value * 255));
         blendPaint.Color = blendPaint.Color.WithAlpha((byte)Math.Round(Opacity.Value * 255));
 
 
-        blendPaint.SetFilters(useFilters ? Filters.Value : null);
+        if (useFilters && Filters.Value != null)
+        {
+            DrawWithFilters(frameImage, context, workingSurface, shouldClear);
+        }
+        else
+        {
+            blendPaint.SetFilters(null);
+
+            if (!frameImage.DrawMostUpToDateChunkOn(
+                    context.ChunkToUpdate,
+                    context.ChunkResolution,
+                    workingSurface.DrawingSurface,
+                    context.ChunkToUpdate * context.ChunkResolution.PixelSize(),
+                    blendPaint) && shouldClear)
+            {
+                workingSurface.DrawingSurface.Canvas.DrawRect(CalculateDestinationRect(context), clearPaint);
+            }
+        }
+    }
 
 
-        if (!frameImage.DrawMostUpToDateChunkOn(
-                context.ChunkToUpdate,
-                context.ChunkResolution,
-                workingSurface.DrawingSurface,
-                context.ChunkToUpdate * context.ChunkResolution.PixelSize(),
-                blendPaint) && shouldClear)
+    // 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
+    private void DrawWithFilters(ChunkyImage frameImage, RenderingContext context, Surface workingSurface,
+        bool shouldClear)
+    {
+        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++;
+        }
+        
+        if(requiresRight)
+        {
+            tempSizeInChunks.X++;
+        }
+        
+        if(requiresTop)
+        {
+            tempSizeInChunks.Y++;
+        }
+        
+        if(requiresBottom)
+        {
+            tempSizeInChunks.Y++;
+        }
+
+        if (shouldClear)
         {
         {
-            workingSurface.DrawingSurface.Canvas.DrawRect(CalculateDestinationRect(context), clearPaint);
-            workingSurface.DrawingSurface.Canvas.Flush();
+            workingSurface.DrawingSurface.Canvas.DrawRect(
+                new RectI(
+                    VecI.Zero,
+                    tempSizeInChunks * context.ChunkResolution.PixelSize()),
+                clearPaint);
+        }
+        
+        using Surface tempSurface = new Surface(tempSizeInChunks * context.ChunkResolution.PixelSize());
+
+        if (requiresTopLeft)
+        {
+            DrawChunk(frameImage, context, tempSurface, new VecI(-1, -1));
+        }
+        
+        if (requiresTop)
+        {
+            DrawChunk(frameImage, context, tempSurface, new VecI(0, -1));
+        }
+        
+        if (requiresLeft)
+        {
+            DrawChunk(frameImage, context, tempSurface, new VecI(-1, 0));
+        }
+        
+        if (requiresTopRight)
+        {
+            DrawChunk(frameImage, context, tempSurface, new VecI(1, -1));
+        }
+        
+        if (requiresRight)
+        {
+            DrawChunk(frameImage, context, tempSurface, new VecI(1, 0));
+        }
+        
+        if (requiresBottomRight)
+        {
+            DrawChunk(frameImage, context, tempSurface, new VecI(1, 1));
+        }
+        
+        if (requiresBottom)
+        {
+            DrawChunk(frameImage, context, tempSurface, new VecI(0, 1));
+        }
+        
+        if (requiresBottomLeft)
+        {
+            DrawChunk(frameImage, context, tempSurface, new VecI(-1, 1));
+        }
+        
+        DrawChunk(frameImage, context, tempSurface, new VecI(0, 0));
+        
+        blendPaint.SetFilters(Filters.Value);
+        var destinationRect = CalculateDestinationRect(context);
+        workingSurface.DrawingSurface.Canvas.DrawSurface(tempSurface.DrawingSurface, VecI.Zero, blendPaint);
+    }
+
+    private void DrawChunk(ChunkyImage frameImage, RenderingContext context, Surface tempSurface, VecI vecI)
+    {
+        VecI chunkPos = context.ChunkToUpdate + vecI;
+        if (frameImage.LatestOrCommittedChunkExists(chunkPos))
+        {
+            frameImage.DrawMostUpToDateChunkOn(
+                chunkPos,
+                context.ChunkResolution,
+                tempSurface.DrawingSurface,
+                chunkPos * context.ChunkResolution.PixelSize(),
+                blendPaint);
         }
         }
     }
     }
 
 

+ 60 - 26
src/PixiEditor/Models/Rendering/CanvasUpdater.cs

@@ -21,18 +21,35 @@ internal class CanvasUpdater
     private readonly DocumentInternalParts internals;
     private readonly DocumentInternalParts internals;
 
 
     private static readonly Paint ReplacingPaint = new() { BlendMode = BlendMode.Src };
     private static readonly Paint ReplacingPaint = new() { BlendMode = BlendMode.Src };
-    private static readonly Paint ClearPaint = new() { BlendMode = BlendMode.Src, Color = PixiEditor.DrawingApi.Core.ColorsImpl.Colors.Transparent };
+
+    private static readonly Paint ClearPaint = new()
+    {
+        BlendMode = BlendMode.Src, Color = PixiEditor.DrawingApi.Core.ColorsImpl.Colors.Transparent
+    };
 
 
     /// <summary>
     /// <summary>
     /// Affected chunks that have not been rerendered yet.
     /// Affected chunks that have not been rerendered yet.
     /// </summary>
     /// </summary>
-    private readonly Dictionary<ChunkResolution, HashSet<VecI>> affectedAndNonRerenderedChunks = new() { [ChunkResolution.Full] = new(), [ChunkResolution.Half] = new(), [ChunkResolution.Quarter] = new(), [ChunkResolution.Eighth] = new() };
+    private readonly Dictionary<ChunkResolution, HashSet<VecI>> affectedAndNonRerenderedChunks = new()
+    {
+        [ChunkResolution.Full] = new(),
+        [ChunkResolution.Half] = new(),
+        [ChunkResolution.Quarter] = new(),
+        [ChunkResolution.Eighth] = new()
+    };
 
 
     /// <summary>
     /// <summary>
     /// Affected chunks that have not been rerendered yet.
     /// Affected chunks that have not been rerendered yet.
     /// Doesn't include chunks that were affected after the last time rerenderDelayed was true.
     /// Doesn't include chunks that were affected after the last time rerenderDelayed was true.
     /// </summary>
     /// </summary>
-    private readonly Dictionary<ChunkResolution, HashSet<VecI>> nonRerenderedChunksAffectedBeforeLastRerenderDelayed = new() { [ChunkResolution.Full] = new(), [ChunkResolution.Half] = new(), [ChunkResolution.Quarter] = new(), [ChunkResolution.Eighth] = new() };
+    private readonly Dictionary<ChunkResolution, HashSet<VecI>> nonRerenderedChunksAffectedBeforeLastRerenderDelayed =
+        new()
+        {
+            [ChunkResolution.Full] = new(),
+            [ChunkResolution.Half] = new(),
+            [ChunkResolution.Quarter] = new(),
+            [ChunkResolution.Eighth] = new()
+        };
 
 
 
 
     public CanvasUpdater(IDocument doc, DocumentInternalParts internals)
     public CanvasUpdater(IDocument doc, DocumentInternalParts internals)
@@ -80,10 +97,12 @@ internal class CanvasUpdater
                 ChunkResolution.Full.PixelSize());
                 ChunkResolution.Full.PixelSize());
             chunks[viewport.Resolution].UnionWith(viewportChunks);
             chunks[viewport.Resolution].UnionWith(viewportChunks);
         }
         }
+
         return chunks;
         return chunks;
     }
     }
 
 
-    private Dictionary<ChunkResolution, HashSet<VecI>> FindGlobalChunksToRerender(AffectedAreasGatherer areasGatherer, bool renderDelayed)
+    private Dictionary<ChunkResolution, HashSet<VecI>> FindGlobalChunksToRerender(AffectedAreasGatherer areasGatherer,
+        bool renderDelayed)
     {
     {
         // find all affected non rerendered chunks
         // find all affected non rerendered chunks
         var chunksToRerender = new Dictionary<ChunkResolution, HashSet<VecI>>();
         var chunksToRerender = new Dictionary<ChunkResolution, HashSet<VecI>>();
@@ -114,7 +133,8 @@ internal class CanvasUpdater
         return chunksToRerender;
         return chunksToRerender;
     }
     }
 
 
-    private void UpdateAffectedNonRerenderedChunks(Dictionary<ChunkResolution, HashSet<VecI>> chunksToRerender, AffectedArea chunkGathererAffectedArea)
+    private void UpdateAffectedNonRerenderedChunks(Dictionary<ChunkResolution, HashSet<VecI>> chunksToRerender,
+        AffectedArea chunkGathererAffectedArea)
     {
     {
         if (chunkGathererAffectedArea.Chunks.Count > 0)
         if (chunkGathererAffectedArea.Chunks.Count > 0)
         {
         {
@@ -133,7 +153,8 @@ internal class CanvasUpdater
 
 
     private List<IRenderInfo> Render(AffectedAreasGatherer chunkGatherer, bool rerenderDelayed)
     private List<IRenderInfo> Render(AffectedAreasGatherer chunkGatherer, bool rerenderDelayed)
     {
     {
-        Dictionary<ChunkResolution, HashSet<VecI>> chunksToRerender = FindGlobalChunksToRerender(chunkGatherer, rerenderDelayed);
+        Dictionary<ChunkResolution, HashSet<VecI>> chunksToRerender =
+            FindGlobalChunksToRerender(chunkGatherer, rerenderDelayed);
 
 
         bool updatingStoredChunks = false;
         bool updatingStoredChunks = false;
         foreach (var (res, stored) in affectedAndNonRerenderedChunks)
         foreach (var (res, stored) in affectedAndNonRerenderedChunks)
@@ -146,7 +167,7 @@ internal class CanvasUpdater
                 break;
                 break;
             }
             }
         }
         }
-        
+
         UpdateAffectedNonRerenderedChunks(chunksToRerender, chunkGatherer.MainImageArea);
         UpdateAffectedNonRerenderedChunks(chunksToRerender, chunkGatherer.MainImageArea);
 
 
         bool anythingToUpdate = false;
         bool anythingToUpdate = false;
@@ -154,27 +175,32 @@ internal class CanvasUpdater
         {
         {
             anythingToUpdate |= chunks.Count > 0;
             anythingToUpdate |= chunks.Count > 0;
         }
         }
+
         if (!anythingToUpdate)
         if (!anythingToUpdate)
             return new();
             return new();
-        
+
         List<IRenderInfo> infos = new();
         List<IRenderInfo> infos = new();
-        UpdateMainImage(chunksToRerender, updatingStoredChunks ? null : chunkGatherer.MainImageArea.GlobalArea.Value, infos);
+        UpdateMainImage(chunksToRerender, updatingStoredChunks ? null : chunkGatherer.MainImageArea.GlobalArea.Value,
+            infos);
         return infos;
         return infos;
     }
     }
 
 
-    private void UpdateMainImage(Dictionary<ChunkResolution, HashSet<VecI>> chunksToRerender, RectI? globalClippingRectangle, List<IRenderInfo> infos)
+    private void UpdateMainImage(Dictionary<ChunkResolution, HashSet<VecI>> chunksToRerender,
+        RectI? globalClippingRectangle, List<IRenderInfo> infos)
     {
     {
         foreach (var (resolution, chunks) in chunksToRerender)
         foreach (var (resolution, chunks) in chunksToRerender)
         {
         {
             int chunkSize = resolution.PixelSize();
             int chunkSize = resolution.PixelSize();
             RectI? globalScaledClippingRectangle = null;
             RectI? globalScaledClippingRectangle = null;
             if (globalClippingRectangle is not null)
             if (globalClippingRectangle is not null)
-                globalScaledClippingRectangle = (RectI?)((RectI)globalClippingRectangle).Scale(resolution.Multiplier()).RoundOutwards();
+                globalScaledClippingRectangle =
+                    (RectI?)((RectI)globalClippingRectangle).Scale(resolution.Multiplier()).RoundOutwards();
 
 
             Surface screenSurface = doc.Surfaces[resolution];
             Surface screenSurface = doc.Surfaces[resolution];
             foreach (var chunkPos in chunks)
             foreach (var chunkPos in chunks)
             {
             {
-                RenderChunk(chunkPos, screenSurface, resolution, globalClippingRectangle, globalScaledClippingRectangle);
+                RenderChunk(chunkPos, screenSurface, resolution, globalClippingRectangle,
+                    globalScaledClippingRectangle);
                 RectI chunkRect = new(chunkPos * chunkSize, new(chunkSize, chunkSize));
                 RectI chunkRect = new(chunkPos * chunkSize, new(chunkSize, chunkSize));
                 if (globalScaledClippingRectangle is RectI rect)
                 if (globalScaledClippingRectangle is RectI rect)
                     chunkRect = chunkRect.Intersect(rect);
                     chunkRect = chunkRect.Intersect(rect);
@@ -188,28 +214,36 @@ internal class CanvasUpdater
         }
         }
     }
     }
 
 
-    private void RenderChunk(VecI chunkPos, Surface screenSurface, ChunkResolution resolution, RectI? globalClippingRectangle, RectI? globalScaledClippingRectangle)
+    private void RenderChunk(VecI chunkPos, Surface screenSurface, ChunkResolution resolution,
+        RectI? globalClippingRectangle, RectI? globalScaledClippingRectangle)
     {
     {
-        if(screenSurface is null || screenSurface.IsDisposed)
+        if (screenSurface is null || screenSurface.IsDisposed)
             return;
             return;
-        
+
         if (globalScaledClippingRectangle is not null)
         if (globalScaledClippingRectangle is not null)
         {
         {
             screenSurface.DrawingSurface.Canvas.Save();
             screenSurface.DrawingSurface.Canvas.Save();
             screenSurface.DrawingSurface.Canvas.ClipRect((RectD)globalScaledClippingRectangle);
             screenSurface.DrawingSurface.Canvas.ClipRect((RectD)globalScaledClippingRectangle);
         }
         }
 
 
-        doc.Renderer.RenderChunk(chunkPos, resolution, doc.AnimationHandler.ActiveFrameTime, globalClippingRectangle).Switch(
-            (Chunk chunk) =>
-            {
-                screenSurface.DrawingSurface.Canvas.DrawSurface(chunk.Surface.DrawingSurface, chunkPos.Multiply(chunk.PixelSize), ReplacingPaint);
-                chunk.Dispose();
-            },
-            (EmptyChunk _) =>
-            {
-                var pos = chunkPos * resolution.PixelSize();
-                screenSurface.DrawingSurface.Canvas.DrawRect(pos.X, pos.Y, resolution.PixelSize(), resolution.PixelSize(), ClearPaint);
-            });
+        doc.Renderer.RenderChunk(chunkPos, resolution, doc.AnimationHandler.ActiveFrameTime, globalClippingRectangle)
+            .Switch(
+                (Chunk chunk) =>
+                {
+                    if (screenSurface.IsDisposed) return;
+                    
+                    screenSurface.DrawingSurface.Canvas.DrawSurface(chunk.Surface.DrawingSurface,
+                        chunkPos.Multiply(chunk.PixelSize), ReplacingPaint);
+                    chunk.Dispose();
+                },
+                (EmptyChunk _) =>
+                {
+                    if (screenSurface.IsDisposed) return;
+                    
+                    var pos = chunkPos * resolution.PixelSize();
+                    screenSurface.DrawingSurface.Canvas.DrawRect(pos.X, pos.Y, resolution.PixelSize(),
+                        resolution.PixelSize(), ClearPaint);
+                });
 
 
         if (globalScaledClippingRectangle is not null)
         if (globalScaledClippingRectangle is not null)
             screenSurface.DrawingSurface.Canvas.Restore();
             screenSurface.DrawingSurface.Canvas.Restore();