Browse Source

Fixed merge members

flabbet 10 months ago
parent
commit
b14e6c5a94

+ 54 - 9
src/ChunkyImageLib/ChunkyImage.cs

@@ -60,7 +60,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
     private bool disposed = false;
     private readonly object lockObject = new();
     private int commitCounter = 0;
-    
+
     private RectI cachedPreciseBounds = RectI.Empty;
     private int lastBoundsCacheHash = -1;
 
@@ -96,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;
@@ -202,7 +202,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
             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))
@@ -239,7 +239,7 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
 
             cachedPreciseBounds = preciseBounds.GetValueOrDefault();
             lastBoundsCacheHash = GetCacheHash();
-            
+
             return preciseBounds;
         }
     }
@@ -358,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)
             {
@@ -647,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)
@@ -1435,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);
     }
 }

+ 0 - 2
src/ChunkyImageLib/Operations/ImageOperation.cs

@@ -78,8 +78,6 @@ internal class ImageOperation : IMirroredDrawOperation
         imageWasCopied = copyImage;
     }
 
-
-
     public void DrawOnChunk(Chunk targetChunk, VecI chunkPos)
     {
         //customPaint.FilterQuality = chunk.Resolution != ChunkResolution.Full;

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

@@ -0,0 +1,127 @@
+using ChunkyImageLib.DataHolders;
+using PixiEditor.DrawingApi.Core;
+using PixiEditor.DrawingApi.Core.Numerics;
+using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
+using PixiEditor.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);
+    }
+}

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

@@ -2,6 +2,7 @@
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using PixiEditor.ChangeableDocument.Changes.Structure;
 using PixiEditor.ChangeableDocument.Rendering;
+using PixiEditor.DrawingApi.Core;
 using PixiEditor.DrawingApi.Core.Bridge;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Numerics;
@@ -91,13 +92,15 @@ internal class CombineStructureMembersOnto_Change : Change
         var toDrawOnImage = ((ImageLayerNode)toDrawOn).GetLayerImageAtFrame(frame);
         toDrawOnImage.EnqueueClear();
 
+        Texture tempTexture = new Texture(target.Size);
+        
         DocumentRenderer renderer = new(target);
 
         AffectedArea affArea = new();
         DrawingBackendApi.Current.RenderingDispatcher.Invoke(() =>
         {
             RectI? globalClippingRect = new RectI(0, 0, target.Size.X, target.Size.Y);
-            foreach (var chunk in chunksToCombine)
+            /*foreach (var chunk in chunksToCombine)
             {
                 OneOf<Chunk, EmptyChunk> combined =
                     renderer.RenderLayersChunk(chunk, ChunkResolution.Full, frame, layersToCombine, globalClippingRect);
@@ -106,11 +109,17 @@ internal class CombineStructureMembersOnto_Change : Change
                     toDrawOnImage.EnqueueDrawImage(chunk * ChunkyImage.FullChunkSize, combined.AsT0.Surface);
                     combined.AsT0.Dispose();
                 }
-            }
+            }*/
+            
+            renderer.RenderLayers(tempTexture.DrawingSurface, layersToCombine, frame, ChunkResolution.Full);
+            
+            toDrawOnImage.EnqueueDrawTexture(VecI.Zero, tempTexture);
 
             affArea = toDrawOnImage.FindAffectedArea();
             originalChunks = new CommittedChunkStorage(toDrawOnImage, affArea.Chunks);
             toDrawOnImage.CommitChanges();
+            
+            tempTexture.Dispose();
         });
 
 

+ 12 - 1
src/PixiEditor.ChangeableDocument/Changes/Drawing/FloodFill/FloodFillChunkCache.cs

@@ -1,6 +1,7 @@
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using PixiEditor.ChangeableDocument.Rendering;
+using PixiEditor.DrawingApi.Core;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Surfaces;
 using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
@@ -49,7 +50,17 @@ internal class FloodFillChunkCache : IDisposable
         {
             if (document is null || membersToRender is null)
                 throw new InvalidOperationException();
-            var chunk = document.Renderer.RenderLayersChunk(pos, ChunkResolution.Full, frame, membersToRender, null);
+            Chunk chunk = Chunk.Create();
+            chunk.Surface.DrawingSurface.Canvas.Save();
+            
+            VecI chunkPos = pos * ChunkyImage.FullChunkSize;
+            
+            chunk.Surface.DrawingSurface.Canvas.Translate(-chunkPos.X, -chunkPos.Y);
+            
+            document.Renderer.RenderLayers(chunk.Surface.DrawingSurface, membersToRender, frame, ChunkResolution.Full);
+            
+            chunk.Surface.DrawingSurface.Canvas.Restore();
+            
             acquiredChunks[pos] = chunk;
             return chunk;
         }

+ 15 - 17
src/PixiEditor.ChangeableDocument/Rendering/DocumentRenderer.cs

@@ -13,7 +13,6 @@ namespace PixiEditor.ChangeableDocument.Rendering;
 
 public class DocumentRenderer : IPreviewRenderable
 {
-    
     private Paint ClearPaint { get; } = new Paint()
     {
         BlendMode = BlendMode.Src, Color = PixiEditor.DrawingApi.Core.ColorsImpl.Colors.Transparent
@@ -53,41 +52,39 @@ public class DocumentRenderer : IPreviewRenderable
         return (RectI?)rect.Scale(multiplier).Translate(-pixelChunkPos).RoundOutwards();
     }
 
-    public OneOf<Chunk, EmptyChunk> RenderLayersChunk(VecI chunkPos, ChunkResolution resolution, KeyFrameTime frame,
-        HashSet<Guid> layersToCombine, RectI? globalClippingRect)
+    public void RenderLayers(DrawingSurface toDrawOn, HashSet<Guid> layersToCombine, int frame, ChunkResolution resolution)
     {
-        //using RenderingContext context = new(frame, chunkPos, resolution, Document.Size);
+        RenderContext context = new(toDrawOn, frame, resolution, Document.Size);
+        context.FullRerender = true;
         IReadOnlyNodeGraph membersOnlyGraph = ConstructMembersOnlyGraph(layersToCombine, Document.NodeGraph);
         try
         {
-            //return RenderChunkOnGraph(chunkPos, resolution, globalClippingRect, membersOnlyGraph, context);
-            return new EmptyChunk();
+            membersOnlyGraph.Execute(context);
         }
         catch (ObjectDisposedException)
         {
-            return new EmptyChunk();
         }
     }
-    
-    
+
+
     public void RenderLayer(DrawingSurface renderOn, Guid nodeId, ChunkResolution resolution, KeyFrameTime frameTime)
     {
         var node = Document.FindNode(nodeId);
-        
+
         if (node is null)
         {
             return;
         }
-        
+
         RenderContext context = new(renderOn, frameTime, resolution, Document.Size);
         context.FullRerender = true;
-        
+
         node.Execute(context);
     }
 
     public static IReadOnlyNodeGraph ConstructMembersOnlyGraph(IReadOnlyNodeGraph fullGraph)
     {
-        return ConstructMembersOnlyGraph(null, fullGraph); 
+        return ConstructMembersOnlyGraph(null, fullGraph);
     }
 
     public static IReadOnlyNodeGraph ConstructMembersOnlyGraph(HashSet<Guid>? layersToCombine,
@@ -123,14 +120,15 @@ public class DocumentRenderer : IPreviewRenderable
         return membersOnlyGraph;
     }
 
-    public RectD? GetPreviewBounds(int frame, string elementNameToRender = "") => 
-        new(0, 0, Document.Size.X, Document.Size.Y); 
+    public RectD? GetPreviewBounds(int frame, string elementNameToRender = "") =>
+        new(0, 0, Document.Size.X, Document.Size.Y);
 
-    public bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame, string elementToRenderName)
+    public bool RenderPreview(DrawingSurface renderOn, ChunkResolution resolution, int frame,
+        string elementToRenderName)
     {
         RenderContext context = new(renderOn, frame, resolution, Document.Size);
         Document.NodeGraph.Execute(context);
-        
+
         return true;
     }
 

+ 25 - 16
src/PixiEditor.DrawingApi.Skia/Implementations/SkiaCanvasImplementation.cs

@@ -55,27 +55,34 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
             canvas.DrawImage(_imageImpl.ManagedInstances[image.ObjectPointer], x, y);
         }
 
-        public void DrawImage(IntPtr objPtr, Image image, float x, float y, Paint paint)
+        public void DrawImage(IntPtr objPtr, Image image, float x, float y, Paint? paint)
         {
-            if(!ManagedInstances.TryGetValue(objPtr, out var canvas))
+            if (!ManagedInstances.TryGetValue(objPtr, out var canvas))
             {
                 throw new ObjectDisposedException(nameof(canvas));
             }
-            
-            if (!_paintImpl.ManagedInstances.TryGetValue(paint.ObjectPointer, out var skPaint))
+
+            if (!_imageImpl.ManagedInstances.TryGetValue(image.ObjectPointer, out var img))
             {
-                throw new ObjectDisposedException(nameof(paint));
+                throw new ObjectDisposedException(nameof(image));
             }
-            
-            if(!_imageImpl.ManagedInstances.TryGetValue(image.ObjectPointer, out var img))
+
+            if (paint != null)
             {
-                throw new ObjectDisposedException(nameof(image));
+                if (!_paintImpl.ManagedInstances.TryGetValue(paint.ObjectPointer, out var skPaint))
+                {
+                    throw new ObjectDisposedException(nameof(paint));
+                }
+
+                canvas.DrawImage(img, x, y, skPaint);
+                return;
             }
-            
-            canvas.DrawImage(img, x, y, skPaint);
+
+            canvas.DrawImage(img, x, y);
         }
 
-        public void DrawRoundRect(IntPtr objectPointer, float x, float y, float width, float height, float radiusX, float radiusY,
+        public void DrawRoundRect(IntPtr objectPointer, float x, float y, float width, float height, float radiusX,
+            float radiusY,
             Paint paint)
         {
             ManagedInstances[objectPointer].DrawRoundRect(
@@ -94,12 +101,14 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
 
         public int SaveLayer(IntPtr objectPtr, Paint? paint)
         {
-            return ManagedInstances[objectPtr].SaveLayer(paint != null ? _paintImpl.ManagedInstances[paint.ObjectPointer] : null);
+            return ManagedInstances[objectPtr]
+                .SaveLayer(paint != null ? _paintImpl.ManagedInstances[paint.ObjectPointer] : null);
         }
-        
+
         public int SaveLayer(IntPtr objectPtr, Paint paint, RectD bounds)
         {
-            return ManagedInstances[objectPtr].SaveLayer(bounds.ToSKRect(), _paintImpl.ManagedInstances[paint.ObjectPointer]);
+            return ManagedInstances[objectPtr]
+                .SaveLayer(bounds.ToSKRect(), _paintImpl.ManagedInstances[paint.ObjectPointer]);
         }
 
         public Matrix3X3 GetActiveMatrix(IntPtr objectPointer)
@@ -114,7 +123,7 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
                 ManagedInstances.TryAdd(skCanvas.Handle, skCanvas);
                 return new Canvas(skCanvas.Handle);
             }
-            
+
             throw new ArgumentException("Native object is not a SKCanvas", nameof(native));
         }
 
@@ -165,7 +174,7 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
         public void DrawRect(IntPtr objPtr, float x, float y, float width, float height, Paint paint)
         {
             SKPaint skPaint = _paintImpl[paint.ObjectPointer];
-            
+
             var canvas = ManagedInstances[objPtr];
             canvas.DrawRect(x, y, width, height, skPaint);
         }