Browse Source

Fixed overlays

Krzysztof Krysiński 1 year ago
parent
commit
d2c832d01d

+ 244 - 234
src/PixiEditor.AvaloniaUI/Views/Main/ViewportControls/Viewport.axaml

@@ -45,92 +45,94 @@
                                         PassEventArgsToCommand="True"/>
                                         PassEventArgsToCommand="True"/>
             </EventTriggerBehavior>-->
             </EventTriggerBehavior>-->
         </Interaction.Behaviors>
         </Interaction.Behaviors>
-        <overlays:TogglableFlyout Margin="5" IconPath="/Images/Settings.png" ui:Translator.TooltipKey="VIEWPORT_SETTINGS"
-                               ZIndex="2" HorizontalAlignment="Right" VerticalAlignment="Top">
+        <overlays:TogglableFlyout Margin="5" IconPath="/Images/Settings.png"
+                                  ui:Translator.TooltipKey="VIEWPORT_SETTINGS"
+                                  ZIndex="2" HorizontalAlignment="Right" VerticalAlignment="Top">
             <overlays:TogglableFlyout.Child>
             <overlays:TogglableFlyout.Child>
                 <Border BorderThickness="1" CornerRadius="5" Padding="5" Background="#C8202020" ZIndex="2">
                 <Border BorderThickness="1" CornerRadius="5" Padding="5" Background="#C8202020" ZIndex="2">
-        <StackPanel Orientation="Vertical">
-            <StackPanel Orientation="Horizontal">
-            <TextBlock Margin="5 0" TextAlignment="Center"
-                       Text="{Binding Path=Angle, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, 
+                    <StackPanel Orientation="Vertical">
+                        <StackPanel Orientation="Horizontal">
+                            <TextBlock Margin="5 0" TextAlignment="Center"
+                                       Text="{Binding Path=Angle, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport},
              Converter={converters:RadiansToDegreesConverter}, StringFormat={}{0}°}"
              Converter={converters:RadiansToDegreesConverter}, StringFormat={}{0}°}"
-                       Width="35" Foreground="White" VerticalAlignment="Center" FontSize="16"/>
-            <Button Width="32" Height="32" ui:Translator.TooltipKey="RESET_VIEWPORT"
-                    Classes="OverlayButton"
-                    Click="ResetViewportClicked"
-                    Cursor="Hand">
-            <Button.Content>
-                <Image Width="28" Height="28" Source="/Images/Layout.png"/>
-            </Button.Content>
-            </Button>
-        </StackPanel>
-            <Separator/>
-            <StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
-                <ToggleButton Width="32" Height="32" ui:Translator.TooltipKey="TOGGLE_VERTICAL_SYMMETRY"
-                        Classes="OverlayToggleButton"
-                        IsChecked="{Binding Document.VerticalSymmetryAxisEnabledBindable, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=TwoWay}"
-                        Cursor="Hand">
-                    <ToggleButton.Content>
-                        <Image Width="28" Height="28" Source="/Images/SymmetryVertical.png"/>
-                    </ToggleButton.Content>
-                </ToggleButton>
-                <ToggleButton Margin="10 0 0 0" Width="32" Height="32" ui:Translator.TooltipKey="TOGGLE_HORIZONTAL_SYMMETRY"
-                              Classes="OverlayToggleButton"
-                              IsChecked="{Binding Document.HorizontalSymmetryAxisEnabledBindable, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=TwoWay}"
-                              Cursor="Hand">
-                    <ToggleButton.Content>
-                        <Image Width="28" Height="28" Source="/Images/SymmetryVertical.png">
-                            <Image.RenderTransform>
-                                <RotateTransform Angle="90"/>
-                            </Image.RenderTransform>
-                        </Image>
-                    </ToggleButton.Content>
-                </ToggleButton>
-            </StackPanel>
-            <Separator/>
-            <StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
-                <ToggleButton Width="32" Height="32" ui:Translator.TooltipKey="FLIP_VIEWPORT_HORIZONTALLY"
-                              Classes="OverlayToggleButton"
-                              IsChecked="{Binding FlipX, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=TwoWay}"
-                              Cursor="Hand">
-                    <ToggleButton.Content>
-                        <Image Width="28" Height="28" Source="/Images/FlipHorizontal.png"/>
-                    </ToggleButton.Content>
-                </ToggleButton>
-                <ToggleButton Margin="10 0 0 0" Width="32" Height="32" ui:Translator.TooltipKey="FLIP_VIEWPORT_VERTICALLY"
-                              Classes="OverlayToggleButton"
-                              IsChecked="{Binding FlipY, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=TwoWay}"
-                              Cursor="Hand">
-                    <ToggleButton.Content>
-                        <Image Width="28" Height="28" Source="/Images/FlipHorizontal.png">
-                            <Image.RenderTransform>
-                                <RotateTransform Angle="90"/>
-                            </Image.RenderTransform>
-                        </Image>
-                    </ToggleButton.Content>
-                </ToggleButton>
-            </StackPanel>
-        </StackPanel>
-        </Border>
+                                       Width="35" Foreground="White" VerticalAlignment="Center" FontSize="16" />
+                            <Button Width="32" Height="32" ui:Translator.TooltipKey="RESET_VIEWPORT"
+                                    Classes="OverlayButton"
+                                    Click="ResetViewportClicked"
+                                    Cursor="Hand">
+                                <Button.Content>
+                                    <Image Width="28" Height="28" Source="/Images/Layout.png" />
+                                </Button.Content>
+                            </Button>
+                        </StackPanel>
+                        <Separator />
+                        <StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
+                            <ToggleButton Width="32" Height="32" ui:Translator.TooltipKey="TOGGLE_VERTICAL_SYMMETRY"
+                                          Classes="OverlayToggleButton"
+                                          IsChecked="{Binding Document.VerticalSymmetryAxisEnabledBindable, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=TwoWay}"
+                                          Cursor="Hand">
+                                <ToggleButton.Content>
+                                    <Image Width="28" Height="28" Source="/Images/SymmetryVertical.png" />
+                                </ToggleButton.Content>
+                            </ToggleButton>
+                            <ToggleButton Margin="10 0 0 0" Width="32" Height="32"
+                                          ui:Translator.TooltipKey="TOGGLE_HORIZONTAL_SYMMETRY"
+                                          Classes="OverlayToggleButton"
+                                          IsChecked="{Binding Document.HorizontalSymmetryAxisEnabledBindable, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=TwoWay}"
+                                          Cursor="Hand">
+                                <ToggleButton.Content>
+                                    <Image Width="28" Height="28" Source="/Images/SymmetryVertical.png">
+                                        <Image.RenderTransform>
+                                            <RotateTransform Angle="90" />
+                                        </Image.RenderTransform>
+                                    </Image>
+                                </ToggleButton.Content>
+                            </ToggleButton>
+                        </StackPanel>
+                        <Separator />
+                        <StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
+                            <ToggleButton Width="32" Height="32" ui:Translator.TooltipKey="FLIP_VIEWPORT_HORIZONTALLY"
+                                          Classes="OverlayToggleButton"
+                                          IsChecked="{Binding FlipX, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=TwoWay}"
+                                          Cursor="Hand">
+                                <ToggleButton.Content>
+                                    <Image Width="28" Height="28" Source="/Images/FlipHorizontal.png" />
+                                </ToggleButton.Content>
+                            </ToggleButton>
+                            <ToggleButton Margin="10 0 0 0" Width="32" Height="32"
+                                          ui:Translator.TooltipKey="FLIP_VIEWPORT_VERTICALLY"
+                                          Classes="OverlayToggleButton"
+                                          IsChecked="{Binding FlipY, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=TwoWay}"
+                                          Cursor="Hand">
+                                <ToggleButton.Content>
+                                    <Image Width="28" Height="28" Source="/Images/FlipHorizontal.png">
+                                        <Image.RenderTransform>
+                                            <RotateTransform Angle="90" />
+                                        </Image.RenderTransform>
+                                    </Image>
+                                </ToggleButton.Content>
+                            </ToggleButton>
+                        </StackPanel>
+                    </StackPanel>
+                </Border>
             </overlays:TogglableFlyout.Child>
             </overlays:TogglableFlyout.Child>
         </overlays:TogglableFlyout>
         </overlays:TogglableFlyout>
-         <visuals:Scene
-                        Focusable="False" Name="scene"
-                        RenderTransformOrigin="0,0"
-                        ZIndex="10"
-                        Width="{Binding RealDimensions.X, ElementName=vpUc}"
-                        Height="{Binding RealDimensions.Y, ElementName=vpUc}"
-                        Surface="{Binding TargetBitmap, ElementName=vpUc}"
-                        Scale="{Binding Scale, ElementName=zoombox, Mode=OneWay}"
-                        Dimensions="{Binding Dimensions, ElementName=zoombox, Mode=OneWay}"
-                        Document="{Binding Document, ElementName=vpUc, Mode=OneWay}"
-                        ContentPosition="{Binding CanvasPos, ElementName=zoombox, Mode=OneWay}"
-                        Angle="{Binding RotateTransformAngle, ElementName=zoombox, Mode=OneWay}"
-                        ui1:RenderOptionsBindable.BitmapInterpolationMode="{Binding Scale, Converter={converters:ScaleToBitmapScalingModeConverter}, ElementName=zoombox}"
-                        FlowDirection="LeftToRight">
-                        <visuals:Scene.Styles>
-                            <!--TODO: Implement-->
-                            <!--<Style>
+        <visuals:Scene
+            Focusable="False" Name="scene"
+            RenderTransformOrigin="0,0"
+            ZIndex="1"
+            Width="{Binding RealDimensions.X, ElementName=vpUc}"
+            Height="{Binding RealDimensions.Y, ElementName=vpUc}"
+            Surface="{Binding TargetBitmap, ElementName=vpUc}"
+            Scale="{Binding Scale, ElementName=zoombox, Mode=OneWay}"
+            Document="{Binding Document, ElementName=vpUc, Mode=OneWay}"
+            ContentPosition="{Binding CanvasPos, ElementName=zoombox, Mode=OneWay}"
+            Angle="{Binding RotateTransformAngle, ElementName=zoombox, Mode=OneWay}"
+            ui1:RenderOptionsBindable.BitmapInterpolationMode="{Binding Scale, Converter={converters:ScaleToBitmapScalingModeConverter}, ElementName=zoombox}"
+            FlowDirection="LeftToRight">
+            <visuals:Scene.Styles>
+                <!--TODO: Implement-->
+                <!--<Style>
                                 <Style.Triggers>
                                 <Style.Triggers>
                                     <DataTrigger Binding="{Binding Source={vm:ToolVM ColorPickerToolViewModel}, Path=PickOnlyFromReferenceLayer, Mode=OneWay}" Value="True">
                                     <DataTrigger Binding="{Binding Source={vm:ToolVM ColorPickerToolViewModel}, Path=PickOnlyFromReferenceLayer, Mode=OneWay}" Value="True">
                                         <DataTrigger.EnterActions>
                                         <DataTrigger.EnterActions>
@@ -154,8 +156,8 @@
                                     </DataTrigger>
                                     </DataTrigger>
                                 </Style.Triggers>
                                 </Style.Triggers>
                             </Style>-->
                             </Style>-->
-                        </visuals:Scene.Styles>
-                    </visuals:Scene>
+            </visuals:Scene.Styles>
+        </visuals:Scene>
         <zoombox:Zoombox
         <zoombox:Zoombox
             Tag="{Binding ElementName=vpUc}"
             Tag="{Binding ElementName=vpUc}"
             x:Name="zoombox"
             x:Name="zoombox"
@@ -170,53 +172,54 @@
             FlipX="{Binding FlipX, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=TwoWay}"
             FlipX="{Binding FlipX, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=TwoWay}"
             FlipY="{Binding FlipY, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=TwoWay}">
             FlipY="{Binding FlipY, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=TwoWay}">
             <zoombox:Zoombox.AdditionalContent>
             <zoombox:Zoombox.AdditionalContent>
-            <Border
-                d:Width="64"
-                d:Height="64"
-                HorizontalAlignment="Center"
-                VerticalAlignment="Center"
-                DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}}"
-                RenderOptions.BitmapInterpolationMode="None">
-                <Border.Background>
-                    <!--TODO: Seems like DestinationRect of anything with size below and equal to 1 is tiling texture wrong-->
-                    <!--Update: Seems like it depends on screen DPI and scaling, value of 2 also produces artifacts on high DPI device-->
-                    <ImageBrush Source="/Images/CheckerTile.png" TileMode="Tile">
-                        <ImageBrush.Transform>
-                            <ScaleTransform ScaleX="0.5" ScaleY="0.5"/>
-                        </ImageBrush.Transform>
-                        <ImageBrush.DestinationRect>
-                            <Binding Path="Scale" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type zoombox:Zoombox}}"
-                                     Converter="{converters:ZoomToViewportConverter}">
-                                <Binding.ConverterParameter>
-                                    <sys:Double>16</sys:Double>
-                                </Binding.ConverterParameter>
-                            </Binding>
-                        </ImageBrush.DestinationRect>
-                    </ImageBrush>
-                </Border.Background>
-                <Grid>
-                    <Canvas
-                        ZIndex="{Binding Document.ReferenceLayerViewModel.ShowHighest, Converter={converters:BoolToIntConverter}}"
-                        IsHitTestVisible="{Binding Document.ReferenceLayerViewModel.IsTransforming}"
-                        ui1:RenderOptionsBindable.BitmapInterpolationMode="{Binding ReferenceLayerScale, Converter={converters:ScaleToBitmapScalingModeConverter}}">
-                        <visuals:SurfaceControl
-                            Focusable="False"
-                            Width="{Binding Document.ReferenceLayerViewModel.ReferenceBitmap.Size.X}"
-                            Height="{Binding Document.ReferenceLayerViewModel.ReferenceBitmap.Size.Y}"
-                            Surface="{Binding Document.ReferenceLayerViewModel.ReferenceBitmap, Mode=OneWay}"
-                            IsVisible="{Binding Document.ReferenceLayerViewModel.IsVisibleBindable}"
-                            RenderTransformOrigin="0, 0"
-                            SizeChanged="OnReferenceImageSizeChanged"
-                            FlowDirection="LeftToRight">
-                            <visuals:SurfaceControl.RenderTransform>
-                                <TransformGroup>
-                                    <MatrixTransform
-                                        Matrix="{Binding Document.ReferenceLayerViewModel.ReferenceTransformMatrix}" />
-                                </TransformGroup>
-                            </visuals:SurfaceControl.RenderTransform>
-                            <visuals:SurfaceControl.Styles>
-                                <!--TODO: Implement this-->
-                                <!--<Style>
+                <Border
+                    d:Width="64"
+                    d:Height="64"
+                    HorizontalAlignment="Center"
+                    VerticalAlignment="Center"
+                    DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}}"
+                    RenderOptions.BitmapInterpolationMode="None">
+                    <Border.Background>
+                        <!--TODO: Seems like DestinationRect of anything with size below and equal to 1 is tiling texture wrong-->
+                        <!--Update: Seems like it depends on screen DPI and scaling, value of 2 also produces artifacts on high DPI device-->
+                        <ImageBrush Source="/Images/CheckerTile.png" TileMode="Tile">
+                            <ImageBrush.Transform>
+                                <ScaleTransform ScaleX="0.5" ScaleY="0.5" />
+                            </ImageBrush.Transform>
+                            <ImageBrush.DestinationRect>
+                                <Binding Path="Scale"
+                                         RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type zoombox:Zoombox}}"
+                                         Converter="{converters:ZoomToViewportConverter}">
+                                    <Binding.ConverterParameter>
+                                        <sys:Double>16</sys:Double>
+                                    </Binding.ConverterParameter>
+                                </Binding>
+                            </ImageBrush.DestinationRect>
+                        </ImageBrush>
+                    </Border.Background>
+                    <Grid>
+                        <Canvas
+                            ZIndex="{Binding Document.ReferenceLayerViewModel.ShowHighest, Converter={converters:BoolToIntConverter}}"
+                            IsHitTestVisible="{Binding Document.ReferenceLayerViewModel.IsTransforming}"
+                            ui1:RenderOptionsBindable.BitmapInterpolationMode="{Binding ReferenceLayerScale, Converter={converters:ScaleToBitmapScalingModeConverter}}">
+                            <visuals:SurfaceControl
+                                Focusable="False"
+                                Width="{Binding Document.ReferenceLayerViewModel.ReferenceBitmap.Size.X}"
+                                Height="{Binding Document.ReferenceLayerViewModel.ReferenceBitmap.Size.Y}"
+                                Surface="{Binding Document.ReferenceLayerViewModel.ReferenceBitmap, Mode=OneWay}"
+                                IsVisible="{Binding Document.ReferenceLayerViewModel.IsVisibleBindable}"
+                                RenderTransformOrigin="0, 0"
+                                SizeChanged="OnReferenceImageSizeChanged"
+                                FlowDirection="LeftToRight">
+                                <visuals:SurfaceControl.RenderTransform>
+                                    <TransformGroup>
+                                        <MatrixTransform
+                                            Matrix="{Binding Document.ReferenceLayerViewModel.ReferenceTransformMatrix}" />
+                                    </TransformGroup>
+                                </visuals:SurfaceControl.RenderTransform>
+                                <visuals:SurfaceControl.Styles>
+                                    <!--TODO: Implement this-->
+                                    <!--<Style>
                                     <Style.Triggers>
                                     <Style.Triggers>
                                         <DataTrigger Binding="{Binding Document.ReferenceLayerViewModel.ShowHighest, Mode=OneWay}" Value="True">
                                         <DataTrigger Binding="{Binding Document.ReferenceLayerViewModel.ShowHighest, Mode=OneWay}" Value="True">
                                             <DataTrigger.EnterActions>
                                             <DataTrigger.EnterActions>
@@ -240,11 +243,11 @@
                                         </DataTrigger>
                                         </DataTrigger>
                                     </Style.Triggers>
                                     </Style.Triggers>
                                 </Style>-->
                                 </Style>-->
-                            </visuals:SurfaceControl.Styles>
-                        </visuals:SurfaceControl>
-                        <Canvas.Styles>
-                            <!--TODO: Implement this-->
-                            <!--<Style>
+                                </visuals:SurfaceControl.Styles>
+                            </visuals:SurfaceControl>
+                            <Canvas.Styles>
+                                <!--TODO: Implement this-->
+                                <!--<Style>
                                 <Style.Triggers>
                                 <Style.Triggers>
                                     <DataTrigger Binding="{Binding Source={vm:ToolVM ColorPickerToolViewModel}, Path=PickFromReferenceLayer, Mode=OneWay}" Value="False">
                                     <DataTrigger Binding="{Binding Source={vm:ToolVM ColorPickerToolViewModel}, Path=PickFromReferenceLayer, Mode=OneWay}" Value="False">
                                         <DataTrigger.EnterActions>
                                         <DataTrigger.EnterActions>
@@ -268,124 +271,131 @@
                                     </DataTrigger>
                                     </DataTrigger>
                                 </Style.Triggers>
                                 </Style.Triggers>
                             </Style>-->
                             </Style>-->
-                        </Canvas.Styles>
-                    </Canvas>
-                    <Panel Width="{Binding Document.Width}" Height="{Binding Document.Height}"/>
-                    <Grid ZIndex="5">
-                        <symmetryOverlay:SymmetryOverlay
-                            Focusable="False"
-                            IsHitTestVisible="{Binding ZoomMode, Converter={converters:ZoomModeToHitTestVisibleConverter}}"
-                            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="{xaml:Command PixiEditor.Document.DragSymmetry, UseProvided=True}"
-                            DragEndCommand="{xaml:Command PixiEditor.Document.EndDragSymmetry, UseProvided=True}"
-                            DragStartCommand="{xaml:Command PixiEditor.Document.StartDragSymmetry, UseProvided=True}"
-                            FlowDirection="LeftToRight" />
-                        <selectionOverlay:SelectionOverlay
-                            Focusable="False"
-                            ShowFill="{Binding ToolsSubViewModel.ActiveTool, Source={viewModels:MainVM}, Converter={converters:IsSelectionToolConverter}}"
-                            Path="{Binding Document.SelectionPathBindable}"
-                            ZoomboxScale="{Binding Zoombox.Scale}"
-                            FlowDirection="LeftToRight" />
-                        <brushShapeOverlay:BrushShapeOverlay
-                            Focusable="False"
-                            IsHitTestVisible="False"
-                            IsVisible="{Binding !Document.TransformViewModel.TransformActive}"
-                            ZoomboxScale="{Binding Zoombox.Scale}"
-                            MouseEventSource="{Binding Zoombox.Tag.BackgroundGrid, Mode=OneTime}"
-                            MouseReference="{Binding Zoombox.Tag.MainImage, 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"/>
-                        <transformOverlay:TransformOverlay
-                            Focusable="False"
-                            Cursor="Arrow"
-                            IsHitTestVisible="{Binding ZoomMode, Converter={converters:ZoomModeToHitTestVisibleConverter}}"
-                            HorizontalAlignment="Stretch"
-                            VerticalAlignment="Stretch"
-                            IsVisible="{Binding Document.TransformViewModel.TransformActive}"
-                            ActionCompleted="{Binding Document.TransformViewModel.ActionCompletedCommand}"
-                            Corners="{Binding Document.TransformViewModel.Corners, Mode=TwoWay}"
-                            RequestedCorners="{Binding Document.TransformViewModel.RequestedCorners, Mode=TwoWay}"
-                            CornerFreedom="{Binding Document.TransformViewModel.CornerFreedom}"
-                            SideFreedom="{Binding Document.TransformViewModel.SideFreedom}"
-                            LockRotation="{Binding Document.TransformViewModel.LockRotation}"
-                            CoverWholeScreen="{Binding Document.TransformViewModel.CoverWholeScreen}"
-                            SnapToAngles="{Binding Document.TransformViewModel.SnapToAngles}"
-                            InternalState="{Binding Document.TransformViewModel.InternalState, Mode=TwoWay}"
-                            ZoomboxScale="{Binding Zoombox.Scale}"
-                            ZoomboxAngle="{Binding Zoombox.Angle}" />
-                        <lineToolOverlay:LineToolOverlay
-                            Focusable="False"
-                            IsVisible="{Binding Document.LineToolOverlayViewModel.IsEnabled}"
-                            ActionCompleted="{Binding Document.LineToolOverlayViewModel.ActionCompletedCommand}"
-                            LineStart="{Binding Document.LineToolOverlayViewModel.LineStart, Mode=TwoWay}"
-                            LineEnd="{Binding Document.LineToolOverlayViewModel.LineEnd, Mode=TwoWay}"
-                            ZoomboxScale="{Binding Zoombox.Scale}"
-                            FlowDirection="LeftToRight"/>
+                            </Canvas.Styles>
+                        </Canvas>
+                        <Panel Width="{Binding Document.Width}" Height="{Binding Document.Height}" />
+                        <Grid IsHitTestVisible="False"
+                              ShowGridLines="True" Width="{Binding Document.Width}" Height="{Binding Document.Height}"
+                              Panel.ZIndex="10"
+                              IsVisible="{Binding GridLinesVisible, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}}">
+                            <Grid.Resources>
+                                <converters:ThresholdVisibilityConverter Threshold="10"
+                                                                         x:Key="ThresholdVisibilityConverter" />
+                            </Grid.Resources>
+                            <visuals:GridLines Scale="{Binding Zoombox.Scale}"
+                                               IsVisible="{Binding Zoombox.Scale, Converter={StaticResource ThresholdVisibilityConverter}}"
+                                               Rows="{Binding Document.Width}" Columns="{Binding Document.Height}" />
+                        </Grid>
+                        <Rectangle Stroke="{DynamicResource ThemeBackgroundBrush1}" Opacity=".8" ZIndex="2"
+                                   IsVisible="{Binding Document.ReferenceLayerViewModel.IsVisibleBindable}">
+                            <Rectangle.StrokeThickness>
+                                <Binding Converter="{converters:ReciprocalConverter}">
+                                    <Binding.Path>Zoombox.Scale</Binding.Path>
+                                    <Binding.ConverterParameter>
+                                        <sys:Double>
+                                            3
+                                        </sys:Double>
+                                    </Binding.ConverterParameter>
+                                </Binding>
+                            </Rectangle.StrokeThickness>
+                            <Rectangle.Margin>
+                                <Binding Converter="{converters:ReciprocalConverter}">
+                                    <Binding.Path>Zoombox.Scale</Binding.Path>
+                                    <Binding.ConverterParameter>
+                                        <sys:Double>
+                                            -3
+                                        </sys:Double>
+                                    </Binding.ConverterParameter>
+                                </Binding>
+                            </Rectangle.Margin>
+                        </Rectangle>
                     </Grid>
                     </Grid>
-                    <Grid IsHitTestVisible="False"
-                        ShowGridLines="True" Width="{Binding Document.Width}" Height="{Binding Document.Height}" Panel.ZIndex="10"
-                        IsVisible="{Binding GridLinesVisible, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}}">
-                        <Grid.Resources>
-                            <converters:ThresholdVisibilityConverter Threshold="10" x:Key="ThresholdVisibilityConverter"/>
-                        </Grid.Resources>
-                        <visuals:GridLines Scale="{Binding Zoombox.Scale}" IsVisible="{Binding Zoombox.Scale, Converter={StaticResource ThresholdVisibilityConverter}}"
-                                           Rows="{Binding Document.Width}" Columns="{Binding Document.Height}"/>
-                    </Grid>
-                    <Rectangle Stroke="{DynamicResource ThemeBackgroundBrush1}" Opacity=".8" ZIndex="2"
-                               IsVisible="{Binding Document.ReferenceLayerViewModel.IsVisibleBindable}">
-                        <Rectangle.StrokeThickness>
-                            <Binding Converter="{converters:ReciprocalConverter}">
-                                <Binding.Path>Zoombox.Scale</Binding.Path>
-                                <Binding.ConverterParameter>
-                                    <sys:Double>
-                                        3
-                                    </sys:Double>
-                                </Binding.ConverterParameter>
-                            </Binding>
-                        </Rectangle.StrokeThickness>
-                        <Rectangle.Margin>
-                            <Binding Converter="{converters:ReciprocalConverter}">
-                                <Binding.Path>Zoombox.Scale</Binding.Path>
-                                <Binding.ConverterParameter>
-                                    <sys:Double>
-                                        -3
-                                    </sys:Double>
-                                </Binding.ConverterParameter>
-                            </Binding>
-                        </Rectangle.Margin>
-                    </Rectangle>
-                </Grid>
-            </Border>
-                </zoombox:Zoombox.AdditionalContent>
+                </Border>
+            </zoombox:Zoombox.AdditionalContent>
         </zoombox:Zoombox>
         </zoombox:Zoombox>
-        <Button 
+        <Grid ZIndex="5" DataContext="{Binding ElementName=vpUc}"
+              RenderTransformOrigin="0, 0" RenderTransform="{Binding #zoombox.CanvasTransform}">
+            <symmetryOverlay:SymmetryOverlay
+                Focusable="False"
+                Size="{Binding Document.SizeBindable, Mode=OneWay}"
+                IsHitTestVisible="{Binding ZoomMode, Converter={converters:ZoomModeToHitTestVisibleConverter}}"
+                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="{xaml:Command PixiEditor.Document.DragSymmetry, UseProvided=True}"
+                DragEndCommand="{xaml:Command PixiEditor.Document.EndDragSymmetry, UseProvided=True}"
+                DragStartCommand="{xaml:Command PixiEditor.Document.StartDragSymmetry, UseProvided=True}"
+                FlowDirection="LeftToRight" />
+            <selectionOverlay:SelectionOverlay
+                Focusable="False"
+                ShowFill="{Binding ToolsSubViewModel.ActiveTool, Source={viewModels:MainVM}, Converter={converters:IsSelectionToolConverter}}"
+                Path="{Binding Document.SelectionPathBindable}"
+                ZoomboxScale="{Binding #zoombox.Scale}"
+                FlowDirection="LeftToRight" />
+            <brushShapeOverlay:BrushShapeOverlay
+                Name="brushShapeOverlay"
+                Focusable="False"
+                IsHitTestVisible="False"
+                IsVisible="{Binding !Document.TransformViewModel.TransformActive}"
+                ZoomboxScale="{Binding #zoombox.Scale}"
+                MouseEventSource="{Binding #vpUc.BackgroundGrid, Mode=OneTime}"
+                MouseReference="{Binding #vpUc.MainImage, 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" />
+            <transformOverlay:TransformOverlay
+                Focusable="False"
+                Cursor="Arrow"
+                IsHitTestVisible="{Binding ZoomMode, Converter={converters:ZoomModeToHitTestVisibleConverter}}"
+                HorizontalAlignment="Stretch"
+                VerticalAlignment="Stretch"
+                IsVisible="{Binding Document.TransformViewModel.TransformActive}"
+                ActionCompleted="{Binding Document.TransformViewModel.ActionCompletedCommand}"
+                Corners="{Binding Document.TransformViewModel.Corners, Mode=TwoWay}"
+                RequestedCorners="{Binding Document.TransformViewModel.RequestedCorners, Mode=TwoWay}"
+                CornerFreedom="{Binding Document.TransformViewModel.CornerFreedom}"
+                SideFreedom="{Binding Document.TransformViewModel.SideFreedom}"
+                LockRotation="{Binding Document.TransformViewModel.LockRotation}"
+                CoverWholeScreen="{Binding Document.TransformViewModel.CoverWholeScreen}"
+                SnapToAngles="{Binding Document.TransformViewModel.SnapToAngles}"
+                InternalState="{Binding Document.TransformViewModel.InternalState, Mode=TwoWay}"
+                ZoomboxScale="{Binding #zoombox.Scale}"
+                ZoomboxAngle="{Binding #zoombox.Angle}" />
+            <lineToolOverlay:LineToolOverlay
+                Focusable="False"
+                IsVisible="{Binding Document.LineToolOverlayViewModel.IsEnabled}"
+                ActionCompleted="{Binding Document.LineToolOverlayViewModel.ActionCompletedCommand}"
+                LineStart="{Binding Document.LineToolOverlayViewModel.LineStart, Mode=TwoWay}"
+                LineEnd="{Binding Document.LineToolOverlayViewModel.LineEnd, Mode=TwoWay}"
+                ZoomboxScale="{Binding #zoombox.Scale}"
+                FlowDirection="LeftToRight" />
+        </Grid>
+        <Button
             ZIndex="99999"
             ZIndex="99999"
             DockPanel.Dock="Bottom"
             DockPanel.Dock="Bottom"
             Margin="5"
             Margin="5"
             Padding="8,5,5,5"
             Padding="8,5,5,5"
-            VerticalAlignment="Bottom" 
+            VerticalAlignment="Bottom"
             HorizontalAlignment="Center"
             HorizontalAlignment="Center"
             Classes="GrayRoundButton"
             Classes="GrayRoundButton"
             Command="{xaml:Command PixiEditor.Tools.ApplyTransform}">
             Command="{xaml:Command PixiEditor.Tools.ApplyTransform}">
             <Button.IsVisible>
             <Button.IsVisible>
                 <MultiBinding Converter="{converters:BoolOrToVisibilityConverter}">
                 <MultiBinding Converter="{converters:BoolOrToVisibilityConverter}">
                     <MultiBinding.Bindings>
                     <MultiBinding.Bindings>
-                        <Binding ElementName="vpUc" Path="Document.TransformViewModel.ShowTransformControls"/>
-                        <Binding ElementName="vpUc" Path="Document.LineToolOverlayViewModel.IsEnabled"/>
+                        <Binding ElementName="vpUc" Path="Document.TransformViewModel.ShowTransformControls" />
+                        <Binding ElementName="vpUc" Path="Document.LineToolOverlayViewModel.IsEnabled" />
                     </MultiBinding.Bindings>
                     </MultiBinding.Bindings>
                 </MultiBinding>
                 </MultiBinding>
             </Button.IsVisible>
             </Button.IsVisible>
             <StackPanel Orientation="Horizontal">
             <StackPanel Orientation="Horizontal">
                 <TextBlock ui:Translator.Key="APPLY_TRANSFORM" VerticalAlignment="Center" Margin="0,0,5,0" />
                 <TextBlock ui:Translator.Key="APPLY_TRANSFORM" VerticalAlignment="Center" Margin="0,0,5,0" />
-                <Border Padding="10,3" CornerRadius="5" Background="{DynamicResource ThemeAccentBrush}" IsVisible="{xaml:ShortcutBinding PixiEditor.Tools.ApplyTransform, Converter={converters:NotNullToVisibilityConverter}}">
+                <Border Padding="10,3" CornerRadius="5" Background="{DynamicResource ThemeAccentBrush}"
+                        IsVisible="{xaml:ShortcutBinding PixiEditor.Tools.ApplyTransform, Converter={converters:NotNullToVisibilityConverter}}">
                     <TextBlock Text="{xaml:ShortcutBinding PixiEditor.Tools.ApplyTransform}" />
                     <TextBlock Text="{xaml:ShortcutBinding PixiEditor.Tools.ApplyTransform}" />
                 </Border>
                 </Border>
             </StackPanel>
             </StackPanel>
         </Button>
         </Button>
     </Grid>
     </Grid>
-</UserControl>
+</UserControl>

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

@@ -304,11 +304,13 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
         viewportGrid.AddHandler(PointerPressedEvent, Image_MouseDown, RoutingStrategies.Bubble);
         viewportGrid.AddHandler(PointerPressedEvent, Image_MouseDown, RoutingStrategies.Bubble);
     }
     }
 
 
-    public Panel? MainImage => (Panel?)((Grid?)((Border?)zoombox.AdditionalContent)?.Child)?.Children[1];
+    public Panel? MainImage => zoombox != null ? (Panel?)((Grid?)((Border?)zoombox.AdditionalContent)?.Child)?.Children[1] : null;
+    public Scene Scene => (Scene)scene;
     public Grid BackgroundGrid => viewportGrid;
     public Grid BackgroundGrid => viewportGrid;
 
 
     private void ForceRefreshFinalImage()
     private void ForceRefreshFinalImage()
     {
     {
+        Scene.RequestNextFrameRendering();
         MainImage?.InvalidateVisual();
         MainImage?.InvalidateVisual();
     }
     }
 
 
@@ -320,10 +322,18 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
 
 
     private void OnLoad(object? sender, RoutedEventArgs e)
     private void OnLoad(object? sender, RoutedEventArgs e)
     {
     {
+        InitializeOverlays();
         Document?.Operations.AddOrUpdateViewport(GetLocation());
         Document?.Operations.AddOrUpdateViewport(GetLocation());
         mouseUpdateController = new MouseUpdateController(this, Image_MouseMove);
         mouseUpdateController = new MouseUpdateController(this, Image_MouseMove);
     }
     }
 
 
+    private void InitializeOverlays()
+    {
+        brushShapeOverlay.MouseEventSource = BackgroundGrid;
+        brushShapeOverlay.MouseReference = MainImage;
+        brushShapeOverlay.Initialize();
+    }
+
     private static void OnDocumentChange(AvaloniaPropertyChangedEventArgs<DocumentViewModel> e)
     private static void OnDocumentChange(AvaloniaPropertyChangedEventArgs<DocumentViewModel> e)
     {
     {
         DocumentViewModel? oldDoc = e.OldValue.Value;
         DocumentViewModel? oldDoc = e.OldValue.Value;

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

@@ -73,7 +73,6 @@ internal class BrushShapeOverlay : Control
 
 
     public BrushShapeOverlay()
     public BrushShapeOverlay()
     {
     {
-        Loaded += ControlLoaded;
         Unloaded += ControlUnloaded;
         Unloaded += ControlUnloaded;
     }
     }
 
 
@@ -85,11 +84,11 @@ internal class BrushShapeOverlay : Control
         mouseUpdateController?.Dispose();
         mouseUpdateController?.Dispose();
     }
     }
 
 
-    private void ControlLoaded(object? sender, RoutedEventArgs e)
+    public void Initialize()
     {
     {
         if (MouseEventSource is null)
         if (MouseEventSource is null)
             return;
             return;
-        
+
         mouseUpdateController = new MouseUpdateController(MouseEventSource, SourceMouseMove);
         mouseUpdateController = new MouseUpdateController(MouseEventSource, SourceMouseMove);
     }
     }
 
 

+ 34 - 29
src/PixiEditor.AvaloniaUI/Views/Overlays/SymmetryOverlay/SymmetryOverlay.cs

@@ -112,6 +112,12 @@ internal class SymmetryOverlay : Overlay
 
 
     private double PenThickness => 1.0 / ZoomboxScale;
     private double PenThickness => 1.0 / ZoomboxScale;
 
 
+    public VecI Size    
+    {
+        get { return (VecI)GetValue(SizeProperty); }
+        set { SetValue(SizeProperty, value); }
+    }
+
     private double horizontalAxisY;
     private double horizontalAxisY;
     private double verticalAxisX;
     private double verticalAxisX;
     private Point pointerPosition;
     private Point pointerPosition;
@@ -162,7 +168,7 @@ internal class SymmetryOverlay : Overlay
                     DrawHorizontalRuler(drawingContext, false);
                     DrawHorizontalRuler(drawingContext, false);
                 }
                 }
 
 
-                if (horizontalAxisY != (int)Bounds.Height)
+                if (horizontalAxisY != (int)Size.Y)
                 {
                 {
                     DrawHorizontalRuler(drawingContext, true);
                     DrawHorizontalRuler(drawingContext, true);
                 }
                 }
@@ -170,14 +176,14 @@ internal class SymmetryOverlay : Overlay
 
 
             var transformState = drawingContext.PushTransform(new TranslateTransform(0, horizontalAxisY).Value);
             var transformState = drawingContext.PushTransform(new TranslateTransform(0, horizontalAxisY).Value);
             drawingContext.DrawGeometry(handleFill, borderPen, handleGeometry);
             drawingContext.DrawGeometry(handleFill, borderPen, handleGeometry);
-            var rotateState = drawingContext.PushTransform(new RotateTransform(180, Bounds.Width / 2, 0).Value);
+            var rotateState = drawingContext.PushTransform(new RotateTransform(180, Size.X / 2, 0).Value);
             drawingContext.DrawGeometry(handleFill, borderPen, handleGeometry);
             drawingContext.DrawGeometry(handleFill, borderPen, handleGeometry);
 
 
             rotateState.Dispose();
             rotateState.Dispose();
             transformState.Dispose();
             transformState.Dispose();
 
 
-            drawingContext.DrawLine(checkerBlack, new(0, horizontalAxisY), new(Bounds.Width, horizontalAxisY));
-            drawingContext.DrawLine(checkerWhite, new(0, horizontalAxisY), new(Bounds.Width, horizontalAxisY));
+            drawingContext.DrawLine(checkerBlack, new(0, horizontalAxisY), new(Size.X, horizontalAxisY));
+            drawingContext.DrawLine(checkerWhite, new(0, horizontalAxisY), new(Size.X, horizontalAxisY));
         }
         }
         if (VerticalAxisVisible)
         if (VerticalAxisVisible)
         {
         {
@@ -188,45 +194,43 @@ internal class SymmetryOverlay : Overlay
                     DrawVerticalRuler(drawingContext, false);
                     DrawVerticalRuler(drawingContext, false);
                 }
                 }
 
 
-                if (verticalAxisX != (int)Bounds.Width)
+                if (verticalAxisX != (int)Size.X)
                 {
                 {
                     DrawVerticalRuler(drawingContext, true);
                     DrawVerticalRuler(drawingContext, true);
                 }
                 }
             }
             }
 
 
-
-
             var rotation = drawingContext.PushTransform(new RotateTransform(90).Value);
             var rotation = drawingContext.PushTransform(new RotateTransform(90).Value);
             var translation = drawingContext.PushTransform(new TranslateTransform(0, -verticalAxisX).Value);
             var translation = drawingContext.PushTransform(new TranslateTransform(0, -verticalAxisX).Value);
             drawingContext.DrawGeometry(handleFill, borderPen, handleGeometry);
             drawingContext.DrawGeometry(handleFill, borderPen, handleGeometry);
-            var rotation1 = drawingContext.PushTransform(new RotateTransform(180, Bounds.Height / 2, 0).Value);
+            var rotation1 = drawingContext.PushTransform(new RotateTransform(180, Size.Y / 2, 0).Value);
             drawingContext.DrawGeometry(handleFill, borderPen, handleGeometry);
             drawingContext.DrawGeometry(handleFill, borderPen, handleGeometry);
 
 
             rotation1.Dispose();
             rotation1.Dispose();
             translation.Dispose();
             translation.Dispose();
             rotation.Dispose();
             rotation.Dispose();
 
 
-            drawingContext.DrawLine(checkerBlack, new(verticalAxisX, 0), new(verticalAxisX, Bounds.Height));
-            drawingContext.DrawLine(checkerWhite, new(verticalAxisX, 0), new(verticalAxisX, Bounds.Height));
+            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)
     private void DrawHorizontalRuler(DrawingContext drawingContext, bool upper)
     {
     {
-        double start = upper ? Bounds.Height : 0;
-        bool drawRight = pointerPosition.X > Bounds.Width / 2;
-        double xOffset = drawRight ? Bounds.Width - RulerOffset * PenThickness * 2 : 0;
+        double start = upper ? Size.Y : 0;
+        bool drawRight = pointerPosition.X > Size.X / 2;
+        double xOffset = drawRight ? Size.X - RulerOffset * PenThickness * 2 : 0;
 
 
         drawingContext.DrawLine(rulerPen, new Point(RulerOffset * PenThickness + xOffset, start), new Point(RulerOffset * PenThickness + xOffset, horizontalAxisY));
         drawingContext.DrawLine(rulerPen, new Point(RulerOffset * PenThickness + xOffset, start), new Point(RulerOffset * PenThickness + xOffset, horizontalAxisY));
         drawingContext.DrawLine(rulerPen, new Point((RulerOffset - RulerWidth) * PenThickness + xOffset, start), new Point((RulerOffset + RulerWidth) * PenThickness + xOffset, start));
         drawingContext.DrawLine(rulerPen, new Point((RulerOffset - RulerWidth) * PenThickness + xOffset, start), new Point((RulerOffset + RulerWidth) * PenThickness + xOffset, start));
         drawingContext.DrawLine(rulerPen, new Point((RulerOffset - RulerWidth) * PenThickness + xOffset, horizontalAxisY), new Point((RulerOffset + RulerWidth) * PenThickness + xOffset, horizontalAxisY));
         drawingContext.DrawLine(rulerPen, new Point((RulerOffset - RulerWidth) * PenThickness + xOffset, horizontalAxisY), new Point((RulerOffset + RulerWidth) * PenThickness + xOffset, horizontalAxisY));
 
 
-        string text = upper ? $"{start - horizontalAxisY}{new LocalizedString("PIXEL_UNIT")} ({(start - horizontalAxisY) / Bounds.Height * 100:F1}%)‎" : $"{horizontalAxisY}{new LocalizedString("PIXEL_UNIT")} ({horizontalAxisY / Bounds.Height * 100:F1}%)‎";
+        string text = upper ? $"{start - horizontalAxisY}{new LocalizedString("PIXEL_UNIT")} ({(start - horizontalAxisY) / Size.Y * 100:F1}%)‎" : $"{horizontalAxisY}{new LocalizedString("PIXEL_UNIT")} ({horizontalAxisY / Size.Y * 100:F1}%)‎";
 
 
         var formattedText = new FormattedText(text, CultureInfo.GetCultureInfo("en-us"),
         var formattedText = new FormattedText(text, CultureInfo.GetCultureInfo("en-us"),
             ILocalizationProvider.Current.CurrentLanguage.FlowDirection, new Typeface("Segeo UI"), 14.0 / ZoomboxScale, Brushes.White);
             ILocalizationProvider.Current.CurrentLanguage.FlowDirection, new Typeface("Segeo UI"), 14.0 / ZoomboxScale, Brushes.White);
 
 
-        if (Bounds.Height < formattedText.Height * 2.5 || horizontalAxisY == (int)Bounds.Height && upper || horizontalAxisY == 0 && !upper)
+        if (Size.Y < formattedText.Height * 2.5 || horizontalAxisY == (int)Size.Y && upper || horizontalAxisY == 0 && !upper)
         {
         {
             return;
             return;
         }
         }
@@ -237,7 +241,7 @@ internal class SymmetryOverlay : Overlay
 
 
         if (upper)
         if (upper)
         {
         {
-            textY += Bounds.Height / 2;
+            textY += Size.Y / 2;
         }
         }
 
 
         drawingContext.DrawText(formattedText, new Point(RulerOffset * PenThickness - (drawRight ? -1 : 1) + xOffset, textY));
         drawingContext.DrawText(formattedText, new Point(RulerOffset * PenThickness - (drawRight ? -1 : 1) + xOffset, textY));
@@ -245,20 +249,20 @@ internal class SymmetryOverlay : Overlay
 
 
     private void DrawVerticalRuler(DrawingContext drawingContext, bool right)
     private void DrawVerticalRuler(DrawingContext drawingContext, bool right)
     {
     {
-        double start = right ? Bounds.Width : 0;
-        bool drawBottom = pointerPosition.Y > Bounds.Height / 2;
-        double yOffset = drawBottom ? Bounds.Height - RulerOffset * PenThickness * 2 : 0;
+        double start = right ? Size.X : 0;
+        bool drawBottom = pointerPosition.Y > Size.Y / 2;
+        double yOffset = drawBottom ? Size.Y - RulerOffset * PenThickness * 2 : 0;
 
 
         drawingContext.DrawLine(rulerPen, new Point(start, RulerOffset * PenThickness + yOffset), new Point(verticalAxisX, RulerOffset * PenThickness + yOffset));
         drawingContext.DrawLine(rulerPen, new Point(start, RulerOffset * PenThickness + yOffset), new Point(verticalAxisX, RulerOffset * PenThickness + yOffset));
         drawingContext.DrawLine(rulerPen, new Point(start, (RulerOffset - RulerWidth) * PenThickness + yOffset), new Point(start, (RulerOffset + RulerWidth) * PenThickness + yOffset));
         drawingContext.DrawLine(rulerPen, new Point(start, (RulerOffset - RulerWidth) * PenThickness + yOffset), new Point(start, (RulerOffset + RulerWidth) * PenThickness + yOffset));
         drawingContext.DrawLine(rulerPen, new Point(verticalAxisX, (RulerOffset - RulerWidth) * PenThickness + yOffset), new Point(verticalAxisX, (RulerOffset + RulerWidth) * PenThickness + yOffset));
         drawingContext.DrawLine(rulerPen, new Point(verticalAxisX, (RulerOffset - RulerWidth) * PenThickness + yOffset), new Point(verticalAxisX, (RulerOffset + RulerWidth) * PenThickness + yOffset));
 
 
-        string text = right ? $"{start - verticalAxisX}{new LocalizedString("PIXEL_UNIT")} ({(start - verticalAxisX) / Bounds.Width * 100:F1}%)‎" : $"{verticalAxisX}{new LocalizedString("PIXEL_UNIT")} ({verticalAxisX / Bounds.Width * 100:F1}%)‎";
+        string text = right ? $"{start - verticalAxisX}{new LocalizedString("PIXEL_UNIT")} ({(start - verticalAxisX) / Size.X * 100:F1}%)‎" : $"{verticalAxisX}{new LocalizedString("PIXEL_UNIT")} ({verticalAxisX / Size.X * 100:F1}%)‎";
 
 
         var formattedText = new FormattedText(text, CultureInfo.GetCultureInfo("en-us"),
         var formattedText = new FormattedText(text, CultureInfo.GetCultureInfo("en-us"),
             ILocalizationProvider.Current.CurrentLanguage.FlowDirection, new Typeface("Segeo UI"), 14.0 / ZoomboxScale, Brushes.White);
             ILocalizationProvider.Current.CurrentLanguage.FlowDirection, new Typeface("Segeo UI"), 14.0 / ZoomboxScale, Brushes.White);
 
 
-        if (Bounds.Width < formattedText.Width * 2.5 || verticalAxisX == (int)Bounds.Width && right || verticalAxisX == 0 && !right)
+        if (Size.X < formattedText.Width * 2.5 || verticalAxisX == (int)Size.X && right || verticalAxisX == 0 && !right)
         {
         {
             return;
             return;
         }
         }
@@ -269,7 +273,7 @@ internal class SymmetryOverlay : Overlay
 
 
         if (right)
         if (right)
         {
         {
-            textX += Bounds.Width / 2;
+            textX += Size.X / 2;
         }
         }
 
 
         drawingContext.DrawText(formattedText, new Point(textX, RulerOffset * PenThickness - (drawBottom ? -0.7 : 0.3 + formattedText.Height) + yOffset));
         drawingContext.DrawText(formattedText, new Point(textX, RulerOffset * PenThickness - (drawBottom ? -0.7 : 0.3 + formattedText.Height) + yOffset));
@@ -280,7 +284,7 @@ internal class SymmetryOverlay : Overlay
     {
     {
         // prevent the line from blocking mouse input
         // prevent the line from blocking mouse input
         var point = hitTestParameters.HitPoint;
         var point = hitTestParameters.HitPoint;
-        if (point.X > 0 && point.Y > 0 && point.X < Bounds.Width && point.Y < Bounds.Height)
+        if (point.X > 0 && point.Y > 0 && point.X < Size.X && point.Y < Size.Y)
             return null;
             return null;
 
 
         return new PointHitTestResult(this, hitTestParameters.HitPoint);
         return new PointHitTestResult(this, hitTestParameters.HitPoint);
@@ -290,9 +294,9 @@ internal class SymmetryOverlay : Overlay
     {
     {
         double radius = HandleSize * 4 / ZoomboxScale / 2;
         double radius = HandleSize * 4 / ZoomboxScale / 2;
         VecD left = new(-radius, horizontalAxisY);
         VecD left = new(-radius, horizontalAxisY);
-        VecD right = new(Bounds.Width + radius, horizontalAxisY);
+        VecD right = new(Size.X + radius, horizontalAxisY);
         VecD up = new(verticalAxisX, -radius);
         VecD up = new(verticalAxisX, -radius);
-        VecD down = new(verticalAxisX, Bounds.Height + radius);
+        VecD down = new(verticalAxisX, Size.Y + radius);
 
 
         if (HorizontalAxisVisible && (Math.Abs((left - position).LongestAxis) < radius || Math.Abs((right - position).LongestAxis) < radius))
         if (HorizontalAxisVisible && (Math.Abs((left - position).LongestAxis) < radius || Math.Abs((right - position).LongestAxis) < radius))
             return SymmetryAxisDirection.Horizontal;
             return SymmetryAxisDirection.Horizontal;
@@ -305,6 +309,7 @@ internal class SymmetryOverlay : Overlay
 
 
     private SymmetryAxisDirection? capturedDirection;
     private SymmetryAxisDirection? capturedDirection;
     private SymmetryAxisDirection? hoveredDirection;
     private SymmetryAxisDirection? hoveredDirection;
+    public static readonly StyledProperty<VecI> SizeProperty = AvaloniaProperty.Register<SymmetryOverlay, VecI>("Size");
 
 
     private void UpdateHovered(SymmetryAxisDirection? direction)
     private void UpdateHovered(SymmetryAxisDirection? direction)
     {
     {
@@ -402,11 +407,11 @@ internal class SymmetryOverlay : Overlay
             return;
             return;
         if (capturedDirection == SymmetryAxisDirection.Horizontal)
         if (capturedDirection == SymmetryAxisDirection.Horizontal)
         {
         {
-            horizontalAxisY = Math.Round(Math.Clamp(pos.Y, 0, Bounds.Height) * 2) / 2;
+            horizontalAxisY = Math.Round(Math.Clamp(pos.Y, 0, Size.Y) * 2) / 2;
 
 
             if (e.KeyModifiers.HasFlag(KeyModifiers.Shift))
             if (e.KeyModifiers.HasFlag(KeyModifiers.Shift))
             {
             {
-                double temp = Math.Round(horizontalAxisY / Bounds.Height * 8) / 8 * Bounds.Height;
+                double temp = Math.Round(horizontalAxisY / Size.Y * 8) / 8 * Size.Y;
                 horizontalAxisY = Math.Round(temp * 2) / 2;
                 horizontalAxisY = Math.Round(temp * 2) / 2;
             }
             }
 
 
@@ -414,12 +419,12 @@ internal class SymmetryOverlay : Overlay
         }
         }
         else if (capturedDirection == SymmetryAxisDirection.Vertical)
         else if (capturedDirection == SymmetryAxisDirection.Vertical)
         {
         {
-            verticalAxisX = Math.Round(Math.Clamp(pos.X, 0, Bounds.Width) * 2) / 2;
+            verticalAxisX = Math.Round(Math.Clamp(pos.X, 0, Size.X) * 2) / 2;
 
 
             if (e.KeyModifiers.HasFlag(KeyModifiers.Control))
             if (e.KeyModifiers.HasFlag(KeyModifiers.Control))
             {
             {
 
 
-                double temp = Math.Round(verticalAxisX / Bounds.Width * 8) / 8 * Bounds.Width;
+                double temp = Math.Round(verticalAxisX / Size.X * 8) / 8 * Size.X;
                 verticalAxisX = Math.Round(temp * 2) / 2;
                 verticalAxisX = Math.Round(temp * 2) / 2;
             }
             }
 
 

+ 24 - 33
src/PixiEditor.AvaloniaUI/Views/Visuals/Scene.cs

@@ -1,3 +1,4 @@
+using System.Diagnostics;
 using System.Linq;
 using System.Linq;
 using Avalonia;
 using Avalonia;
 using Avalonia.OpenGL;
 using Avalonia.OpenGL;
@@ -21,9 +22,6 @@ internal class Scene : OpenGlControlBase
     public static readonly StyledProperty<VecI> ContentPositionProperty = AvaloniaProperty.Register<Scene, VecI>(
     public static readonly StyledProperty<VecI> ContentPositionProperty = AvaloniaProperty.Register<Scene, VecI>(
         nameof(ContentPosition));
         nameof(ContentPosition));
 
 
-    public static readonly StyledProperty<VecD> DimensionsProperty = AvaloniaProperty.Register<Scene, VecD>(
-        nameof(Dimensions));
-
     public static readonly StyledProperty<DocumentViewModel> DocumentProperty = AvaloniaProperty.Register<Scene, DocumentViewModel>(
     public static readonly StyledProperty<DocumentViewModel> DocumentProperty = AvaloniaProperty.Register<Scene, DocumentViewModel>(
         nameof(Document));
         nameof(Document));
 
 
@@ -47,12 +45,6 @@ internal class Scene : OpenGlControlBase
         get => GetValue(ContentPositionProperty);
         get => GetValue(ContentPositionProperty);
         set => SetValue(ContentPositionProperty, value);
         set => SetValue(ContentPositionProperty, value);
     }
     }
-    
-    public VecD Dimensions
-    {
-        get => GetValue(DimensionsProperty);
-        set => SetValue(DimensionsProperty, value);
-    }
 
 
     public double Scale
     public double Scale
     {
     {
@@ -74,6 +66,7 @@ internal class Scene : OpenGlControlBase
 
 
     static Scene()
     static Scene()
     {
     {
+        AffectsRender<Scene>(BoundsProperty, WidthProperty, HeightProperty);
         BoundsProperty.Changed.AddClassHandler<Scene>(BoundsChanged);
         BoundsProperty.Changed.AddClassHandler<Scene>(BoundsChanged);
         WidthProperty.Changed.AddClassHandler<Scene>(BoundsChanged);
         WidthProperty.Changed.AddClassHandler<Scene>(BoundsChanged);
         HeightProperty.Changed.AddClassHandler<Scene>(BoundsChanged);
         HeightProperty.Changed.AddClassHandler<Scene>(BoundsChanged);
@@ -98,8 +91,6 @@ internal class Scene : OpenGlControlBase
         GRGlFramebufferInfo frameBuffer = new GRGlFramebufferInfo(0, SKColorType.Rgba8888.ToGlSizedFormat());
         GRGlFramebufferInfo frameBuffer = new GRGlFramebufferInfo(0, SKColorType.Rgba8888.ToGlSizedFormat());
         GRBackendRenderTarget desc = new GRBackendRenderTarget((int)Bounds.Width, (int)Bounds.Height, 4, 0, frameBuffer);
         GRBackendRenderTarget desc = new GRBackendRenderTarget((int)Bounds.Width, (int)Bounds.Height, 4, 0, frameBuffer);
         _outputSurface = SKSurface.Create(gr, desc, GRSurfaceOrigin.BottomLeft, SKImageInfo.PlatformColorType);
         _outputSurface = SKSurface.Create(gr, desc, GRSurfaceOrigin.BottomLeft, SKImageInfo.PlatformColorType);
-        
-        //FinalBounds = new(dime)
     }
     }
 
 
     protected override void OnOpenGlRender(GlInterface gl, int fb)
     protected override void OnOpenGlRender(GlInterface gl, int fb)
@@ -112,28 +103,10 @@ internal class Scene : OpenGlControlBase
         canvas.ClipRect(new SKRect(0, 0, (float)Bounds.Width, (float)Bounds.Height));
         canvas.ClipRect(new SKRect(0, 0, (float)Bounds.Width, (float)Bounds.Height));
         canvas.Clear(SKColors.Transparent);
         canvas.Clear(SKColors.Transparent);
 
 
-        float resolutionScale = CalculateResolutionScale();
         float finalScale = CalculateFinalScale();
         float finalScale = CalculateFinalScale();
         float radians = (float)(Angle * Math.PI / 180);
         float radians = (float)(Angle * Math.PI / 180);
 
 
-        RectD viewport = new(FinalBounds.X, FinalBounds.Y, FinalBounds.Width, FinalBounds.Height);
-
-        ShapeCorners surfaceInViewportSpace = SurfaceToViewport(new RectI(VecI.Zero, Surface.Size), finalScale, radians);
-        RectI surfaceBoundsInViewportSpace = (RectI)surfaceInViewportSpace.AABBBounds.RoundOutwards();
-        RectI viewportBoundsInViewportSpace = (RectI)(new RectD(FinalBounds.X, FinalBounds.Y, FinalBounds.Width, FinalBounds.Height)).RoundOutwards();
-        RectI firstIntersectionInViewportSpace = surfaceBoundsInViewportSpace.Intersect(viewportBoundsInViewportSpace);
-        ShapeCorners firstIntersectionInSurfaceSpace = ViewportToSurface(firstIntersectionInViewportSpace, finalScale, radians);
-        RectI firstIntersectionBoundsInSurfaceSpace = (RectI)firstIntersectionInSurfaceSpace.AABBBounds.RoundOutwards();
-
-        VecD scaledCenter = ViewportToSurface(new VecD(FinalBounds.Center.X, FinalBounds.Center.Y), finalScale, radians);
-        ShapeCorners viewportInSurfaceSpace = new ShapeCorners(scaledCenter, Dimensions * resolutionScale).AsRotated(-radians, scaledCenter);
-        RectD viewportBoundsInSurfaceSpace = viewportInSurfaceSpace.AABBBounds;
-        RectD surfaceBoundsInSurfaceSpace = new(VecD.Zero, Surface.Size);
-        RectI secondIntersectionInSurfaceSpace = (RectI)viewportBoundsInSurfaceSpace.Intersect(surfaceBoundsInSurfaceSpace).RoundOutwards();
-
-        //Inflate makes sure rounding doesn't cut any pixels.
-        RectI surfaceRectToRender = firstIntersectionBoundsInSurfaceSpace.Intersect(secondIntersectionInSurfaceSpace).Inflate(1);
-        surfaceRectToRender = surfaceRectToRender.Intersect(new RectI(VecI.Zero, Surface.Size)); // Clamp to surface size
+        RectI surfaceRectToRender = FindRectToRender(finalScale, radians);
 
 
         if (surfaceRectToRender.IsZeroOrNegativeArea)
         if (surfaceRectToRender.IsZeroOrNegativeArea)
         {
         {
@@ -147,13 +120,31 @@ internal class Scene : OpenGlControlBase
         canvas.Scale(finalScale, finalScale, ContentPosition.X, ContentPosition.Y);
         canvas.Scale(finalScale, finalScale, ContentPosition.X, ContentPosition.Y);
         canvas.Translate(ContentPosition.X, ContentPosition.Y);
         canvas.Translate(ContentPosition.X, ContentPosition.Y);
 
 
-        using Image snapshot = Surface.DrawingSurface.Snapshot((RectI)surfaceRectToRender);
-        canvas.DrawImage((SKImage)snapshot.Native, (float)surfaceRectToRender.X, (float)surfaceRectToRender.Y, _paint);
+        using Image snapshot = Surface.DrawingSurface.Snapshot(surfaceRectToRender);
+        canvas.DrawImage((SKImage)snapshot.Native, surfaceRectToRender.X, surfaceRectToRender.Y, _paint);
 
 
         canvas.Restore();
         canvas.Restore();
 
 
         canvas.Flush();
         canvas.Flush();
-        RequestNextFrameRendering();
+    }
+
+    private RectI FindRectToRender(float finalScale, float radians)
+    {
+        ShapeCorners surfaceInViewportSpace = SurfaceToViewport(new RectI(VecI.Zero, Surface.Size), finalScale, radians);
+        RectI surfaceBoundsInViewportSpace = (RectI)surfaceInViewportSpace.AABBBounds.RoundOutwards();
+        RectI viewportBoundsInViewportSpace = (RectI)(new RectD(FinalBounds.X, FinalBounds.Y, FinalBounds.Width, FinalBounds.Height)).RoundOutwards();
+        RectI firstIntersectionInViewportSpace = surfaceBoundsInViewportSpace.Intersect(viewportBoundsInViewportSpace);
+        ShapeCorners firstIntersectionInSurfaceSpace = ViewportToSurface(firstIntersectionInViewportSpace, finalScale, radians);
+        RectI firstIntersectionBoundsInSurfaceSpace = (RectI)firstIntersectionInSurfaceSpace.AABBBounds.RoundOutwards();
+
+        ShapeCorners viewportInSurfaceSpace = ViewportToSurface(viewportBoundsInViewportSpace, finalScale, radians);
+        RectD viewportBoundsInSurfaceSpace = viewportInSurfaceSpace.AABBBounds;
+        RectD surfaceBoundsInSurfaceSpace = new(VecD.Zero, Surface.Size);
+        RectI secondIntersectionInSurfaceSpace = (RectI)viewportBoundsInSurfaceSpace.Intersect(surfaceBoundsInSurfaceSpace).RoundOutwards();
+
+        //Inflate makes sure rounding doesn't cut any pixels.
+        RectI surfaceRectToRender = firstIntersectionBoundsInSurfaceSpace.Intersect(secondIntersectionInSurfaceSpace).Inflate(1);
+        return surfaceRectToRender.Intersect(new RectI(VecI.Zero, Surface.Size)); // Clamp to surface size
     }
     }
 
 
     private void DrawDebugRect(SKCanvas canvas, RectD rect)
     private void DrawDebugRect(SKCanvas canvas, RectD rect)

+ 16 - 4
src/PixiEditor.Zoombox/Zoombox.axaml.cs

@@ -5,6 +5,7 @@ using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls;
 using Avalonia.Input;
 using Avalonia.Input;
 using Avalonia.Interactivity;
 using Avalonia.Interactivity;
+using Avalonia.Media;
 using Avalonia.Metadata;
 using Avalonia.Metadata;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Zoombox.Operations;
 using PixiEditor.Zoombox.Operations;
@@ -43,8 +44,8 @@ public partial class Zoombox : UserControl, INotifyPropertyChanged
     public static readonly StyledProperty<bool> FlipYProperty =
     public static readonly StyledProperty<bool> FlipYProperty =
         AvaloniaProperty.Register<Zoombox, bool>(nameof(FlipY), defaultValue: false);
         AvaloniaProperty.Register<Zoombox, bool>(nameof(FlipY), defaultValue: false);
 
 
-    public static readonly StyledProperty<AvaloniaObject> AdditionalContentProperty =
-        AvaloniaProperty.Register<Zoombox, AvaloniaObject>(nameof(AdditionalContent));
+    public static readonly StyledProperty<Control> AdditionalContentProperty =
+        AvaloniaProperty.Register<Zoombox, Control>(nameof(AdditionalContent));
 
 
     public static readonly RoutedEvent<ViewportRoutedEventArgs> ViewportMovedEvent = RoutedEvent.Register<Zoombox, ViewportRoutedEventArgs>(
     public static readonly RoutedEvent<ViewportRoutedEventArgs> ViewportMovedEvent = RoutedEvent.Register<Zoombox, ViewportRoutedEventArgs>(
         nameof(ViewportMoved), RoutingStrategies.Bubble);
         nameof(ViewportMoved), RoutingStrategies.Bubble);
@@ -109,9 +110,9 @@ public partial class Zoombox : UserControl, INotifyPropertyChanged
         set => SetValue(RealDimensionsProperty, value);
         set => SetValue(RealDimensionsProperty, value);
     }
     }
 
 
-    public AvaloniaObject AdditionalContent
+    public Control AdditionalContent
     {
     {
-        get => (AvaloniaObject)GetValue(AdditionalContentProperty);
+        get => (Control)GetValue(AdditionalContentProperty);
         set => SetValue(AdditionalContentProperty, value);
         set => SetValue(AdditionalContentProperty, value);
     }
     }
 
 
@@ -125,6 +126,16 @@ public partial class Zoombox : UserControl, INotifyPropertyChanged
     public double CanvasX => ToScreenSpace(VecD.Zero).X;
     public double CanvasX => ToScreenSpace(VecD.Zero).X;
     public double CanvasY => ToScreenSpace(VecD.Zero).Y;
     public double CanvasY => ToScreenSpace(VecD.Zero).Y;
 
 
+    public TransformGroup CanvasTransform => new TransformGroup
+    {
+        Children =
+        {
+            new ScaleTransform(Scale, Scale),
+            new RotateTransform(Angle * 180 / Math.PI),
+            new TranslateTransform(CanvasX, CanvasY),
+        },
+    };
+
     public double ScaleTransformXY => Scale;
     public double ScaleTransformXY => Scale;
     public double FlipTransformX => FlipX ? -1 : 1;
     public double FlipTransformX => FlipX ? -1 : 1;
     public double FlipTransformY => FlipY ? -1 : 1;
     public double FlipTransformY => FlipY ? -1 : 1;
@@ -467,6 +478,7 @@ public partial class Zoombox : UserControl, INotifyPropertyChanged
         zoombox.PropertyChanged?.Invoke(zoombox, new(nameof(zoombox.CanvasX)));
         zoombox.PropertyChanged?.Invoke(zoombox, new(nameof(zoombox.CanvasX)));
         zoombox.PropertyChanged?.Invoke(zoombox, new(nameof(zoombox.CanvasY)));
         zoombox.PropertyChanged?.Invoke(zoombox, new(nameof(zoombox.CanvasY)));
         zoombox.PropertyChanged?.Invoke(zoombox, new(nameof(zoombox.CanvasPos)));
         zoombox.PropertyChanged?.Invoke(zoombox, new(nameof(zoombox.CanvasPos)));
+        zoombox.PropertyChanged?.Invoke(zoombox, new(nameof(zoombox.CanvasTransform)));
         zoombox.RaiseViewportEvent();
         zoombox.RaiseViewportEvent();
     }
     }