Quellcode durchsuchen

Improve the UI of the reference layer

Equbuxu vor 3 Jahren
Ursprung
Commit
4f3b9b1f7a

+ 3 - 3
src/PixiEditor.ChangeableDocument/Changeables/Document.cs

@@ -1,5 +1,4 @@
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
-using SkiaSharp;
 
 namespace PixiEditor.ChangeableDocument.Changeables;
 
@@ -12,8 +11,8 @@ internal class Document : IChangeable, IReadOnlyDocument, IDisposable
     IReadOnlyStructureMember IReadOnlyDocument.FindMemberOrThrow(Guid guid) => FindMemberOrThrow(guid);
     (IReadOnlyStructureMember, IReadOnlyFolder) IReadOnlyDocument.FindChildAndParentOrThrow(Guid guid) => FindChildAndParentOrThrow(guid);
 
-    IReadOnlyReferenceLayer? IReadOnlyDocument.GetReferenceLayer() => ReferenceLayer;
-    
+    IReadOnlyReferenceLayer? IReadOnlyDocument.ReferenceLayer => ReferenceLayer;
+
     public static VecI DefaultSize { get; } = new VecI(64, 64);
     internal Folder StructureRoot { get; } = new() { GuidValue = Guid.Empty };
     internal Selection Selection { get; } = new();
@@ -31,6 +30,7 @@ internal class Document : IChangeable, IReadOnlyDocument, IDisposable
     }
 
     public StructureMember FindMemberOrThrow(Guid guid) => FindMember(guid) ?? throw new ArgumentException("Could not find member with guid " + guid.ToString());
+
     public StructureMember? FindMember(Guid guid)
     {
         var list = FindMemberPath(guid);

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlyDocument.cs

@@ -13,5 +13,5 @@ public interface IReadOnlyDocument
     IReadOnlyStructureMember FindMemberOrThrow(Guid guid);
     (IReadOnlyStructureMember, IReadOnlyFolder) FindChildAndParentOrThrow(Guid guid);
     IReadOnlyList<IReadOnlyStructureMember> FindMemberPath(Guid guid);
-    IReadOnlyReferenceLayer? GetReferenceLayer();
+    IReadOnlyReferenceLayer? ReferenceLayer { get; }
 }

+ 4 - 4
src/PixiEditor.ChangeableDocument/Changes/Root/CreateReferenceLayer_UpdateableChange.cs

@@ -6,7 +6,7 @@ internal class CreateReferenceLayer_UpdateableChange : UpdateableChange
 {
     private readonly Surface? surface;
     private ShapeCorners shape;
-    
+
     [GenerateUpdateableChangeActions]
     public CreateReferenceLayer_UpdateableChange(Surface? surface, ShapeCorners shape)
     {
@@ -19,14 +19,14 @@ internal class CreateReferenceLayer_UpdateableChange : UpdateableChange
     {
         this.shape = shape;
     }
-    
+
     public override OneOf<Success, Error> InitializeAndValidate(Document target)
     {
-        if (surface == null)
+        if (surface is null)
         {
             return new Error();
         }
-        
+
         target.ReferenceLayer = new ReferenceLayer(surface!, shape);
         return new Success();
     }

+ 12 - 4
src/PixiEditorPrototype/Models/DocumentUpdater.cs

@@ -18,6 +18,7 @@ internal class DocumentUpdater
 {
     private DocumentViewModel doc;
     private DocumentHelpers helper;
+
     public DocumentUpdater(DocumentViewModel doc, DocumentHelpers helper)
     {
         this.doc = doc;
@@ -82,14 +83,21 @@ internal class DocumentUpdater
             case StructureMemberMaskIsVisible_ChangeInfo info:
                 ProcessMaskIsVisible(info);
                 break;
-            case CreateReferenceLayer_ChangeInfo:
-                doc.RaisePropertyChanged(nameof(doc.ReferenceLayer));
-                doc.RaisePropertyChanged(nameof(doc.ReferenceBitmap));
-                doc.RaisePropertyChanged(nameof(doc.ReferenceShape));
+            case CreateReferenceLayer_ChangeInfo info:
+                ProcessCreateReferenceLayer(info);
                 break;
         }
     }
 
+    private void ProcessCreateReferenceLayer(CreateReferenceLayer_ChangeInfo info)
+    {
+        doc.RaisePropertyChanged(nameof(doc.ReferenceLayer));
+        doc.RaisePropertyChanged(nameof(doc.ReferenceBitmap));
+        doc.RaisePropertyChanged(nameof(doc.ReferenceBitmapSize));
+        doc.RaisePropertyChanged(nameof(doc.ReferenceTransformMatrix));
+        doc.RaisePropertyChanged(nameof(doc.ReferenceShape));
+    }
+
     private void ProcessMaskIsVisible(StructureMemberMaskIsVisible_ChangeInfo info)
     {
         var member = helper.StructureHelper.FindOrThrow(info.GuidValue);

+ 94 - 60
src/PixiEditorPrototype/UserControls/Viewport/Viewport.xaml

@@ -1,102 +1,136 @@
-<UserControl x:Class="PixiEditorPrototype.UserControls.Viewport.Viewport"
-             x:ClassModifier="internal"
-             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
-             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
-             xmlns:local="clr-namespace:PixiEditorPrototype.UserControls.Viewport"
-             xmlns:zoombox="clr-namespace:PixiEditor.Zoombox;assembly=PixiEditor.Zoombox"
-             xmlns:to="clr-namespace:PixiEditorPrototype.CustomControls.TransformOverlay"
-             xmlns:cust="clr-namespace:PixiEditorPrototype.CustomControls"
-             xmlns:sym="clr-namespace:PixiEditorPrototype.CustomControls.SymmetryOverlay"
-             xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
-             xmlns:conv="clr-namespace:PixiEditorPrototype.Converters"
-             mc:Ignorable="d"
-             x:Name="vpUc"
-             d:DesignHeight="450" d:DesignWidth="800">
+<UserControl
+    x:Class="PixiEditorPrototype.UserControls.Viewport.Viewport"
+    x:ClassModifier="internal"
+    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:local="clr-namespace:PixiEditorPrototype.UserControls.Viewport"
+    xmlns:zoombox="clr-namespace:PixiEditor.Zoombox;assembly=PixiEditor.Zoombox"
+    xmlns:to="clr-namespace:PixiEditorPrototype.CustomControls.TransformOverlay"
+    xmlns:cust="clr-namespace:PixiEditorPrototype.CustomControls"
+    xmlns:sym="clr-namespace:PixiEditorPrototype.CustomControls.SymmetryOverlay"
+    xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
+    xmlns:conv="clr-namespace:PixiEditorPrototype.Converters"
+    mc:Ignorable="d"
+    x:Name="vpUc"
+    d:DesignHeight="450"
+    d:DesignWidth="800">
     <UserControl.Resources>
-        <conv:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
-        <conv:ScaleToBitmapScalingModeConverter x:Key="ScaleToBitmapScalingModeConverter"/>
+        <conv:BoolToVisibilityConverter
+            x:Key="BoolToVisibilityConverter" />
+        <conv:ScaleToBitmapScalingModeConverter
+            x:Key="ScaleToBitmapScalingModeConverter" />
     </UserControl.Resources>
     <Grid>
-        <zoombox:Zoombox 
-            x:Name="zoombox" UseTouchGestures="True"
+        <zoombox:Zoombox
+            x:Name="zoombox"
+            UseTouchGestures="True"
+            Scale="{Binding ZoomboxScale, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}, Mode=OneWayToSource}"
             Center="{Binding Center, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}, Mode=OneWayToSource}"
             Angle="{Binding Angle, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}, Mode=OneWayToSource}"
             RealDimensions="{Binding RealDimensions, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}, Mode=OneWayToSource}"
             Dimensions="{Binding Dimensions, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}, Mode=OneWayToSource}"
-            ZoomMode="{Binding ZoomMode, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}, Mode=TwoWay}" 
+            ZoomMode="{Binding ZoomMode, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}, Mode=TwoWay}"
             FlipX="{Binding FlipX, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}}"
             FlipY="{Binding FlipY, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}}">
-            <Border 
-                BorderThickness="1" Background="White" BorderBrush="Black" HorizontalAlignment="Center" VerticalAlignment="Center"
+            <Border
+                BorderThickness="1"
+                Background="White"
+                BorderBrush="Black"
+                HorizontalAlignment="Center"
+                VerticalAlignment="Center"
                 DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}}">
                 <Grid>
+                    <Canvas>
+                        <Image
+                            Width="{Binding Document.ReferenceBitmap.Width}"
+                            Height="{Binding Document.ReferenceBitmap.Height}"
+                            Source="{Binding Document.ReferenceBitmap, Mode=OneWay}"
+                            SizeChanged="OnReferenceImageSizeChanged"
+                            RenderOptions.BitmapScalingMode="{Binding ReferenceLayerScale, Converter={StaticResource ScaleToBitmapScalingModeConverter}}">
+                            <Image.RenderTransform>
+                                <TransformGroup>
+                                    <MatrixTransform
+                                        Matrix="{Binding Document.ReferenceTransformMatrix}" />
+                                </TransformGroup>
+                            </Image.RenderTransform>
+                        </Image>
+                    </Canvas>
                     <Image
-                        Width="{Binding Document.Width}" Height="{Binding Document.Height}"/>
-                    <Image
-                        Width="{Binding Document.Width}" Height="{Binding Document.Height}"
-                        Source="{Binding ReferenceBitmap, Mode=OneWay}"
-                        RenderOptions.BitmapScalingMode="{Binding Zoombox.Scale, Converter={StaticResource ScaleToBitmapScalingModeConverter}}">
-                        <Image.RenderTransform>
-                            <TransformGroup>
-                                <TranslateTransform
-                                    X="{Binding ReferenceShape.TopLeft.X}"
-                                    Y="{Binding ReferenceShape.TopLeft.Y}"/>
-                            </TransformGroup>
-                        </Image.RenderTransform>
-                    </Image>
-                    <Image 
                         Focusable="True"
-                        Width="{Binding Document.Width}" Height="{Binding Document.Height}"
+                        Width="{Binding Document.Width}"
+                        Height="{Binding Document.Height}"
                         Source="{Binding TargetBitmap}"
                         RenderOptions.BitmapScalingMode="{Binding Zoombox.Scale, Converter={StaticResource ScaleToBitmapScalingModeConverter}}">
                         <i:Interaction.Triggers>
-                            <i:EventTrigger EventName="MouseDown">
-                                <i:InvokeCommandAction Command="{Binding MouseDownCommand}" PassEventArgsToCommand="True"/>
+                            <i:EventTrigger
+                                EventName="MouseDown">
+                                <i:InvokeCommandAction
+                                    Command="{Binding MouseDownCommand}"
+                                    PassEventArgsToCommand="True" />
                             </i:EventTrigger>
-                            <i:EventTrigger EventName="MouseMove">
-                                <i:InvokeCommandAction Command="{Binding MouseMoveCommand}" PassEventArgsToCommand="True"/>
+                            <i:EventTrigger
+                                EventName="MouseMove">
+                                <i:InvokeCommandAction
+                                    Command="{Binding MouseMoveCommand}"
+                                    PassEventArgsToCommand="True" />
                             </i:EventTrigger>
-                            <i:EventTrigger EventName="MouseUp">
-                                <i:InvokeCommandAction Command="{Binding MouseUpCommand}" PassEventArgsToCommand="True"/>
+                            <i:EventTrigger
+                                EventName="MouseUp">
+                                <i:InvokeCommandAction
+                                    Command="{Binding MouseUpCommand}"
+                                    PassEventArgsToCommand="True" />
                             </i:EventTrigger>
                         </i:Interaction.Triggers>
                     </Image>
-                    <sym:SymmetryOverlay 
+                    <sym:SymmetryOverlay
                         ZoomboxScale="{Binding Zoombox.Scale}"
                         HorizontalAxisVisible="{Binding Document.HorizontalSymmetryAxisEnabledBindable}"
                         VerticalAxisVisible="{Binding Document.VerticalSymmetryAxisEnabledBindable}"
                         HorizontalAxisY="{Binding Document.HorizontalSymmetryAxisYBindable, Mode=OneWay}"
                         VerticalAxisX="{Binding Document.VerticalSymmetryAxisXBindable, Mode=OneWay}"
                         DragCommand="{Binding Document.DragSymmetryCommand}"
-                        DragEndCommand="{Binding Document.EndDragSymmetryCommand}"
-                        />
-                    <cust:SelectionOverlay Path="{Binding Document.SelectionPathBindable}" ZoomboxScale="{Binding Zoombox.Scale}"/>
-                    <to:TransformOverlay 
-                        HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
+                        DragEndCommand="{Binding Document.EndDragSymmetryCommand}" />
+                    <cust:SelectionOverlay
+                        Path="{Binding Document.SelectionPathBindable}"
+                        ZoomboxScale="{Binding Zoombox.Scale}" />
+                    <to:TransformOverlay
+                        HorizontalAlignment="Stretch"
+                        VerticalAlignment="Stretch"
                         Visibility="{Binding Document.TransformViewModel.TransformActive, Converter={StaticResource BoolToVisibilityConverter}}"
                         Corners="{Binding Document.TransformViewModel.Corners, Mode=TwoWay}"
                         RequestedCorners="{Binding Document.TransformViewModel.RequestedCorners}"
                         CornerFreedom="{Binding Document.TransformViewModel.CornerFreedom}"
                         SideFreedom="{Binding Document.TransformViewModel.SideFreedom}"
                         InternalState="{Binding Document.TransformViewModel.InternalState, Mode=TwoWay}"
-                        ZoomboxScale="{Binding Zoombox.Scale}"/>
+                        ZoomboxScale="{Binding Zoombox.Scale}" />
                 </Grid>
             </Border>
         </zoombox:Zoombox>
-        <Grid Focusable="False">
+        <Grid
+            Focusable="False">
             <Grid.ColumnDefinitions>
-                <ColumnDefinition Width="1*"/>
-                <ColumnDefinition Width="2*"/>
-                <ColumnDefinition Width="1*"/>
+                <ColumnDefinition
+                    Width="1*" />
+                <ColumnDefinition
+                    Width="2*" />
+                <ColumnDefinition
+                    Width="1*" />
             </Grid.ColumnDefinitions>
             <Grid.RowDefinitions>
-                <RowDefinition Height="1*"/>
-                <RowDefinition Height="2*"/>
-                <RowDefinition Height="1*"/>
+                <RowDefinition
+                    Height="1*" />
+                <RowDefinition
+                    Height="2*" />
+                <RowDefinition
+                    Height="1*" />
             </Grid.RowDefinitions>
-            <Border BorderBrush="Red" Grid.Row="1" Grid.Column="1" BorderThickness="1"/>
+            <Border
+                BorderBrush="Red"
+                Grid.Row="1"
+                Grid.Column="1"
+                BorderThickness="1" />
         </Grid>
     </Grid>
-</UserControl>
+</UserControl>

+ 27 - 47
src/PixiEditorPrototype/UserControls/Viewport/Viewport.xaml.cs

@@ -7,7 +7,6 @@ using System.Windows.Data;
 using System.Windows.Input;
 using System.Windows.Media.Imaging;
 using ChunkyImageLib.DataHolders;
-using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using PixiEditor.Zoombox;
 using PixiEditorPrototype.Models;
 using PixiEditorPrototype.ViewModels;
@@ -37,16 +36,7 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
         DependencyProperty.Register(nameof(MouseMoveCommand), typeof(ICommand), typeof(Viewport), new(null));
 
     public static readonly DependencyProperty MouseUpCommandProperty =
-            DependencyProperty.Register(nameof(MouseUpCommand), typeof(ICommand), typeof(Viewport), new(null));
-
-    public static readonly DependencyProperty ReferenceBitmapProperty =
-        DependencyProperty.Register(nameof(ReferenceBitmap), typeof(BitmapSource), typeof(Viewport));
-    
-    public static readonly DependencyProperty ReferenceLayerProperty =
-        DependencyProperty.Register(nameof(ReferenceLayer), typeof(IReadOnlyReferenceLayer), typeof(Viewport));
-
-    public static readonly DependencyProperty ReferenceShapeProperty =
-        DependencyProperty.Register(nameof(ReferenceShape), typeof(ShapeCorners), typeof(Viewport));
+        DependencyProperty.Register(nameof(MouseUpCommand), typeof(ICommand), typeof(Viewport), new(null));
 
     private static readonly DependencyProperty BitmapsProperty =
         DependencyProperty.Register(nameof(Bitmaps), typeof(Dictionary<ChunkResolution, WriteableBitmap>), typeof(Viewport), new(null, OnBitmapsChange));
@@ -88,6 +78,16 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
         set => SetValue(ZoomModeProperty, value);
     }
 
+    public double ZoomboxScale
+    {
+        get => zoombox.Scale;
+        // ReSharper disable once ValueParameterNotUsed
+        set
+        {
+            PropertyChanged?.Invoke(this, new(nameof(ReferenceLayerScale)));
+        }
+    }
+
     public bool FlipX
     {
         get => (bool)GetValue(FlipXProperty);
@@ -101,6 +101,7 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
     }
 
     private double angle = 0;
+
     public double Angle
     {
         get => angle;
@@ -113,6 +114,7 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
     }
 
     private VecD center = new(32, 32);
+
     public VecD Center
     {
         get => center;
@@ -125,6 +127,7 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
     }
 
     private VecD realDimensions = new(double.MaxValue, double.MaxValue);
+
     public VecD RealDimensions
     {
         get => realDimensions;
@@ -143,6 +146,7 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
     }
 
     private VecD dimensions = new(64, 64);
+
     public VecD Dimensions
     {
         get => dimensions;
@@ -168,23 +172,10 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
         }
     }
 
-    public IReadOnlyReferenceLayer? ReferenceLayer
-    {
-        get => (IReadOnlyReferenceLayer?)GetValue(ReferenceLayerProperty);
-        set => SetValue(ReferenceLayerProperty, value);
-    }
-
-    public ShapeCorners ReferenceShape
-    {
-        get => (ShapeCorners)GetValue(ReferenceShapeProperty);
-        set => SetValue(ReferenceLayerProperty, value);
-    }
-
-    public WriteableBitmap? ReferenceBitmap
-    {
-        get => (WriteableBitmap?)GetValue(ReferenceBitmapProperty);
-        set => SetValue(ReferenceBitmapProperty, value);
-    }
+    public double ReferenceLayerScale =>
+        ZoomboxScale * ((Document?.ReferenceBitmap != null)
+            ? (Document.ReferenceShape.RectSize.X / (double)Document.ReferenceBitmap.Width)
+            : 1);
 
     public Zoombox Zoombox => zoombox;
 
@@ -194,31 +185,15 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
     {
         InitializeComponent();
 
-        Binding binding = new Binding();
-        binding.Source = this;
-        binding.Path = new PropertyPath("Document.Bitmaps");
+        Binding binding = new Binding { Source = this, Path = new PropertyPath("Document.Bitmaps") };
         SetBinding(BitmapsProperty, binding);
 
-        Binding referenceBitmapBinding = new Binding();
-        referenceBitmapBinding.Source = this;
-        referenceBitmapBinding.Path = new PropertyPath("Document.ReferenceBitmap");
-        SetBinding(ReferenceBitmapProperty, referenceBitmapBinding);
-
-        Binding referenceBinding = new Binding();
-        referenceBinding.Source = this;
-        referenceBinding.Path = new PropertyPath("Document.ReferenceLayer");
-        SetBinding(ReferenceLayerProperty, referenceBinding);
-
-        Binding shapeBinding = new Binding();
-        shapeBinding.Source = this;
-        shapeBinding.Path = new PropertyPath("Document.ReferenceShape");
-        SetBinding(ReferenceShapeProperty, shapeBinding);
-
         Loaded += OnLoad;
         Unloaded += OnUnload;
     }
 
-    private Image? GetImage() => (Image?)((Grid?)((Border?)zoombox.AdditionalContent)?.Child)?.Children[0];
+    private Image? GetImage() => (Image?)((Grid?)((Border?)zoombox.AdditionalContent)?.Child)?.Children[1];
+
     private void ForceRefreshFinalImage()
     {
         GetImage()?.InvalidateVisual();
@@ -265,4 +240,9 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
     {
         return new(Angle, Center, RealDimensions / 2, Dimensions / 2, CalculateResolution(), GuidValue, ForceRefreshFinalImage);
     }
+
+    private void OnReferenceImageSizeChanged(object? sender, SizeChangedEventArgs e)
+    {
+        PropertyChanged?.Invoke(this, new(nameof(ReferenceLayerScale)));
+    }
 }

+ 36 - 22
src/PixiEditorPrototype/ViewModels/DocumentViewModel.cs

@@ -6,6 +6,7 @@ using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using ChunkyImageLib;
 using ChunkyImageLib.DataHolders;
+using ChunkyImageLib.Operations;
 using Microsoft.Win32;
 using PixiEditor.ChangeableDocument.Actions.Undo;
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
@@ -46,6 +47,7 @@ internal class DocumentViewModel : INotifyPropertyChanged
     public RelayCommand? TransformSelectedAreaCommand { get; }
 
     private VecI size = new VecI(64, 64);
+
     public void SetSize(VecI size)
     {
         this.size = size;
@@ -53,39 +55,48 @@ internal class DocumentViewModel : INotifyPropertyChanged
         RaisePropertyChanged(nameof(Width));
         RaisePropertyChanged(nameof(Height));
     }
+
     public VecI SizeBindable => size;
 
     private SKPath selectionPath = new SKPath();
+
     public void SetSelectionPath(SKPath selectionPath)
     {
         (var toDispose, this.selectionPath) = (this.selectionPath, selectionPath);
         toDispose.Dispose();
         RaisePropertyChanged(nameof(SelectionPathBindable));
     }
+
     public SKPath SelectionPathBindable => selectionPath;
 
     private int horizontalSymmetryAxisY;
+
     public void SetHorizontalSymmetryAxisY(int horizontalSymmetryAxisY)
     {
         this.horizontalSymmetryAxisY = horizontalSymmetryAxisY;
         RaisePropertyChanged(nameof(HorizontalSymmetryAxisYBindable));
     }
+
     public int HorizontalSymmetryAxisYBindable => horizontalSymmetryAxisY;
 
     private int verticalSymmetryAxisX;
+
     public void SetVerticalSymmetryAxisX(int verticalSymmetryAxisX)
     {
         this.verticalSymmetryAxisX = verticalSymmetryAxisX;
         RaisePropertyChanged(nameof(VerticalSymmetryAxisXBindable));
     }
+
     public int VerticalSymmetryAxisXBindable => verticalSymmetryAxisX;
 
     private bool horizontalSymmetryAxisEnabled;
+
     public void SetHorizontalSymmetryAxisEnabled(bool horizontalSymmetryAxisEnabled)
     {
         this.horizontalSymmetryAxisEnabled = horizontalSymmetryAxisEnabled;
         RaisePropertyChanged(nameof(HorizontalSymmetryAxisEnabledBindable));
     }
+
     public bool HorizontalSymmetryAxisEnabledBindable
     {
         get => horizontalSymmetryAxisEnabled;
@@ -93,11 +104,13 @@ internal class DocumentViewModel : INotifyPropertyChanged
     }
 
     private bool verticalSymmetryAxisEnabled;
+
     public void SetVerticalSymmetryAxisEnabled(bool verticalSymmetryAxisEnabled)
     {
         this.verticalSymmetryAxisEnabled = verticalSymmetryAxisEnabled;
         RaisePropertyChanged(nameof(VerticalSymmetryAxisEnabledBindable));
     }
+
     public bool VerticalSymmetryAxisEnabledBindable
     {
         get => verticalSymmetryAxisEnabled;
@@ -105,6 +118,7 @@ internal class DocumentViewModel : INotifyPropertyChanged
     }
 
     private string name = string.Empty;
+
     public string Name
     {
         get => name;
@@ -123,10 +137,7 @@ internal class DocumentViewModel : INotifyPropertyChanged
 
     public Dictionary<ChunkResolution, WriteableBitmap> Bitmaps { get; set; } = new()
     {
-        [ChunkResolution.Full] = new WriteableBitmap(64, 64, 96, 96, PixelFormats.Pbgra32, null),
-        [ChunkResolution.Half] = new WriteableBitmap(32, 32, 96, 96, PixelFormats.Pbgra32, null),
-        [ChunkResolution.Quarter] = new WriteableBitmap(16, 16, 96, 96, PixelFormats.Pbgra32, null),
-        [ChunkResolution.Eighth] = new WriteableBitmap(8, 8, 96, 96, PixelFormats.Pbgra32, null),
+        [ChunkResolution.Full] = new WriteableBitmap(64, 64, 96, 96, PixelFormats.Pbgra32, null), [ChunkResolution.Half] = new WriteableBitmap(32, 32, 96, 96, PixelFormats.Pbgra32, null), [ChunkResolution.Quarter] = new WriteableBitmap(16, 16, 96, 96, PixelFormats.Pbgra32, null), [ChunkResolution.Eighth] = new WriteableBitmap(8, 8, 96, 96, PixelFormats.Pbgra32, null),
     };
 
     public Dictionary<ChunkResolution, SKSurface> Surfaces { get; set; } = new();
@@ -135,11 +146,22 @@ internal class DocumentViewModel : INotifyPropertyChanged
     public int ResizeWidth { get; set; } = 1024;
     public int ResizeHeight { get; set; } = 1024;
 
-    public IReadOnlyReferenceLayer? ReferenceLayer => Helpers.Tracker.Document.GetReferenceLayer();
-    
+    public IReadOnlyReferenceLayer? ReferenceLayer => Helpers.Tracker.Document.ReferenceLayer;
+
     public BitmapSource? ReferenceBitmap => ReferenceLayer?.Image.ToWriteableBitmap();
+    public VecI ReferenceBitmapSize => ReferenceLayer?.Image.Size ?? VecI.Zero;
+    public ShapeCorners ReferenceShape => ReferenceLayer?.Shape ?? default;
 
-    public ShapeCorners ReferenceShape => transformingReferenceLayer ? TransformViewModel.Corners : ReferenceLayer?.Shape ?? default;
+    public Matrix ReferenceTransformMatrix
+    {
+        get
+        {
+            if (ReferenceLayer is null)
+                return Matrix.Identity;
+            var skiaMatrix = OperationHelper.CreateMatrixFromPoints(ReferenceLayer.Shape, ReferenceLayer.Image.Size);
+            return new Matrix(skiaMatrix.ScaleX, skiaMatrix.SkewY, skiaMatrix.SkewX, skiaMatrix.ScaleY, skiaMatrix.TransX, skiaMatrix.TransY);
+        }
+    }
 
     private DocumentHelpers Helpers { get; }
 
@@ -231,7 +253,7 @@ internal class DocumentViewModel : INotifyPropertyChanged
                 new StructureMemberName_Action(guid, layer.Name),
                 new PasteImage_Action(surface, new(new RectD(new VecD(layer.OffsetX, layer.OffsetY), new(layer.Width, layer.Height))), guid, true, false),
                 new EndPasteImage_Action()
-                );
+            );
             if (layer.Opacity != 1)
                 acc.AddFinishedActions(
                     new StructureMemberOpacity_Action(guid, layer.Opacity),
@@ -263,6 +285,7 @@ internal class DocumentViewModel : INotifyPropertyChanged
             return false;
         return true;
     }
+
     private void TransformSelectedArea(object? obj)
     {
         if (updateableChangeActive || FindFirstSelectedMember() is not LayerViewModel layer || SelectionPathBindable.IsEmpty)
@@ -478,7 +501,7 @@ internal class DocumentViewModel : INotifyPropertyChanged
 
     public void ApplyTransform(object? param)
     {
-        if (!transformingRectangle && !pastingImage && !transformingSelectionPath && !transformingEllipse)
+        if (!transformingRectangle && !pastingImage && !transformingSelectionPath && !transformingEllipse && !transformingReferenceLayer)
             return;
 
         if (transformingRectangle)
@@ -565,7 +588,7 @@ internal class DocumentViewModel : INotifyPropertyChanged
             Helpers.ActionAccumulator.AddActions(new CreateReferenceLayer_Action(null, newCorners));
         }
     }
-    
+
     public void FloodFill(VecI pos, SKColor color)
     {
         var member = FindFirstSelectedMember();
@@ -610,18 +633,9 @@ internal class DocumentViewModel : INotifyPropertyChanged
         double shapeWidth;
         double shapeHeight;
 
-        if (referenceImage.Size.X > referenceImage.Size.Y)
-        {
-            shapeWidth = Width;
-            shapeHeight = Width / referenceImage.Size.X * referenceImage.Size.Y;
-        }
-        else
-        {
-            shapeWidth = Height / referenceImage.Size.Y * referenceImage.Size.X;
-            shapeHeight = Height;
-        }
-        
-        ShapeCorners corners = new ShapeCorners(new RectD(VecD.Zero, new(shapeWidth, shapeHeight)));
+        RectD referenceImageRect = new RectD(VecD.Zero, SizeBindable).AspectFit(new RectD(VecD.Zero, referenceImage.Size));
+        ShapeCorners corners = new ShapeCorners(referenceImageRect);
+
         Helpers.ActionAccumulator.AddActions(new CreateReferenceLayer_Action(referenceImage, corners));
         transformingReferenceLayer = true;
         TransformViewModel.ShowShapeTransform(corners);