Browse Source

Implemented all reference layer features

Krzysztof Krysiński 1 year ago
parent
commit
baf27e603f

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

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

+ 1 - 1
src/PixiEditor.AvaloniaUI/ViewModels/Document/DocumentViewModel.cs

@@ -449,7 +449,7 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
             return null;
         
         Matrix matrix = ReferenceLayerViewModel.ReferenceTransformMatrix;
-        matrix.Invert();
+        matrix = matrix.Invert();
         var transformed = matrix.Transform(new Point(pos.X, pos.Y));
 
         if (transformed.X < 0 || transformed.Y < 0 || transformed.X >= bitmap.Size.X || transformed.Y >= bitmap.Size.Y)

+ 6 - 103
src/PixiEditor.AvaloniaUI/Views/Main/ViewportControls/Viewport.axaml

@@ -130,35 +130,9 @@
             Angle="{Binding RotateTransformAngle, ElementName=zoombox, Mode=OneWay}"
             FlipX="{Binding FlipX, ElementName=zoombox, Mode=OneWay}"
             FlipY="{Binding FlipY, ElementName=zoombox, Mode=OneWay}"
+            FadeOut="{Binding Source={viewModels:ToolVM ColorPickerToolViewModel}, Path=PickOnlyFromReferenceLayer, Mode=OneWay}"
             ui1:RenderOptionsBindable.BitmapInterpolationMode="{Binding Scale, Converter={converters:ScaleToBitmapScalingModeConverter}, ElementName=zoombox}"
             FlowDirection="LeftToRight">
-            <visuals:Scene.Styles>
-                <!--TODO: Implement-->
-                <!--<Style>
-                                <Style.Triggers>
-                                    <DataTrigger Binding="{Binding Source={vm:ToolVM ColorPickerToolViewModel}, Path=PickOnlyFromReferenceLayer, Mode=OneWay}" Value="True">
-                                        <DataTrigger.EnterActions>
-                                            <BeginStoryboard>
-                                                <Storyboard>
-                                                    <DoubleAnimation
-                                                        Storyboard.TargetProperty="(Button.Opacity)"
-                                                        From="1" To="0" Duration="0:0:0.1" />
-                                                </Storyboard>
-                                            </BeginStoryboard>
-                                        </DataTrigger.EnterActions>
-                                        <DataTrigger.ExitActions>
-                                            <BeginStoryboard>
-                                                <Storyboard>
-                                                    <DoubleAnimation
-                                                        Storyboard.TargetProperty="(Button.Opacity)"
-                                                        From="0" To="1" Duration="0:0:0.1" />
-                                                </Storyboard>
-                                            </BeginStoryboard>
-                                        </DataTrigger.ExitActions>
-                                    </DataTrigger>
-                                </Style.Triggers>
-                            </Style>-->
-            </visuals:Scene.Styles>
         </visuals:Scene>
         <zoombox:Zoombox
             Tag="{Binding ElementName=vpUc}"
@@ -240,82 +214,11 @@
                 </Border>
             </zoombox:Zoombox.AdditionalContent>
         </zoombox:Zoombox>
-        <Canvas RenderTransformOrigin="0, 0" RenderTransform="{Binding #zoombox.CanvasTransform}"
-                DataContext="{Binding ElementName=vpUc}"
-                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>
-                                        <DataTrigger Binding="{Binding Document.ReferenceLayerViewModel.ShowHighest, Mode=OneWay}" Value="True">
-                                            <DataTrigger.EnterActions>
-                                                <BeginStoryboard>
-                                                    <Storyboard>
-                                                        <DoubleAnimation
-                                                            Storyboard.TargetProperty="(Button.Opacity)"
-                                                            From="1" To="{x:Static subviews:ReferenceLayerViewModel.TopMostOpacity}" Duration="0:0:0.1" />
-                                                    </Storyboard>
-                                                </BeginStoryboard>
-                                            </DataTrigger.EnterActions>
-                                            <DataTrigger.ExitActions>
-                                                <BeginStoryboard>
-                                                    <Storyboard>
-                                                        <DoubleAnimation
-                                                            Storyboard.TargetProperty="(Button.Opacity)"
-                                                            From="{x:Static subviews:ReferenceLayerViewModel.TopMostOpacity}" To="1" Duration="0:0:0.1" />
-                                                    </Storyboard>
-                                                </BeginStoryboard>
-                                            </DataTrigger.ExitActions>
-                                        </DataTrigger>
-                                    </Style.Triggers>
-                                </Style>-->
-                    </visuals:SurfaceControl.Styles>
-                </visuals:SurfaceControl>
-                <Canvas.Styles>
-                    <!--TODO: Implement this-->
-                    <!--<Style>
-                                <Style.Triggers>
-                                    <DataTrigger Binding="{Binding Source={vm:ToolVM ColorPickerToolViewModel}, Path=PickFromReferenceLayer, Mode=OneWay}" Value="False">
-                                        <DataTrigger.EnterActions>
-                                            <BeginStoryboard>
-                                                <Storyboard>
-                                                    <DoubleAnimation
-                                                        Storyboard.TargetProperty="(Button.Opacity)"
-                                                        From="1" To="0" Duration="0:0:0.1" />
-                                                </Storyboard>
-                                            </BeginStoryboard>
-                                        </DataTrigger.EnterActions>
-                                        <DataTrigger.ExitActions>
-                                            <BeginStoryboard>
-                                                <Storyboard>
-                                                    <DoubleAnimation
-                                                        Storyboard.TargetProperty="(Button.Opacity)"
-                                                        From="0" To="1" Duration="0:0:0.1" />
-                                                </Storyboard>
-                                            </BeginStoryboard>
-                                        </DataTrigger.ExitActions>
-                                    </DataTrigger>
-                                </Style.Triggers>
-                            </Style>-->
-                </Canvas.Styles>
-            </Canvas>
+        <overlays:ReferenceLayerOverlay SizeChanged="OnReferenceImageSizeChanged"
+                                        ReferenceLayer="{Binding Document.ReferenceLayerViewModel}"
+                                        ReferenceLayerScale="{Binding ReferenceLayerScale}"
+                                        FadeOut="{Binding Source={viewModels:ToolVM ColorPickerToolViewModel}, Path=!PickFromReferenceLayer, Mode=OneWay}"
+                                        RenderTransformOrigin="0, 0" RenderTransform="{Binding #zoombox.CanvasTransform}"/>
         <Grid ZIndex="5" DataContext="{Binding ElementName=vpUc}"
               RenderTransformOrigin="0, 0" RenderTransform="{Binding #zoombox.CanvasTransform}">
             <symmetryOverlay:SymmetryOverlay

+ 57 - 0
src/PixiEditor.AvaloniaUI/Views/Overlays/ReferenceLayerOverlay.axaml

@@ -0,0 +1,57 @@
+<ResourceDictionary xmlns="https://github.com/avaloniaui"
+                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+                    xmlns:ui="clr-namespace:PixiEditor.AvaloniaUI.Helpers.UI"
+                    xmlns:converters="clr-namespace:PixiEditor.AvaloniaUI.Helpers.Converters"
+                    xmlns:visuals="clr-namespace:PixiEditor.AvaloniaUI.Views.Visuals"
+                    xmlns:overlays="clr-namespace:PixiEditor.AvaloniaUI.Views.Overlays"
+                    xmlns:document="clr-namespace:PixiEditor.AvaloniaUI.ViewModels.Document">
+    <ControlTheme TargetType="overlays:ReferenceLayerOverlay" x:Key="{x:Type overlays:ReferenceLayerOverlay}">
+        <Setter Property="ZIndex" Value="0"/>
+        <Setter Property="Transitions">
+            <Transitions>
+                <DoubleTransition Property="Opacity" Duration="0:0:0.1"/>
+            </Transitions>
+        </Setter>
+        <Setter Property="Template">
+            <ControlTemplate TargetType="overlays:ReferenceLayerOverlay">
+                <Canvas
+                    IsHitTestVisible="{Binding ReferenceLayer.IsTransforming, RelativeSource={RelativeSource TemplatedParent}}"
+                    ui:RenderOptionsBindable.BitmapInterpolationMode="{Binding ReferenceLayerScale, Converter={converters:ScaleToBitmapScalingModeConverter}, RelativeSource={RelativeSource TemplatedParent}}">
+                    <visuals:SurfaceControl
+                        Focusable="False"
+                        Opacity="{TemplateBinding Opacity}"
+                        Width="{Binding ReferenceLayer.ReferenceBitmap.Size.X, RelativeSource={RelativeSource TemplatedParent}}"
+                        Height="{Binding ReferenceLayer.ReferenceBitmap.Size.Y, RelativeSource={RelativeSource TemplatedParent}}"
+                        Surface="{Binding ReferenceLayer.ReferenceBitmap, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}"
+                        IsVisible="{Binding ReferenceLayer.IsVisibleBindable, RelativeSource={RelativeSource TemplatedParent}}"
+                        RenderTransformOrigin="0, 0"
+                        FlowDirection="LeftToRight">
+                        <visuals:SurfaceControl.Transitions>
+                            <Transitions>
+                                <DoubleTransition Property="Opacity" Duration="0:0:0.1"/>
+                            </Transitions>
+                        </visuals:SurfaceControl.Transitions>
+                        <visuals:SurfaceControl.RenderTransform>
+                            <TransformGroup>
+                                <MatrixTransform
+                                    Matrix="{Binding ReferenceLayer.ReferenceTransformMatrix, RelativeSource={RelativeSource TemplatedParent}}" />
+                            </TransformGroup>
+                        </visuals:SurfaceControl.RenderTransform>
+                    </visuals:SurfaceControl>
+                </Canvas>
+            </ControlTemplate>
+        </Setter>
+
+        <Style Selector="^:showHighest">
+            <Setter Property="ZIndex" Value="1"/>
+        </Style>
+
+        <Style Selector="^:showHighest /template/ visuals|SurfaceControl">
+            <Setter Property="Opacity" Value="{x:Static document:ReferenceLayerViewModel.TopMostOpacity}"/>
+        </Style>
+
+        <Style Selector="^:fadedOut">
+            <Setter Property="Opacity" Value="0"/>
+        </Style>
+    </ControlTheme>
+</ResourceDictionary>

+ 71 - 0
src/PixiEditor.AvaloniaUI/Views/Overlays/ReferenceLayerOverlay.axaml.cs

@@ -0,0 +1,71 @@
+using System.ComponentModel;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Metadata;
+using Avalonia.Controls.Primitives;
+using PixiEditor.AvaloniaUI.ViewModels.Document;
+
+namespace PixiEditor.AvaloniaUI.Views.Overlays;
+
+[PseudoClasses(":showHighest", ":fadedOut")]
+internal class ReferenceLayerOverlay : TemplatedControl
+{
+    public static readonly StyledProperty<ReferenceLayerViewModel> ReferenceLayerProperty = AvaloniaProperty.Register<ReferenceLayerOverlay, ReferenceLayerViewModel>(
+        nameof(ReferenceLayerViewModel));
+
+    public static readonly StyledProperty<double> ReferenceLayerScaleProperty = AvaloniaProperty.Register<ReferenceLayerOverlay, double>(
+        nameof(ReferenceLayerScale), defaultValue: 1.0f);
+
+    public static readonly StyledProperty<bool> FadeOutProperty = AvaloniaProperty.Register<ReferenceLayerOverlay, bool>(
+        nameof(FadeOut), defaultValue: false);
+
+    public bool FadeOut
+    {
+        get => GetValue(FadeOutProperty);
+        set => SetValue(FadeOutProperty, value);
+    }
+
+    public double ReferenceLayerScale
+    {
+        get => GetValue(ReferenceLayerScaleProperty);
+        set => SetValue(ReferenceLayerScaleProperty, value);
+    }
+
+    public ReferenceLayerViewModel ReferenceLayer
+    {
+        get => GetValue(ReferenceLayerProperty);
+        set => SetValue(ReferenceLayerProperty, value);
+    }
+
+    static ReferenceLayerOverlay()
+    {
+        ReferenceLayerProperty.Changed.Subscribe(ReferenceLayerChanged);
+        FadeOutProperty.Changed.Subscribe(FadeOutChanged);
+    }
+
+    private static void ReferenceLayerChanged(AvaloniaPropertyChangedEventArgs<ReferenceLayerViewModel> obj)
+    {
+        ReferenceLayerOverlay objSender = (ReferenceLayerOverlay)obj.Sender;
+        if (obj.OldValue.Value != null)
+        {
+            obj.OldValue.Value.PropertyChanged -= objSender.ReferenceLayerOnPropertyChanged;
+        }
+
+        if (obj.NewValue.Value != null)
+        {
+            obj.NewValue.Value.PropertyChanged += objSender.ReferenceLayerOnPropertyChanged;
+        }
+    }
+
+    private static void FadeOutChanged(AvaloniaPropertyChangedEventArgs<bool> obj)
+    {
+        ReferenceLayerOverlay objSender = (ReferenceLayerOverlay)obj.Sender;
+        objSender.PseudoClasses.Set(":fadedOut", obj.NewValue.Value);
+    }
+
+    private void ReferenceLayerOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
+    {
+        PseudoClasses.Set(":showHighest", ReferenceLayer.ShowHighest);
+    }
+}
+

+ 23 - 2
src/PixiEditor.AvaloniaUI/Views/Visuals/Scene.cs

@@ -1,4 +1,5 @@
 using Avalonia;
+using Avalonia.Animation;
 using Avalonia.Controls;
 using Avalonia.Media;
 using Avalonia.Rendering.SceneGraph;
@@ -6,6 +7,7 @@ using Avalonia.Skia;
 using ChunkyImageLib;
 using ChunkyImageLib.DataHolders;
 using PixiEditor.AvaloniaUI.ViewModels.Document;
+using PixiEditor.AvaloniaUI.ViewModels.Tools.Tools;
 using PixiEditor.DrawingApi.Core.Numerics;
 using Image = PixiEditor.DrawingApi.Core.Surface.ImageData.Image;
 
@@ -33,6 +35,15 @@ internal class Scene : Control
     public static readonly StyledProperty<bool> FlipYProperty = AvaloniaProperty.Register<Scene, bool>(
         nameof(FlipY), false);
 
+    public static readonly StyledProperty<bool> FadeOutProperty = AvaloniaProperty.Register<Scene, bool>(
+        nameof(FadeOut), false);
+
+    public bool FadeOut
+    {
+        get => GetValue(FadeOutProperty);
+        set => SetValue(FadeOutProperty, value);
+    }
+
     public double Angle
     {
         get => GetValue(AngleProperty);
@@ -83,18 +94,21 @@ internal class Scene : Control
         BoundsProperty.Changed.AddClassHandler<Scene>(BoundsChanged);
         FlipXProperty.Changed.AddClassHandler<Scene>(RequestRendering);
         FlipYProperty.Changed.AddClassHandler<Scene>(RequestRendering);
+        FadeOutProperty.Changed.AddClassHandler<Scene>(FadeOutChanged);
     }
 
     public Scene()
     {
         ClipToBounds = true;
+        Transitions = new Transitions();
+        Transitions.Add(new DoubleTransition() { Property = OpacityProperty, Duration = new TimeSpan(0, 0, 0, 0, 100) });
     }
 
     public override void Render(DrawingContext context)
     {
         if (Surface == null || Document == null) return;
 
-        var operation = new DrawSceneOperation(Surface, Document, ContentPosition, Scale, Angle, FlipX, FlipY, Bounds);
+        var operation = new DrawSceneOperation(Surface, Document, ContentPosition, Scale, Angle, FlipX, FlipY, Bounds, Opacity);
         context.Custom(operation);
     }
 
@@ -107,6 +121,11 @@ internal class Scene : Control
     {
         sender.InvalidateVisual();
     }
+
+    private static void FadeOutChanged(Scene scene, AvaloniaPropertyChangedEventArgs arg2)
+    {
+        scene.Opacity = arg2.NewValue is true ? 0 : 1;
+    }
 }
 
 internal class DrawSceneOperation : SkiaDrawOperation
@@ -121,7 +140,8 @@ internal class DrawSceneOperation : SkiaDrawOperation
 
     private SKPaint _paint = new SKPaint();
 
-    public DrawSceneOperation(Surface surface, DocumentViewModel document, VecI contentPosition, double scale, double angle, bool flipX, bool flipY, Rect bounds) : base(bounds)
+    public DrawSceneOperation(Surface surface, DocumentViewModel document, VecI contentPosition, double scale,
+        double angle, bool flipX, bool flipY, Rect bounds, double opacity) : base(bounds)
     {
         Surface = surface;
         Document = document;
@@ -130,6 +150,7 @@ internal class DrawSceneOperation : SkiaDrawOperation
         Angle = angle;
         FlipX = flipX;
         FlipY = flipY;
+        _paint.Color = _paint.Color.WithAlpha((byte)(opacity * 255));
     }
 
     public override void Render(ISkiaSharpApiLease lease)

+ 6 - 2
src/PixiEditor.AvaloniaUI/Views/Visuals/SurfaceControl.cs

@@ -89,7 +89,7 @@ internal class SurfaceControl : Control
         }
 
         var bounds = new Rect(Bounds.Size);
-        var operation = new DrawSurfaceOperation(bounds, Surface, Stretch);
+        var operation = new DrawSurfaceOperation(bounds, Surface, Stretch, Opacity);
         context.Custom(operation);
     }
 
@@ -134,12 +134,15 @@ internal class DrawSurfaceOperation : SkiaDrawOperation
     public Surface Surface { get; }
     public Stretch Stretch { get; }
 
+    public double Opacity { get; set; } = 1.0;
+
     private SKPaint _paint = new SKPaint();
 
-    public DrawSurfaceOperation(Rect bounds, Surface surface, Stretch stretch) : base(bounds)
+    public DrawSurfaceOperation(Rect bounds, Surface surface, Stretch stretch, double opacity = 1) : base(bounds)
     {
         Surface = surface;
         Stretch = stretch;
+        Opacity = opacity;
     }
 
     public override void Render(ISkiaSharpApiLease lease)
@@ -164,6 +167,7 @@ internal class DrawSurfaceOperation : SkiaDrawOperation
             canvas.DrawSurface((SKSurface)Surface.DrawingSurface.Native, new SKPoint(0, 0), _paint);
         }*/
 
+        _paint.Color = _paint.Color.WithAlpha((byte)(Opacity * 255));
         canvas.DrawSurface((SKSurface)Surface.DrawingSurface.Native, new SKPoint(0, 0), _paint);
         canvas.Restore();
     }