Browse Source

Overlays wip

flabbet 10 months ago
parent
commit
1631508a99

+ 19 - 15
src/PixiEditor/Models/Rendering/SceneRenderer.cs

@@ -11,33 +11,37 @@ internal class SceneRenderer
 {
     public IReadOnlyDocument Document { get; }
     public IDocument DocumentViewModel { get; }
-    public ChunkResolution Resolution { get; set; }
 
     public SceneRenderer(IReadOnlyDocument trackerDocument, IDocument documentViewModel)
     {
         Document = trackerDocument;
         DocumentViewModel = documentViewModel;
-        Resolution = ChunkResolution.Full;
     }
 
-    public void RenderScene(DrawingSurface target)
+    public void RenderScene(DrawingSurface target, ChunkResolution resolution)
     {
-        RenderOnionSkin(target);
-        //RenderContext ctx = new(target, DocumentViewModel.AnimationHandler.ActiveFrameTime, Resolution, Document.Size); 
-        //Document.NodeGraph.Execute(ctx);
+        RenderOnionSkin(target, resolution);
+        RenderGraph(target, resolution);
     }
 
-    private void RenderOnionSkin(DrawingSurface target)
+    private void RenderGraph(DrawingSurface target, ChunkResolution resolution)
+    {
+        RenderContext context = new(target, DocumentViewModel.AnimationHandler.ActiveFrameTime,
+            resolution, Document.Size);
+        Document.NodeGraph.Execute(context);
+    }
+
+    private void RenderOnionSkin(DrawingSurface target, ChunkResolution resolution)
     {
         var animationData = Document.AnimationData;
         if (!DocumentViewModel.AnimationHandler.OnionSkinningEnabledBindable)
         {
             return;
         }
-        
+
         double onionOpacity = animationData.OnionOpacity / 100.0;
         double alphaFalloffMultiplier = 1.0 / animationData.OnionFrames;
-        
+
         // Render previous frames'
         for (int i = 1; i <= animationData.OnionFrames; i++)
         {
@@ -48,22 +52,22 @@ internal class SceneRenderer
             }
 
             double finalOpacity = onionOpacity * alphaFalloffMultiplier * (animationData.OnionFrames - i + 1);
-            
-            RenderContext onionContext = new(target, frame, Resolution, Document.Size, finalOpacity);
+
+            RenderContext onionContext = new(target, frame, resolution, Document.Size, finalOpacity);
             Document.NodeGraph.Execute(onionContext);
         }
-        
+
         // Render next frames
-        for (int i = 1; i <= animationData.OnionFrames; i++) 
+        for (int i = 1; i <= animationData.OnionFrames; i++)
         {
             int frame = DocumentViewModel.AnimationHandler.ActiveFrameTime.Frame + i;
             if (frame > DocumentViewModel.AnimationHandler.LastFrame)
             {
                 break;
             }
-            
+
             double finalOpacity = onionOpacity * alphaFalloffMultiplier * (animationData.OnionFrames - i + 1);
-            RenderContext onionContext = new(target, frame, Resolution, Document.Size, finalOpacity);
+            RenderContext onionContext = new(target, frame, resolution, Document.Size, finalOpacity);
             Document.NodeGraph.Execute(onionContext);
         }
     }

+ 0 - 22
src/PixiEditor/Views/Main/ViewportControls/Viewport.axaml

@@ -143,28 +143,6 @@
             DefaultCursor="{Binding Source={viewModels:MainVM}, Path=ToolsSubViewModel.ToolCursor, Mode=OneWay}"
             CheckerImagePath="/Images/CheckerTile.png"
             ui:RenderOptionsBindable.BitmapInterpolationMode="{Binding Scale, Converter={converters:ScaleToBitmapScalingModeConverter}, RelativeSource={RelativeSource Self}}" />
-
-        <!--Brush shape overlay is rendered separately, so it doesn't trigger rerender each mouse movement to scene-->
-        <!--I didn't measure it, but I thought that could impact performance-->
-        <brushShapeOverlay:BrushShapeOverlay
-            DataContext="{Binding ElementName=vpUc}"
-            RenderTransform="{Binding #scene.CanvasTransform}"
-            RenderTransformOrigin="0, 0"
-            Name="brushShapeOverlay"
-            Focusable="False" ZIndex="6"
-            IsHitTestVisible="False"
-            ZoomScale="{Binding #scene.Scale}"
-            Scene="{Binding #scene, Mode=OneTime}"
-            BrushSize="{Binding ToolsSubViewModel.ActiveBasicToolbar.ToolSize, Source={viewModels:MainVM}}"
-            BrushShape="{Binding ToolsSubViewModel.ActiveTool.BrushShape, Source={viewModels:MainVM}, FallbackValue={x:Static brushShapeOverlay:BrushShape.Hidden}}"
-            FlowDirection="LeftToRight">
-            <brushShapeOverlay:BrushShapeOverlay.IsVisible>
-                <MultiBinding Converter="{converters:AllTrueConverter}">
-                    <Binding Path="!Document.TransformViewModel.TransformActive" />
-                    <Binding Path="IsOverCanvas" />
-                </MultiBinding>
-            </brushShapeOverlay:BrushShapeOverlay.IsVisible>
-        </brushShapeOverlay:BrushShapeOverlay>
         <Button
             ZIndex="99999"
             DockPanel.Dock="Bottom"

+ 0 - 6
src/PixiEditor/Views/Main/ViewportControls/Viewport.axaml.cs

@@ -340,16 +340,10 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
 
     private void OnLoad(object? sender, RoutedEventArgs e)
     {
-        InitializeOverlays();
         Document?.Operations.AddOrUpdateViewport(GetLocation());
         mouseUpdateController = new MouseUpdateController(this, Image_MouseMove);
     }
 
-    private void InitializeOverlays()
-    {
-        brushShapeOverlay.Initialize();
-    }
-
     private static void OnDocumentChange(AvaloniaPropertyChangedEventArgs<DocumentViewModel> e)
     {
         Viewport? viewport = (Viewport)e.Sender;

+ 124 - 89
src/PixiEditor/Views/Main/ViewportControls/ViewportOverlays.cs

@@ -8,7 +8,9 @@ using PixiEditor.Helpers.Converters;
 using PixiEditor.Models.Commands.XAML;
 using PixiEditor.ViewModels;
 using PixiEditor.Views.Overlays;
+using PixiEditor.Views.Overlays.BrushShapeOverlay;
 using PixiEditor.Views.Overlays.LineToolOverlay;
+using PixiEditor.Views.Overlays.Pointers;
 using PixiEditor.Views.Overlays.SelectionOverlay;
 using PixiEditor.Views.Overlays.SymmetryOverlay;
 using PixiEditor.Views.Overlays.TransformOverlay;
@@ -26,6 +28,7 @@ internal class ViewportOverlays
     private TransformOverlay transformOverlay;
     private ReferenceLayerOverlay referenceLayerOverlay;
     private SnappingOverlay snappingOverlay;
+    private BrushShapeOverlay brushShapeOverlay;
 
     public void Init(Viewport viewport)
     {
@@ -47,10 +50,12 @@ internal class ViewportOverlays
 
         referenceLayerOverlay = new ReferenceLayerOverlay();
         BindReferenceLayerOverlay();
-        
+
         snappingOverlay = new SnappingOverlay();
         BindSnappingOverlay();
-        
+
+        brushShapeOverlay = new BrushShapeOverlay();
+        BindMouseOverlayPointer();
 
         Viewport.ActiveOverlays.Add(gridLinesOverlay);
         Viewport.ActiveOverlays.Add(referenceLayerOverlay);
@@ -59,6 +64,7 @@ internal class ViewportOverlays
         Viewport.ActiveOverlays.Add(lineToolOverlay);
         Viewport.ActiveOverlays.Add(transformOverlay);
         Viewport.ActiveOverlays.Add(snappingOverlay);
+        Viewport.ActiveOverlays.Add(brushShapeOverlay);
     }
 
     private void BindReferenceLayerOverlay()
@@ -72,9 +78,7 @@ internal class ViewportOverlays
 
         Binding referenceLayerBinding = new()
         {
-            Source = Viewport,
-            Path = "Document.ReferenceLayerViewModel",
-            Mode = BindingMode.OneWay
+            Source = Viewport, Path = "Document.ReferenceLayerViewModel", Mode = BindingMode.OneWay
         };
 
         Binding referenceShapeBinding = new()
@@ -99,29 +103,14 @@ internal class ViewportOverlays
 
     private void BindGridLines()
     {
-        Binding isVisBinding = new()
-        {
-            Source = Viewport,
-            Path = "GridLinesVisible",
-            Mode = BindingMode.OneWay
-        };
+        Binding isVisBinding = new() { Source = Viewport, Path = "GridLinesVisible", Mode = BindingMode.OneWay };
 
-        Binding binding = new()
-        {
-            Source = Viewport,
-            Path = "Document.Width",
-            Mode = BindingMode.OneWay
-        };
+        Binding binding = new() { Source = Viewport, Path = "Document.Width", Mode = BindingMode.OneWay };
 
         gridLinesOverlay.Bind(GridLinesOverlay.PixelWidthProperty, binding);
         gridLinesOverlay.Bind(GridLinesOverlay.ColumnsProperty, binding);
 
-        binding = new Binding
-        {
-            Source = Viewport,
-            Path = "Document.Height",
-            Mode = BindingMode.OneWay
-        };
+        binding = new Binding { Source = Viewport, Path = "Document.Height", Mode = BindingMode.OneWay };
 
         gridLinesOverlay.Bind(GridLinesOverlay.PixelHeightProperty, binding);
         gridLinesOverlay.Bind(GridLinesOverlay.RowsProperty, binding);
@@ -140,9 +129,7 @@ internal class ViewportOverlays
 
         Binding pathBinding = new()
         {
-            Source = Viewport,
-            Path = "Document.SelectionPathBindable",
-            Mode = BindingMode.OneWay
+            Source = Viewport, Path = "Document.SelectionPathBindable", Mode = BindingMode.OneWay
         };
 
         Binding isVisibleBinding = new()
@@ -160,13 +147,34 @@ internal class ViewportOverlays
 
     private void BindSymmetryOverlay()
     {
-        Binding isVisibleBinding = new() {Source = Viewport, Path = "Document.AnySymmetryAxisEnabledBindable", Mode = BindingMode.OneWay };
+        Binding isVisibleBinding = new()
+        {
+            Source = Viewport, Path = "Document.AnySymmetryAxisEnabledBindable", Mode = BindingMode.OneWay
+        };
         Binding sizeBinding = new() { Source = Viewport, Path = "Document.SizeBindable", Mode = BindingMode.OneWay };
-        Binding isHitTestVisibleBinding = new() {Source = Viewport, Path = "ZoomMode", Converter = new ZoomModeToHitTestVisibleConverter(), Mode = BindingMode.OneWay };
-        Binding horizontalAxisVisibleBinding = new() {Source = Viewport, Path = "Document.HorizontalSymmetryAxisEnabledBindable", Mode = BindingMode.OneWay };
-        Binding verticalAxisVisibleBinding = new() {Source = Viewport, Path = "Document.VerticalSymmetryAxisEnabledBindable", Mode = BindingMode.OneWay };
-        Binding horizontalAxisYBinding = new() {Source = Viewport, Path = "Document.HorizontalSymmetryAxisYBindable", Mode = BindingMode.OneWay };
-        Binding verticalAxisXBinding = new() {Source = Viewport, Path = "Document.VerticalSymmetryAxisXBindable", Mode = BindingMode.OneWay };
+        Binding isHitTestVisibleBinding = new()
+        {
+            Source = Viewport,
+            Path = "ZoomMode",
+            Converter = new ZoomModeToHitTestVisibleConverter(),
+            Mode = BindingMode.OneWay
+        };
+        Binding horizontalAxisVisibleBinding = new()
+        {
+            Source = Viewport, Path = "Document.HorizontalSymmetryAxisEnabledBindable", Mode = BindingMode.OneWay
+        };
+        Binding verticalAxisVisibleBinding = new()
+        {
+            Source = Viewport, Path = "Document.VerticalSymmetryAxisEnabledBindable", Mode = BindingMode.OneWay
+        };
+        Binding horizontalAxisYBinding = new()
+        {
+            Source = Viewport, Path = "Document.HorizontalSymmetryAxisYBindable", Mode = BindingMode.OneWay
+        };
+        Binding verticalAxisXBinding = new()
+        {
+            Source = Viewport, Path = "Document.VerticalSymmetryAxisXBindable", Mode = BindingMode.OneWay
+        };
 
         symmetryOverlay.Bind(Visual.IsVisibleProperty, isVisibleBinding);
         symmetryOverlay.Bind(SymmetryOverlay.SizeProperty, sizeBinding);
@@ -175,25 +183,24 @@ internal class ViewportOverlays
         symmetryOverlay.Bind(SymmetryOverlay.VerticalAxisVisibleProperty, verticalAxisVisibleBinding);
         symmetryOverlay.Bind(SymmetryOverlay.HorizontalAxisYProperty, horizontalAxisYBinding);
         symmetryOverlay.Bind(SymmetryOverlay.VerticalAxisXProperty, verticalAxisXBinding);
-        symmetryOverlay.DragCommand = (ICommand)new Command("PixiEditor.Document.DragSymmetry") { UseProvided = true }.ProvideValue(null);
-        symmetryOverlay.DragEndCommand = (ICommand)new Command("PixiEditor.Document.EndDragSymmetry") { UseProvided = true }.ProvideValue(null);
-        symmetryOverlay.DragStartCommand = (ICommand)new Command("PixiEditor.Document.StartDragSymmetry") { UseProvided = true }.ProvideValue(null);
+        symmetryOverlay.DragCommand =
+            (ICommand)new Command("PixiEditor.Document.DragSymmetry") { UseProvided = true }.ProvideValue(null);
+        symmetryOverlay.DragEndCommand =
+            (ICommand)new Command("PixiEditor.Document.EndDragSymmetry") { UseProvided = true }.ProvideValue(null);
+        symmetryOverlay.DragStartCommand =
+            (ICommand)new Command("PixiEditor.Document.StartDragSymmetry") { UseProvided = true }.ProvideValue(null);
     }
 
     private void BindLineToolOverlay()
     {
         Binding isVisibleBinding = new()
         {
-            Source = Viewport,
-            Path = "Document.LineToolOverlayViewModel.IsEnabled",
-            Mode = BindingMode.OneWay
+            Source = Viewport, Path = "Document.LineToolOverlayViewModel.IsEnabled", Mode = BindingMode.OneWay
         };
-        
+
         Binding snappingBinding = new()
         {
-            Source = Viewport,
-            Path = "Document.SnappingViewModel.SnappingController",
-            Mode = BindingMode.OneWay
+            Source = Viewport, Path = "Document.SnappingViewModel.SnappingController", Mode = BindingMode.OneWay
         };
 
         Binding actionCompletedBinding = new()
@@ -205,18 +212,14 @@ internal class ViewportOverlays
 
         Binding lineStartBinding = new()
         {
-            Source = Viewport,
-            Path = "Document.LineToolOverlayViewModel.LineStart",
-            Mode = BindingMode.TwoWay
+            Source = Viewport, Path = "Document.LineToolOverlayViewModel.LineStart", Mode = BindingMode.TwoWay
         };
 
         Binding lineEndBinding = new()
         {
-            Source = Viewport,
-            Path = "Document.LineToolOverlayViewModel.LineEnd",
-            Mode = BindingMode.TwoWay
+            Source = Viewport, Path = "Document.LineToolOverlayViewModel.LineEnd", Mode = BindingMode.TwoWay
         };
-        
+
         lineToolOverlay.Bind(Visual.IsVisibleProperty, isVisibleBinding);
         lineToolOverlay.Bind(LineToolOverlay.SnappingControllerProperty, snappingBinding);
         lineToolOverlay.Bind(LineToolOverlay.ActionCompletedProperty, actionCompletedBinding);
@@ -228,16 +231,12 @@ internal class ViewportOverlays
     {
         Binding isVisibleBinding = new()
         {
-            Source = Viewport,
-            Path = "Document.TransformViewModel.TransformActive",
-            Mode = BindingMode.OneWay
+            Source = Viewport, Path = "Document.TransformViewModel.TransformActive", Mode = BindingMode.OneWay
         };
-        
+
         Binding snappingBinding = new()
         {
-            Source = Viewport,
-            Path = "Document.SnappingViewModel.SnappingController",
-            Mode = BindingMode.OneWay
+            Source = Viewport, Path = "Document.SnappingViewModel.SnappingController", Mode = BindingMode.OneWay
         };
 
         Binding actionCompletedBinding = new()
@@ -249,65 +248,45 @@ internal class ViewportOverlays
 
         Binding cornersBinding = new()
         {
-            Source = Viewport,
-            Path = "Document.TransformViewModel.Corners",
-            Mode = BindingMode.TwoWay
+            Source = Viewport, Path = "Document.TransformViewModel.Corners", Mode = BindingMode.TwoWay
         };
 
         Binding requestedCornersBinding = new()
         {
-            Source = Viewport,
-            Path = "Document.TransformViewModel.RequestCornersExecutor",
+            Source = Viewport, Path = "Document.TransformViewModel.RequestCornersExecutor",
         };
 
         Binding cornerFreedomBinding = new()
         {
-            Source = Viewport,
-            Path = "Document.TransformViewModel.CornerFreedom",
-            Mode = BindingMode.OneWay
+            Source = Viewport, Path = "Document.TransformViewModel.CornerFreedom", Mode = BindingMode.OneWay
         };
 
         Binding sideFreedomBinding = new()
         {
-            Source = Viewport,
-            Path = "Document.TransformViewModel.SideFreedom",
-            Mode = BindingMode.OneWay
+            Source = Viewport, Path = "Document.TransformViewModel.SideFreedom", Mode = BindingMode.OneWay
         };
 
         Binding lockRotationBinding = new()
         {
-            Source = Viewport,
-            Path = "Document.TransformViewModel.LockRotation",
-            Mode = BindingMode.OneWay
+            Source = Viewport, Path = "Document.TransformViewModel.LockRotation", Mode = BindingMode.OneWay
         };
 
         Binding coverWholeScreenBinding = new()
         {
-            Source = Viewport,
-            Path = "Document.TransformViewModel.CoverWholeScreen",
-            Mode = BindingMode.OneWay
+            Source = Viewport, Path = "Document.TransformViewModel.CoverWholeScreen", Mode = BindingMode.OneWay
         };
 
         Binding snapToAnglesBinding = new()
         {
-            Source = Viewport,
-            Path = "Document.TransformViewModel.SnapToAngles",
-            Mode = BindingMode.OneWay
+            Source = Viewport, Path = "Document.TransformViewModel.SnapToAngles", Mode = BindingMode.OneWay
         };
 
         Binding internalStateBinding = new()
         {
-            Source = Viewport,
-            Path = "Document.TransformViewModel.InternalState",
-            Mode = BindingMode.TwoWay
+            Source = Viewport, Path = "Document.TransformViewModel.InternalState", Mode = BindingMode.TwoWay
         };
 
-        Binding zoomboxAngleBinding = new()
-        {
-            Source = Viewport,
-            Path = "Zoombox.Angle",
-            Mode = BindingMode.OneWay
-        };
+        Binding zoomboxAngleBinding = new() { Source = Viewport, Path = "Zoombox.Angle", Mode = BindingMode.OneWay };
 
         transformOverlay.Bind(Visual.IsVisibleProperty, isVisibleBinding);
         transformOverlay.Bind(TransformOverlay.ActionCompletedProperty, actionCompletedBinding);
@@ -327,11 +306,67 @@ internal class ViewportOverlays
     {
         Binding snappingControllerBinding = new()
         {
-            Source = Viewport,
-            Path = "Document.SnappingViewModel.SnappingController",
-            Mode = BindingMode.OneWay
+            Source = Viewport, Path = "Document.SnappingViewModel.SnappingController", Mode = BindingMode.OneWay
         };
-        
+
         snappingOverlay.Bind(SnappingOverlay.SnappingControllerProperty, snappingControllerBinding);
     }
+
+/**  <brushShapeOverlay:BrushShapeOverlay
+               DataContext="{Binding ElementName=vpUc}"
+               RenderTransform="{Binding #scene.CanvasTransform}"
+               RenderTransformOrigin="0, 0"
+               Name="brushShapeOverlay"
+               Focusable="False" ZIndex="6"
+               IsHitTestVisible="False"
+               ZoomScale="{Binding #scene.Scale}"
+               Scene="{Binding #scene, Mode=OneTime}"
+               BrushSize="{Binding ToolsSubViewModel.ActiveBasicToolbar.ToolSize, Source={viewModels:MainVM}}"
+               BrushShape="{Binding ToolsSubViewModel.ActiveTool.BrushShape, Source={viewModels:MainVM}, FallbackValue={x:Static brushShapeOverlay:BrushShape.Hidden}}"
+               FlowDirection="LeftToRight">
+               <brushShapeOverlay:BrushShapeOverlay.IsVisible>
+                   <MultiBinding Converter="{converters:AllTrueConverter}">
+                       <Binding Path="!Document.TransformViewModel.TransformActive" />
+                       <Binding Path="IsOverCanvas" />
+                   </MultiBinding>
+               </brushShapeOverlay:BrushShapeOverlay.IsVisible>
+           </brushShapeOverlay:BrushShapeOverlay>*/
+    private void BindMouseOverlayPointer()
+    {
+        Binding isTransformingBinding = new()
+        {
+            Source = Viewport, Path = "!Document.TransformViewModel.TransformActive", Mode = BindingMode.OneWay
+        };
+
+        Binding isOverCanvasBinding = new()
+        {
+            Source = Viewport, Path = "IsOverCanvas", Mode = BindingMode.OneWay
+        };
+
+        Binding brushSizeBinding = new()
+        {
+            Source = ViewModelMain.Current.ToolsSubViewModel, Path = "ActiveBasicToolbar.ToolSize", Mode = BindingMode.OneWay
+        };
+
+        Binding brushShapeBinding = new()
+        {
+            Source = ViewModelMain.Current.ToolsSubViewModel, Path = "ActiveTool.BrushShape", Mode = BindingMode.OneWay
+        };
+        
+        MultiBinding isVisibleMultiBinding = new()
+        {
+            Converter = new AllTrueConverter(),
+            Mode = BindingMode.OneWay,
+            Bindings = new List<IBinding>() 
+            {
+                isTransformingBinding,
+                isOverCanvasBinding
+            }
+        };
+
+        brushShapeOverlay.Bind(Visual.IsVisibleProperty, isVisibleMultiBinding);
+        brushShapeOverlay.Bind(BrushShapeOverlay.BrushSizeProperty, brushSizeBinding);
+        brushShapeOverlay.Bind(BrushShapeOverlay.BrushShapeProperty, brushShapeBinding); 
+    }
 }
+

+ 78 - 86
src/PixiEditor/Views/Overlays/BrushShapeOverlay/BrushShapeOverlay.cs

@@ -1,18 +1,13 @@
-using System.Collections.Generic;
-using System.Linq;
-using Avalonia;
-using Avalonia.Controls;
-using Avalonia.Input;
-using Avalonia.Interactivity;
-using Avalonia.Media;
+using Avalonia;
 using ChunkyImageLib.Operations;
-using PixiEditor.Views.Visuals;
 using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Backend.Core.Surfaces.Vector;
 using PixiEditor.Extensions.UI.Overlays;
-using PixiEditor.Models.Controllers.InputDevice;
 using Drawie.Numerics;
 using PixiEditor.Views.Rendering;
-using Point = Avalonia.Point;
+using Canvas = Drawie.Backend.Core.Surfaces.Canvas;
+using Colors = Drawie.Backend.Core.ColorsImpl.Colors;
 
 namespace PixiEditor.Views.Overlays.BrushShapeOverlay;
 #nullable enable
@@ -38,131 +33,129 @@ internal class BrushShapeOverlay : Overlay
         get => (BrushShape)GetValue(BrushShapeProperty);
         set => SetValue(BrushShapeProperty, value);
     }
-    
+
     public int BrushSize
     {
         get => (int)GetValue(BrushSizeProperty);
         set => SetValue(BrushSizeProperty, value);
     }
 
-    private Pen whitePen = new Pen(Brushes.LightGray, 1);
+    private Paint paint = new Paint() { Color = Colors.LightGray, StrokeWidth = 1, Style = PaintStyle.Stroke};
     private VecD lastMousePos = new();
 
-    private MouseUpdateController? mouseUpdateController;
+    private VectorPath threePixelCircle;
+    private int lastSize;
+    private VectorPath lastNonTranslatedCircle;
+
 
     static BrushShapeOverlay()
     {
-        AffectsRender<BrushShapeOverlay>(BrushShapeProperty);
-        AffectsRender<BrushShapeOverlay>(BrushSizeProperty);
+        AffectsOverlayRender(BrushShapeProperty, BrushSizeProperty);
     }
 
     public BrushShapeOverlay()
     {
-        Unloaded += ControlUnloaded;
-    }
-
-    private void ControlUnloaded(object? sender, RoutedEventArgs e)
-    {
-        if (Scene is null)
-            return;
-
-        mouseUpdateController?.Dispose();
+        threePixelCircle = CreateThreePixelCircle();
     }
 
-    public void Initialize()
+    protected override void OnOverlayPointerMoved(OverlayPointerArgs args)
     {
-        if (Scene is null)
+        if (BrushShape == BrushShape.Hidden)
             return;
 
-        mouseUpdateController = new MouseUpdateController(Scene, SourceMouseMove);
+        VecD rawPoint = args.Point;
+        lastMousePos = rawPoint; 
+        Refresh();
     }
 
-    private void SourceMouseMove(PointerEventArgs args)
-    {
-        if (Scene is null || BrushShape == BrushShape.Hidden) 
-            return;
-
-        Point rawPoint = args.GetPosition(Scene);
-        lastMousePos = Scene.ToZoomboxSpace(new VecD(rawPoint.X, rawPoint.Y));
-        InvalidateVisual();
-    }
-    
-    public override void RenderOverlay(DrawingContext context, RectD canvasBounds) => Render(context);
+    public override void RenderOverlay(Canvas context, RectD canvasBounds) => Render(context);
 
-    public override void Render(DrawingContext drawingContext)
+    public void Render(Canvas targetCanvas)
     {
-        var winRect = new Rect(
-            (Point)(new Point(Math.Floor(lastMousePos.X), Math.Floor(lastMousePos.Y)) - new Point(BrushSize / 2, BrushSize / 2)),
-            new Size(BrushSize, BrushSize));
+        var winRect = new RectD(
+            (VecD)(new VecD(Math.Floor(lastMousePos.X), Math.Floor(lastMousePos.Y)) -
+                   new VecD(BrushSize / 2, BrushSize / 2)),
+            new VecD(BrushSize, BrushSize));
         switch (BrushShape)
         {
             case BrushShape.Pixel:
-                drawingContext.DrawRectangle(
-                    null, whitePen, new Rect(new Point(Math.Floor(lastMousePos.X), Math.Floor(lastMousePos.Y)), new Size(1, 1)));
+                targetCanvas.DrawRect(
+                    new RectD(new VecD(Math.Floor(lastMousePos.X), Math.Floor(lastMousePos.Y)), new VecD(1, 1)),
+                    paint);
                 break;
             case BrushShape.Square:
-                drawingContext.DrawRectangle(null, whitePen, winRect);
+                targetCanvas.DrawRect(winRect, paint);
                 break;
             case BrushShape.Circle:
-                DrawCircleBrushShape(drawingContext, winRect);
+                DrawCircleBrushShape(targetCanvas, winRect);
                 break;
         }
     }
 
-    private void DrawCircleBrushShape(DrawingContext drawingContext, Rect winRect)
+    private void DrawCircleBrushShape(Canvas drawingContext, RectD winRect)
     {
         var rectI = new RectI((int)winRect.X, (int)winRect.Y, (int)winRect.Width, (int)winRect.Height);
         if (BrushSize < 3)
         {
-            drawingContext.DrawRectangle(null, whitePen, winRect);
+            drawingContext.DrawRect(winRect, paint);
         }
         else if (BrushSize == 3)
         {
             var lp = new VecI((int)lastMousePos.X, (int)lastMousePos.Y);
-            PathFigure figure = new PathFigure()
-            {
-                StartPoint = new Point(lp.X, lp.Y),
-                Segments = new PathSegments()
-                {
-                    new LineSegment { Point = new Point(lp.X, lp.Y - 1) },
-                    new LineSegment { Point = new Point(lp.X + 1, lp.Y - 1) },
-                    new LineSegment { Point = new Point(lp.X + 1, lp.Y) },
-                    new LineSegment { Point = new Point(lp.X + 2, lp.Y) },
-                    new LineSegment { Point = new Point(lp.X + 2, lp.Y + 1) },
-                    new LineSegment { Point = new Point(lp.X + 2, lp.Y + 1) },
-                    new LineSegment { Point = new Point(lp.X + 1, lp.Y + 1) },
-                    new LineSegment { Point = new Point(lp.X + 1, lp.Y + 2) },
-                    new LineSegment { Point = new Point(lp.X, lp.Y + 2) },
-                    new LineSegment { Point = new Point(lp.X, lp.Y + 1) },
-                    new LineSegment { Point = new Point(lp.X - 1, lp.Y + 1) },
-                    new LineSegment { Point = new Point(lp.X - 1, lp.Y) }
-                },
-                IsClosed = true
-            };
-
-            var geometry = new PathGeometry() { Figures = new PathFigures() { figure } };
-            drawingContext.DrawGeometry(null, whitePen, geometry);
+            using VectorPath shifted = new VectorPath(threePixelCircle);
+            shifted.Transform(Matrix3X3.CreateTranslation(lp.X, lp.Y));
+            drawingContext.DrawPath(shifted, paint);
         }
         else if (BrushSize > 200)
         {
             VecD center = rectI.Center;
-            drawingContext.DrawEllipse(null, whitePen, new Point(center.X, center.Y), rectI.Width / 2.0, rectI.Height / 2.0);
+            drawingContext.DrawOval(new VecD(center.X, center.Y), new VecD(rectI.Width / 2.0, rectI.Height / 2.0),
+                paint);
         }
         else
         {
-            var geometry = ConstructEllipseOutline(rectI);
-            drawingContext.DrawGeometry(null, whitePen, geometry);
+            if (BrushSize != lastSize)
+            {
+                var geometry = ConstructEllipseOutline(new RectI(0, 0, rectI.Width, rectI.Height));
+                lastNonTranslatedCircle = new VectorPath(geometry);
+                lastSize = BrushSize;
+            }
+
+            var lp = new VecI((int)lastMousePos.X, (int)lastMousePos.Y);
+            using VectorPath shifted = new VectorPath(lastNonTranslatedCircle);
+            shifted.Transform(Matrix3X3.CreateTranslation(lp.X - rectI.Width / 2, lp.Y - rectI.Height / 2)); // don't use float, truncation is intended 
+            drawingContext.DrawPath(shifted, paint);
         }
     }
 
     protected override void ZoomChanged(double newZoom)
     {
-        whitePen.Thickness = 1.0 / newZoom;
+        paint.StrokeWidth = (float)(1.0f / newZoom);
     }
 
     private static int Mod(int x, int m) => (x % m + m) % m;
 
-    private static PathGeometry ConstructEllipseOutline(RectI rectangle)
+    private static VectorPath CreateThreePixelCircle()
+    {
+        var path = new VectorPath();
+        path.MoveTo(new VecF(0, 0));
+        path.LineTo(new VecF(0, -1));
+        path.LineTo(new VecF(1, -1));
+        path.LineTo(new VecF(1, 0));
+        path.LineTo(new VecF(2, 0));
+        path.LineTo(new VecF(2, 1));
+        path.LineTo(new VecF(2, 1));
+        path.LineTo(new VecF(1, 1));
+        path.LineTo(new VecF(1, 2));
+        path.LineTo(new VecF(0, 2));
+        path.LineTo(new VecF(0, 1));
+        path.LineTo(new VecF(-1, 1));
+        path.LineTo(new VecF(-1, 0));
+        path.Close();
+        return path;
+    }
+
+    private static VectorPath ConstructEllipseOutline(RectI rectangle)
     {
         var center = rectangle.Center;
         var points = EllipseHelper.GenerateEllipseFromRect(rectangle, 0).ToList();
@@ -185,7 +178,6 @@ internal class BrushShapeOverlay : Overlay
                     finalPoints.Add(new(point.X + 1, point.Y + 1));
                     if (next.X != point.X)
                         finalPoints.Add(new(point.X, point.Y + 1));
-
                 }
                 else
                 {
@@ -217,17 +209,17 @@ internal class BrushShapeOverlay : Overlay
             }
         }
 
-        PathSegments segments = new();
-        segments.AddRange(finalPoints.Select(static point => new LineSegment { Point = new(point.X, point.Y) }));
+        VectorPath path = new();
 
-        PathFigure figure = new PathFigure()
+        path.MoveTo(new VecF(finalPoints[0].X, finalPoints[0].Y));
+        for (var index = 1; index < finalPoints.Count; index++)
         {
-            StartPoint = new Point(finalPoints[0].X, finalPoints[0].Y),
-            Segments = segments,
-            IsClosed = true
-        };
+            var point = finalPoints[index];
+            path.LineTo(new VecF(point.X, point.Y));
+        }
+
+        path.Close();
 
-        var geometry = new PathGeometry() { Figures = new PathFigures() { figure }};
-        return geometry;
+        return path;
     }
 }

+ 4 - 3
src/PixiEditor/Views/Overlays/GridLinesOverlay.cs

@@ -1,6 +1,7 @@
 using Avalonia;
 using Avalonia.Media;
 using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces;
 using PixiEditor.Helpers.Converters;
 using Drawie.Numerics;
 using Point = Avalonia.Point;
@@ -65,11 +66,11 @@ public class GridLinesOverlay : Overlay
         return visibilityConverter.Check(ZoomScale);
     }
 
-    public override void RenderOverlay(DrawingContext context, RectD canvasBounds)
+    public override void RenderOverlay(Canvas context, RectD canvasBounds)
     {
         // Draw lines in vertical and horizontal directions, size should be relative to the scale
 
-        base.Render(context);
+        /*base.Render(context);
         double width = PixelWidth;
         double height = PixelHeight;
 
@@ -91,7 +92,7 @@ public class GridLinesOverlay : Overlay
             double y = i * rowHeight;
             context.DrawLine(pen1, new Point(0, y), new Point(width, y));
             context.DrawLine(pen2, new Point(0, y), new Point(width, y));
-        }
+        }*/
     }
 
     private static void OnIsVisibleChanged(AvaloniaPropertyChangedEventArgs<bool> e)

+ 4 - 3
src/PixiEditor/Views/Overlays/LineToolOverlay/LineToolOverlay.cs

@@ -5,6 +5,7 @@ using Avalonia.Media;
 using ChunkyImageLib.DataHolders;
 using PixiEditor.Models.Controllers.InputDevice;
 using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces;
 using PixiEditor.Extensions.UI.Overlays;
 using Drawie.Numerics;
 using PixiEditor.Views.Overlays.Handles;
@@ -118,7 +119,7 @@ internal class LineToolOverlay : Overlay
         whiteDashedPen.Thickness = 2.0 / newZoom;
     }
 
-    public override void RenderOverlay(DrawingContext context, RectD canvasBounds)
+    public override void RenderOverlay(Canvas context, RectD canvasBounds)
     {
         VecD mappedStart = LineStart;
         VecD mappedEnd = LineEnd;
@@ -131,12 +132,12 @@ internal class LineToolOverlay : Overlay
         
         moveHandle.Position = TransformHelper.GetHandlePos(new ShapeCorners(center, size), ZoomScale, moveHandle.Size);
 
-        context.DrawLine(blackDashedPen, new Point(mappedStart.X, mappedStart.Y), new Point(mappedEnd.X, mappedEnd.Y));
+        /*context.DrawLine(blackDashedPen, new Point(mappedStart.X, mappedStart.Y), new Point(mappedEnd.X, mappedEnd.Y));
         context.DrawLine(whiteDashedPen, new Point(mappedStart.X, mappedStart.Y), new Point(mappedEnd.X, mappedEnd.Y));
         
         startHandle.Draw(context);
         endHandle.Draw(context);
-        moveHandle.Draw(context);
+        moveHandle.Draw(context);*/
     }
 
     protected override void OnOverlayPointerPressed(OverlayPointerArgs args)

+ 17 - 1
src/PixiEditor/Views/Overlays/Overlay.cs

@@ -10,10 +10,12 @@ using Avalonia.Media;
 using Avalonia.Styling;
 using Avalonia.Threading;
 using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces;
 using PixiEditor.Extensions.UI.Overlays;
 using Drawie.Numerics;
 using PixiEditor.Views.Overlays.Handles;
 using PixiEditor.Views.Overlays.Transitions;
+using Canvas = Drawie.Backend.Core.Surfaces.Canvas;
 
 namespace PixiEditor.Views.Overlays;
 
@@ -56,7 +58,7 @@ public abstract class Overlay : Decorator, IOverlay // TODO: Maybe make it not a
 
     public virtual bool CanRender() => true;
     
-    public abstract void RenderOverlay(DrawingContext context, RectD canvasBounds);
+    public abstract void RenderOverlay(Canvas context, RectD canvasBounds);
 
     public void Refresh()
     {
@@ -218,6 +220,20 @@ public abstract class Overlay : Decorator, IOverlay // TODO: Maybe make it not a
             Refresh();
         }
     }
+    
+    protected static void AffectsOverlayRender(params AvaloniaProperty[] properties)
+    {
+        foreach (var property in properties)
+        {
+            property.Changed.Subscribe((args) =>
+            {
+                if (args.Sender is Overlay overlay)
+                {
+                    overlay.Refresh();
+                }
+            });
+        }
+    }
 }
 
 public enum OverlayRenderSorting

+ 5 - 4
src/PixiEditor/Views/Overlays/ReferenceLayerOverlay.cs

@@ -17,6 +17,7 @@ using PixiEditor.Helpers.Converters;
 using Drawie.Numerics;
 using PixiEditor.ViewModels.Document;
 using PixiEditor.Views.Visuals;
+using Canvas = Drawie.Backend.Core.Surfaces.Canvas;
 using Color = Drawie.Backend.Core.ColorsImpl.Color;
 
 namespace PixiEditor.Views.Overlays;
@@ -77,9 +78,9 @@ internal class ReferenceLayerOverlay : Overlay
         FadeOutProperty.Changed.Subscribe(FadeOutChanged);
     }
 
-    public override void RenderOverlay(DrawingContext context, RectD dirtyCanvasBounds)
+    public override void RenderOverlay(Canvas context, RectD dirtyCanvasBounds)
     {
-        if (ReferenceLayer is { ReferenceBitmap: not null })
+        /*if (ReferenceLayer is { ReferenceBitmap: not null })
         {
             using var renderOptions = context.PushRenderOptions(new RenderOptions
             {
@@ -99,14 +100,14 @@ internal class ReferenceLayerOverlay : Overlay
             
             // TODO: Implement this
             /*DrawTextureOperation drawOperation =
-                new DrawTextureOperation(dirtyRect, Stretch.None, referenceBitmap, overlayPaint);*/
+                new DrawTextureOperation(dirtyRect, Stretch.None, referenceBitmap, overlayPaint);#1#
 
             //context.Custom(drawOperation);
 
             matrix.Dispose();
 
             DrawBorder(context, dirtyCanvasBounds);
-        }
+        }*/
     }
 
     private void DrawBorder(DrawingContext context, RectD dirtyCanvasBounds)

+ 4 - 3
src/PixiEditor/Views/Overlays/SelectionOverlay/SelectionOverlay.cs

@@ -6,6 +6,7 @@ using Avalonia.Media;
 using Avalonia.Styling;
 using PixiEditor.Animation;
 using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces;
 using Drawie.Backend.Core.Surfaces.Vector;
 using Drawie.Numerics;
 
@@ -82,9 +83,9 @@ internal class SelectionOverlay : Overlay
         }
     }
 
-    public override void RenderOverlay(DrawingContext context, RectD canvasBounds)
+    public override void RenderOverlay(Canvas context, RectD canvasBounds)
     {
-        base.Render(context);
+        /*base.Render(context);
         if (Path is null)
             return;
 
@@ -101,7 +102,7 @@ internal class SelectionOverlay : Overlay
             return;
         }
         context.DrawGeometry(null, whitePen, renderPath);
-        context.DrawGeometry(fillBrush, blackDashedPen, renderPath);
+        context.DrawGeometry(fillBrush, blackDashedPen, renderPath);*/
     }
 
     protected override void ZoomChanged(double newZoom)

+ 4 - 3
src/PixiEditor/Views/Overlays/SnappingOverlay.cs

@@ -1,6 +1,7 @@
 using Avalonia;
 using Avalonia.Media;
 using Avalonia.Styling;
+using Drawie.Backend.Core.Surfaces;
 using PixiEditor.Models.Controllers.InputDevice;
 using Drawie.Numerics;
 using Point = Avalonia.Point;
@@ -39,9 +40,9 @@ internal class SnappingOverlay : Overlay
         IsHitTestVisible = false;
     }
 
-    public override void RenderOverlay(DrawingContext context, RectD canvasBounds)
+    public override void RenderOverlay(Canvas context, RectD canvasBounds)
     {
-        if (SnappingController is null)
+        /*if (SnappingController is null)
         {
             return;
         }
@@ -71,7 +72,7 @@ internal class SnappingOverlay : Overlay
         if (SnappingController.HighlightedPoint.HasValue)
         {
             context.DrawEllipse(previewPointPen.Brush, previewPointPen, new Point(SnappingController.HighlightedPoint.Value.X, SnappingController.HighlightedPoint.Value.Y), 5 / ZoomScale, 5 / ZoomScale);
-        }
+        }*/
     }
 
     protected override void ZoomChanged(double newZoom)

+ 4 - 3
src/PixiEditor/Views/Overlays/SymmetryOverlay/SymmetryOverlay.cs

@@ -8,6 +8,7 @@ using PixiEditor.Helpers;
 using PixiEditor.Models.Controllers.InputDevice;
 using PixiEditor.ChangeableDocument.Enums;
 using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Extensions.UI.Overlays;
 using Drawie.Numerics;
@@ -120,9 +121,9 @@ internal class SymmetryOverlay : Overlay
         VerticalAxisXProperty.Changed.Subscribe(OnPositionUpdate);
     }
 
-    public override void RenderOverlay(DrawingContext drawingContext, RectD canvasBounds)
+    public override void RenderOverlay(Canvas drawingContext, RectD canvasBounds)
     {
-        base.Render(drawingContext);
+        /*base.Render(drawingContext);
         if (!HorizontalAxisVisible && !VerticalAxisVisible)
             return;
 
@@ -186,7 +187,7 @@ internal class SymmetryOverlay : Overlay
 
             drawingContext.DrawLine(checkerBlack, new(verticalAxisX, 0), new(verticalAxisX, Size.Y));
             drawingContext.DrawLine(checkerWhite, new(verticalAxisX, 0), new(verticalAxisX, Size.Y));
-        }
+        }*/
     }
 
     private void DrawHorizontalRuler(DrawingContext drawingContext, bool upper)

+ 4 - 3
src/PixiEditor/Views/Overlays/TransformOverlay/TransformOverlay.cs

@@ -10,6 +10,7 @@ using ChunkyImageLib.DataHolders;
 using PixiEditor.Helpers;
 using PixiEditor.Helpers.Extensions;
 using Drawie.Backend.Core.Numerics;
+using Drawie.Backend.Core.Surfaces;
 using PixiEditor.Extensions.UI.Overlays;
 using PixiEditor.Helpers.UI;
 using PixiEditor.Models.Controllers.InputDevice;
@@ -237,13 +238,13 @@ internal class TransformOverlay : Overlay
 
     private VecD pos;
 
-    public override void RenderOverlay(DrawingContext drawingContext, RectD canvasBounds)
+    public override void RenderOverlay(Canvas drawingContext, RectD canvasBounds)
     {
-        base.Render(drawingContext);
+        /*base.Render(drawingContext);
         DrawOverlay(drawingContext, new(Bounds.Width, Bounds.Height), Corners, InternalState.Origin, ZoomScale);
 
         if (capturedAnchor is null)
-            UpdateRotationCursor(lastPointerPos);
+            UpdateRotationCursor(lastPointerPos);*/
     }
 
     private void DrawMouseInputArea(DrawingContext context, VecD size)

+ 14 - 16
src/PixiEditor/Views/Rendering/Scene.cs

@@ -220,6 +220,11 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
         }
     }
 
+    public new void InvalidateVisual()
+    {
+        QueueNextFrame();
+    }
+
     public void Draw(DrawingSurface renderTexture)
     {
         renderTexture.Canvas.Save();
@@ -228,10 +233,10 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
         renderTexture.Canvas.SetMatrix(matrix.ToSKMatrix().ToMatrix3X3());
 
         RectD dirtyBounds = new RectD(0, 0, Document.Width, Document.Height);
+        Cursor = DefaultCursor;
         RenderScene(dirtyBounds);
 
         renderTexture.Canvas.Restore();
-        QueueNextFrame();
     }
 
     public override void Render(DrawingContext context)
@@ -252,7 +257,6 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
 
         RectD dirtyBounds = new RectD(0, 0, Document.Width, Document.Height);
 
-        SceneRenderer.Resolution = CalculateResolution();
 
         /*using var operation = new DrawSceneOperation(SceneRenderer.RenderScene, Document, CanvasPos,
             Scale * resolutionScale,
@@ -268,21 +272,22 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
         context.PushRenderOptions(new RenderOptions { BitmapInterpolationMode = BitmapInterpolationMode.None });
         var pushedMatrix = context.PushTransform(matrix);
 
-        Cursor = DefaultCursor;
 
-        DrawOverlays(context, dirtyBounds, OverlayRenderSorting.Background);
 
         pushedMatrix.Dispose();
 
+        //RenderFrame(new PixelSize(width, height));
+
         context.PushTransform(matrix);
 
-        DrawOverlays(context, dirtyBounds, OverlayRenderSorting.Foreground);
     }
 
     private void RenderScene(RectD bounds)
     {
         DrawCheckerboard(bounds);
-        RenderGraph(renderSurface);
+        DrawOverlays(renderSurface, bounds, OverlayRenderSorting.Background);
+        SceneRenderer.RenderScene(renderSurface, CalculateResolution());
+        DrawOverlays(renderSurface, bounds, OverlayRenderSorting.Foreground);
     }
 
     private void DrawCheckerboard(RectD dirtyBounds)
@@ -303,15 +308,8 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
 
         renderSurface.Canvas.DrawRect(operationSurfaceRectToRender, checkerPaint);
     }
-
-    private void RenderGraph(DrawingSurface target)
-    {
-        RenderContext context = new(target, SceneRenderer.DocumentViewModel.AnimationHandler.ActiveFrameTime,
-            SceneRenderer.Resolution, SceneRenderer.Document.Size);
-        SceneRenderer.Document.NodeGraph.Execute(context);
-    }
-
-    private void DrawOverlays(DrawingContext context, RectD dirtyBounds, OverlayRenderSorting sorting)
+    
+    private void DrawOverlays(DrawingSurface renderSurface, RectD dirtyBounds, OverlayRenderSorting sorting)
     {
         if (AllOverlays != null)
         {
@@ -326,7 +324,7 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
 
                 if (!overlay.CanRender()) continue;
 
-                overlay.RenderOverlay(context, dirtyBounds);
+                overlay.RenderOverlay(renderSurface.Canvas, dirtyBounds);
                 if (overlay.IsHitTestVisible)
                 {
                     Cursor = overlay.Cursor ?? DefaultCursor;

+ 3 - 2
src/PixiEditor/Views/Visuals/PreviewPainterControl.cs

@@ -58,13 +58,14 @@ public class PreviewPainterControl : DrawieControl
 
     public override void Draw(DrawingSurface surface)
     {
-        RectD? previewBounds =
-            PreviewPainter.PreviewRenderable.GetPreviewBounds(FrameToRender, PreviewPainter.ElementToRenderName);
         if (PreviewPainter == null)
         {
             return;
         }
 
+        RectD? previewBounds =
+            PreviewPainter.PreviewRenderable.GetPreviewBounds(FrameToRender, PreviewPainter.ElementToRenderName);
+        
         float x = (float)(previewBounds?.Width ?? 0);
         float y = (float)(previewBounds?.Height ?? 0);