Browse Source

Support transform when reusing stamps

Krzysztof Krysiński 2 days ago
parent
commit
08dcc3a20c

+ 7 - 3
src/PixiEditor.ChangeableDocument/Changeables/Brushes/BrushEngine.cs

@@ -31,6 +31,7 @@ public class BrushEngine : IDisposable
     private int lastAppliedHistoryIndex = -1;
     private VecI lastCachedTexturePaintableSize = VecI.Zero;
     private TexturePaintable? lastCachedTexturePaintable = null;
+    private Matrix3X3 lastCachedTransform = Matrix3X3.Identity;
     private readonly List<RecordedPoint> pointsHistory = new();
 
     private bool drawnOnce = false;
@@ -217,6 +218,7 @@ public class BrushEngine : IDisposable
             lastPos = point;
             drawnOnce = true;
             target?.SetBlendMode(imageBlendMode);
+            brushNode.ResetContentTexture();
         }
 
         float strokeWidth = brushData.StrokeWidth;
@@ -333,10 +335,11 @@ public class BrushEngine : IDisposable
         bool snapToPixels = brushNode.SnapToPixels.Value;
         bool canReuseStamps = brushNode.CanReuseStamps.Value;
         Blender? stampBlender = brushNode.UseCustomStampBlender.Value ? brushNode.LastStampBlender : null;
+        Matrix3X3 transform = brushNode.Transform.Value;
         //Blender? imageBlender = brushNode.UseCustomImageBlender.Value ? brushNode.LastImageBlender : null;
 
         if (PaintBrush(target, autoPosition, vectorShape, rect, fitToStrokeSize, pressure, content, contentTexture,
-                stampBlender, brushNode.StampBlendMode.Value, antiAliasing, fill, stroke, snapToPixels, canReuseStamps))
+                stampBlender, brushNode.StampBlendMode.Value, antiAliasing, fill, stroke, snapToPixels, canReuseStamps, transform))
         {
             lastPos = point;
         }
@@ -345,7 +348,7 @@ public class BrushEngine : IDisposable
     public bool PaintBrush(ChunkyImage target, bool autoPosition, ShapeVectorData vectorShape,
         RectD rect, bool fitToStrokeSize, float pressure, Painter? content,
         Texture? contentTexture, Blender? blender, DrawingApiBlendMode blendMode, bool antiAliasing, Paintable fill, Paintable stroke,
-        bool snapToPixels, bool canReuseStamps)
+        bool snapToPixels, bool canReuseStamps, Matrix3X3 transform)
     {
         var path = vectorShape.ToPath(true);
         if (path == null)
@@ -411,11 +414,12 @@ public class BrushEngine : IDisposable
                 TexturePaintable brushPaintable;
                 if (canReuseStamps)
                 {
-                    if (lastCachedTexturePaintableSize != contentTexture.Size || lastCachedTexturePaintable == null)
+                    if (lastCachedTexturePaintableSize != contentTexture.Size || lastCachedTexturePaintable == null || lastCachedTransform != transform)
                     {
                         lastCachedTexturePaintable?.Dispose();
                         lastCachedTexturePaintable = new TexturePaintable(new Texture(contentTexture), false);
                         lastCachedTexturePaintableSize = contentTexture.Size;
+                        lastCachedTransform = transform;
                     }
 
                     brushPaintable = lastCachedTexturePaintable;

+ 22 - 7
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Brushes/BrushOutputNode.cs

@@ -89,8 +89,12 @@ public class BrushOutputNode : Node
     public const string UseCustomStampBlenderProperty = "UseCustomStampBlender";
     public const string CustomStampBlenderCodeProperty = "CustomStampBlender";
     public const string StampBlendModeProperty = "StampBlendMode";
+    public const string ContentProperty = "Content";
+    public const string ContentTransformProperty = "Transform";
 
     private VectorPath? previewVectorPath;
+    private bool drawnContentTextureOnce = false;
+    private Matrix3X3 lastTranform = Matrix3X3.Identity;
 
     public BrushOutputNode()
     {
@@ -98,8 +102,8 @@ public class BrushOutputNode : Node
         VectorShape = CreateInput<ShapeVectorData>("VectorShape", "SHAPE", null);
         Stroke = CreateInput<Paintable>("Stroke", "STROKE", null);
         Fill = CreateInput<Paintable>("Fill", "FILL", null);
-        Content = CreateRenderInput("Content", "CONTENT");
-        Transform = CreateInput<Matrix3X3>("Transform", "TRANSFORM", Matrix3X3.Identity);
+        Content = CreateRenderInput(ContentProperty, "CONTENT");
+        Transform = CreateInput<Matrix3X3>(ContentTransformProperty, "CONTENT_TRANSFORM", Matrix3X3.Identity);
         ImageBlendMode = CreateInput<Drawie.Backend.Core.Surfaces.BlendMode>("BlendMode", "BLEND_MODE",
             Drawie.Backend.Core.Surfaces.BlendMode.SrcOver);
         StampBlendMode = CreateInput<Drawie.Backend.Core.Surfaces.BlendMode>(StampBlendModeProperty, "STAMP_BLEND_MODE",
@@ -146,11 +150,17 @@ public class BrushOutputNode : Node
         {
             if (context.RenderOutputSize.LongestAxis > 0)
             {
-                ContentTexture = cache.RequestTexture(0, context.RenderOutputSize, context.ProcessingColorSpace);
-                ContentTexture.DrawingSurface.Canvas.Save();
-                ContentTexture.DrawingSurface.Canvas.SetMatrix(Transform.Value);
-                Content.Value.Paint(context, ContentTexture.DrawingSurface.Canvas);
-                ContentTexture.DrawingSurface.Canvas.Restore();
+                if (!CanReuseStamps.Value || ContentTexture == null || ContentTexture.Size != context.RenderOutputSize ||
+                    !drawnContentTextureOnce || Transform.Value != lastTranform)
+                {
+                    ContentTexture = cache.RequestTexture(0, context.RenderOutputSize, context.ProcessingColorSpace);
+                    ContentTexture.DrawingSurface.Canvas.Save();
+                    ContentTexture.DrawingSurface.Canvas.SetMatrix(Transform.Value);
+                    Content.Value.Paint(context, ContentTexture.DrawingSurface.Canvas);
+                    ContentTexture.DrawingSurface.Canvas.Restore();
+                    drawnContentTextureOnce = true;
+                    lastTranform = Transform.Value;
+                }
             }
         }
 
@@ -179,6 +189,11 @@ public class BrushOutputNode : Node
         additionalData["PersistentId"] = PersistentId;
     }
 
+    public void ResetContentTexture()
+    {
+        drawnContentTextureOnce = false;
+    }
+
     internal override void DeserializeAdditionalData(IReadOnlyDocument target, IReadOnlyDictionary<string, object> data,
         List<IChangeInfo> infos)
     {

+ 2 - 1
src/PixiEditor/Data/Localization/Languages/en.json

@@ -1290,5 +1290,6 @@
   "VIEWPORT_INFO_NODE": "Viewport Info",
   "EQUALS_NODE": "Equals",
   "IS_LEFT_BUTTON_PRESSED": "Is Left Button Pressed",
-  "IS_RIGHT_BUTTON_PRESSED": "Is Right Button Pressed"
+  "IS_RIGHT_BUTTON_PRESSED": "Is Right Button Pressed",
+  "CONTENT_TRANSFORM": "Content Transform"
 }

+ 8 - 0
src/PixiEditor/ViewModels/Document/Nodes/Brushes/BrushOutputNodeViewModel.cs

@@ -15,6 +15,8 @@ internal class BrushOutputNodeViewModel : NodeViewModel<BrushOutputNode>
         InputPropertyMap[BrushOutputNode.BrushNameProperty].SocketEnabled = false;
         InputPropertyMap[BrushOutputNode.FitToStrokeSizeProperty].SocketEnabled = false;
         InputPropertyMap[BrushOutputNode.UseCustomStampBlenderProperty].ValueChanged += OnValueChanged;
+        InputPropertyMap[BrushOutputNode.ContentProperty].ConnectedOutputChanged += OnContentConnectionChanged;
+        InputPropertyMap[BrushOutputNode.ContentTransformProperty].IsVisible = InputPropertyMap[BrushOutputNode.ContentProperty].ConnectedOutput != null;
         if(InputPropertyMap[BrushOutputNode.CustomStampBlenderCodeProperty] is StringPropertyViewModel codeProperty)
         {
             codeProperty.IsVisible = (bool)InputPropertyMap[BrushOutputNode.UseCustomStampBlenderProperty].Value;
@@ -27,4 +29,10 @@ internal class BrushOutputNodeViewModel : NodeViewModel<BrushOutputNode>
         InputPropertyMap[BrushOutputNode.CustomStampBlenderCodeProperty].IsVisible = (bool)args.NewValue;
         InputPropertyMap[BrushOutputNode.StampBlendModeProperty].IsVisible = !(bool)args.NewValue;
     }
+
+    private void OnContentConnectionChanged(object? sender, EventArgs eventArgs)
+    {
+        var connection = InputPropertyMap[BrushOutputNode.ContentProperty].ConnectedOutput;
+        InputPropertyMap[BrushOutputNode.ContentTransformProperty].IsVisible = connection != null;
+    }
 }