Parcourir la source

Add posterization node

Jakub Ciemała il y a 3 semaines
Parent
commit
62dff9a3b4

+ 100 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Effects/PosterizationNode.cs

@@ -0,0 +1,100 @@
+using Drawie.Backend.Core;
+using Drawie.Backend.Core.Shaders;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Numerics;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+using PixiEditor.ChangeableDocument.Rendering;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Effects;
+
+[NodeInfo("Posterization")]
+public class PosterizationNode : RenderNode, IRenderInput
+{
+    public RenderInputProperty Background { get; }
+    public InputProperty<int> Levels { get; }
+    
+    private Paint paint;
+    private Shader shader;
+    
+    private Shader? lastImageShader;
+    private VecI lastDocumentSize;
+    
+    private string shaderCode = """
+                                 uniform shader iImage;
+                                 uniform float iLevels;
+
+                                 half4 main(float2 uv)
+                                 {
+                                    half4 color = iImage.eval(uv);
+                                    half3 posterized = floor(color.rgb * iLevels) / iLevels;
+                                    return half4(posterized, color.a);
+                                 }
+                                 """;
+
+    protected override bool ExecuteOnlyOnCacheChange => true;
+
+    public PosterizationNode()
+    {
+        Background = CreateRenderInput("Background", "BACKGROUND");
+        Levels = CreateInput("Levels", "LEVELS", 8)
+            .WithRules(v => v.Min(2).Max(256));
+
+        paint = new Paint();
+        Output.FirstInChain = null;
+    }
+
+    protected override void OnExecute(RenderContext context)
+    {
+        base.OnExecute(context);
+        lastDocumentSize = context.RenderOutputSize;
+        
+        Uniforms uniforms = new Uniforms();
+        uniforms.Add("iImage", new Uniform("iImage", lastImageShader));
+        uniforms.Add("iLevels", new Uniform("iLevels", (float)Levels.Value));
+        shader?.Dispose();
+        shader = Shader.Create(shaderCode, uniforms, out _);
+    }
+    
+    protected override void OnPaint(RenderContext context, DrawingSurface surface)
+    {
+        if (Background.Value == null)
+        {
+            return;
+        }
+        using Texture temp = Texture.ForProcessing(surface, context.ProcessingColorSpace);
+        Background.Value.Paint(context, temp.DrawingSurface);
+        var snapshot = temp.DrawingSurface.Snapshot();
+        
+        lastImageShader?.Dispose();
+        lastImageShader = snapshot.ToShader();
+
+        Uniforms uniforms = new Uniforms();
+        uniforms.Add("iImage", new Uniform("iImage", lastImageShader));
+        uniforms.Add("iLevels", new Uniform("iLevels", (float)Levels.Value));
+        shader = shader.WithUpdatedUniforms(uniforms);
+        paint.Shader = shader;
+        snapshot.Dispose();
+        
+        var saved = surface.Canvas.Save();
+        surface.Canvas.Scale((float)context.ChunkResolution.InvertedMultiplier());
+        surface.Canvas.DrawRect(0, 0, context.RenderOutputSize.X, context.RenderOutputSize.Y, paint);
+        surface.Canvas.RestoreToCount(saved);
+    }
+    
+    public override RectD? GetPreviewBounds(int frame, string elementToRenderName = "")
+    {
+        return new RectD(0, 0, lastDocumentSize.X, lastDocumentSize.Y);
+    }
+
+    public override bool RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
+    {
+        OnPaint(context, renderOn);
+        return true;
+    }
+
+    public override Node CreateCopy()
+    {
+        return new PosterizationNode();
+    }
+}

+ 11 - 0
src/PixiEditor/ViewModels/Document/Nodes/Effects/PosterizationNodeViewModel.cs

@@ -0,0 +1,11 @@
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Effects;
+using PixiEditor.ViewModels.Nodes;
+
+namespace PixiEditor.ViewModels.Document.Nodes.Effects;
+
+[NodeViewModel("POSTERIZATION_NODE", "EFFECTS", PixiPerfectIcons.Outline)]
+internal class PosterizationNodeViewModel : NodeViewModel<PosterizationNode>
+{
+    
+}
+