فهرست منبع

Rendering with custom textures work

flabbet 10 ماه پیش
والد
کامیت
e8ef03ea8d

+ 9 - 9
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CreateImageNode.cs

@@ -1,9 +1,8 @@
-using PixiEditor.ChangeableDocument.Changeables.Animations;
-using PixiEditor.ChangeableDocument.Rendering;
+using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core;
+using PixiEditor.DrawingApi.Core.Bridge;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.DrawingApi.Core.Surfaces;
-using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
 using PixiEditor.Numerics;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
@@ -18,7 +17,7 @@ public class CreateImageNode : Node
     public InputProperty<Color> Fill { get; }
 
     public RenderInputProperty Content { get; }
-    
+
     public RenderOutputProperty RenderOutput { get; }
 
     public CreateImageNode()
@@ -36,24 +35,25 @@ public class CreateImageNode : Node
         {
             return;
         }
-        
+
         var surface = RequestTexture(0, Size.Value, false);
 
         surface.DrawingSurface.Canvas.Clear(Fill.Value);
-        
+
         int saved = surface.DrawingSurface.Canvas.Save();
 
         RenderContext ctx = new RenderContext(surface.DrawingSurface, context.FrameTime, context.ChunkResolution,
             context.DocumentSize);
-        
+
         Content.Value?.Paint(ctx, surface.DrawingSurface);
 
         surface.DrawingSurface.Canvas.RestoreToCount(saved);
         Output.Value = surface;
-        
+
+
         RenderOutput.ChainToPainterValue();
     }
-    
+
     private void OnPaint(RenderContext context, DrawingSurface surface)
     {
         surface.Canvas.DrawSurface(Output.Value.DrawingSurface, 0, 0);

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

@@ -202,8 +202,6 @@ public abstract class StructureNode : RenderNode, IReadOnlyStructureNode, IRende
         }
         
         renderSurface.DrawingSurface.Canvas.RestoreToCount(saved);
-        
-        renderSurface?.DrawingSurface.Flush(true, true);
     }
 
     protected void ApplyRasterClip(DrawingSurface toClip, DrawingSurface clipSource)

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

@@ -52,7 +52,8 @@ public class DocumentRenderer : IPreviewRenderable
         return (RectI?)rect.Scale(multiplier).Translate(-pixelChunkPos).RoundOutwards();
     }
 
-    public void RenderLayers(DrawingSurface toDrawOn, HashSet<Guid> layersToCombine, int frame, ChunkResolution resolution)
+    public void RenderLayers(DrawingSurface toDrawOn, HashSet<Guid> layersToCombine, int frame,
+        ChunkResolution resolution)
     {
         RenderContext context = new(toDrawOn, frame, resolution, Document.Size);
         context.FullRerender = true;

+ 2 - 0
src/PixiEditor.DrawingApi.Core/Bridge/NativeObjectsImpl/IShaderImplementation.cs

@@ -2,6 +2,7 @@
 using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Shaders;
+using PixiEditor.DrawingApi.Core.Surfaces;
 using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
 using PixiEditor.Numerics;
 
@@ -19,4 +20,5 @@ public interface IShaderImplementation
     public object GetNativeShader(IntPtr objectPointer);
     public Shader WithUpdatedUniforms(IntPtr objectPointer, Uniforms uniforms);
     public void SetLocalMatrix(IntPtr objectPointer, Matrix3X3 matrix);
+    public Shader CreateBitmap(Bitmap bitmap, ShaderTileMode tileModeX, ShaderTileMode tileModeY, Matrix3X3 matrix);
 }

+ 5 - 0
src/PixiEditor.DrawingApi.Core/Shaders/Shader.cs

@@ -63,4 +63,9 @@ public class Shader : NativeObject
     {
         DrawingBackendApi.Current.ShaderImplementation.SetLocalMatrix(ObjectPointer, matrix);
     }
+
+    public static Shader CreateBitmap(Bitmap bitmap, ShaderTileMode tileModeX, ShaderTileMode tileModeY, Matrix3X3 matrix)
+    {
+        return DrawingBackendApi.Current.ShaderImplementation.CreateBitmap(bitmap, tileModeX, tileModeY, matrix);
+    }
 }

+ 13 - 0
src/PixiEditor.DrawingApi.Core/Shaders/ShaderTileMode.cs

@@ -0,0 +1,13 @@
+namespace PixiEditor.DrawingApi.Core.Shaders;
+
+public enum ShaderTileMode
+{
+    Clamp,
+
+    /// <summary>Repeat the shader's image horizontally and vertically.</summary>
+    Repeat,
+
+    /// <summary>Repeat the shader's image horizontally and vertically, alternating mirror images so that adjacent images always seam.</summary>
+    Mirror,
+    Decal,
+}

+ 11 - 1
src/PixiEditor.DrawingApi.Skia/Implementations/SkiaShaderImplementation.cs

@@ -4,6 +4,7 @@ using PixiEditor.DrawingApi.Core.Bridge.NativeObjectsImpl;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Shaders;
+using PixiEditor.DrawingApi.Core.Surfaces;
 using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
 using PixiEditor.Numerics;
 using SkiaSharp;
@@ -13,8 +14,10 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
     public class SkiaShaderImplementation : SkObjectImplementation<SKShader>, IShaderImplementation
     {
         private Dictionary<IntPtr, SKRuntimeEffect> runtimeEffects = new();
-        public SkiaShaderImplementation()
+        private SkiaBitmapImplementation _bitmapImplementation;
+        public SkiaShaderImplementation(SkiaBitmapImplementation bitmapImplementation)
         {
+            _bitmapImplementation = bitmapImplementation;
         }
 
         public IntPtr CreateShader()
@@ -133,6 +136,13 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
             shader.WithLocalMatrix(matrix.ToSkMatrix());
         }
 
+        public Shader CreateBitmap(Bitmap bitmap, ShaderTileMode tileModeX, ShaderTileMode tileModeY, Matrix3X3 matrix)
+        {
+            SKShader shader = SKShader.CreateBitmap(_bitmapImplementation[bitmap.ObjectPointer], (SKShaderTileMode)(int)tileModeX, (SKShaderTileMode)(int)tileModeY, matrix.ToSkMatrix()); 
+            ManagedInstances[shader.Handle] = shader;
+            return new Shader(shader.Handle);
+        }
+
         public void Dispose(IntPtr shaderObjPointer)
         {
             if (!ManagedInstances.TryGetValue(shaderObjPointer, out var shader)) return;

+ 25 - 22
src/PixiEditor.DrawingApi.Skia/SkiaDrawingBackend.cs

@@ -22,13 +22,13 @@ namespace PixiEditor.DrawingApi.Skia
                 {
                     throw new GrContextAlreadyInitializedException();
                 }
-                
+
                 _grContext = value;
             }
         }
-        
+
         public bool IsHardwareAccelerated => GraphicsContext != null;
-        
+
         public IRenderingServer RenderingDispatcher { get; set; }
 
         public IColorImplementation ColorImplementation { get; }
@@ -52,40 +52,43 @@ namespace PixiEditor.DrawingApi.Skia
         public SkiaDrawingBackend()
         {
             ColorImplementation = new SkiaColorImplementation();
-            
+
             SkiaImgDataImplementation dataImpl = new SkiaImgDataImplementation();
             ImgDataImplementation = dataImpl;
-            
+
             SkiaColorFilterImplementation colorFilterImpl = new SkiaColorFilterImplementation();
             ColorFilterImplementation = colorFilterImpl;
 
             SkiaImageFilterImplementation imageFilterImpl = new SkiaImageFilterImplementation();
             ImageFilterImplementation = imageFilterImpl;
-            
-            SkiaShaderImplementation shader = new SkiaShaderImplementation();
-            ShaderImplementation = shader;
-            
-            SkiaPaintImplementation paintImpl = new SkiaPaintImplementation(colorFilterImpl, imageFilterImpl, shader);
-            PaintImplementation = paintImpl;
-            
-            SkiaPathImplementation pathImpl = new SkiaPathImplementation();
-            PathImplementation = pathImpl;
-            
-            MatrixImplementation = new SkiaMatrixImplementation();
-            
+
             SkiaColorSpaceImplementation colorSpaceImpl = new SkiaColorSpaceImplementation();
             ColorSpaceImplementation = colorSpaceImpl;
 
             SkiaPixmapImplementation pixmapImpl = new SkiaPixmapImplementation(colorSpaceImpl);
             PixmapImplementation = pixmapImpl;
-            
+
+            SkiaShaderImplementation shader = null;
+
             SkiaImageImplementation imgImpl = new SkiaImageImplementation(dataImpl, pixmapImpl, shader);
             ImageImplementation = imgImpl;
             SkiaBitmapImplementation bitmapImpl = new SkiaBitmapImplementation(imgImpl, pixmapImpl);
             BitmapImplementation = bitmapImpl;
-            
-            SkiaCanvasImplementation canvasImpl = new SkiaCanvasImplementation(paintImpl, imgImpl, bitmapImpl, pathImpl);
-            
+
+            shader = new SkiaShaderImplementation(bitmapImpl);
+            ShaderImplementation = shader;
+
+            SkiaPaintImplementation paintImpl = new SkiaPaintImplementation(colorFilterImpl, imageFilterImpl, shader);
+            PaintImplementation = paintImpl;
+
+            SkiaPathImplementation pathImpl = new SkiaPathImplementation();
+            PathImplementation = pathImpl;
+
+            MatrixImplementation = new SkiaMatrixImplementation();
+
+            SkiaCanvasImplementation canvasImpl =
+                new SkiaCanvasImplementation(paintImpl, imgImpl, bitmapImpl, pathImpl);
+
             SurfaceImplementation = new SkiaSurfaceImplementation(GraphicsContext, pixmapImpl, canvasImpl, paintImpl);
 
             canvasImpl.SetSurfaceImplementation(SurfaceImplementation);
@@ -93,7 +96,7 @@ namespace PixiEditor.DrawingApi.Skia
 
             CanvasImplementation = canvasImpl;
         }
-        
+
         public void Setup()
         {
             SurfaceImplementation.GrContext = GraphicsContext;

+ 1 - 99
src/PixiEditor/Models/Rendering/CanvasUpdater.cs

@@ -1,6 +1,7 @@
 using ChunkyImageLib.DataHolders;
 using ChunkyImageLib.Operations;
 using PixiEditor.ChangeableDocument.Changeables.Animations;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.DrawingApi.Core;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.DrawingApi.Core.Surfaces;
@@ -209,105 +210,6 @@ internal class CanvasUpdater
         }
     }
 
-    /*
-    private void UpdateOnionSkinning(Texture lastRendered)
-    {
-        if (doc.AnimationHandler.OnionSkinningEnabledBindable)
-        {
-            if (lastRenderedFrameNumber > 0)
-            {
-                UpdateLastRenderedFrame(lastRendered, lastRenderedFrameNumber);
-            }
-
-            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();
-                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)
-                {
-                    doc.Renderer.OnionSkinTexture?.Dispose();
-                    doc.Renderer.OnionSkinTexture = new Texture(doc.SizeBindable);
-                }
-
-                doc.Renderer.OnionSkinTexture.DrawingSurface.Canvas.Clear();
-
-                float alphaFalloffMultiplier = 1f / framesToRender;
-
-                for (int i = 1; i <= framesToRender; i++)
-                {
-                    int previousFrameIndex = doc.AnimationHandler.ActiveFrameBindable - i;
-                    int nextFrameIndex = doc.AnimationHandler.ActiveFrameBindable + i;
-
-                    if (!renderedFramesCache.ContainsKey(previousFrameIndex))
-                    {
-                        RenderNextOnionSkinningFrame(previousFrameIndex);
-                    }
-
-                    if (!renderedFramesCache.ContainsKey(nextFrameIndex))
-                    {
-                        RenderNextOnionSkinningFrame(nextFrameIndex);
-                    }
-
-                    DrawOnionSkinningFrame(previousFrameIndex, doc.Renderer.OnionSkinTexture, onionPaint);
-                    DrawOnionSkinningFrame(nextFrameIndex, doc.Renderer.OnionSkinTexture, onionPaint);
-
-                    onionPaint.Color = onionPaint.Color.WithAlpha((byte)(opacity -
-                                                                         (opacity *
-                                                                          alphaFalloffMultiplier * i)));
-                }
-            }
-
-            lastRenderedFrameNumber = doc.AnimationHandler.ActiveFrameBindable;
-            lastOnionKeyFrames = doc.AnimationHandler.OnionFramesBindable;
-            lastOnionOpacity = doc.AnimationHandler.OnionOpacityBindable;
-        }
-    }
-
-    private void RenderNextOnionSkinningFrame(int frameIndex)
-    {
-        int firstFrame = doc.AnimationHandler.FirstFrame;
-        int lastFrame = doc.AnimationHandler.LastFrame;
-        if (frameIndex < firstFrame || frameIndex >= lastFrame)
-            return;
-
-        double newNormalizedTime = (double)(frameIndex - firstFrame) / (lastFrame - firstFrame);
-
-        KeyFrameTime newFrameTime = new(frameIndex, newNormalizedTime);
-
-        using Texture rendered = doc.Renderer.RenderDocument(newFrameTime, ChunkResolution.Full);
-        UpdateLastRenderedFrame(rendered, frameIndex);
-    }*/
-
-    private void UpdateLastRenderedFrame(Texture rendered, int index)
-    {
-        if (renderedFramesCache.ContainsKey(index))
-        {
-            renderedFramesCache[index].Dispose();
-            renderedFramesCache[index] = new Texture(rendered);
-        }
-        else
-        {
-            renderedFramesCache.Add(index, new Texture(rendered));
-        }
-    }
-
-    private void DrawOnionSkinningFrame(int frameIndex, Texture onionSkinTexture, Paint paint)
-    {
-        if (frameIndex < 1 || frameIndex >= doc.AnimationHandler.LastFrame)
-            return;
-
-        if (renderedFramesCache.TryGetValue(frameIndex, out var frame))
-        {
-            onionSkinTexture.DrawingSurface.Canvas.DrawSurface(frame.DrawingSurface, 0, 0, paint);
-        }
-    }
-
     private void RenderChunk(VecI chunkPos, ChunkResolution resolution)
     {
         doc.Renderer.UpdateChunk(chunkPos, resolution, doc.AnimationHandler.ActiveFrameTime);

+ 2 - 2
src/PixiEditor/Models/Rendering/SceneRenderer.cs

@@ -23,8 +23,8 @@ internal class SceneRenderer
     public void RenderScene(DrawingSurface target)
     {
         RenderOnionSkin(target);
-        RenderContext ctx = new(target, DocumentViewModel.AnimationHandler.ActiveFrameTime, Resolution, Document.Size); 
-        Document.NodeGraph.Execute(ctx);
+        //RenderContext ctx = new(target, DocumentViewModel.AnimationHandler.ActiveFrameTime, Resolution, Document.Size); 
+        //Document.NodeGraph.Execute(ctx);
     }
 
     private void RenderOnionSkin(DrawingSurface target)

+ 75 - 13
src/PixiEditor/Views/Rendering/Scene.cs

@@ -14,6 +14,8 @@ using ChunkyImageLib.Operations;
 using PixiEditor.ChangeableDocument.Rendering;
 using PixiEditor.DrawingApi.Core;
 using PixiEditor.DrawingApi.Core.Bridge;
+using PixiEditor.DrawingApi.Core.Numerics;
+using PixiEditor.DrawingApi.Core.Shaders;
 using PixiEditor.DrawingApi.Core.Surfaces;
 using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
 using PixiEditor.DrawingApi.Skia;
@@ -112,6 +114,9 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
 
     private double sceneOpacity = 1;
 
+    private Texture renderTexture;
+    private Paint checkerPaint;
+
     static Scene()
     {
         AffectsRender<Scene>(BoundsProperty, WidthProperty, HeightProperty, ScaleProperty, AngleRadiansProperty,
@@ -157,39 +162,93 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
     {
         if (Document == null || SceneRenderer == null) return;
 
+        int width = (int)Math.Ceiling(Bounds.Width);
+        int height = (int)Math.Ceiling(Bounds.Height);
+        if (renderTexture == null || renderTexture.Size.X != width || renderTexture.Size.Y != height)
+        {
+            renderTexture?.Dispose();
+            renderTexture = new Texture(new VecI(width, height));
+        }
+
         float angle = (float)MathUtil.RadiansToDegrees(AngleRadians);
 
         float resolutionScale = CalculateResolutionScale();
 
         RectD dirtyBounds = new RectD(0, 0, Document.Width, Document.Height);
-        Rect dirtyRect = new Rect(0, 0, Document.Width, Document.Height);
 
         SceneRenderer.Resolution = CalculateResolution();
 
         using var operation = new DrawSceneOperation(SceneRenderer.RenderScene, Document, CanvasPos,
             Scale * resolutionScale,
-            resolutionScale, angle,
+            resolutionScale,
+            sceneOpacity,
+            angle,
             FlipX, FlipY,
-            dirtyRect,
             Bounds,
-            sceneOpacity);
+            Bounds,
+            renderTexture);
 
         var matrix = CalculateTransformMatrix();
-        context.PushTransform(matrix);
         context.PushRenderOptions(new RenderOptions { BitmapInterpolationMode = BitmapInterpolationMode.None });
-
-        DrawCheckerboard(context, dirtyRect,
-            new RectI(0, 0, operation.Document.SizeBindable.X, operation.Document.SizeBindable.Y));
+        var pushedMatrix = context.PushTransform(matrix);
 
         Cursor = DefaultCursor;
 
         DrawOverlays(context, dirtyBounds, OverlayRenderSorting.Background);
 
+        pushedMatrix.Dispose();
+
+        renderTexture.DrawingSurface.Canvas.Clear();
+        renderTexture.DrawingSurface.Canvas.Save();
+
+        renderTexture.DrawingSurface.Canvas.SetMatrix(matrix.ToSKMatrix().ToMatrix3X3());
+
+        RenderScene(dirtyBounds);
+
+        renderTexture.DrawingSurface.Flush();
+
         context.Custom(operation);
 
+        renderTexture.DrawingSurface.Canvas.Restore();
+
+        context.PushTransform(matrix);
+
         DrawOverlays(context, dirtyBounds, OverlayRenderSorting.Foreground);
     }
 
+    private void RenderScene(RectD dirtyBounds)
+    {
+        DrawCheckerboard(dirtyBounds);
+        RenderGraph(renderTexture);
+    }
+
+    private void DrawCheckerboard(RectD dirtyBounds)
+    {
+        if (checkerBitmap == null) return;
+
+        RectD operationSurfaceRectToRender = new RectD(0, 0, dirtyBounds.Width, dirtyBounds.Height);
+        float checkerScale = (float)ZoomToViewportConverter.ZoomToViewport(16, Scale) * 0.25f;
+        checkerPaint?.Dispose();
+        checkerPaint = new Paint
+        {
+            Shader = Shader.CreateBitmap(
+                checkerBitmap,
+                ShaderTileMode.Repeat, ShaderTileMode.Repeat,
+                Matrix3X3.CreateScale(checkerScale, checkerScale)),
+            FilterQuality = FilterQuality.None
+        };
+        
+        renderTexture.DrawingSurface.Canvas.DrawRect(operationSurfaceRectToRender, checkerPaint);
+    }
+
+    private void RenderGraph(Texture targetTexture)
+    {
+        DrawingSurface surface = targetTexture.DrawingSurface;
+        RenderContext context = new(surface, SceneRenderer.DocumentViewModel.AnimationHandler.ActiveFrameTime,
+            SceneRenderer.Resolution, SceneRenderer.Document.Size);
+        SceneRenderer.Document.NodeGraph.Execute(context);
+    }
+
     private void DrawOverlays(DrawingContext context, RectD dirtyBounds, OverlayRenderSorting sorting)
     {
         if (AllOverlays != null)
@@ -502,16 +561,19 @@ internal class DrawSceneOperation : SkiaDrawOperation
     public bool FlipY { get; set; }
     public Rect ViewportBounds { get; }
 
-    public RectD ClippingBounds { get; }
 
     public Action<DrawingSurface> RenderScene;
 
+    private Texture renderTexture;
+
     private double opacity;
 
     public DrawSceneOperation(Action<DrawingSurface> renderAction, DocumentViewModel document, VecD contentPosition,
         double scale,
         double resolutionScale,
-        double angle, bool flipX, bool flipY, Rect dirtyBounds, Rect viewportBounds, double opacity) : base(dirtyBounds)
+        double opacity,
+        double angle, bool flipX, bool flipY, Rect dirtyBounds, Rect viewportBounds,
+        Texture renderTexture) : base(dirtyBounds)
     {
         RenderScene = renderAction;
         Document = document;
@@ -523,7 +585,7 @@ internal class DrawSceneOperation : SkiaDrawOperation
         ViewportBounds = viewportBounds;
         ResolutionScale = resolutionScale;
         this.opacity = opacity;
-        ClippingBounds = new RectD(dirtyBounds.X, dirtyBounds.Y, dirtyBounds.Width, dirtyBounds.Height);
+        this.renderTexture = renderTexture;
     }
 
     public override void Render(ISkiaSharpApiLease lease)
@@ -538,8 +600,8 @@ internal class DrawSceneOperation : SkiaDrawOperation
 
         DrawingSurface surface = DrawingSurface.FromNative(lease.SkSurface);
 
-        surface.Canvas.ClipRect(ClippingBounds);
-        
+        surface.Canvas.DrawSurface(renderTexture.DrawingSurface, 0, 0);
+
         RenderScene?.Invoke(surface);
 
         canvas.RestoreToCount(count);