Browse Source

Barebones prototype

flabbet 10 months ago
parent
commit
7b23e2f55e

+ 4 - 1
src/PixiEditor.ChangeableDocument/Rendering/RenderingContext.cs

@@ -4,6 +4,7 @@ using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
+using PixiEditor.DrawingApi.Core.Surfaces;
 using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
 using PixiEditor.Numerics;
 using BlendMode = PixiEditor.ChangeableDocument.Enums.BlendMode;
@@ -22,11 +23,13 @@ public class RenderingContext : IDisposable
     public ChunkResolution ChunkResolution { get; }
     public VecI DocumentSize { get; set; }
     
+    public DrawingSurface TargetSurface { get; set; }
 
     public bool IsDisposed { get; private set; }
     
-    public RenderingContext(KeyFrameTime frameTime, VecI chunkToUpdate, ChunkResolution chunkResolution, VecI docSize)
+    public RenderingContext(DrawingSurface targetSurface, KeyFrameTime frameTime, VecI chunkToUpdate, ChunkResolution chunkResolution, VecI docSize)
     {
+        TargetSurface = targetSurface;
         FrameTime = frameTime;
         ChunkToUpdate = chunkToUpdate;
         ChunkResolution = chunkResolution;

+ 1 - 0
src/PixiEditor.DrawingApi.Core/Bridge/Operations/ICanvasImplementation.cs

@@ -43,5 +43,6 @@ namespace PixiEditor.DrawingApi.Core.Bridge.Operations
         public void DrawPaint(IntPtr objectPointer, Paint paint);
         public void DrawImage(IntPtr objectPointer, Image image, int x, int y, Paint paint);
         public Matrix3X3 GetActiveMatrix(IntPtr objectPointer);
+        public Canvas FromNative(object native);
     }
 }

+ 5 - 0
src/PixiEditor.DrawingApi.Core/Surfaces/Canvas.cs

@@ -222,5 +222,10 @@ namespace PixiEditor.DrawingApi.Core.Surfaces
         {
             DrawingBackendApi.Current.CanvasImplementation.Dispose(ObjectPointer);
         }
+
+        public static Canvas FromNative(object native)
+        {
+            return DrawingBackendApi.Current.CanvasImplementation.FromNative(native);
+        }
     }
 }

+ 11 - 0
src/PixiEditor.DrawingApi.Skia/Implementations/SkiaCanvasImplementation.cs

@@ -80,6 +80,17 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
             return ManagedInstances[objectPointer].TotalMatrix.ToMatrix3X3();
         }
 
+        public Canvas FromNative(object native)
+        {
+            if (native is SKCanvas skCanvas)
+            {
+                ManagedInstances.TryAdd(skCanvas.Handle, skCanvas);
+                return new Canvas(skCanvas.Handle);
+            }
+            
+            throw new ArgumentException("Native object is not a SKCanvas", nameof(native));
+        }
+
         public int Save(IntPtr objPtr)
         {
             return ManagedInstances[objPtr].Save();

+ 1 - 5
src/PixiEditor/Models/DocumentModels/ActionAccumulator.cs

@@ -163,11 +163,7 @@ internal class ActionAccumulator
             {
                 case DirtyRect_RenderInfo info:
                 {
-                    var bitmap = document.Surfaces[info.Resolution];
-                    RectI finalRect = new RectI(VecI.Zero, new(bitmap.Size.X, bitmap.Size.Y));
-
-                    RectI dirtyRect = new RectI(info.Pos, info.Size).Intersect(finalRect);
-                    bitmap.AddDirtyRect(dirtyRect);
+                    //TODO: Validate if it's required
                 }
                 break;
                 case PreviewDirty_RenderInfo info:

+ 0 - 1
src/PixiEditor/Models/Handlers/IDocument.cs

@@ -26,7 +26,6 @@ internal interface IDocument : IHandler
     public IAnimationHandler AnimationHandler { get; }
     public VectorPath SelectionPathBindable { get; }
     public INodeGraphHandler NodeGraphHandler { get; }
-    public Dictionary<ChunkResolution, Texture> Surfaces { get; set; }
     public DocumentStructureModule StructureHelper { get; }
     public Texture PreviewSurface { get; set; }
     public bool AllChangesSaved { get; }

+ 0 - 13
src/PixiEditor/ViewModels/Document/DocumentViewModel.cs

@@ -178,13 +178,6 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
 
     public IStructureMemberHandler? SelectedStructureMember { get; private set; } = null;
 
-    public Dictionary<ChunkResolution, Texture> Surfaces { get; set; } = new()
-    {
-        [ChunkResolution.Full] = new Texture(new VecI(64, 64)),
-        [ChunkResolution.Half] = new Texture(new VecI(32, 32)),
-        [ChunkResolution.Quarter] = new Texture(new VecI(16, 16)),
-        [ChunkResolution.Eighth] = new Texture(new VecI(8, 8))
-    };
 
     private Texture previewSurface;
 
@@ -195,7 +188,6 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
         {
             VecI? oldSize = previewSurface?.Size;
             SetProperty(ref previewSurface, value);
-            OnPropertyChanged(nameof(Surfaces));
             if (oldSize != null && value != null && oldSize != value.Size)
             {
                 RaiseSizeChanged(new DocumentSizeChangedEventArgs(this, oldSize.Value, value.Size));
@@ -1001,11 +993,6 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
 
     public void Dispose()
     {
-        foreach (var (_, surface) in Surfaces)
-        {
-            surface.Dispose();
-        }
-
         PreviewSurface.Dispose();
         Internals.Tracker.Dispose();
         Internals.Tracker.Document.Dispose();

+ 0 - 27
src/PixiEditor/Views/Main/ViewportControls/FixedViewport.axaml.cs

@@ -19,9 +19,6 @@ internal partial class FixedViewport : UserControl, INotifyPropertyChanged
     public static readonly StyledProperty<DocumentViewModel> DocumentProperty =
         AvaloniaProperty.Register<FixedViewport, DocumentViewModel>(nameof(Document), null);
 
-    private static readonly StyledProperty<Dictionary<ChunkResolution, Texture>> BitmapsProperty =
-        AvaloniaProperty.Register<FixedViewport, Dictionary<ChunkResolution, Texture>>(nameof(Bitmaps), null);
-
     public static readonly StyledProperty<bool> DelayedProperty =
         AvaloniaProperty.Register<FixedViewport, bool>(nameof(Delayed), false);
 
@@ -33,41 +30,22 @@ internal partial class FixedViewport : UserControl, INotifyPropertyChanged
         set => SetValue(DelayedProperty, value);
     }
 
-    public Dictionary<ChunkResolution, Texture>? Bitmaps
-    {
-        get => GetValue(BitmapsProperty);
-        set => SetValue(BitmapsProperty, value);
-    }
-
     public DocumentViewModel? Document
     {
         get => GetValue(DocumentProperty);
         set => SetValue(DocumentProperty, value);
     }
 
-    public Texture? TargetBitmap
-    {
-        get
-        {
-            if (Document?.Surfaces.TryGetValue(CalculateResolution(), out Texture? value) == true)
-                return value;
-            return null;
-        }
-    }
-
     public Guid GuidValue { get; } = Guid.NewGuid();
 
     static FixedViewport()
     {
         DocumentProperty.Changed.Subscribe(OnDocumentChange);
-        BitmapsProperty.Changed.Subscribe(OnBitmapsChange);
     }
 
     public FixedViewport()
     {
         InitializeComponent();
-        Binding binding = new Binding { Source = this, Path = $"{nameof(Document)}.{nameof(Document.Surfaces)}" };
-        this.Bind(BitmapsProperty, binding);
         Loaded += OnLoad;
         Unloaded += OnUnload;
     }
@@ -134,26 +112,21 @@ internal partial class FixedViewport : UserControl, INotifyPropertyChanged
         {
             newDoc.SizeChanged += viewport.DocSizeChanged;
         }
-
-        viewport.PropertyChanged?.Invoke(viewport, new(nameof(TargetBitmap)));
     }
 
     private void DocSizeChanged(object? sender, DocumentSizeChangedEventArgs e)
     {
-        PropertyChanged?.Invoke(this, new(nameof(TargetBitmap)));
         Document?.Operations.AddOrUpdateViewport(GetLocation());
     }
 
     private static void OnBitmapsChange(AvaloniaPropertyChangedEventArgs<Dictionary<ChunkResolution, Texture>> args)
     {
         FixedViewport? viewport = (FixedViewport)args.Sender;
-        viewport.PropertyChanged?.Invoke(viewport, new(nameof(TargetBitmap)));
         viewport.Document?.Operations.AddOrUpdateViewport(viewport.GetLocation());
     }
 
     private void OnImageSizeChanged(object sender, SizeChangedEventArgs e)
     {
-        PropertyChanged?.Invoke(this, new(nameof(TargetBitmap)));
         Document?.Operations.AddOrUpdateViewport(GetLocation());
     }
 }

+ 3 - 2
src/PixiEditor/Views/Main/ViewportControls/Viewport.axaml

@@ -124,7 +124,8 @@
                            VerticalAlignment="Top"
                            ToolSet="{Binding Source={viewModels:MainVM}, Path=ToolsSubViewModel.ActiveToolSet}" 
                            SwitchToolSetCommand="{xaml:Command Name=PixiEditor.Tools.SwitchToolSet, UseProvided=True}"/>
-        <rendering:Scene
+        <rendering:UniversalScene Name="scene" ZIndex=""/>
+        <!--<rendering:Scene
             Focusable="False" Name="scene"
             ZIndex="1"
             Surface="{Binding TargetBitmap, ElementName=vpUc}"
@@ -142,7 +143,7 @@
             FadeOut="{Binding Source={viewModels:ToolVM ColorPickerToolViewModel}, Path=PickOnlyFromReferenceLayer, Mode=OneWay}"
             DefaultCursor="{Binding Source={viewModels:MainVM}, Path=ToolsSubViewModel.ToolCursor, Mode=OneWay}"
             CheckerImagePath="/Images/CheckerTile.png"
-            ui:RenderOptionsBindable.BitmapInterpolationMode="{Binding Scale, Converter={converters:ScaleToBitmapScalingModeConverter}, RelativeSource={RelativeSource Self}}" />
+            ui:RenderOptionsBindable.BitmapInterpolationMode="{Binding Scale, Converter={converters:ScaleToBitmapScalingModeConverter}, RelativeSource={RelativeSource Self}}" />-->
 
         <!--Brush shape overlay is rendered separately, so it doesn't trigger rerender each mouse movement to scene-->
         <!--I didn't measure it, but I thought that could impact performance-->

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

@@ -262,9 +262,6 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
 
             PropertyChanged?.Invoke(this, new(nameof(RealDimensions)));
             Document?.Operations.AddOrUpdateViewport(GetLocation());
-
-            if (oldRes != newRes)
-                PropertyChanged?.Invoke(this, new(nameof(TargetBitmap)));
         }
     }
 
@@ -281,17 +278,6 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
 
             PropertyChanged?.Invoke(this, new(nameof(Dimensions)));
             Document?.Operations.AddOrUpdateViewport(GetLocation());
-
-            if (oldRes != newRes)
-                PropertyChanged?.Invoke(this, new(nameof(TargetBitmap)));
-        }
-    }
-
-    public Texture? TargetBitmap
-    {
-        get
-        {
-            return Document?.Surfaces.TryGetValue(CalculateResolution(), out Texture? value) == true ? value : null;
         }
     }
 
@@ -339,7 +325,7 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
         TextBoxFocusBehavior.FallbackFocusElement.Focus();
     }
 
-    public Scene Scene => scene;
+    public UniversalScene Scene => scene;
 
     private void ForceRefreshFinalImage()
     {
@@ -369,26 +355,13 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
         Viewport? viewport = (Viewport)e.Sender;
 
         DocumentViewModel? oldDoc = e.OldValue.Value;
-        if (oldDoc != null)
-        {
-            oldDoc.SizeChanged -= viewport.OnImageSizeChanged;
-        }
 
         DocumentViewModel? newDoc = e.NewValue.Value;
-        if (newDoc != null)
-        {
-            newDoc.SizeChanged += viewport.OnImageSizeChanged;
-        }
 
         oldDoc?.Operations.RemoveViewport(viewport.GuidValue);
         newDoc?.Operations.AddOrUpdateViewport(viewport.GetLocation());
     }
 
-    private void OnImageSizeChanged(object? sender, DocumentSizeChangedEventArgs e)
-    {
-        PropertyChanged?.Invoke(this, new(nameof(TargetBitmap)));
-    }
-
     private ChunkResolution CalculateResolution()
     {
         VecD densityVec = Dimensions.Divide(RealDimensions);

+ 0 - 7
src/PixiEditor/Views/Rendering/Scene.cs

@@ -1,4 +1,3 @@
-using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Collections.Specialized;
 using Avalonia;
@@ -10,14 +9,9 @@ using Avalonia.Rendering;
 using Avalonia.Rendering.SceneGraph;
 using Avalonia.Skia;
 using Avalonia.Threading;
-using ChunkyImageLib;
 using ChunkyImageLib.DataHolders;
-using PixiEditor.ChangeableDocument.Changeables.Animations;
 using PixiEditor.DrawingApi.Core;
 using PixiEditor.DrawingApi.Core.Bridge;
-using PixiEditor.DrawingApi.Core.Numerics;
-using PixiEditor.DrawingApi.Core.Surfaces;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
 using PixiEditor.DrawingApi.Skia;
 using PixiEditor.DrawingApi.Skia.Extensions;
 using PixiEditor.Extensions.UI.Overlays;
@@ -30,7 +24,6 @@ using PixiEditor.Views.Overlays;
 using PixiEditor.Views.Overlays.Pointers;
 using PixiEditor.Views.Visuals;
 using Bitmap = PixiEditor.DrawingApi.Core.Surfaces.Bitmap;
-using Colors = PixiEditor.DrawingApi.Core.ColorsImpl.Colors;
 using Image = PixiEditor.DrawingApi.Core.Surfaces.ImageData.Image;
 using Point = Avalonia.Point;
 

+ 29 - 0
src/PixiEditor/Views/Rendering/SceneObject.cs

@@ -0,0 +1,29 @@
+using PixiEditor.DrawingApi.Core.Surfaces;
+using PixiEditor.Numerics;
+
+namespace PixiEditor.Views.Rendering;
+
+public interface ISceneObject
+{
+    public VecD Position { get; set; }
+    public VecD Size { get; set; }
+    
+    public RectD GlobalBounds => new RectD(Position, Size);
+    
+    public RenderGraph Graph { get; set; }
+
+    public void RenderInScene(DrawingSurface surface)
+    {
+        RectD localBounds = new RectD(0, 0, Size.X, Size.Y);
+        
+        int savedNum = surface.Canvas.Save();
+        surface.Canvas.ClipRect(RectD.Create((VecI)Position.Floor(), (VecI)Size.Ceiling()));
+        surface.Canvas.Translate((float)Position.X, (float)Position.Y);
+        
+        RenderContext context = new RenderContext(surface, localBounds);
+        
+        Graph.RenderInLocalSpace(context);
+        
+        surface.Canvas.RestoreToCount(savedNum);
+    }
+}

+ 158 - 0
src/PixiEditor/Views/Rendering/UniversalScene.cs

@@ -0,0 +1,158 @@
+using Avalonia;
+using Avalonia.Media;
+using Avalonia.Rendering;
+using Avalonia.Rendering.SceneGraph;
+using Avalonia.Skia;
+using Avalonia.Threading;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
+using PixiEditor.DrawingApi.Core;
+using PixiEditor.DrawingApi.Core.Surfaces;
+using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
+using PixiEditor.Numerics;
+using PixiEditor.Views.Visuals;
+using Colors = PixiEditor.DrawingApi.Core.ColorsImpl.Colors;
+using Point = Avalonia.Point;
+
+namespace PixiEditor.Views.Rendering;
+
+public class UniversalScene : Zoombox.Zoombox, ICustomHitTest
+{
+    public List<ISceneObject> SceneObjects { get; set; } = new List<ISceneObject>();
+
+    public override void Render(DrawingContext context)
+    {
+        // TODO: Do bounds pass, that will be used to calculate dirty bounds
+        var drawOperation = new DrawUniversalSceneOperation(SceneObjects, Bounds, CalculateTransformMatrix());
+        context.Custom(drawOperation);
+
+        Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Render);
+    }
+
+    bool ICustomHitTest.HitTest(Point point)
+    {
+        return Bounds.Contains(point);
+    }
+
+    private Matrix CalculateTransformMatrix()
+    {
+        Matrix transform = Matrix.Identity;
+        transform = transform.Append(Matrix.CreateRotation((float)AngleRadians));
+        transform = transform.Append(Matrix.CreateScale(FlipX ? -1 : 1, FlipY ? -1 : 1));
+        transform = transform.Append(Matrix.CreateScale((float)Scale, (float)Scale));
+        transform = transform.Append(Matrix.CreateTranslation(CanvasPos.X, CanvasPos.Y));
+        return transform;
+    }
+}
+
+class DrawUniversalSceneOperation : SkiaDrawOperation
+{
+    public List<ISceneObject> SceneObjects { get; set; }
+    public Matrix TransformMatrix { get; set; }
+
+    public DrawUniversalSceneOperation(List<ISceneObject> sceneObjects, Rect dirtyBounds,
+        Matrix calculateTransformMatrix) : base(dirtyBounds)
+    {
+        SceneObjects = sceneObjects;
+        TransformMatrix = calculateTransformMatrix;
+    }
+
+    public override bool Equals(ICustomDrawOperation? other)
+    {
+        return false;
+    }
+
+    public override void Render(ISkiaSharpApiLease lease)
+    {
+        var originalMatrix = lease.SkSurface.Canvas.TotalMatrix;
+
+        lease.SkSurface.Canvas.SetMatrix(TransformMatrix.ToSKMatrix());
+
+        foreach (ISceneObject sceneObject in SceneObjects)
+        {
+            RenderObject(lease, sceneObject);
+        }
+
+        DrawDebugGrid(lease.SkSurface.Canvas);
+        lease.SkSurface.Canvas.SetMatrix(originalMatrix);
+    }
+
+    private static void RenderObject(ISkiaSharpApiLease lease, ISceneObject sceneObject)
+    {
+        DrawingSurface surface = DrawingSurface.FromNative(lease.SkSurface);
+        sceneObject.RenderInScene(surface);
+    } 
+
+    private void DrawDebugGrid(SKCanvas canvas)
+    {
+        canvas.DrawText("(0, 0)", 5, -5, new SKPaint() { Color = SKColors.White });
+        canvas.DrawCircle(0, 0, 5, new SKPaint() { Color = SKColors.White });
+        
+        canvas.DrawText("(100, 100)", 105, 95, new SKPaint() { Color = SKColors.White });
+        canvas.DrawCircle(100, 100, 5, new SKPaint() { Color = SKColors.White });
+        
+        canvas.DrawText("(-100, -100)", -105, -95, new SKPaint() { Color = SKColors.White });
+        canvas.DrawCircle(-100, -100, 5, new SKPaint() { Color = SKColors.White });
+        
+        canvas.DrawText("(100, -100)", 105, -95, new SKPaint() { Color = SKColors.White });
+        canvas.DrawCircle(100, -100, 5, new SKPaint() { Color = SKColors.White });
+        
+        canvas.DrawText("(-100, 100)", -105, 95, new SKPaint() { Color = SKColors.White });
+        canvas.DrawCircle(-100, 100, 5, new SKPaint() { Color = SKColors.White });
+        
+        for (int i = -1000; i < 1000; i += 100)
+        {
+            canvas.DrawLine(i, -1000, i, 1000, new SKPaint() { Color = SKColors.White });
+            canvas.DrawLine(-1000, i, 1000, i, new SKPaint() { Color = SKColors.White });
+        }
+    }
+}
+
+public class RenderContext
+{
+    public DrawingSurface Surface { get; }
+    public RectD LocalBounds { get; }
+    
+    public RenderContext(DrawingSurface surface, RectD localBounds)
+    {
+        Surface = surface;
+        LocalBounds = localBounds;
+    }
+}
+
+public class RenderGraph
+{
+    public List<EffectNode> Nodes { get; set; } = new List<EffectNode>();
+
+    public void RenderInLocalSpace(RenderContext context)
+    {
+        foreach (EffectNode node in Nodes)
+        {
+            node.Render(context);
+        }
+    }
+}
+
+public abstract class EffectNode
+{
+    public abstract void Render(RenderContext context);
+}
+
+class DrawRectNode : EffectNode
+{
+    public override void Render(RenderContext context)
+    {
+        context.Surface.Canvas.DrawRect(0, 0, (int)context.LocalBounds.Width, (int)context.LocalBounds.Height, new Paint 
+            { Color = Colors.Aquamarine, BlendMode = BlendMode.Difference} );
+    }
+}
+
+class ApplyEffectNode : EffectNode
+{
+    public override void Render(RenderContext context)
+    {
+        using Paint paint = new Paint();
+        paint.ColorFilter = Filters.RedGrayscaleFilter;
+
+        context.Surface.Canvas.DrawPaint(paint);
+    }
+}