Răsfoiți Sursa

Scaffolding

Krzysztof Krysiński 3 săptămâni în urmă
părinte
comite
3fc96ca780

+ 13 - 0
src/ChunkyImageLib/ChunkyImage.cs

@@ -742,6 +742,19 @@ public class ChunkyImage : IReadOnlyChunkyImage, IDisposable, ICloneable, ICache
         }
     }
 
+    /// <param name="customBounds">Bounds used for affected chunks, will be computed from path in O(n) if null is passed</param>
+    /// <exception cref="ObjectDisposedException">This image is disposed</exception>
+    public void EnqueueDrawPath(VectorPath path, Paintable paintable, float strokeWidth, StrokeCap strokeCap,
+        BlendMode blendMode, PaintStyle style, RectI? customBounds = null)
+    {
+        lock (lockObject)
+        {
+            ThrowIfDisposed();
+            PathOperation operation = new(path, paintable, strokeWidth, strokeCap, blendMode, style, customBounds);
+            EnqueueOperation(operation);
+        }
+    }
+
     /// <exception cref="ObjectDisposedException">This image is disposed</exception>
     public void EnqueueDrawBresenhamLine(VecI from, VecI to, Paintable paintable, BlendMode blendMode)
     {

+ 10 - 0
src/ChunkyImageLib/Operations/PathOperation.cs

@@ -1,5 +1,6 @@
 using ChunkyImageLib.DataHolders;
 using Drawie.Backend.Core.ColorsImpl;
+using Drawie.Backend.Core.ColorsImpl.Paintables;
 using Drawie.Backend.Core.Numerics;
 using Drawie.Backend.Core.Surfaces;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
@@ -25,6 +26,15 @@ internal class PathOperation : IMirroredDrawOperation
         bounds = floatBounds.Inflate((int)Math.Ceiling(strokeWidth) + 1);
     }
 
+    public PathOperation(VectorPath path, Paintable paintable, float strokeWidth, StrokeCap cap, BlendMode blendMode, PaintStyle style, RectI? customBounds = null)
+    {
+        this.path = new VectorPath(path);
+        paint = new() { Paintable = paintable, Style = style, StrokeWidth = strokeWidth, StrokeCap = cap, BlendMode = blendMode };
+
+        RectI floatBounds = customBounds ?? (RectI)(path.TightBounds).RoundOutwards();
+        bounds = floatBounds.Inflate((int)Math.Ceiling(strokeWidth) + 1);
+    }
+
     public void DrawOnChunk(Chunk targetChunk, VecI chunkPos)
     {
         paint.IsAntiAliased = targetChunk.Resolution != ChunkResolution.Full;

+ 14 - 0
src/PixiEditor.ChangeableDocument/Changeables/Brushes/BrushData.cs

@@ -0,0 +1,14 @@
+using PixiEditor.ChangeableDocument.Changeables.Graph;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Brushes;
+
+public struct BrushData
+{
+    public IReadOnlyShapeVectorData? VectorShape { get; }
+
+    public BrushData(IReadOnlyShapeVectorData vectorShape)
+    {
+        VectorShape = vectorShape;
+    }
+}

+ 28 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Brushes/BrushOutputNode.cs

@@ -0,0 +1,28 @@
+using Drawie.Backend.Core.ColorsImpl.Paintables;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
+using PixiEditor.ChangeableDocument.Rendering;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Brushes;
+
+[NodeInfo("BrushOutput")]
+public class BrushOutputNode : Node
+{
+    public InputProperty<ShapeVectorData> VectorShape { get; }
+    public InputProperty<Paintable> Paint { get; }
+
+    public BrushOutputNode()
+    {
+        VectorShape = CreateInput<ShapeVectorData>("VectorShape", "VECTOR_SHAPE", null);
+    }
+
+    protected override void OnExecute(RenderContext context)
+    {
+
+    }
+
+    public override Node CreateCopy()
+    {
+        return new BrushOutputNode();
+    }
+}

+ 29 - 8
src/PixiEditor.ChangeableDocument/Changes/Drawing/LineBasedPen_UpdateableChange.cs

@@ -6,6 +6,8 @@ using Drawie.Backend.Core.Shaders;
 using Drawie.Backend.Core.Surfaces;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
 using Drawie.Numerics;
+using PixiEditor.ChangeableDocument.Changeables.Brushes;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Brushes;
 
 namespace PixiEditor.ChangeableDocument.Changes.Drawing;
 
@@ -17,7 +19,8 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
     private readonly bool erasing;
     private readonly bool drawOnMask;
     private readonly bool antiAliasing;
-    private bool squareBrush;
+    private Guid brushOutputGuid;
+    private BrushData brushData;
     private float hardness;
     private float spacing = 1;
     private readonly Paint srcPaint = new Paint() { BlendMode = BlendMode.Src };
@@ -34,7 +37,7 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
         bool antiAliasing,
         float hardness,
         float spacing,
-        bool squareBrush,
+        Guid brushOutputGuid,
         bool drawOnMask, int frame)
     {
         this.memberGuid = memberGuid;
@@ -43,9 +46,9 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
         this.erasing = erasing;
         this.antiAliasing = antiAliasing;
         this.drawOnMask = drawOnMask;
+        this.brushOutputGuid = brushOutputGuid;
         this.hardness = hardness;
         this.spacing = spacing;
-        this.squareBrush = squareBrush;
         points.Add(pos);
         this.frame = frame;
 
@@ -89,6 +92,18 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
             image.SetBlendMode(BlendMode.SrcOver);
         DrawingChangeHelper.ApplyClipsSymmetriesEtc(target, image, memberGuid, drawOnMask);
         srcPaint.IsAntiAliased = antiAliasing;
+
+        if (brushOutputGuid != Guid.Empty)
+        {
+            BrushOutputNode? brushOutputNode = target.FindNode<BrushOutputNode>(brushOutputGuid);
+            if (brushOutputNode != null)
+            {
+                brushData = new BrushData(brushOutputNode.VectorShape.Value);
+            }
+
+            return brushOutputNode != null;
+        }
+
         return true;
     }
 
@@ -110,7 +125,7 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
             var rect = new RectI(point - new VecI((int)(strokeWidth / 2f)), new VecI((int)strokeWidth));
             finalPaintable = color;
 
-            if (!squareBrush)
+            if (brushData.VectorShape == null)
             {
                 if (antiAliasing)
                 {
@@ -122,9 +137,15 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
             else
             {
                 BlendMode blendMode = srcPaint.BlendMode;
-                ShapeData shapeData = new ShapeData(rect.Center, rect.Size, 0, 0, 0, finalPaintable, finalPaintable,
+                /*ShapeData shapeData = new ShapeData(rect.Center, rect.Size, 0, 0, 0, finalPaintable, finalPaintable,
                     blendMode);
-                image.EnqueueDrawRectangle(shapeData);
+                image.EnqueueDrawRectangle(shapeData);*/
+
+                var path = brushData.VectorShape.ToPath(true);
+                path.Offset(brushData.VectorShape.TransformedAABB.Pos - brushData.VectorShape.GeometryAABB.Pos);
+                path.Offset(rect.Center - path.Bounds.Center);
+                path.Transform(Matrix3X3.CreateScale(rect.Size.X / (float)path.Bounds.Width, rect.Size.Y / (float)path.Bounds.Height, (float)rect.Center.X, (float)rect.Center.Y));
+                image.EnqueueDrawPath(path, finalPaintable, 1, StrokeCap.Butt, blendMode, PaintStyle.StrokeAndFill);
             }
         }
 
@@ -142,7 +163,7 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
             var rect = new RectI(points[0] - new VecI((int)(strokeWidth / 2f)), new VecI((int)strokeWidth));
             finalPaintable = color;
 
-            if (!squareBrush)
+            if (brushData.VectorShape == null)
             {
                 if (antiAliasing)
                 {
@@ -176,7 +197,7 @@ internal class LineBasedPen_UpdateableChange : UpdateableChange
             var rect = new RectI(points[i] - new VecI((int)(strokeWidth / 2f)), new VecI((int)strokeWidth));
             finalPaintable = color;
 
-            if (!squareBrush)
+            if (brushData.VectorShape == null)
             {
                 if (antiAliasing)
                 {

+ 2 - 2
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/EraserToolExecutor.cs

@@ -51,7 +51,7 @@ internal class EraserToolExecutor : UpdateableChangeExecutor
 
         colorsHandler.AddSwatch(new PaletteColor(color.R, color.G, color.B));
         IAction? action = new LineBasedPen_Action(guidValue, Colors.White, controller!.LastPixelPosition, (float)eraserTool.ToolSize, true,
-            antiAliasing, hardness, spacing, SquareBrush, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
+            antiAliasing, hardness, spacing, Guid.Empty, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
         internals!.ActionAccumulator.AddActions(action);
 
         return ExecutionState.Success;
@@ -59,7 +59,7 @@ internal class EraserToolExecutor : UpdateableChangeExecutor
 
     public override void OnPixelPositionChange(VecI pos)
     {
-        IAction? action = new LineBasedPen_Action(guidValue, Colors.White, pos, (float)toolSize, true, antiAliasing, hardness, spacing, SquareBrush, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
+        IAction? action = new LineBasedPen_Action(guidValue, Colors.White, pos, (float)toolSize, true, antiAliasing, hardness, spacing, Guid.Empty, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
         internals!.ActionAccumulator.AddActions(action);
     }
 

+ 8 - 2
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/PenToolExecutor.cs

@@ -1,12 +1,14 @@
 using PixiEditor.ChangeableDocument.Actions;
 using PixiEditor.ChangeableDocument.Actions.Generated;
 using Drawie.Backend.Core.ColorsImpl;
+using Drawie.Backend.Core.Vector;
 using PixiEditor.Extensions.CommonApi.Palettes;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers.Toolbars;
 using PixiEditor.Models.Handlers.Tools;
 using PixiEditor.Models.Tools;
 using Drawie.Numerics;
+using PixiEditor.ChangeableDocument.Changeables.Brushes;
 
 namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 #nullable enable
@@ -24,6 +26,8 @@ internal class PenToolExecutor : UpdateableChangeExecutor
     private float spacing = 1;
     private bool transparentErase;
 
+    private Guid brushOutputGuid = Guid.Empty;
+
     private IPenToolbar penToolbar;
 
     public override ExecutionState Start()
@@ -53,10 +57,12 @@ internal class PenToolExecutor : UpdateableChangeExecutor
             colorsHandler.AddSwatch(new PaletteColor(color.R, color.G, color.B));
         }
 
+        brushOutputGuid = document.NodeGraphHandler.AllNodes.FirstOrDefault(x => x.InternalName == "PixiEditor.BrushOutput")?.Id ?? Guid.Empty;
+
         transparentErase = color.A == 0;
         IAction? action = pixelPerfect switch
         {
-            false => new LineBasedPen_Action(guidValue, color, controller!.LastPixelPosition, (float)ToolSize, transparentErase, antiAliasing, hardness, spacing, SquareBrush, drawOnMask, document!.AnimationHandler.ActiveFrameBindable),
+            false => new LineBasedPen_Action(guidValue, color, controller!.LastPixelPosition, (float)ToolSize, transparentErase, antiAliasing, hardness, spacing, brushOutputGuid, drawOnMask, document!.AnimationHandler.ActiveFrameBindable),
             true => new PixelPerfectPen_Action(guidValue, controller!.LastPixelPosition, color, drawOnMask, document!.AnimationHandler.ActiveFrameBindable)
         };
         internals!.ActionAccumulator.AddActions(action);
@@ -68,7 +74,7 @@ internal class PenToolExecutor : UpdateableChangeExecutor
     {
         IAction? action = pixelPerfect switch
         {
-            false => new LineBasedPen_Action(guidValue, color, pos, (float)ToolSize, transparentErase, antiAliasing, hardness, spacing, SquareBrush, drawOnMask, document!.AnimationHandler.ActiveFrameBindable),
+            false => new LineBasedPen_Action(guidValue, color, pos, (float)ToolSize, transparentErase, antiAliasing, hardness, spacing, brushOutputGuid, drawOnMask, document!.AnimationHandler.ActiveFrameBindable),
             true => new PixelPerfectPen_Action(guidValue, pos, color, drawOnMask, document!.AnimationHandler.ActiveFrameBindable)
         };
         internals!.ActionAccumulator.AddActions(action);

+ 10 - 0
src/PixiEditor/ViewModels/Document/Nodes/Brushes/BrushOutputNodeViewModel.cs

@@ -0,0 +1,10 @@
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Brushes;
+using PixiEditor.ViewModels.Nodes;
+
+namespace PixiEditor.ViewModels.Document.Nodes.Brushes;
+
+[NodeViewModel("BRUSH_OUTPUT_NODE", "BRUSHES", null)]
+internal class BrushOutputNodeViewModel : NodeViewModel<BrushOutputNode>
+{
+
+}