Pārlūkot izejas kodu

Another overlay and scene drawing changes

Krzysztof Krysiński 1 gadu atpakaļ
vecāks
revīzija
dbe5070a7e

+ 0 - 1
src/PixiEditor.AvaloniaUI/Styles/PixiEditor.Controls.axaml

@@ -8,7 +8,6 @@
                 <MergeResourceInclude Source="avares://PixiEditor.AvaloniaUI/Styles/BlendModeComboBox.axaml"/>
                 <MergeResourceInclude Source="avares://PixiEditor.AvaloniaUI/Styles/Buttons/DialogButtonTheme.axaml"/>
                 <MergeResourceInclude Source="avares://PixiEditor.AvaloniaUI/Styles/Templates/ShortcutBoxTemplate.axaml"/>
-                <MergeResourceInclude Source="avares://PixiEditor.AvaloniaUI/Styles/Templates/ReferenceLayerOverlay.axaml"/>
                 <MergeResourceInclude Source="avares://PixiEditor.AvaloniaUI/Styles/Templates/DocumentTabTemplate.axaml"/>
             </ResourceDictionary.MergedDictionaries>
         </ResourceDictionary>

+ 2 - 2
src/PixiEditor.AvaloniaUI/Styles/Templates/ReferenceLayerOverlay.axaml

@@ -5,7 +5,7 @@
                     xmlns:visuals="clr-namespace:PixiEditor.AvaloniaUI.Views.Visuals"
                     xmlns:overlays="clr-namespace:PixiEditor.AvaloniaUI.Views.Overlays"
                     xmlns:document="clr-namespace:PixiEditor.AvaloniaUI.ViewModels.Document">
-    <ControlTheme TargetType="overlays:ReferenceLayerOverlay" x:Key="{x:Type overlays:ReferenceLayerOverlay}">
+    <!--<ControlTheme TargetType="overlays:ReferenceLayerOverlay" x:Key="{x:Type overlays:ReferenceLayerOverlay}">
         <Setter Property="ZIndex" Value="0"/>
         <Setter Property="Transitions">
             <Transitions>
@@ -53,5 +53,5 @@
         <Style Selector="^:fadedOut">
             <Setter Property="Opacity" Value="0"/>
         </Style>
-    </ControlTheme>
+    </ControlTheme>-->
 </ResourceDictionary>

+ 4 - 3
src/PixiEditor.AvaloniaUI/Views/Main/ViewportControls/Viewport.axaml

@@ -20,6 +20,7 @@
     xmlns:viewportControls="clr-namespace:PixiEditor.AvaloniaUI.Views.Main.ViewportControls"
     xmlns:overlays="clr-namespace:PixiEditor.AvaloniaUI.Views.Overlays"
     xmlns:selectionOverlay="clr-namespace:PixiEditor.AvaloniaUI.Views.Overlays.SelectionOverlay"
+    xmlns:rendering="clr-namespace:PixiEditor.AvaloniaUI.Views.Rendering"
     mc:Ignorable="d"
     x:Name="vpUc"
     d:DesignHeight="450"
@@ -118,7 +119,7 @@
                 </Border>
             </overlays:TogglableFlyout.Child>
         </overlays:TogglableFlyout>
-        <visuals:Scene
+        <rendering:Scene
             Focusable="False" Name="scene"
             ZIndex="1"
             Surface="{Binding TargetBitmap, ElementName=vpUc}"
@@ -187,11 +188,11 @@
                 </Border>
             </zoombox:Zoombox.AdditionalContent>
         </zoombox:Zoombox>-->
-        <overlays:ReferenceLayerOverlay SizeChanged="OnReferenceImageSizeChanged"
+        <!--<overlays:ReferenceLayerOverlay SizeChanged="OnReferenceImageSizeChanged"
                                         ReferenceLayer="{Binding Document.ReferenceLayerViewModel}"
                                         ReferenceLayerScale="{Binding ReferenceLayerScale}"
                                         FadeOut="{Binding Source={viewModels:ToolVM ColorPickerToolViewModel}, Path=!PickFromReferenceLayer, Mode=OneWay}"
-                                        RenderTransformOrigin="0, 0" RenderTransform="{Binding #scene.CanvasTransform}"/>
+                                        RenderTransformOrigin="0, 0" RenderTransform="{Binding #scene.CanvasTransform}"/>-->
 
         <!--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-->

+ 1 - 20
src/PixiEditor.AvaloniaUI/Views/Main/ViewportControls/Viewport.axaml.cs

@@ -14,6 +14,7 @@ using PixiEditor.AvaloniaUI.Models.DocumentModels;
 using PixiEditor.AvaloniaUI.Models.Position;
 using PixiEditor.AvaloniaUI.ViewModels.Document;
 using PixiEditor.AvaloniaUI.Views.Overlays;
+using PixiEditor.AvaloniaUI.Views.Rendering;
 using PixiEditor.AvaloniaUI.Views.Visuals;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Zoombox;
@@ -178,16 +179,6 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
         set => SetValue(ZoomModeProperty, value);
     }
 
-    public double ZoomboxScale
-    {
-        get => scene?.Scale ?? 1;
-        // ReSharper disable once ValueParameterNotUsed
-        set
-        {
-            PropertyChanged?.Invoke(this, new(nameof(ReferenceLayerScale)));
-        }
-    }
-
     public bool FlipX
     {
         get => (bool)GetValue(FlipXProperty);
@@ -272,11 +263,6 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
         }
     }
 
-    public double ReferenceLayerScale =>
-        ZoomboxScale * ((Document?.ReferenceLayerViewModel.ReferenceBitmap != null && Document?.ReferenceLayerViewModel.ReferenceShapeBindable != null)
-            ? (Document.ReferenceLayerViewModel.ReferenceShapeBindable.RectSize.X / (double)Document.ReferenceLayerViewModel.ReferenceBitmap.Size.X)
-            : 1);
-
     public ObservableCollection<Overlay> ActiveOverlays { get; } = new();
 
     public Guid GuidValue { get; } = Guid.NewGuid();
@@ -373,11 +359,6 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
         return new(AngleRadians, Center, RealDimensions, Dimensions, CalculateResolution(), GuidValue, Delayed, ForceRefreshFinalImage);
     }
 
-    private void OnReferenceImageSizeChanged(object? sender, SizeChangedEventArgs sizeChangedEventArgs)
-    {
-        PropertyChanged?.Invoke(this, new(nameof(ReferenceLayerScale)));
-    }
-
     private void Image_MouseDown(object? sender, PointerPressedEventArgs e)
     {
         if(Document is null)

+ 37 - 8
src/PixiEditor.AvaloniaUI/Views/Main/ViewportControls/ViewportOverlays.cs

@@ -18,16 +18,17 @@ internal class ViewportOverlays
 {
     public Viewport Viewport { get; set; }
 
-    private GridLines gridLinesOverlay;
+    private GridLinesOverlay gridLinesOverlayOverlay;
     private SelectionOverlay selectionOverlay;
     private SymmetryOverlay symmetryOverlay;
     private LineToolOverlay lineToolOverlay;
     private TransformOverlay transformOverlay;
+    private ReferenceLayerOverlay referenceLayerOverlay;
 
     public void Init(Viewport viewport)
     {
         Viewport = viewport;
-        gridLinesOverlay = new GridLines();
+        gridLinesOverlayOverlay = new GridLinesOverlay();
         BindGridLines();
 
         selectionOverlay = new SelectionOverlay();
@@ -42,13 +43,41 @@ internal class ViewportOverlays
         transformOverlay = new TransformOverlay();
         BindTransformOverlay();
 
-        Viewport.ActiveOverlays.Add(gridLinesOverlay);
+        Viewport.ActiveOverlays.Add(gridLinesOverlayOverlay);
         Viewport.ActiveOverlays.Add(selectionOverlay);
         Viewport.ActiveOverlays.Add(symmetryOverlay);
         Viewport.ActiveOverlays.Add(lineToolOverlay);
         Viewport.ActiveOverlays.Add(transformOverlay);
     }
 
+    private void BindReferenceLayerOverlay()
+    {
+        Binding referenceLayerBinding = new()
+        {
+            Source = Viewport,
+            Path = "Document.ReferenceLayerViewModel",
+            Mode = BindingMode.OneWay
+        };
+
+        Binding referenceShapeBinding = new()
+        {
+            Source = Viewport,
+            Path = "Document.ReferenceLayerViewModel.ReferenceShapeBindable",
+            Mode = BindingMode.OneWay
+        };
+
+        Binding fadeOutBinding = new()
+        {
+            Source = Viewport,
+            Path = "!Document.ToolsSubViewModel.ColorPickerToolViewModel.PickFromReferenceLayer",
+            Mode = BindingMode.OneWay,
+        };
+
+        referenceLayerOverlay.Bind(ReferenceLayerOverlay.ReferenceLayerProperty, referenceLayerBinding);
+        referenceLayerOverlay.Bind(ReferenceLayerOverlay.ReferenceShapeProperty, referenceShapeBinding);
+        referenceLayerOverlay.Bind(ReferenceLayerOverlay.FadeOutProperty, fadeOutBinding);
+    }
+
     private void BindGridLines()
     {
         Binding isVisBinding = new()
@@ -65,8 +94,8 @@ internal class ViewportOverlays
             Mode = BindingMode.OneWay
         };
 
-        gridLinesOverlay.Bind(GridLines.PixelWidthProperty, binding);
-        gridLinesOverlay.Bind(GridLines.ColumnsProperty, binding);
+        gridLinesOverlayOverlay.Bind(GridLinesOverlay.PixelWidthProperty, binding);
+        gridLinesOverlayOverlay.Bind(GridLinesOverlay.ColumnsProperty, binding);
 
         binding = new Binding
         {
@@ -75,9 +104,9 @@ internal class ViewportOverlays
             Mode = BindingMode.OneWay
         };
 
-        gridLinesOverlay.Bind(GridLines.PixelHeightProperty, binding);
-        gridLinesOverlay.Bind(GridLines.RowsProperty, binding);
-        gridLinesOverlay.Bind(Visual.IsVisibleProperty, isVisBinding);
+        gridLinesOverlayOverlay.Bind(GridLinesOverlay.PixelHeightProperty, binding);
+        gridLinesOverlayOverlay.Bind(GridLinesOverlay.RowsProperty, binding);
+        gridLinesOverlayOverlay.Bind(Visual.IsVisibleProperty, isVisBinding);
     }
 
     private void BindSelectionOverlay()

+ 3 - 0
src/PixiEditor.AvaloniaUI/Views/Overlays/BrushShapeOverlay/BrushShapeOverlay.cs

@@ -7,6 +7,7 @@ using Avalonia.Interactivity;
 using Avalonia.Media;
 using ChunkyImageLib.Operations;
 using PixiEditor.AvaloniaUI.Models.Controllers.InputDevice;
+using PixiEditor.AvaloniaUI.Views.Rendering;
 using PixiEditor.AvaloniaUI.Views.Visuals;
 using PixiEditor.DrawingApi.Core.Numerics;
 
@@ -83,6 +84,8 @@ internal class BrushShapeOverlay : Overlay
         InvalidateVisual();
     }
 
+    public override void RenderOverlay(DrawingContext context, RectD canvasBounds) => Render(context);
+
     public override void Render(DrawingContext drawingContext)
     {
         var winRect = new Rect(

+ 11 - 12
src/PixiEditor.AvaloniaUI/Views/Visuals/GridLines.cs → src/PixiEditor.AvaloniaUI/Views/Overlays/GridLinesOverlay.cs

@@ -1,23 +1,22 @@
 using Avalonia;
-using Avalonia.Controls;
 using Avalonia.Media;
 using PixiEditor.AvaloniaUI.Helpers.Converters;
-using PixiEditor.AvaloniaUI.Views.Overlays;
+using PixiEditor.DrawingApi.Core.Numerics;
 
-namespace PixiEditor.AvaloniaUI.Views.Visuals;
+namespace PixiEditor.AvaloniaUI.Views.Overlays;
 
-public class GridLines : Overlay
+public class GridLinesOverlay : Overlay
 {
-    public static readonly StyledProperty<int> RowsProperty = AvaloniaProperty.Register<GridLines, int>(
+    public static readonly StyledProperty<int> RowsProperty = AvaloniaProperty.Register<GridLinesOverlay, int>(
         nameof(Rows));
 
-    public static readonly StyledProperty<int> ColumnsProperty = AvaloniaProperty.Register<GridLines, int>(
+    public static readonly StyledProperty<int> ColumnsProperty = AvaloniaProperty.Register<GridLinesOverlay, int>(
         nameof(Columns));
 
-    public static readonly StyledProperty<int> PixelWidthProperty = AvaloniaProperty.Register<GridLines, int>(
+    public static readonly StyledProperty<int> PixelWidthProperty = AvaloniaProperty.Register<GridLinesOverlay, int>(
         nameof(PixelWidth));
 
-    public static readonly StyledProperty<int> PixelHeightProperty = AvaloniaProperty.Register<GridLines, int>(
+    public static readonly StyledProperty<int> PixelHeightProperty = AvaloniaProperty.Register<GridLinesOverlay, int>(
         nameof(PixelHeight));
 
     public int PixelHeight
@@ -49,12 +48,12 @@ public class GridLines : Overlay
     private Pen pen2 = new(Brushes.White, PenWidth);
     private ThresholdVisibilityConverter visibilityConverter = new(){ Threshold = 10 };
 
-    static GridLines()
+    static GridLinesOverlay()
     {
         IsVisibleProperty.Changed.Subscribe(OnIsVisibleChanged);
     }
 
-    public GridLines()
+    public GridLinesOverlay()
     {
         IsHitTestVisible = false;
     }
@@ -64,7 +63,7 @@ public class GridLines : Overlay
         IsVisible = IsVisible && visibilityConverter.Check(newZoom);
     }
 
-    public override void Render(DrawingContext context)
+    public override void RenderOverlay(DrawingContext context, RectD canvasBounds)
     {
         // Draw lines in vertical and horizontal directions, size should be relative to the scale
 
@@ -95,7 +94,7 @@ public class GridLines : Overlay
 
     private static void OnIsVisibleChanged(AvaloniaPropertyChangedEventArgs<bool> e)
     {
-        if (e.Sender is GridLines gridLines)
+        if (e.Sender is GridLinesOverlay gridLines)
         {
             gridLines.Refresh();
         }

+ 1 - 1
src/PixiEditor.AvaloniaUI/Views/Overlays/LineToolOverlay/LineToolOverlay.cs

@@ -84,7 +84,7 @@ internal class LineToolOverlay : Overlay
         blackPen.Thickness = 1.0 / newZoom;
     }
 
-    public override void Render(DrawingContext context)
+    public override void RenderOverlay(DrawingContext context, RectD canvasBounds)
     {
         startHandle.Position = LineStart;
         endHandle.Position = LineEnd;

+ 3 - 1
src/PixiEditor.AvaloniaUI/Views/Overlays/Overlay.cs

@@ -4,6 +4,7 @@ using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Data;
 using Avalonia.Input;
+using Avalonia.Media;
 using PixiEditor.AvaloniaUI.Views.Overlays.Handles;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Extensions.UI.Overlays;
@@ -24,7 +25,6 @@ public abstract class Overlay : Decorator, IOverlay // TODO: Maybe make it not a
     }
 
     public event Action? RefreshRequested;
-
     public event PointerEvent? PointerEnteredOverlay;
     public event PointerEvent? PointerExitedOverlay;
     public event PointerEvent? PointerMovedOverlay;
@@ -105,6 +105,8 @@ public abstract class Overlay : Decorator, IOverlay // TODO: Maybe make it not a
         }
     }
 
+    public abstract void RenderOverlay(DrawingContext context, RectD canvasBounds);
+
     protected virtual void OnOverlayPointerReleased(OverlayPointerArgs args)
     {
 

+ 18 - 11
src/PixiEditor.AvaloniaUI/Views/Overlays/ReferenceLayerOverlay.cs

@@ -4,8 +4,10 @@ using Avalonia.Controls;
 using Avalonia.Controls.Metadata;
 using Avalonia.Controls.Primitives;
 using Avalonia.Media;
+using ChunkyImageLib.DataHolders;
 using PixiEditor.AvaloniaUI.ViewModels.Document;
 using PixiEditor.AvaloniaUI.Views.Visuals;
+using PixiEditor.DrawingApi.Core.Numerics;
 
 namespace PixiEditor.AvaloniaUI.Views.Overlays;
 
@@ -15,23 +17,27 @@ internal class ReferenceLayerOverlay : Overlay
     public static readonly StyledProperty<ReferenceLayerViewModel> ReferenceLayerProperty = AvaloniaProperty.Register<ReferenceLayerOverlay, ReferenceLayerViewModel>(
         nameof(ReferenceLayerViewModel));
 
-    public static readonly StyledProperty<double> ReferenceLayerScaleProperty = AvaloniaProperty.Register<ReferenceLayerOverlay, double>(
-        nameof(ReferenceLayerScale), defaultValue: 1.0f);
-
     public static readonly StyledProperty<bool> FadeOutProperty = AvaloniaProperty.Register<ReferenceLayerOverlay, bool>(
         nameof(FadeOut), defaultValue: false);
 
+    public static readonly StyledProperty<ShapeCorners> ReferenceShapeProperty = AvaloniaProperty.Register<ReferenceLayerOverlay, ShapeCorners>(
+        nameof(ReferenceShape));
+
+    public ShapeCorners ReferenceShape
+    {
+        get => GetValue(ReferenceShapeProperty);
+        set => SetValue(ReferenceShapeProperty, value);
+    }
+
     public bool FadeOut
     {
         get => GetValue(FadeOutProperty);
         set => SetValue(FadeOutProperty, value);
     }
 
-    public double ReferenceLayerScale
-    {
-        get => GetValue(ReferenceLayerScaleProperty);
-        set => SetValue(ReferenceLayerScaleProperty, value);
-    }
+    public double ReferenceLayerScale => ZoomScale * ((ReferenceLayer.ReferenceBitmap != null && ReferenceShape != null)
+        ? (ReferenceShape.RectSize.X / (double)ReferenceLayer.ReferenceBitmap.Size.X)
+        : 1);
 
     public ReferenceLayerViewModel ReferenceLayer
     {
@@ -45,12 +51,13 @@ internal class ReferenceLayerOverlay : Overlay
         FadeOutProperty.Changed.Subscribe(FadeOutChanged);
     }
 
-    public override void Render(DrawingContext context)
+    public override void RenderOverlay(DrawingContext context, RectD dirtyCanvasBounds)
     {
-        if (ReferenceLayer != null && ReferenceLayer.ReferenceBitmap != null)
+        if (ReferenceLayer is { ReferenceBitmap: not null })
         {
-            Rect dirtyRect = new Rect(CanvasDirtyBounds.X, CanvasDirtyBounds.Y, CanvasDirtyBounds.Width, CanvasDirtyBounds.Height);
+            Rect dirtyRect = new Rect(dirtyCanvasBounds.X, dirtyCanvasBounds.Y, dirtyCanvasBounds.Width, dirtyCanvasBounds.Height);
             DrawSurfaceOperation drawOperation = new DrawSurfaceOperation(dirtyRect, ReferenceLayer.ReferenceBitmap, Stretch.Uniform, Opacity);
+            context.Custom(drawOperation);
         }
     }
 

+ 5 - 4
src/PixiEditor.AvaloniaUI/Views/Overlays/SelectionOverlay/SelectionOverlay.cs

@@ -5,6 +5,7 @@ using Avalonia.Animation;
 using Avalonia.Media;
 using Avalonia.Styling;
 using PixiEditor.AvaloniaUI.Animation;
+using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Surface.Vector;
 
 namespace PixiEditor.AvaloniaUI.Views.Overlays.SelectionOverlay;
@@ -75,9 +76,9 @@ internal class SelectionOverlay : Overlay
         }
     }
 
-    public override void Render(DrawingContext drawingContext)
+    public override void RenderOverlay(DrawingContext context, RectD canvasBounds)
     {
-        base.Render(drawingContext);
+        base.Render(context);
         if (Path is null)
             return;
 
@@ -93,8 +94,8 @@ internal class SelectionOverlay : Overlay
         {
             return;
         }
-        drawingContext.DrawGeometry(null, whitePen, renderPath);
-        drawingContext.DrawGeometry(fillBrush, blackDashedPen, renderPath);
+        context.DrawGeometry(null, whitePen, renderPath);
+        context.DrawGeometry(fillBrush, blackDashedPen, renderPath);
     }
 
     protected override void ZoomChanged(double newZoom)

+ 1 - 1
src/PixiEditor.AvaloniaUI/Views/Overlays/SymmetryOverlay/SymmetryOverlay.cs

@@ -118,7 +118,7 @@ internal class SymmetryOverlay : Overlay
         VerticalAxisXProperty.Changed.Subscribe(OnPositionUpdate);
     }
 
-    public override void Render(DrawingContext drawingContext)
+    public override void RenderOverlay(DrawingContext drawingContext, RectD canvasBounds)
     {
         base.Render(drawingContext);
         if (!HorizontalAxisVisible && !VerticalAxisVisible)

+ 1 - 1
src/PixiEditor.AvaloniaUI/Views/Overlays/TransformOverlay/TransformOverlay.cs

@@ -214,7 +214,7 @@ internal class TransformOverlay : Overlay
         moveHandle.OnRelease += OnMoveHandleReleased;
     }
 
-    public override void Render(DrawingContext drawingContext)
+    public override void RenderOverlay(DrawingContext drawingContext, RectD canvasBounds)
     {
         base.Render(drawingContext);
         DrawOverlay(drawingContext, new(Bounds.Width, Bounds.Height), Corners, InternalState.Origin, ZoomScale);

+ 53 - 61
src/PixiEditor.AvaloniaUI/Views/Visuals/Scene.cs → src/PixiEditor.AvaloniaUI/Views/Rendering/Scene.cs

@@ -3,9 +3,9 @@ using System.Collections.ObjectModel;
 using System.Collections.Specialized;
 using Avalonia;
 using Avalonia.Animation;
-using Avalonia.Controls;
 using Avalonia.Input;
 using Avalonia.Media;
+using Avalonia.Media.Imaging;
 using Avalonia.Rendering;
 using Avalonia.Rendering.SceneGraph;
 using Avalonia.Skia;
@@ -17,15 +17,14 @@ using PixiEditor.AvaloniaUI.Helpers.Converters;
 using PixiEditor.AvaloniaUI.ViewModels.Document;
 using PixiEditor.AvaloniaUI.Views.Overlays;
 using PixiEditor.AvaloniaUI.Views.Overlays.Pointers;
-using PixiEditor.AvaloniaUI.Views.Overlays.TransformOverlay;
+using PixiEditor.AvaloniaUI.Views.Visuals;
 using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.DrawingApi.Core.Surface;
-using PixiEditor.DrawingApi.Skia;
 using PixiEditor.Extensions.UI.Overlays;
+using Bitmap = Avalonia.Media.Imaging.Bitmap;
 using Image = PixiEditor.DrawingApi.Core.Surface.ImageData.Image;
 using Point = Avalonia.Point;
 
-namespace PixiEditor.AvaloniaUI.Views.Visuals;
+namespace PixiEditor.AvaloniaUI.Views.Rendering;
 
 internal class Scene : Zoombox.Zoombox, ICustomHitTest
 {
@@ -77,6 +76,9 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
     }
 
     private Bitmap? checkerBitmap;
+
+    private Brush? checkerBrush;
+
     private Overlay? capturedOverlay;
 
     private List<Overlay> mouseOverOverlays = new();
@@ -105,8 +107,6 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
     {
         if (Surface == null || Document == null) return;
 
-        float finalScale = CalculateFinalScale();
-
         float angle = (float)MathUtil.RadiansToDegrees(AngleRadians);
         if (FlipX)
         {
@@ -118,30 +118,33 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
             angle = 360 - angle;
         }
 
-        VecD dirtyDimensions = new VecD(ContentDimensions.X * finalScale, ContentDimensions.Y * finalScale);
+        VecD dirtyDimensions = new VecD(ContentDimensions.X * Scale, ContentDimensions.Y * Scale);
         VecD dirtyCenterShift = new VecD(FlipX ? -dirtyDimensions.X / 2 : dirtyDimensions.X / 2, FlipY ? -dirtyDimensions.Y / 2 : dirtyDimensions.Y / 2);
         VecD dirtyCenter = new VecD(CanvasPos.X + dirtyCenterShift.X, CanvasPos.Y + dirtyCenterShift.Y);
         RectD dirtyBounds = new ShapeCorners(dirtyCenter, dirtyDimensions)
             .AsRotated(MathUtil.DegreesToRadians(angle), new VecD(CanvasPos.X, CanvasPos.Y)).AABBBounds;
-        /*dirtyBounds.Flip*/
 
-        using var operation = new DrawSceneOperation(Surface, Document, CanvasPos, finalScale, angle, FlipX, FlipY,
+        using var operation = new DrawSceneOperation(Surface, Document, CanvasPos, Scale, angle, FlipX, FlipY,
             new Rect(dirtyBounds.X, dirtyBounds.Y, dirtyBounds.Width, dirtyBounds.Height),
             Bounds,
-            Opacity, (SKBitmap)checkerBitmap.Native);
-        context.Custom(operation);
+            Opacity);
 
         var matrix = CalculateTransformMatrix();
         context.PushTransform(matrix);
+        context.PushRenderOptions(new RenderOptions { BitmapInterpolationMode = BitmapInterpolationMode.None });
+
+        DrawCheckerboard(context);
+
+        context.Custom(operation);
 
         if (ActiveOverlays != null)
         {
             foreach (Overlay overlay in ActiveOverlays)
             {
-                overlay.ZoomScale = finalScale;
+                overlay.ZoomScale = Scale;
                 if (!overlay.IsVisible) continue;
 
-                overlay.Render(context);
+                overlay.RenderOverlay(context, dirtyBounds);
                 Cursor = overlay.Cursor;
             }
         }
@@ -299,27 +302,29 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
     private Matrix CalculateTransformMatrix()
     {
         Matrix transform = Matrix.Identity;
-        float finalScale = CalculateFinalScale();
         transform = transform.Append(Matrix.CreateRotation((float)AngleRadians));
         transform = transform.Append(Matrix.CreateScale(FlipX ? -1 : 1, FlipY ? -1 : 1));
-        transform = transform.Append(Matrix.CreateScale(finalScale, finalScale));
+        transform = transform.Append(Matrix.CreateScale((float)Scale, (float)Scale));
         transform = transform.Append(Matrix.CreateTranslation(CanvasPos.X, CanvasPos.Y));
         return transform;
     }
 
-    private float CalculateFinalScale()
-    {
-        var scaleUniform = CalculateResolutionScale();
-        float scale = (float)Scale * scaleUniform;
-        return scale;
-    }
-
-    private float CalculateResolutionScale()
+    private void DrawCheckerboard(DrawingContext context)
     {
-        float scaleX = (float)Document.Width / Surface.Size.X;
-        float scaleY = (float)Document.Height / Surface.Size.Y;
-        var scaleUniform = Math.Min(scaleX, scaleY);
-        return scaleUniform;
+        if (checkerBitmap != null)
+        {
+            float checkerScale = (float)ZoomToViewportConverter.ZoomToViewport(16, Scale);
+            checkerBrush = new ImageBrush
+            {
+                Source = checkerBitmap,
+                TileMode = TileMode.Tile,
+                DestinationRect = new RelativeRect(0, 0, checkerScale, checkerScale, RelativeUnit.Absolute),
+                Transform = new ScaleTransform(0.5f, 0.5f)
+            };
+
+            Rect surfaceRect = new(0, 0, Document.Width, Document.Height);
+            context.DrawRectangle(checkerBrush, null, surfaceRect);
+        }
     }
 
     private void CaptureOverlay(Overlay? overlay, IPointer pointer)
@@ -387,11 +392,17 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
     {
         if (e.NewValue is string path)
         {
-            scene.checkerBitmap = ImagePathToBitmapConverter.LoadDrawingApiBitmapFromRelativePath(path);
+            scene.checkerBitmap = ImagePathToBitmapConverter.LoadBitmapFromRelativePath(path);
+            scene.checkerBrush = new ImageBrush
+            {
+                Source = scene.checkerBitmap,
+                TileMode = TileMode.Tile
+            };
         }
         else
         {
             scene.checkerBitmap = null;
+            scene.checkerBrush = null;
         }
     }
 
@@ -418,15 +429,14 @@ internal class DrawSceneOperation : SkiaDrawOperation
     public double Angle { get; set; }
     public bool FlipX { get; set; }
     public bool FlipY { get; set; }
+    public Rect ViewportBounds { get; }
 
-    public Rect ViewportBounds { get; set; }
-    public SKBitmap? CheckerBitmap { get; set; }
+    public RectI SurfaceRectToRender { get; }
 
     private SKPaint _paint = new SKPaint();
-    private SKPaint _checkerPaint;
 
     public DrawSceneOperation(Surface surface, DocumentViewModel document, VecD contentPosition, double scale,
-        double angle, bool flipX, bool flipY, Rect dirtyBounds, Rect viewportBounds, double opacity, SKBitmap checkerBitmap) : base(dirtyBounds)
+        double angle, bool flipX, bool flipY, Rect dirtyBounds, Rect viewportBounds, double opacity) : base(dirtyBounds)
     {
         Surface = surface;
         Document = document;
@@ -436,18 +446,8 @@ internal class DrawSceneOperation : SkiaDrawOperation
         FlipX = flipX;
         FlipY = flipY;
         ViewportBounds = viewportBounds;
-        CheckerBitmap = checkerBitmap;
         _paint.Color = _paint.Color.WithAlpha((byte)(opacity * 255));
-
-        float checkerScale = (float)ZoomToViewportConverter.ZoomToViewport(16, scale) * 0.25f;
-        _checkerPaint = new SKPaint()
-        {
-            Shader = SKShader.CreateBitmap(
-                CheckerBitmap,
-                SKShaderTileMode.Repeat, SKShaderTileMode.Repeat,
-                SKMatrix.CreateScale(checkerScale, checkerScale)),
-            FilterQuality = SKFilterQuality.None
-        };
+        SurfaceRectToRender = FindRectToRender((float)scale);
     }
 
     public override void Render(ISkiaSharpApiLease lease)
@@ -458,34 +458,26 @@ internal class DrawSceneOperation : SkiaDrawOperation
 
         canvas.Save();
 
-        RectI surfaceRectToRender = FindRectToRender((float)Scale);
+        canvas.Scale(CalculateResolutionScale());
 
-        if (surfaceRectToRender.IsZeroOrNegativeArea)
+        if (SurfaceRectToRender.IsZeroOrNegativeArea)
         {
             canvas.Restore();
             return;
         }
 
-        canvas.Scale((float)Scale, (float)Scale, (float)ContentPosition.X, (float)ContentPosition.Y);
-
-        canvas.RotateDegrees((float)Angle, (float)ContentPosition.X, (float)ContentPosition.Y);
-        canvas.Scale(FlipX ? -1 : 1, FlipY ? -1 : 1, (float)ContentPosition.X, (float)ContentPosition.Y);
-        canvas.Translate((float)ContentPosition.X, (float)ContentPosition.Y);
-
-        DrawCheckerboard(canvas, surfaceRectToRender);
-
-        using Image snapshot = Surface.DrawingSurface.Snapshot(surfaceRectToRender);
-        canvas.DrawImage((SKImage)snapshot.Native, surfaceRectToRender.X, surfaceRectToRender.Y, _paint);
+        using Image snapshot = Surface.DrawingSurface.Snapshot(SurfaceRectToRender);
+        canvas.DrawImage((SKImage)snapshot.Native, SurfaceRectToRender.X, SurfaceRectToRender.Y, _paint);
 
         canvas.Restore();
     }
 
-    private void DrawCheckerboard(SKCanvas canvas, RectI surfaceRectToRender)
+    private float CalculateResolutionScale()
     {
-        if (CheckerBitmap != null)
-        {
-            canvas.DrawRect(surfaceRectToRender.ToSkRect(), _checkerPaint);
-        }
+        float scaleX = (float)Document.Width / Surface.Size.X;
+        float scaleY = (float)Document.Height / Surface.Size.Y;
+        var scaleUniform = Math.Min(scaleX, scaleY);
+        return scaleUniform;
     }
 
     private RectI FindRectToRender(float finalScale)

+ 6 - 0
src/PixiEditor.AvaloniaUI/Views/Rendering/SceneInput.cs

@@ -0,0 +1,6 @@
+namespace PixiEditor.AvaloniaUI.Views.Rendering;
+
+public class SceneInput
+{
+
+}