|
@@ -11,65 +11,124 @@ using Drawie.Numerics;
|
|
namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.FilterNodes;
|
|
namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.FilterNodes;
|
|
|
|
|
|
[NodeInfo("ApplyFilter")]
|
|
[NodeInfo("ApplyFilter")]
|
|
-public class ApplyFilterNode : RenderNode, IRenderInput
|
|
|
|
|
|
+public sealed class ApplyFilterNode : RenderNode, IRenderInput
|
|
{
|
|
{
|
|
- private Paint _paint = new();
|
|
|
|
|
|
+ private readonly Paint _paint = new();
|
|
|
|
+ private readonly Paint _maskPaint = new()
|
|
|
|
+ {
|
|
|
|
+ BlendMode = BlendMode.DstIn,
|
|
|
|
+ ColorFilter = Filters.MaskFilter
|
|
|
|
+ };
|
|
|
|
+
|
|
public InputProperty<Filter?> Filter { get; }
|
|
public InputProperty<Filter?> Filter { get; }
|
|
|
|
|
|
public RenderInputProperty Background { get; }
|
|
public RenderInputProperty Background { get; }
|
|
|
|
|
|
|
|
+ public RenderInputProperty Mask { get; }
|
|
|
|
+
|
|
|
|
+ public InputProperty<bool> InvertMask { get; }
|
|
|
|
+
|
|
public ApplyFilterNode()
|
|
public ApplyFilterNode()
|
|
{
|
|
{
|
|
Background = CreateRenderInput("Input", "IMAGE");
|
|
Background = CreateRenderInput("Input", "IMAGE");
|
|
Filter = CreateInput<Filter>("Filter", "FILTER", null);
|
|
Filter = CreateInput<Filter>("Filter", "FILTER", null);
|
|
|
|
+ Mask = CreateRenderInput("Mask", "MASK");
|
|
|
|
+ InvertMask = CreateInput("InvertMask", "INVERT_MASK", false);
|
|
Output.FirstInChain = null;
|
|
Output.FirstInChain = null;
|
|
AllowHighDpiRendering = true;
|
|
AllowHighDpiRendering = true;
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
protected override void Paint(RenderContext context, DrawingSurface surface)
|
|
protected override void Paint(RenderContext context, DrawingSurface surface)
|
|
{
|
|
{
|
|
AllowHighDpiRendering = (Background.Connection.Node as RenderNode)?.AllowHighDpiRendering ?? true;
|
|
AllowHighDpiRendering = (Background.Connection.Node as RenderNode)?.AllowHighDpiRendering ?? true;
|
|
base.Paint(context, surface);
|
|
base.Paint(context, surface);
|
|
}
|
|
}
|
|
|
|
|
|
- protected override void OnPaint(RenderContext context, DrawingSurface surface)
|
|
|
|
|
|
+ protected override void OnPaint(RenderContext context, DrawingSurface outputSurface)
|
|
{
|
|
{
|
|
- if (_paint == null)
|
|
|
|
- return;
|
|
|
|
|
|
+ using var _ = DetermineTargetSurface(context, outputSurface, out var processingSurface);
|
|
|
|
+
|
|
|
|
+ DrawWithFilter(context, outputSurface, processingSurface);
|
|
|
|
+
|
|
|
|
+ // If the Mask is null, we already drew to the output surface, otherwise we still need to draw to it (and apply the mask)
|
|
|
|
+ if (processingSurface != outputSurface)
|
|
|
|
+ {
|
|
|
|
+ ApplyWithMask(context, processingSurface, outputSurface);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
|
|
|
|
+ private void DrawWithFilter(RenderContext context, DrawingSurface outputSurface, DrawingSurface processingSurface)
|
|
|
|
+ {
|
|
_paint.SetFilters(Filter.Value);
|
|
_paint.SetFilters(Filter.Value);
|
|
|
|
|
|
if (!context.ProcessingColorSpace.IsSrgb)
|
|
if (!context.ProcessingColorSpace.IsSrgb)
|
|
{
|
|
{
|
|
- using var intermediate = Texture.ForProcessing(surface, context.ProcessingColorSpace);
|
|
|
|
|
|
+ HandleNonSrgbContext(context, outputSurface, processingSurface);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var layer = processingSurface.Canvas.SaveLayer(_paint);
|
|
|
|
+ Background.Value?.Paint(context, processingSurface);
|
|
|
|
+ processingSurface.Canvas.RestoreToCount(layer);
|
|
|
|
+ }
|
|
|
|
|
|
- int saved = surface.Canvas.Save();
|
|
|
|
- surface.Canvas.SetMatrix(Matrix3X3.Identity);
|
|
|
|
|
|
+ private void HandleNonSrgbContext(RenderContext context, DrawingSurface surface, DrawingSurface targetSurface)
|
|
|
|
+ {
|
|
|
|
+ using var intermediate = Texture.ForProcessing(surface, context.ProcessingColorSpace);
|
|
|
|
|
|
- Background.Value?.Paint(context, intermediate.DrawingSurface);
|
|
|
|
|
|
+ Background.Value?.Paint(context, intermediate.DrawingSurface);
|
|
|
|
|
|
- using var srgbSurface = Texture.ForProcessing(intermediate.Size, ColorSpace.CreateSrgb());
|
|
|
|
|
|
+ using var srgbSurface = Texture.ForProcessing(intermediate.Size, ColorSpace.CreateSrgb());
|
|
|
|
|
|
- srgbSurface.DrawingSurface.Canvas.SaveLayer(_paint);
|
|
|
|
- srgbSurface.DrawingSurface.Canvas.DrawSurface(intermediate.DrawingSurface, 0, 0);
|
|
|
|
- srgbSurface.DrawingSurface.Canvas.Restore();
|
|
|
|
|
|
+ srgbSurface.DrawingSurface.Canvas.SaveLayer(_paint);
|
|
|
|
+ srgbSurface.DrawingSurface.Canvas.DrawSurface(intermediate.DrawingSurface, 0, 0);
|
|
|
|
+ srgbSurface.DrawingSurface.Canvas.Restore();
|
|
|
|
|
|
- surface.Canvas.DrawSurface(srgbSurface.DrawingSurface, 0, 0);
|
|
|
|
- surface.Canvas.RestoreToCount(saved);
|
|
|
|
- }
|
|
|
|
- else
|
|
|
|
- {
|
|
|
|
- int layer = surface.Canvas.SaveLayer(_paint);
|
|
|
|
- Background.Value?.Paint(context, surface);
|
|
|
|
- surface.Canvas.RestoreToCount(layer);
|
|
|
|
- }
|
|
|
|
|
|
+ var saved = targetSurface.Canvas.Save();
|
|
|
|
+ targetSurface.Canvas.SetMatrix(Matrix3X3.Identity);
|
|
|
|
+
|
|
|
|
+ targetSurface.Canvas.DrawSurface(srgbSurface.DrawingSurface, 0, 0);
|
|
|
|
+ targetSurface.Canvas.RestoreToCount(saved);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private Texture? DetermineTargetSurface(RenderContext context, DrawingSurface outputSurface, out DrawingSurface targetSurface)
|
|
|
|
+ {
|
|
|
|
+ targetSurface = outputSurface;
|
|
|
|
+
|
|
|
|
+ if (Mask.Value == null)
|
|
|
|
+ return null;
|
|
|
|
+
|
|
|
|
+ Background.Value?.Paint(context, outputSurface);
|
|
|
|
+ var texture = Texture.ForProcessing(outputSurface, context.ProcessingColorSpace);
|
|
|
|
+ targetSurface = texture.DrawingSurface;
|
|
|
|
+
|
|
|
|
+ return texture;
|
|
}
|
|
}
|
|
|
|
|
|
- public override RectD? GetPreviewBounds(int frame, string elementToRenderName = "")
|
|
|
|
|
|
+ private void ApplyWithMask(RenderContext context, DrawingSurface processedSurface, DrawingSurface finalSurface)
|
|
{
|
|
{
|
|
- return PreviewUtils.FindPreviewBounds(Background.Connection, frame, elementToRenderName);
|
|
|
|
|
|
+ _maskPaint.BlendMode = !InvertMask.Value ? BlendMode.DstIn : BlendMode.DstOut;
|
|
|
|
+ var maskLayer = processedSurface.Canvas.SaveLayer(_maskPaint);
|
|
|
|
+ Mask.Value?.Paint(context, processedSurface);
|
|
|
|
+ processedSurface.Canvas.RestoreToCount(maskLayer);
|
|
|
|
+
|
|
|
|
+ var saved = finalSurface.Canvas.Save();
|
|
|
|
+ finalSurface.Canvas.SetMatrix(Matrix3X3.Identity);
|
|
|
|
+
|
|
|
|
+ finalSurface.Canvas.DrawSurface(processedSurface, 0, 0);
|
|
|
|
+ finalSurface.Canvas.RestoreToCount(saved);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ public override RectD? GetPreviewBounds(int frame, string elementToRenderName = "") =>
|
|
|
|
+ PreviewUtils.FindPreviewBounds(Background.Connection, frame, elementToRenderName);
|
|
|
|
+
|
|
public override Node CreateCopy() => new ApplyFilterNode();
|
|
public override Node CreateCopy() => new ApplyFilterNode();
|
|
|
|
+
|
|
|
|
+ public override void Dispose()
|
|
|
|
+ {
|
|
|
|
+ base.Dispose();
|
|
|
|
+
|
|
|
|
+ _paint.Dispose();
|
|
|
|
+ _maskPaint.Dispose();
|
|
|
|
+ }
|
|
}
|
|
}
|