Browse Source

Fixed shape layer opacity

flabbet 10 months ago
parent
commit
5afc48e328

+ 2 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/SceneObjectRenderContext.cs

@@ -10,7 +10,8 @@ public class SceneObjectRenderContext : RenderContext
     public RectD LocalBounds { get; }
     public bool RenderSurfaceIsScene { get; }
 
-    public SceneObjectRenderContext(DrawingSurface surface, RectD localBounds, KeyFrameTime frameTime, ChunkResolution chunkResolution, VecI docSize, bool renderSurfaceIsScene) : base(surface, frameTime, chunkResolution, docSize)
+    public SceneObjectRenderContext(DrawingSurface surface, RectD localBounds, KeyFrameTime frameTime,
+        ChunkResolution chunkResolution, VecI docSize, bool renderSurfaceIsScene, double opacity) : base(surface, frameTime, chunkResolution, docSize, opacity)
     {
         LocalBounds = localBounds;
         RenderSurfaceIsScene = renderSurfaceIsScene;

+ 15 - 7
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageLayerNode.cs

@@ -85,16 +85,16 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
     protected override void DrawWithoutFilters(SceneObjectRenderContext ctx, DrawingSurface workingSurface,
         Paint paint)
     {
-        DrawLayer(workingSurface, paint, ctx.ChunkResolution); 
+        DrawLayer(workingSurface, paint, ctx.FrameTime); 
     }
 
     protected override void DrawWithFilters(SceneObjectRenderContext context, DrawingSurface workingSurface,
         Paint paint)
     {
-        DrawLayer(workingSurface, paint, context.ChunkResolution);
+        DrawLayer(workingSurface, paint, context.FrameTime);
     }
 
-    private void DrawLayer(DrawingSurface workingSurface, Paint paint, ChunkResolution resolution)
+    private void DrawLayer(DrawingSurface workingSurface, Paint paint, KeyFrameTime frameTime)
     {
         if (fullResrenderedSurface is null)
         {
@@ -103,11 +103,19 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
 
         int saved = workingSurface.Canvas.Save();
 
-        //workingSurface.Canvas.Scale((float)resolution.Multiplier());
-        
         VecD topLeft = SceneSize / 2f;
-        workingSurface.Canvas.DrawSurface(fullResrenderedSurface.DrawingSurface, -(VecI)topLeft, paint);
-        
+        if (frameTime.Frame != renderedSurfaceFrame)
+        {
+            GetLayerImageAtFrame(frameTime.Frame).DrawMostUpToDateRegionOn(
+                new RectI(0, 0, layerImage.LatestSize.X, layerImage.LatestSize.Y),
+                ChunkResolution.Full,
+                workingSurface, -(VecI)topLeft, paint);   
+        }
+        else
+        {
+            workingSurface.Canvas.DrawSurface(fullResrenderedSurface.DrawingSurface, -(VecI)topLeft, paint);
+        }
+
         workingSurface.Canvas.RestoreToCount(saved);
     }
 

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/LayerNode.cs

@@ -130,7 +130,7 @@ public abstract class LayerNode : StructureNode, IReadOnlyLayerNode, IClipSource
 
     private void DrawLayerOnto(SceneObjectRenderContext ctx, DrawingSurface workingSurface, bool useFilters)
     {
-        blendPaint.Color = blendPaint.Color.WithAlpha((byte)Math.Round(Opacity.Value * 255));
+        blendPaint.Color = blendPaint.Color.WithAlpha((byte)Math.Round(Opacity.Value * ctx.Opacity * 255));
 
         if (useFilters && Filters.Value != null)
         {

+ 14 - 12
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/EllipseVectorData.cs

@@ -29,15 +29,15 @@ public class EllipseVectorData : ShapeVectorData, IReadOnlyEllipseData
 
     public override void RasterizeGeometry(DrawingSurface drawingSurface, ChunkResolution resolution, Paint paint)
     {
-        Rasterize(drawingSurface, paint, false);
+        Rasterize(drawingSurface, false);
     }
 
     public override void RasterizeTransformed(DrawingSurface drawingSurface, ChunkResolution resolution, Paint paint)
     {
-        Rasterize(drawingSurface, paint, true);
+        Rasterize(drawingSurface, true);
     }
 
-    private void Rasterize(DrawingSurface drawingSurface, Paint paint, bool applyTransform)
+    private void Rasterize(DrawingSurface drawingSurface, bool applyTransform)
     {
         int saved = 0;
         if (applyTransform)
@@ -45,15 +45,17 @@ public class EllipseVectorData : ShapeVectorData, IReadOnlyEllipseData
             saved = drawingSurface.Canvas.Save();
             ApplyTransformTo(drawingSurface);
         }
-
-        paint.Color = FillColor;
-        paint.Style = PaintStyle.Fill;
-        drawingSurface.Canvas.DrawOval(Center, Radius, paint);
-
-        paint.Color = StrokeColor;
-        paint.Style = PaintStyle.Stroke;
-        paint.StrokeWidth = StrokeWidth;
-        drawingSurface.Canvas.DrawOval(Center, Radius - new VecD(StrokeWidth / 2f), paint);
+        
+        using Paint shapePaint = new Paint();
+        
+        shapePaint.Color = FillColor;
+        shapePaint.Style = PaintStyle.Fill;
+        drawingSurface.Canvas.DrawOval(Center, Radius, shapePaint);
+
+        shapePaint.Color = StrokeColor;
+        shapePaint.Style = PaintStyle.Stroke;
+        shapePaint.StrokeWidth = StrokeWidth;
+        drawingSurface.Canvas.DrawOval(Center, Radius - new VecD(StrokeWidth / 2f), shapePaint);
 
         if (applyTransform)
         {

+ 2 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/StructureNode.cs

@@ -103,7 +103,8 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IRenderInput
         renderTarget.Canvas.ClipRect(new RectD(ScenePosition - (SceneSize / 2f), SceneSize));
 
         SceneObjectRenderContext renderObjectContext = new SceneObjectRenderContext(renderTarget, localBounds,
-            context.FrameTime, context.ChunkResolution, context.DocumentSize, renderTarget == context.RenderSurface);
+            context.FrameTime, context.ChunkResolution, context.DocumentSize, renderTarget == context.RenderSurface,
+            context.Opacity);
 
         Render(renderObjectContext);
 

+ 7 - 3
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/VectorLayerNode.cs

@@ -6,6 +6,7 @@ using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using PixiEditor.ChangeableDocument.ChangeInfos.Vectors;
 using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core;
+using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Surfaces;
 using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
@@ -38,7 +39,7 @@ public class VectorLayerNode : LayerNode, ITransformableObject, IReadOnlyVectorN
 
     public override VecD ScenePosition => ShapeData?.TransformedAABB.Center ?? VecD.Zero;
     public override VecD SceneSize => ShapeData?.TransformedAABB.Size ?? VecD.Zero;
-
+    
     protected override VecI GetTargetSize(RenderContext ctx)
     {
         return ctx.DocumentSize;
@@ -51,7 +52,7 @@ public class VectorLayerNode : LayerNode, ITransformableObject, IReadOnlyVectorN
         {
             return;
         }
-
+        
         Rasterize(workingSurface, ctx.ChunkResolution, paint);
     }
 
@@ -61,7 +62,7 @@ public class VectorLayerNode : LayerNode, ITransformableObject, IReadOnlyVectorN
         {
             return;
         }
-
+        
         Rasterize(workingSurface, ctx.ChunkResolution, paint);
     }
 
@@ -141,7 +142,10 @@ public class VectorLayerNode : LayerNode, ITransformableObject, IReadOnlyVectorN
 
     public void Rasterize(DrawingSurface surface, ChunkResolution resolution, Paint paint)
     {
+        int layer = surface.Canvas.SaveLayer(paint);
         ShapeData?.RasterizeTransformed(surface, resolution, paint);
+        
+        surface.Canvas.RestoreToCount(layer);
     }
 
     public override Node CreateCopy()

+ 1 - 1
src/PixiEditor.ChangeableDocument/Rendering/DocumentRenderer.cs

@@ -25,7 +25,7 @@ public class DocumentRenderer : IPreviewRenderable
     }
 
     private IReadOnlyDocument Document { get; }
-    public Texture OnionSkinTexture { get; set; }
+    //public Texture OnionSkinTexture { get; set; }
 
     /*public Texture RenderDocument(KeyFrameTime frameTime, ChunkResolution resolution, Texture toDrawOn = null,
         Paint paint = null)

+ 6 - 2
src/PixiEditor.ChangeableDocument/Rendering/RenderContext.cs

@@ -18,6 +18,8 @@ public class RenderContext : IDisposable
     public Paint BlendModeOpacityPaint = new() { BlendMode = DrawingApiBlendMode.SrcOver };
     public Paint ReplacingPaintWithOpacity = new() { BlendMode = DrawingApiBlendMode.Src };
 
+    public double Opacity { get; set; }
+
     public KeyFrameTime FrameTime { get; }
     public ChunkResolution ChunkResolution { get; }
     public VecI DocumentSize { get; set; }
@@ -25,13 +27,15 @@ public class RenderContext : IDisposable
     public DrawingSurface RenderSurface { get; set; }
 
     public bool IsDisposed { get; private set; }
-    
-    public RenderContext(DrawingSurface renderSurface, KeyFrameTime frameTime, ChunkResolution chunkResolution, VecI docSize)
+
+    public RenderContext(DrawingSurface renderSurface, KeyFrameTime frameTime, ChunkResolution chunkResolution,
+        VecI docSize, double opacity = 1) 
     {
         RenderSurface = renderSurface;
         FrameTime = frameTime;
         ChunkResolution = chunkResolution;
         DocumentSize = docSize;
+        Opacity = opacity;
     }
 
     public static DrawingApiBlendMode GetDrawingBlendMode(BlendMode blendMode)

+ 0 - 26
src/PixiEditor.ChangeableDocument/Rendering/SceneRenderer.cs

@@ -1,26 +0,0 @@
-using PixiEditor.ChangeableDocument.Changeables.Animations;
-using PixiEditor.ChangeableDocument.Changeables.Interfaces;
-using PixiEditor.DrawingApi.Core.Surfaces;
-using PixiEditor.Numerics;
-
-namespace PixiEditor.ChangeableDocument.Rendering;
-
-public class SceneRenderer
-{
-    public IReadOnlyDocument Document { get; }
-    public ChunkResolution Resolution { get; set; }
-
-    private readonly Func<KeyFrameTime> getActiveFrameTime;
-    
-    public SceneRenderer(IReadOnlyDocument document, Func<KeyFrameTime> getActiveFrameTime)
-    {
-        Document = document;
-        this.getActiveFrameTime = getActiveFrameTime;
-    }
-
-    public void RenderScene(DrawingSurface target)
-    {
-        using RenderContext ctx = new(target, getActiveFrameTime(), Resolution, Document.Size); 
-        Document.NodeGraph.Execute(ctx);
-    }
-}

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

@@ -44,5 +44,7 @@ namespace PixiEditor.DrawingApi.Core.Bridge.Operations
         public void DrawImage(IntPtr objectPointer, Image image, float x, float y, Paint paint);
         public void DrawRoundRect(IntPtr objectPointer, float x, float y, float width, float height, float radiusX, float radiusY, Paint paint);
         public Matrix3X3 GetTotalMatrix(IntPtr objectPointer);
+        public int SaveLayer(IntPtr objectPtr);
+        public int SaveLayer(IntPtr objectPtr, Paint paint);
     }
 }

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

@@ -217,6 +217,16 @@ namespace PixiEditor.DrawingApi.Core.Surfaces
             DrawingBackendApi.Current.CanvasImplementation.DrawPaint(ObjectPointer, paint);
             Changed?.Invoke(null);
         }
+        
+        public int SaveLayer()
+        {
+            return DrawingBackendApi.Current.CanvasImplementation.SaveLayer(ObjectPointer);
+        }
+
+        public int SaveLayer(Paint paint)
+        {
+            return DrawingBackendApi.Current.CanvasImplementation.SaveLayer(ObjectPointer, paint);
+        }
 
         public override void Dispose()
         {

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

@@ -87,6 +87,16 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
             return ManagedInstances[objectPointer].TotalMatrix.ToMatrix3X3();
         }
 
+        public int SaveLayer(IntPtr objectPointer)
+        {
+            return ManagedInstances[objectPointer].SaveLayer();
+        }
+
+        public int SaveLayer(IntPtr objectPtr, Paint paint)
+        {
+            return ManagedInstances[objectPtr].SaveLayer(_paintImpl.ManagedInstances[paint.ObjectPointer]);
+        }
+
         public Matrix3X3 GetActiveMatrix(IntPtr objectPointer)
         {
             return ManagedInstances[objectPointer].TotalMatrix.ToMatrix3X3();

+ 4 - 6
src/PixiEditor/Models/Rendering/CanvasUpdater.cs

@@ -158,8 +158,6 @@ internal class CanvasUpdater
             FindGlobalChunksToRerender(chunkGatherer, rerenderDelayed);
         
         ChunkResolution onionSkinResolution = chunksToRerender.Min(x => x.Key);
-        // TODO: Don't forget to implement this
-        //UpdateOnionSkinning(doc.Surfaces[onionSkinResolution]);
 
         bool updatingStoredChunks = false;
         foreach (var (res, stored) in affectedAndNonRerenderedChunks)
@@ -211,6 +209,7 @@ internal class CanvasUpdater
         }
     }
 
+    /*
     private void UpdateOnionSkinning(Texture lastRendered)
     {
         if (doc.AnimationHandler.OnionSkinningEnabledBindable)
@@ -281,10 +280,9 @@ internal class CanvasUpdater
 
         KeyFrameTime newFrameTime = new(frameIndex, newNormalizedTime);
 
-        //TODO: fix this
-        //using Texture rendered = doc.Renderer.RenderDocument(newFrameTime, ChunkResolution.Full);
-        //UpdateLastRenderedFrame(rendered, frameIndex);
-    }
+        using Texture rendered = doc.Renderer.RenderDocument(newFrameTime, ChunkResolution.Full);
+        UpdateLastRenderedFrame(rendered, frameIndex);
+    }*/
 
     private void UpdateLastRenderedFrame(Texture rendered, int index)
     {

+ 70 - 0
src/PixiEditor/Models/Rendering/SceneRenderer.cs

@@ -0,0 +1,70 @@
+using ChunkyImageLib.DataHolders;
+using PixiEditor.ChangeableDocument.Changeables.Animations;
+using PixiEditor.ChangeableDocument.Changeables.Interfaces;
+using PixiEditor.ChangeableDocument.Rendering;
+using PixiEditor.DrawingApi.Core.Surfaces;
+using PixiEditor.Models.Handlers;
+
+namespace PixiEditor.Models.Rendering;
+
+internal class SceneRenderer
+{
+    public IReadOnlyDocument Document { get; }
+    public IDocument DocumentViewModel { get; }
+    public ChunkResolution Resolution { get; set; }
+
+    public SceneRenderer(IReadOnlyDocument trackerDocument, IDocument documentViewModel)
+    {
+        Document = trackerDocument;
+        DocumentViewModel = documentViewModel;
+        Resolution = ChunkResolution.Full;
+    }
+
+    public void RenderScene(DrawingSurface target)
+    {
+        RenderOnionSkin(target);
+        using RenderContext ctx = new(target, DocumentViewModel.AnimationHandler.ActiveFrameTime, Resolution, Document.Size); 
+        Document.NodeGraph.Execute(ctx);
+    }
+
+    private void RenderOnionSkin(DrawingSurface target)
+    {
+        var animationData = Document.AnimationData;
+        if (!DocumentViewModel.AnimationHandler.OnionSkinningEnabledBindable)
+        {
+            return;
+        }
+        
+        double onionOpacity = animationData.OnionOpacity / 100.0;
+        double alphaFalloffMultiplier = 1.0 / animationData.OnionFrames;
+        
+        // Render previous frames'
+        for (int i = 1; i <= animationData.OnionFrames; i++)
+        {
+            int frame = DocumentViewModel.AnimationHandler.ActiveFrameTime.Frame - i;
+            if (frame < DocumentViewModel.AnimationHandler.FirstFrame)
+            {
+                break;
+            }
+
+            double finalOpacity = onionOpacity * alphaFalloffMultiplier * (animationData.OnionFrames - i + 1);
+            
+            using RenderContext onionContext = new(target, frame, Resolution, Document.Size, finalOpacity);
+            Document.NodeGraph.Execute(onionContext);
+        }
+        
+        // Render next frames
+        for (int i = 1; i <= animationData.OnionFrames; i++) 
+        {
+            int frame = DocumentViewModel.AnimationHandler.ActiveFrameTime.Frame + i;
+            if (frame > DocumentViewModel.AnimationHandler.LastFrame)
+            {
+                break;
+            }
+            
+            double finalOpacity = onionOpacity * alphaFalloffMultiplier * (animationData.OnionFrames - i + 1);
+            using RenderContext onionContext = new(target, frame, Resolution, Document.Size, finalOpacity);
+            Document.NodeGraph.Execute(onionContext);
+        }
+    }
+}

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

@@ -259,7 +259,7 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
         ReferenceLayerViewModel = new(this, Internals);
 
         Renderer = new DocumentRenderer(Internals.Tracker.Document);
-        SceneRenderer = new SceneRenderer(Internals.Tracker.Document, () => AnimationDataViewModel.ActiveFrameTime);
+        SceneRenderer = new SceneRenderer(Internals.Tracker.Document, this);
     }
 
     /// <summary>

+ 2 - 26
src/PixiEditor/Views/Rendering/Scene.cs

@@ -15,12 +15,14 @@ using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core;
 using PixiEditor.DrawingApi.Core.Bridge;
 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;
 using PixiEditor.Helpers;
 using PixiEditor.Helpers.Converters;
 using PixiEditor.Models.DocumentModels;
+using PixiEditor.Models.Rendering;
 using PixiEditor.Numerics;
 using PixiEditor.ViewModels.Document;
 using PixiEditor.Views.Overlays;
@@ -534,11 +536,6 @@ internal class DrawSceneOperation : SkiaDrawOperation
 
         using var ctx = DrawingBackendApi.Current.RenderOnDifferentGrContext(lease.GrContext);
 
-        using SKPaint paint = new SKPaint();
-        paint.Color = paint.Color.WithAlpha((byte)(opacity * 255));
-
-        RenderOnionSkin(canvas, paint);
-
         DrawingSurface surface = DrawingSurface.FromNative(lease.SkSurface);
 
         surface.Canvas.ClipRect(ClippingBounds);
@@ -549,27 +546,6 @@ internal class DrawSceneOperation : SkiaDrawOperation
         DrawingSurface.Unmanage(surface);
     }
 
-    private void RenderOnionSkin(SKCanvas canvas, SKPaint paint)
-    {
-        if (Document.AnimationDataViewModel.OnionSkinningEnabledBindable)
-        {
-            var onionSkinTexture = Document.Renderer.OnionSkinTexture;
-
-            if (onionSkinTexture == null)
-            {
-                return;
-            }
-
-            int count = canvas.Save();
-
-            canvas.Scale(1f / (float)ResolutionScale, 1f / (float)ResolutionScale);
-
-            canvas.DrawSurface(onionSkinTexture.DrawingSurface.Native as SKSurface, 0, 0, paint);
-
-            canvas.RestoreToCount(count);
-        }
-    }
-
     public override bool Equals(ICustomDrawOperation? other)
     {
         return false;

+ 0 - 2
src/PixiEditor/Views/Visuals/PreviewPainterControl.cs

@@ -69,13 +69,11 @@ public class PreviewPainterControl : Control
 internal class DrawPreviewOperation : SkiaDrawOperation
 {
     public PreviewPainter PreviewPainter { get; }
-    private RectD bounds;
     private int frame;
 
     public DrawPreviewOperation(Rect dirtyBounds, PreviewPainter previewPainter, int frameToRender) : base(dirtyBounds)
     {
         PreviewPainter = previewPainter;
-        bounds = new RectD(dirtyBounds.X, dirtyBounds.Y, dirtyBounds.Width, dirtyBounds.Height);
         frame = frameToRender;
     }