Browse Source

Added MatrixTransformNode, SeparateChannelNode and Matrix4x5F property view

CPKreuz 1 year ago
parent
commit
0574b19398

+ 170 - 0
src/PixiEditor.AvaloniaUI/ViewModels/Nodes/Properties/Matrix4x5FPropertyViewModel.cs

@@ -0,0 +1,170 @@
+using PixiEditor.Numerics;
+
+namespace PixiEditor.AvaloniaUI.ViewModels.Nodes.Properties;
+
+internal class Matrix4x5FPropertyViewModel : NodePropertyViewModel<Matrix4x5F>
+{
+    public Matrix4x5FPropertyViewModel(NodeViewModel node, Type valueType) : base(node, valueType)
+    {
+    }
+
+    public float M11
+    {
+        get => Value.M11;
+        set => Value = new Matrix4x5F(
+            (value, M12, M13, M14, M15), (M21, M22, M23, M24, M25),
+            (M31, M32, M33, M34, M35), (M41, M42, M43, M44, M45));
+    }
+
+    public float M12
+    {
+        get => Value.M12;
+        set => Value = new Matrix4x5F(
+            (M11, value, M13, M14, M15), (M21, M22, M23, M24, M25),
+            (M31, M32, M33, M34, M35), (M41, M42, M43, M44, M45));
+    }
+
+    public float M13
+    {
+        get => Value.M13;
+        set => Value = new Matrix4x5F(
+            (M11, M12, value, M14, M15), (M21, M22, M23, M24, M25),
+            (M31, M32, M33, M34, M35), (M41, M42, M43, M44, M45));
+    }
+
+    public float M14
+    {
+        get => Value.M14;
+        set => Value = new Matrix4x5F(
+            (M11, M12, M13, value, M15), (M21, M22, M23, M24, M25),
+            (M31, M32, M33, M34, M35), (M41, M42, M43, M44, M45));
+    }
+
+    public float M15
+    {
+        get => Value.M15;
+        set => Value = new Matrix4x5F(
+            (M11, M12, M13, M14, value), (M21, M22, M23, M24, M25),
+            (M31, M32, M33, M34, M35), (M41, M42, M43, M44, M45));
+    }
+
+    public float M21
+    {
+        get => Value.M21;
+        set => Value = new Matrix4x5F(
+            (M11, M12, M13, M14, M15), (value, M22, M23, M24, M25),
+            (M31, M32, M33, M34, M35), (M41, M42, M43, M44, M45));
+    }
+
+    public float M22
+    {
+        get => Value.M22;
+        set => Value = new Matrix4x5F(
+            (M11, M12, M13, M14, M15), (M21, value, M23, M24, M25),
+            (M31, M32, M33, M34, M35), (M41, M42, M43, M44, M45));
+    }
+
+    public float M23
+    {
+        get => Value.M23;
+        set => Value = new Matrix4x5F(
+            (M11, M12, M13, M14, M15), (M21, M22, value, M24, M25),
+            (M31, M32, M33, M34, M35), (M41, M42, M43, M44, M45));
+    }
+
+    public float M24
+    {
+        get => Value.M24;
+        set => Value = new Matrix4x5F(
+            (M11, M12, M13, M14, M15), (M21, M22, M23, value, M25),
+            (M31, M32, M33, M34, M35), (M41, M42, M43, M44, M45));
+    }
+
+    public float M25
+    {
+        get => Value.M25;
+        set => Value = new Matrix4x5F(
+            (M11, M12, M13, M14, M15), (M21, M22, M23, M24, value),
+            (M31, M32, M33, M34, M35), (M41, M42, M43, M44, M45));
+    }
+
+    public float M31
+    {
+        get => Value.M31;
+        set => Value = new Matrix4x5F(
+            (M11, M12, M13, M14, M15), (M21, M22, M23, M24, M25),
+            (value, M32, M33, M34, M35), (M41, M42, M43, M44, M45));
+    }
+
+    public float M32
+    {
+        get => Value.M32;
+        set => Value = new Matrix4x5F(
+            (M11, M12, M13, M14, M15), (M21, M22, M23, M24, M25),
+            (M31, value, M33, M34, M35), (M41, M42, M43, M44, M45));
+    }
+
+    public float M33
+    {
+        get => Value.M33;
+        set => Value = new Matrix4x5F(
+            (M11, M12, M13, M14, M15), (M21, M22, M23, M24, M25),
+            (M31, M32, value, M34, M35), (M41, M42, M43, M44, M45));
+    }
+
+    public float M34
+    {
+        get => Value.M34;
+        set => Value = new Matrix4x5F(
+            (M11, M12, M13, M14, M15), (M21, M22, M23, M24, M25),
+            (M31, M32, M33, value, M35), (M41, M42, M43, M44, M45));
+    }
+
+    public float M35
+    {
+        get => Value.M35;
+        set => Value = new Matrix4x5F(
+            (M11, M12, M13, M14, M15), (M21, M22, M23, M24, M25),
+            (M31, M32, M33, M34, value), (M41, M42, M43, M44, M45));
+    }
+
+    public float M41
+    {
+        get => Value.M41;
+        set => Value = new Matrix4x5F(
+            (M11, M12, M13, M14, M15), (M21, M22, M23, M24, M25),
+            (M31, M32, M33, M34, M35), (value, M42, M43, M44, M45));
+    }
+
+    public float M42
+    {
+        get => Value.M42;
+        set => Value = new Matrix4x5F(
+            (M11, M12, M13, M14, M15), (M21, M22, M23, M24, M25),
+            (M31, M32, M33, M34, M35), (M41, value, M43, M44, M45));
+    }
+
+    public float M43
+    {
+        get => Value.M43;
+        set => Value = new Matrix4x5F(
+            (M11, M12, M13, M14, M15), (M21, M22, M23, M24, M25),
+            (M31, M32, M33, M34, M35), (M41, M42, value, M44, M45));
+    }
+
+    public float M44
+    {
+        get => Value.M44;
+        set => Value = new Matrix4x5F(
+            (M11, M12, M13, M14, M15), (M21, M22, M23, M24, M25),
+            (M31, M32, M33, M34, M35), (M41, M42, M43, value, M45));
+    }
+
+    public float M45
+    {
+        get => Value.M45;
+        set => Value = new Matrix4x5F(
+            (M11, M12, M13, M14, M15), (M21, M22, M23, M24, M25),
+            (M31, M32, M33, M34, M35), (M41, M42, M43, M44, value));
+    }
+}

+ 46 - 0
src/PixiEditor.AvaloniaUI/Views/Nodes/Properties/Matrix4x5FPropertyView.axaml

@@ -0,0 +1,46 @@
+<properties:NodePropertyView xmlns="https://github.com/avaloniaui"
+                             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+                             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+                             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+                             xmlns:properties="clr-namespace:PixiEditor.AvaloniaUI.Views.Nodes.Properties"
+                             xmlns:input="clr-namespace:PixiEditor.AvaloniaUI.Views.Input"
+                             xmlns:ui="clr-namespace:PixiEditor.Extensions.UI;assembly=PixiEditor.Extensions"
+                             mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+                             x:Class="PixiEditor.AvaloniaUI.Views.Nodes.Properties.Matrix4x5FPropertyView">
+    <StackPanel>
+        <TextBlock ui:Translator.Key="{Binding DisplayName}" />
+        <Grid ColumnDefinitions="Auto,*,*,*,*,*" RowDefinitions="Auto, Auto, Auto, Auto, Auto">
+            <TextBlock Grid.Row="1" Grid.Column="0" Text="1" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="1,0" />
+            <TextBlock Grid.Row="2" Grid.Column="0" Text="2" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="1,0" />
+            <TextBlock Grid.Row="3" Grid.Column="0" Text="3" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="1,0" />
+            <TextBlock Grid.Row="4" Grid.Column="0" Text="4" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="1,0" />
+            
+            <TextBlock Grid.Row="0" Grid.Column="1" Text="1" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="1,0" />
+            <TextBlock Grid.Row="0" Grid.Column="2" Text="2" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="1,0" />
+            <TextBlock Grid.Row="0" Grid.Column="3" Text="3" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="1,0" />
+            <TextBlock Grid.Row="0" Grid.Column="4" Text="4" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="1,0" />
+            <TextBlock Grid.Row="0" Grid.Column="5" Text="5" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="1,0" />
+            
+            <input:NumberInput Grid.Row="1" Grid.Column="1" IsVisible="{Binding IsInput}" Value="{Binding M11, Mode=TwoWay}" />
+            <input:NumberInput Grid.Row="1" Grid.Column="2" IsVisible="{Binding IsInput}" Value="{Binding M12, Mode=TwoWay}" />
+            <input:NumberInput Grid.Row="1" Grid.Column="3" IsVisible="{Binding IsInput}" Value="{Binding M13, Mode=TwoWay}" />
+            <input:NumberInput Grid.Row="1" Grid.Column="4" IsVisible="{Binding IsInput}" Value="{Binding M14, Mode=TwoWay}" />
+            <input:NumberInput Grid.Row="1" Grid.Column="5" IsVisible="{Binding IsInput}" Value="{Binding M15, Mode=TwoWay}" />
+            <input:NumberInput Grid.Row="2" Grid.Column="1" IsVisible="{Binding IsInput}" Value="{Binding M21, Mode=TwoWay}" />
+            <input:NumberInput Grid.Row="2" Grid.Column="2" IsVisible="{Binding IsInput}" Value="{Binding M22, Mode=TwoWay}" />
+            <input:NumberInput Grid.Row="2" Grid.Column="3" IsVisible="{Binding IsInput}" Value="{Binding M23, Mode=TwoWay}" />
+            <input:NumberInput Grid.Row="2" Grid.Column="4" IsVisible="{Binding IsInput}" Value="{Binding M24, Mode=TwoWay}" />
+            <input:NumberInput Grid.Row="2" Grid.Column="5" IsVisible="{Binding IsInput}" Value="{Binding M25, Mode=TwoWay}" />
+            <input:NumberInput Grid.Row="3" Grid.Column="1" IsVisible="{Binding IsInput}" Value="{Binding M31, Mode=TwoWay}" />
+            <input:NumberInput Grid.Row="3" Grid.Column="2" IsVisible="{Binding IsInput}" Value="{Binding M32, Mode=TwoWay}" />
+            <input:NumberInput Grid.Row="3" Grid.Column="3" IsVisible="{Binding IsInput}" Value="{Binding M33, Mode=TwoWay}" />
+            <input:NumberInput Grid.Row="3" Grid.Column="4" IsVisible="{Binding IsInput}" Value="{Binding M34, Mode=TwoWay}" />
+            <input:NumberInput Grid.Row="3" Grid.Column="5" IsVisible="{Binding IsInput}" Value="{Binding M35, Mode=TwoWay}" />
+            <input:NumberInput Grid.Row="4" Grid.Column="1" IsVisible="{Binding IsInput}" Value="{Binding M41, Mode=TwoWay}" />
+            <input:NumberInput Grid.Row="4" Grid.Column="2" IsVisible="{Binding IsInput}" Value="{Binding M42, Mode=TwoWay}" />
+            <input:NumberInput Grid.Row="4" Grid.Column="3" IsVisible="{Binding IsInput}" Value="{Binding M43, Mode=TwoWay}" />
+            <input:NumberInput Grid.Row="4" Grid.Column="4" IsVisible="{Binding IsInput}" Value="{Binding M44, Mode=TwoWay}" />
+            <input:NumberInput Grid.Row="4" Grid.Column="5" IsVisible="{Binding IsInput}" Value="{Binding M45, Mode=TwoWay}" />
+        </Grid>
+    </StackPanel>
+</properties:NodePropertyView>

+ 14 - 0
src/PixiEditor.AvaloniaUI/Views/Nodes/Properties/Matrix4x5FPropertyView.axaml.cs

@@ -0,0 +1,14 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace PixiEditor.AvaloniaUI.Views.Nodes.Properties;
+
+public partial class Matrix4x5FPropertyView : NodePropertyView
+{
+    public Matrix4x5FPropertyView()
+    {
+        InitializeComponent();
+    }
+}
+

+ 55 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/MatrixTransformNode.cs

@@ -0,0 +1,55 @@
+using PixiEditor.ChangeableDocument.Rendering;
+using PixiEditor.DrawingApi.Core.Surface.ImageData;
+using PixiEditor.DrawingApi.Core.Surface.PaintImpl;
+using PixiEditor.Numerics;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
+
+public class MatrixTransformNode : Node
+{
+    
+    private Matrix4x5F previousMatrix = new(
+        (1, 0, 0, 0, 0),
+        (0, 1, 0, 0, 0),
+        (0, 0, 1, 0, 0),
+        (0, 0, 0, 1, 0));
+    
+    private Paint paint;
+    
+    public OutputProperty<Image> Transformed { get; }
+    
+    public InputProperty<Image?> Input { get; }
+    
+    public InputProperty<Matrix4x5F> Matrix { get; }
+
+    public MatrixTransformNode()
+    {
+        Transformed = CreateOutput<Image>(nameof(Transformed), "TRANSFORMED", null);
+        Input = CreateInput<Image>(nameof(Input), "INPUT", null);
+        Matrix = CreateInput(nameof(Matrix), "MATRIX", previousMatrix);
+
+        paint = new Paint { ColorFilter = ColorFilter.CreateColorMatrix(previousMatrix) };
+    }
+    
+    protected override Image? OnExecute(RenderingContext context)
+    {
+        var currentMatrix = Matrix.Value;
+        if (previousMatrix != currentMatrix)
+        {
+            paint.ColorFilter = ColorFilter.CreateColorMatrix(Matrix.Value);
+            previousMatrix = currentMatrix;
+        }
+
+        using var workingSurface = new Surface(Input.Value.Size);
+        
+        workingSurface.DrawingSurface.Canvas.DrawImage(Input.Value, 0, 0, paint);
+
+        Transformed.Value = workingSurface.DrawingSurface.Snapshot();
+        
+        return Transformed.Value;
+    }
+
+    public override bool Validate() => Input.Value != null;
+
+    public override Node CreateCopy() => new MatrixTransformNode();
+}

+ 94 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/SeparateChannelsNode.cs

@@ -0,0 +1,94 @@
+using PixiEditor.ChangeableDocument.Rendering;
+using PixiEditor.DrawingApi.Core.ColorsImpl;
+using PixiEditor.DrawingApi.Core.Surface;
+using PixiEditor.DrawingApi.Core.Surface.ImageData;
+using PixiEditor.DrawingApi.Core.Surface.PaintImpl;
+using PixiEditor.Numerics;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
+
+public class SeparateChannelsNode : Node
+{
+    private readonly Paint _paint = new();
+    
+    private readonly ColorFilter _redFilter = ColorFilter.CreateColorMatrix(ColorMatrix.UseRed + ColorMatrix.OpaqueAlphaOffset);
+    private readonly ColorFilter _greenFilter = ColorFilter.CreateColorMatrix(ColorMatrix.UseGreen + ColorMatrix.OpaqueAlphaOffset);
+    private readonly ColorFilter _blueFilter = ColorFilter.CreateColorMatrix(ColorMatrix.UseBlue + ColorMatrix.OpaqueAlphaOffset);
+    private readonly ColorFilter _alphaFilter = ColorFilter.CreateColorMatrix(ColorMatrix.UseAlpha);
+    
+    private readonly ColorFilter _redGrayscaleFilter = ColorFilter.CreateColorMatrix(ColorMatrix.UseRed + ColorMatrix.MapRedToGreenBlue + ColorMatrix.OpaqueAlphaOffset);
+    private readonly ColorFilter _greenGrayscaleFilter = ColorFilter.CreateColorMatrix(ColorMatrix.UseGreen + ColorMatrix.MapGreenToRedBlue + ColorMatrix.OpaqueAlphaOffset);
+    private readonly ColorFilter _blueGrayscaleFilter = ColorFilter.CreateColorMatrix(ColorMatrix.UseBlue + ColorMatrix.MapBlueToRedGreen + ColorMatrix.OpaqueAlphaOffset);
+    private readonly ColorFilter _alphaGrayscaleFilter = ColorFilter.CreateColorMatrix(ColorMatrix.MapAlphaToRedGreenBlue + ColorMatrix.OpaqueAlphaOffset);
+
+    public OutputProperty<Image?> Red { get; }
+    
+    public OutputProperty<Image?> Green { get; }
+    
+    public OutputProperty<Image?> Blue { get; }
+
+    public OutputProperty<Image?> Alpha { get; }
+    
+    public InputProperty<Image?> Image { get; }
+    
+    public InputProperty<bool> Grayscale { get; }
+
+    public SeparateChannelsNode()
+    {
+        Red = CreateOutput<Image>(nameof(Red), "RED", null);
+        Green = CreateOutput<Image>(nameof(Green), "GREEN", null);
+        Blue = CreateOutput<Image>(nameof(Blue), "BLUE", null);
+        Alpha = CreateOutput<Image>(nameof(Alpha), "ALPHA", null);
+        
+        Image = CreateInput<Image>(nameof(Image), "IMAGE", null);
+        Grayscale = CreateInput(nameof(Grayscale), "GRAYSCALE", false);
+    }
+    
+    protected override Image OnExecute(RenderingContext context)
+    {
+        var image = Image.Value;
+        var grayscale = Grayscale.Value;
+
+        var red = !grayscale ? _redFilter : _redGrayscaleFilter;
+        var green = !grayscale ? _greenFilter : _greenGrayscaleFilter;
+        var blue = !grayscale ? _blueFilter : _blueGrayscaleFilter;
+        var alpha = !grayscale ? _alphaFilter : _alphaGrayscaleFilter;
+
+        Red.Value = GetImage(image, red);
+        Green.Value = GetImage(image, green);
+        Blue.Value = GetImage(image, blue);
+        Alpha.Value = GetImage(image, alpha);
+
+        using var previewSurface = new Surface(image.Size);
+
+        var half = image.Size / 2;
+        var halfX = half.X;
+        var halfY = half.Y;
+        
+        var redRect = new RectD(new VecD(), half);
+        var greenRect = new RectD(new VecD(halfX, 0), half);
+        var blueRect = new RectD(new VecD(0, halfY), half);
+        var alphaRect = new RectD(new VecD(halfX, halfY), half);
+        
+        previewSurface.DrawingSurface.Canvas.DrawImage(Red.Value, redRect, context.ReplacingPaintWithOpacity);
+        previewSurface.DrawingSurface.Canvas.DrawImage(Green.Value, greenRect, context.ReplacingPaintWithOpacity);
+        previewSurface.DrawingSurface.Canvas.DrawImage(Blue.Value, blueRect, context.ReplacingPaintWithOpacity);
+        previewSurface.DrawingSurface.Canvas.DrawImage(Alpha.Value, alphaRect, context.ReplacingPaintWithOpacity);
+        
+        return previewSurface.DrawingSurface.Snapshot();
+    }
+
+    private Image GetImage(Image image, ColorFilter filter)
+    {
+        using var imageSurface = new Surface(image.Size);
+
+        _paint.ColorFilter = filter;
+        imageSurface.DrawingSurface.Canvas.DrawImage(image, 0, 0, _paint);
+
+        return imageSurface.DrawingSurface.Snapshot();
+    }
+
+    public override bool Validate() => Image.Value != null;
+
+    public override Node CreateCopy() => new SeparateChannelsNode();
+}