Browse Source

Added onion frames opacity

flabbet 1 year ago
parent
commit
ab6c1e59c9

+ 1 - 1
src/PixiEditor.ChangeableDocument/ChangeInfos/Animation/OnionFrames_ChangeInfo.cs

@@ -1,3 +1,3 @@
 namespace PixiEditor.ChangeableDocument.ChangeInfos.Animation;
 
-public record OnionFrames_ChangeInfo(int OnionFrames) : IChangeInfo;
+public record OnionFrames_ChangeInfo(int OnionFrames, double Opacity) : IChangeInfo;

+ 1 - 0
src/PixiEditor.ChangeableDocument/Changeables/Animations/AnimationData.cs

@@ -8,6 +8,7 @@ internal class AnimationData : IReadOnlyAnimationData
     public int FrameRate { get; set; } = 24;
     public int OnionFrames { get; set; } = 1;
     public IReadOnlyList<IReadOnlyKeyFrame> KeyFrames => keyFrames;
+    public double OnionOpacity { get; set; }
 
     private List<KeyFrame> keyFrames = new List<KeyFrame>();
     private readonly Document document;

+ 1 - 0
src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlyAnimationData.cs

@@ -5,5 +5,6 @@ public interface IReadOnlyAnimationData
     public int FrameRate { get; }
     public IReadOnlyList<IReadOnlyKeyFrame> KeyFrames { get; }
     public int OnionFrames { get; }
+    public double OnionOpacity { get; }
     public bool TryFindKeyFrame<T>(Guid id, out T keyFrame) where T : IReadOnlyKeyFrame;
 }

+ 10 - 4
src/PixiEditor.ChangeableDocument/Changes/Animation/SetOnionFrames_Change.cs → src/PixiEditor.ChangeableDocument/Changes/Animation/SetOnionSettings_Change.cs

@@ -2,36 +2,42 @@
 
 namespace PixiEditor.ChangeableDocument.Changes.Animation;
 
-internal class SetOnionFrames_Change : Change
+internal class SetOnionSettings_Change : Change
 {
     public int OnionFrames { get; set; }
+    public double Opacity { get; set; }
     
     private int oldOnionFrames;
+    private double oldOpacity;
     
     [GenerateMakeChangeAction]
-    public SetOnionFrames_Change(int onionFrames)
+    public SetOnionSettings_Change(int onionFrames, double opacity)
     {
         OnionFrames = onionFrames;
+        Opacity = opacity;
     }
     
     public override bool InitializeAndValidate(Document target)
     {
         oldOnionFrames = target.AnimationData.OnionFrames;
+        oldOpacity = target.AnimationData.OnionOpacity;
         return true;    
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Apply(Document target, bool firstApply, out bool ignoreInUndo)
     {
         target.AnimationData.OnionFrames = OnionFrames;
+        target.AnimationData.OnionOpacity = Opacity;
         
         ignoreInUndo = true;
-        return new OnionFrames_ChangeInfo(OnionFrames);
+        return new OnionFrames_ChangeInfo(OnionFrames, Opacity);
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
     {
         target.AnimationData.OnionFrames = oldOnionFrames;
+        target.AnimationData.OnionOpacity = oldOpacity;
 
-        return new OnionFrames_ChangeInfo(oldOnionFrames);
+        return new OnionFrames_ChangeInfo(oldOnionFrames, oldOpacity);
     }
 }

+ 2 - 1
src/PixiEditor/Data/Localization/Languages/en.json

@@ -722,5 +722,6 @@
   "SELECT_FILE_FORMAT_DESCRIPTION": "Multiple file types of the same format are supported. Please select the one you want to use.",
   "NEW_PALETTE_FILE": "palette",
   "ISLAND_EXAMPLE": "Islands",
-  "ONION_FRAMES_COUNT": "Onion frames"
+  "ONION_FRAMES_COUNT": "Onion frames",
+  "ONION_OPACITY": "Onion opacity"
 }

+ 8 - 0
src/PixiEditor/Helpers/DocumentViewModelBuilder.cs

@@ -89,6 +89,7 @@ internal class DocumentViewModelBuilder
         {
             AnimationData.WithFrameRate(animationData.FrameRate);
             AnimationData.WithOnionFrames(animationData.OnionFrames);
+            AnimationData.WithOnionOpacity(animationData.OnionOpacity);
             BuildKeyFrames(animationData.KeyFrameGroups.ToList(), AnimationData.KeyFrameGroups);
         }
 
@@ -196,6 +197,7 @@ internal class AnimationDataBuilder
     public int FrameRate { get; set; } = 24;
     public List<KeyFrameBuilder> KeyFrameGroups { get; set; } = new List<KeyFrameBuilder>();
     public int OnionFrames { get; set; }
+    public double OnionOpacity { get; set; } = 50;
 
     public AnimationDataBuilder WithFrameRate(int frameRate)
     {
@@ -208,6 +210,12 @@ internal class AnimationDataBuilder
         OnionFrames = onionFrames;
         return this;
     }
+    
+    public AnimationDataBuilder WithOnionOpacity(double onionOpacity)
+    {
+        OnionOpacity = onionOpacity;
+        return this;
+    }
 
     public AnimationDataBuilder WithKeyFrameGroups(Action<List<KeyFrameBuilder>> builder)
     {

+ 1 - 1
src/PixiEditor/Models/DocumentModels/DocumentUpdater.cs

@@ -620,6 +620,6 @@ internal class DocumentUpdater
     
     private void ProcessSetOnionFrames(OnionFrames_ChangeInfo info)
     {
-        doc.AnimationHandler.SetOnionFrames(info.OnionFrames);
+        doc.AnimationHandler.SetOnionFrames(info.OnionFrames, info.Opacity);
     }
 }

+ 2 - 1
src/PixiEditor/Models/Handlers/IAnimationHandler.cs

@@ -9,6 +9,7 @@ internal interface IAnimationHandler
     public KeyFrameTime ActiveFrameTime { get; }
     public bool OnionSkinningEnabledBindable { get; set; }
     public int OnionFramesBindable { get; set; }
+    public double OnionOpacityBindable { get; set; }
     public void CreateRasterKeyFrame(Guid targetLayerGuid, int frame, Guid? toCloneFrom = null, int? frameToCopyFrom = null);
     public void SetFrameRate(int newFrameRate);
     public void SetActiveFrame(int newFrame);
@@ -23,5 +24,5 @@ internal interface IAnimationHandler
     public void SetOnionSkinning(bool enabled);
     public int FirstFrame { get; }
     public int LastFrame { get; }
-    public void SetOnionFrames(int frames);
+    public void SetOnionFrames(int frames, double opacity);
 }

+ 11 - 8
src/PixiEditor/Models/Rendering/CanvasUpdater.cs

@@ -21,14 +21,13 @@ namespace PixiEditor.Models.Rendering;
 #nullable enable
 internal class CanvasUpdater
 {
-    private const int NearestOnionFrameAlpha = 129;
-
     private readonly IDocument doc;
     private readonly DocumentInternalParts internals;
 
     private Dictionary<int, Texture> renderedFramesCache = new();
     private int lastRenderedFrameNumber = -1;
     private int lastOnionKeyFrames = -1;
+    private double lastOnionOpacity = -1;
 
     private static readonly Paint ReplacingPaint = new() { BlendMode = BlendMode.Src };
 
@@ -239,11 +238,14 @@ internal class CanvasUpdater
                 UpdateLastRenderedFrame(lastRendered, lastRenderedFrameNumber);
             }
 
-            if (lastRenderedFrameNumber != doc.AnimationHandler.ActiveFrameBindable || doc.AnimationHandler.OnionFramesBindable != lastOnionKeyFrames)
+            if (lastRenderedFrameNumber != doc.AnimationHandler.ActiveFrameBindable 
+                || doc.AnimationHandler.OnionFramesBindable != lastOnionKeyFrames
+                || Math.Abs(doc.AnimationHandler.OnionOpacityBindable - lastOnionOpacity) > 0.01)
             {
                 int framesToRender = doc.AnimationHandler.OnionFramesBindable;
                 using Paint onionPaint = new Paint();
-                onionPaint.Color = new Color(0, 0, 0, NearestOnionFrameAlpha);
+                byte opacity = (byte)((doc.AnimationHandler.OnionOpacityBindable / 100f) * 255);
+                onionPaint.Color = new Color(0, 0, 0, opacity); 
                 onionPaint.BlendMode = BlendMode.SrcOver;
 
                 if (doc.Renderer.OnionSkinTexture == null || doc.Renderer.OnionSkinTexture.Size != doc.SizeBindable)
@@ -254,7 +256,7 @@ internal class CanvasUpdater
 
                 doc.Renderer.OnionSkinTexture.DrawingSurface.Canvas.Clear();
 
-                float alphaFaloffMultiplier = 1f / framesToRender;
+                float alphaFalloffMultiplier = 1f / framesToRender;
 
                 for (int i = 1; i <= framesToRender; i++)
                 {
@@ -274,14 +276,15 @@ internal class CanvasUpdater
                     DrawOnionSkinningFrame(previousFrameIndex, doc.Renderer.OnionSkinTexture, onionPaint);
                     DrawOnionSkinningFrame(nextFrameIndex, doc.Renderer.OnionSkinTexture, onionPaint);
 
-                    onionPaint.Color = onionPaint.Color.WithAlpha((byte)(NearestOnionFrameAlpha -
-                                                                         (NearestOnionFrameAlpha *
-                                                                          alphaFaloffMultiplier * i)));
+                    onionPaint.Color = onionPaint.Color.WithAlpha((byte)(opacity -
+                                                                         (opacity *
+                                                                          alphaFalloffMultiplier * i)));
                 }
             }
 
             lastRenderedFrameNumber = doc.AnimationHandler.ActiveFrameBindable;
             lastOnionKeyFrames = doc.AnimationHandler.OnionFramesBindable;
+            lastOnionOpacity = doc.AnimationHandler.OnionOpacityBindable;
         }
     }
 

+ 13 - 3
src/PixiEditor/Styles/Templates/Timeline.axaml

@@ -40,14 +40,24 @@
                                         <StackPanel>
                                             <TextBlock Classes="h4" Margin="5" ui1:Translator.Key="SETTINGS" />
                                             <StackPanel Orientation="Horizontal" Spacing="5">
-                                                <TextBlock VerticalAlignment="Center" ui1:Translator.Key="ONION_FRAMES_COUNT" />
+                                                <TextBlock VerticalAlignment="Center"
+                                                           ui1:Translator.Key="ONION_FRAMES_COUNT" />
                                                 <input:NumberInput
-                                                   HorizontalAlignment="Left" Width="50"
+                                                    HorizontalAlignment="Left" Width="50"
                                                     Min="1" Decimals="0"
                                                     Max="128"
                                                     Value="{Binding OnionFrames, RelativeSource={RelativeSource TemplatedParent}, 
                                                     Mode=TwoWay}" />
                                             </StackPanel>
+                                            <StackPanel Orientation="Horizontal" Spacing="5">
+                                                <TextBlock VerticalAlignment="Center"
+                                                           ui1:Translator.Key="ONION_OPACITY" />
+                                                <input:SizeInput
+                                                    HorizontalAlignment="Left" Width="80"
+                                                    MaxSize="100" Unit="%"
+                                                    Size="{Binding OnionOpacity, RelativeSource={RelativeSource TemplatedParent}, 
+                                                    Mode=TwoWay}" />
+                                            </StackPanel>
                                         </StackPanel>
                                     </Flyout>
                                 </Button.Flyout>
@@ -101,7 +111,7 @@
                             <ToggleButton Classes="pixi-icon"
                                           Content="{DynamicResource icon-onion-skin}"
                                           ui1:Translator.TooltipKey="TOGGLE_ONION_SKINNING"
-                                          IsChecked="{Binding OnionSkinningEnabled, RelativeSource={RelativeSource TemplatedParent}}"/>
+                                          IsChecked="{Binding OnionSkinningEnabled, RelativeSource={RelativeSource TemplatedParent}}" />
                             <Button Classes="pixi-icon"
                                     Content="{DynamicResource icon-trash}"
                                     ui1:Translator.TooltipKey="DELETE_FRAME"

+ 17 - 2
src/PixiEditor/ViewModels/Document/AnimationDataViewModel.cs

@@ -16,6 +16,7 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
     private int _activeFrameBindable = 1;
     private int frameRateBindable = 60;
     private int onionFrames = 1;
+    private double onionOpacity = 50;
     
     public DocumentViewModel Document { get; }
     protected DocumentInternalParts Internals { get; }
@@ -73,7 +74,19 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
             if (Document.UpdateableChangeActive)
                 return;
 
-            Internals.ActionAccumulator.AddFinishedActions(new SetOnionFrames_Action(value));
+            Internals.ActionAccumulator.AddFinishedActions(new SetOnionSettings_Action(value, OnionOpacityBindable));
+        }
+    }
+    
+    public double OnionOpacityBindable
+    {
+        get => onionOpacity;
+        set
+        {
+            if (Document.UpdateableChangeActive)
+                return;
+
+            Internals.ActionAccumulator.AddFinishedActions(new SetOnionSettings_Action(OnionFramesBindable, value)); 
         }
     }
 
@@ -170,10 +183,12 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
         OnPropertyChanged(nameof(OnionSkinningEnabledBindable));
     }
     
-    public void SetOnionFrames(int frames)
+    public void SetOnionFrames(int frames, double opacity)
     {
         onionFrames = frames;
+        onionOpacity = opacity;
         OnPropertyChanged(nameof(OnionFramesBindable));
+        OnPropertyChanged(nameof(OnionOpacityBindable));
     }
 
     public void SetFrameLength(Guid keyFrameId, int newStartFrame, int newDuration)

+ 1 - 0
src/PixiEditor/ViewModels/Document/DocumentViewModel.Serialization.cs

@@ -210,6 +210,7 @@ internal partial class DocumentViewModel
         animData.KeyFrameGroups = new List<KeyFrameGroup>();
         animData.FrameRate = animationData.FrameRate;
         animData.OnionFrames = animationData.OnionFrames;
+        animData.OnionOpacity = animationData.OnionOpacity;
         BuildKeyFrames(animationData.KeyFrames, animData, graph, nodeIdMap, keyFrameIds);
 
         return animData;

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

@@ -461,7 +461,7 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
                 return;
 
             acc.AddActions(new SetFrameRate_Action(data.FrameRate));
-            acc.AddActions(new SetOnionFrames_Action(data.OnionFrames));
+            acc.AddActions(new SetOnionSettings_Action(data.OnionFrames, data.OnionOpacity));
             foreach (var keyFrame in data.KeyFrameGroups)
             {
                 if (keyFrame is GroupKeyFrameBuilder group)

+ 9 - 0
src/PixiEditor/Views/Animations/Timeline.cs

@@ -97,6 +97,15 @@ internal class Timeline : TemplatedControl, INotifyPropertyChanged
     public static readonly StyledProperty<bool> OnionSkinningEnabledProperty = AvaloniaProperty.Register<Timeline, bool>(
         nameof(OnionSkinningEnabled));
 
+    public static readonly StyledProperty<double> OnionOpacityProperty = AvaloniaProperty.Register<Timeline, double>(
+        nameof(OnionOpacity), 50);
+
+    public double OnionOpacity
+    {
+        get => GetValue(OnionOpacityProperty);
+        set => SetValue(OnionOpacityProperty, value);
+    }
+
     public bool OnionSkinningEnabled
     {
         get => GetValue(OnionSkinningEnabledProperty);

+ 1 - 0
src/PixiEditor/Views/Dock/TimelineDockView.axaml

@@ -20,6 +20,7 @@
         DefaultEndFrame="{Binding DocumentManagerSubViewModel.ActiveDocument.AnimationDataViewModel.DefaultEndFrame}"
         OnionSkinningEnabled="{Binding DocumentManagerSubViewModel.ActiveDocument.AnimationDataViewModel.OnionSkinningEnabledBindable, Mode=TwoWay}"
         OnionFrames="{Binding DocumentManagerSubViewModel.ActiveDocument.AnimationDataViewModel.OnionFramesBindable, Mode=TwoWay}"
+        OnionOpacity="{Binding DocumentManagerSubViewModel.ActiveDocument.AnimationDataViewModel.OnionOpacityBindable, Mode=TwoWay}"
         DuplicateKeyFrameCommand="{xaml:Command PixiEditor.Animation.DuplicateRasterKeyFrame}"
         DeleteKeyFrameCommand="{xaml:Command PixiEditor.Animation.DeleteKeyFrames, UseProvided=True}"
         ChangeKeyFramesLengthCommand="{xaml:Command PixiEditor.Animation.ChangeKeyFramesStartPos, UseProvided=True}"/>