|
@@ -1,22 +1,25 @@
|
|
-using ChunkyImageLib.Operations;
|
|
|
|
-using PixiEditor.ChangeableDocument.Changeables.Animations;
|
|
|
|
|
|
+using PixiEditor.ChangeableDocument.Changeables.Animations;
|
|
using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
|
|
using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
|
|
using PixiEditor.ChangeableDocument.Changeables.Interfaces;
|
|
using PixiEditor.ChangeableDocument.Changeables.Interfaces;
|
|
using PixiEditor.ChangeableDocument.ChangeInfos.Properties;
|
|
using PixiEditor.ChangeableDocument.ChangeInfos.Properties;
|
|
-using PixiEditor.ChangeableDocument.Enums;
|
|
|
|
using PixiEditor.ChangeableDocument.Helpers;
|
|
using PixiEditor.ChangeableDocument.Helpers;
|
|
using PixiEditor.ChangeableDocument.Rendering;
|
|
using PixiEditor.ChangeableDocument.Rendering;
|
|
using PixiEditor.DrawingApi.Core;
|
|
using PixiEditor.DrawingApi.Core;
|
|
using PixiEditor.DrawingApi.Core.ColorsImpl;
|
|
using PixiEditor.DrawingApi.Core.ColorsImpl;
|
|
|
|
+using PixiEditor.DrawingApi.Core.Surfaces;
|
|
using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
|
|
using PixiEditor.DrawingApi.Core.Surfaces.PaintImpl;
|
|
using PixiEditor.Numerics;
|
|
using PixiEditor.Numerics;
|
|
|
|
+using BlendMode = PixiEditor.ChangeableDocument.Enums.BlendMode;
|
|
|
|
|
|
namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
|
|
namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
|
|
|
|
|
|
public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundInput
|
|
public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundInput
|
|
{
|
|
{
|
|
|
|
+ public abstract VecD ScenePosition { get; }
|
|
|
|
+ public abstract VecD SceneSize { get; }
|
|
|
|
+
|
|
public const string DefaultMemberName = "DEFAULT_MEMBER_NAME";
|
|
public const string DefaultMemberName = "DEFAULT_MEMBER_NAME";
|
|
- public InputProperty<Texture?> Background { get; }
|
|
|
|
|
|
+ public InputProperty<DrawingSurface?> Background { get; }
|
|
public InputProperty<float> Opacity { get; }
|
|
public InputProperty<float> Opacity { get; }
|
|
public InputProperty<bool> IsVisible { get; }
|
|
public InputProperty<bool> IsVisible { get; }
|
|
public bool ClipToPreviousMember { get; set; }
|
|
public bool ClipToPreviousMember { get; set; }
|
|
@@ -24,11 +27,12 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundI
|
|
public InputProperty<Texture?> CustomMask { get; }
|
|
public InputProperty<Texture?> CustomMask { get; }
|
|
public InputProperty<bool> MaskIsVisible { get; }
|
|
public InputProperty<bool> MaskIsVisible { get; }
|
|
public InputProperty<Filter> Filters { get; }
|
|
public InputProperty<Filter> Filters { get; }
|
|
- public OutputProperty<Texture?> Output { get; }
|
|
|
|
|
|
+ public OutputProperty<DrawingSurface?> Output { get; }
|
|
|
|
|
|
public OutputProperty<Texture?> FilterlessOutput { get; }
|
|
public OutputProperty<Texture?> FilterlessOutput { get; }
|
|
|
|
|
|
public ChunkyImage? EmbeddedMask { get; set; }
|
|
public ChunkyImage? EmbeddedMask { get; set; }
|
|
|
|
+
|
|
public virtual ShapeCorners GetTransformationCorners(KeyFrameTime frameTime)
|
|
public virtual ShapeCorners GetTransformationCorners(KeyFrameTime frameTime)
|
|
{
|
|
{
|
|
return new ShapeCorners(GetTightBounds(frameTime).GetValueOrDefault());
|
|
return new ShapeCorners(GetTightBounds(frameTime).GetValueOrDefault());
|
|
@@ -38,41 +42,59 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundI
|
|
{
|
|
{
|
|
get => DisplayName;
|
|
get => DisplayName;
|
|
set => DisplayName = value;
|
|
set => DisplayName = value;
|
|
- }
|
|
|
|
-
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
private Paint maskPaint = new Paint() { BlendMode = DrawingApi.Core.Surfaces.BlendMode.DstIn };
|
|
private Paint maskPaint = new Paint() { BlendMode = DrawingApi.Core.Surfaces.BlendMode.DstIn };
|
|
protected Paint blendPaint = new Paint();
|
|
protected Paint blendPaint = new Paint();
|
|
-
|
|
|
|
|
|
+
|
|
private int maskCacheHash = 0;
|
|
private int maskCacheHash = 0;
|
|
|
|
|
|
protected StructureNode()
|
|
protected StructureNode()
|
|
{
|
|
{
|
|
- Background = CreateInput<Texture?>("Background", "BACKGROUND", null);
|
|
|
|
|
|
+ Background = CreateInput<DrawingSurface?>("Background", "BACKGROUND", null);
|
|
Opacity = CreateInput<float>("Opacity", "OPACITY", 1);
|
|
Opacity = CreateInput<float>("Opacity", "OPACITY", 1);
|
|
IsVisible = CreateInput<bool>("IsVisible", "IS_VISIBLE", true);
|
|
IsVisible = CreateInput<bool>("IsVisible", "IS_VISIBLE", true);
|
|
- BlendMode = CreateInput<BlendMode>("BlendMode", "BLEND_MODE", Enums.BlendMode.Normal);
|
|
|
|
|
|
+ BlendMode = CreateInput("BlendMode", "BLEND_MODE", Enums.BlendMode.Normal);
|
|
CustomMask = CreateInput<Texture?>("Mask", "MASK", null);
|
|
CustomMask = CreateInput<Texture?>("Mask", "MASK", null);
|
|
MaskIsVisible = CreateInput<bool>("MaskIsVisible", "MASK_IS_VISIBLE", true);
|
|
MaskIsVisible = CreateInput<bool>("MaskIsVisible", "MASK_IS_VISIBLE", true);
|
|
Filters = CreateInput<Filter>(nameof(Filters), "FILTERS", null);
|
|
Filters = CreateInput<Filter>(nameof(Filters), "FILTERS", null);
|
|
|
|
|
|
- Output = CreateOutput<Texture?>("Output", "OUTPUT", null);
|
|
|
|
|
|
+ Output = CreateOutput<DrawingSurface?>("Output", "OUTPUT", null);
|
|
FilterlessOutput = CreateOutput<Texture?>(nameof(FilterlessOutput), "WITHOUT_FILTERS", null);
|
|
FilterlessOutput = CreateOutput<Texture?>(nameof(FilterlessOutput), "WITHOUT_FILTERS", null);
|
|
-
|
|
|
|
|
|
+
|
|
MemberName = DefaultMemberName;
|
|
MemberName = DefaultMemberName;
|
|
}
|
|
}
|
|
|
|
|
|
protected override bool AffectedByChunkResolution => true;
|
|
protected override bool AffectedByChunkResolution => true;
|
|
protected override bool AffectedByChunkToUpdate => true;
|
|
protected override bool AffectedByChunkToUpdate => true;
|
|
|
|
|
|
- protected abstract override Texture? OnExecute(RenderingContext context);
|
|
|
|
|
|
+ protected override void OnExecute(RenderContext context)
|
|
|
|
+ {
|
|
|
|
+ RectD localBounds = new RectD(0, 0, SceneSize.X, SceneSize.Y);
|
|
|
|
|
|
- protected void ApplyMaskIfPresent(Texture surface, RenderingContext context)
|
|
|
|
|
|
+ DrawingSurface sceneSurface = context.TargetSurface;
|
|
|
|
+
|
|
|
|
+ int savedNum = sceneSurface.Canvas.Save();
|
|
|
|
+ sceneSurface.Canvas.ClipRect(RectD.Create((VecI)ScenePosition.Floor(), (VecI)SceneSize.Ceiling()));
|
|
|
|
+ sceneSurface.Canvas.Translate((float)ScenePosition.X, (float)ScenePosition.Y);
|
|
|
|
+
|
|
|
|
+ SceneObjectRenderContext renderObjectContext = new SceneObjectRenderContext(sceneSurface, localBounds,
|
|
|
|
+ context.FrameTime, context.ChunkToUpdate, context.ChunkResolution, context.DocumentSize);
|
|
|
|
+
|
|
|
|
+ Render(renderObjectContext);
|
|
|
|
+
|
|
|
|
+ sceneSurface.Canvas.RestoreToCount(savedNum);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public abstract void Render(SceneObjectRenderContext sceneContext);
|
|
|
|
+
|
|
|
|
+ protected void ApplyMaskIfPresent(Texture surface, RenderContext context)
|
|
{
|
|
{
|
|
if (MaskIsVisible.Value)
|
|
if (MaskIsVisible.Value)
|
|
{
|
|
{
|
|
if (CustomMask.Value != null)
|
|
if (CustomMask.Value != null)
|
|
{
|
|
{
|
|
- surface.DrawingSurface.Canvas.DrawSurface(CustomMask.Value.DrawingSurface, 0, 0, maskPaint);
|
|
|
|
|
|
+ surface.DrawingSurface.Canvas.DrawSurface(CustomMask.Value.DrawingSurface, 0, 0, maskPaint);
|
|
}
|
|
}
|
|
else if (EmbeddedMask != null)
|
|
else if (EmbeddedMask != null)
|
|
{
|
|
{
|
|
@@ -86,13 +108,13 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundI
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- protected override bool CacheChanged(RenderingContext context)
|
|
|
|
|
|
+ protected override bool CacheChanged(RenderContext context)
|
|
{
|
|
{
|
|
int cacheHash = EmbeddedMask?.GetCacheHash() ?? 0;
|
|
int cacheHash = EmbeddedMask?.GetCacheHash() ?? 0;
|
|
return base.CacheChanged(context) || maskCacheHash != cacheHash;
|
|
return base.CacheChanged(context) || maskCacheHash != cacheHash;
|
|
}
|
|
}
|
|
|
|
|
|
- protected override void UpdateCache(RenderingContext context)
|
|
|
|
|
|
+ protected override void UpdateCache(RenderContext context)
|
|
{
|
|
{
|
|
base.UpdateCache(context);
|
|
base.UpdateCache(context);
|
|
maskCacheHash = EmbeddedMask?.GetCacheHash() ?? 0;
|
|
maskCacheHash = EmbeddedMask?.GetCacheHash() ?? 0;
|
|
@@ -102,7 +124,7 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundI
|
|
{
|
|
{
|
|
if (ClipToPreviousMember && Background.Value != null)
|
|
if (ClipToPreviousMember && Background.Value != null)
|
|
{
|
|
{
|
|
- toClip.DrawingSurface.Canvas.DrawSurface(clipSource.DrawingSurface, 0, 0, maskPaint);
|
|
|
|
|
|
+ toClip.DrawingSurface.Canvas.DrawSurface(clipSource.DrawingSurface, 0, 0, maskPaint);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -116,32 +138,35 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundI
|
|
return (MaskIsVisible.Value && (EmbeddedMask != null || CustomMask.Value != null)) || ClipToPreviousMember;
|
|
return (MaskIsVisible.Value && (EmbeddedMask != null || CustomMask.Value != null)) || ClipToPreviousMember;
|
|
}
|
|
}
|
|
|
|
|
|
- protected void DrawBackground(Texture workingSurface, RenderingContext context)
|
|
|
|
|
|
+ protected void DrawBackground(DrawingSurface drawOnto, RenderContext context)
|
|
{
|
|
{
|
|
blendPaint.Color = Colors.White;
|
|
blendPaint.Color = Colors.White;
|
|
- DrawSurface(workingSurface, Background.Value, context, null);
|
|
|
|
|
|
+ DrawSurface(drawOnto, Background.Value, context, null);
|
|
}
|
|
}
|
|
|
|
|
|
- protected void DrawSurface(Texture workingSurface, Texture source, RenderingContext context, Filter? filter)
|
|
|
|
|
|
+ protected void DrawSurface(DrawingSurface workingSurface, DrawingSurface source, RenderContext context,
|
|
|
|
+ Filter? filter)
|
|
{
|
|
{
|
|
// Maybe clip rect will allow to avoid snapshotting? Idk if it will be faster
|
|
// Maybe clip rect will allow to avoid snapshotting? Idk if it will be faster
|
|
|
|
+ /*
|
|
RectI sourceRect = CalculateSourceRect(workingSurface.Size, source.Size, context);
|
|
RectI sourceRect = CalculateSourceRect(workingSurface.Size, source.Size, context);
|
|
RectI targetRect = CalculateDestinationRect(context);
|
|
RectI targetRect = CalculateDestinationRect(context);
|
|
using var snapshot = source.DrawingSurface.Snapshot(sourceRect);
|
|
using var snapshot = source.DrawingSurface.Snapshot(sourceRect);
|
|
|
|
+ */
|
|
|
|
|
|
blendPaint.SetFilters(filter);
|
|
blendPaint.SetFilters(filter);
|
|
- workingSurface.DrawingSurface.Canvas.DrawImage(snapshot, targetRect.X, targetRect.Y, blendPaint);
|
|
|
|
|
|
+ workingSurface.Canvas.DrawSurface(source, 0, 0, blendPaint);
|
|
}
|
|
}
|
|
|
|
|
|
- protected RectI CalculateSourceRect(VecI targetSize, VecI sourceSize, RenderingContext context)
|
|
|
|
|
|
+ protected RectI CalculateSourceRect(VecI targetSize, VecI sourceSize, RenderContext context)
|
|
{
|
|
{
|
|
float divider = 1;
|
|
float divider = 1;
|
|
-
|
|
|
|
- if(sourceSize.X < targetSize.X || sourceSize.Y < targetSize.Y)
|
|
|
|
|
|
+
|
|
|
|
+ if (sourceSize.X < targetSize.X || sourceSize.Y < targetSize.Y)
|
|
{
|
|
{
|
|
divider = Math.Min((float)targetSize.X / sourceSize.X, (float)targetSize.Y / sourceSize.Y);
|
|
divider = Math.Min((float)targetSize.X / sourceSize.X, (float)targetSize.Y / sourceSize.Y);
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
int chunkSize = (int)Math.Round(context.ChunkResolution.PixelSize() / divider);
|
|
int chunkSize = (int)Math.Round(context.ChunkResolution.PixelSize() / divider);
|
|
VecI chunkPos = context.ChunkToUpdate;
|
|
VecI chunkPos = context.ChunkToUpdate;
|
|
|
|
|
|
@@ -152,11 +177,11 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundI
|
|
|
|
|
|
x = Math.Clamp(x, 0, Math.Max(sourceSize.X - width, 0));
|
|
x = Math.Clamp(x, 0, Math.Max(sourceSize.X - width, 0));
|
|
y = Math.Clamp(y, 0, Math.Max(sourceSize.Y - height, 0));
|
|
y = Math.Clamp(y, 0, Math.Max(sourceSize.Y - height, 0));
|
|
-
|
|
|
|
|
|
+
|
|
return new RectI(x, y, width, height);
|
|
return new RectI(x, y, width, height);
|
|
}
|
|
}
|
|
|
|
|
|
- protected RectI CalculateDestinationRect(RenderingContext context)
|
|
|
|
|
|
+ protected RectI CalculateDestinationRect(RenderContext context)
|
|
{
|
|
{
|
|
int chunkSize = context.ChunkResolution.PixelSize();
|
|
int chunkSize = context.ChunkResolution.PixelSize();
|
|
VecI chunkPos = context.ChunkToUpdate;
|
|
VecI chunkPos = context.ChunkToUpdate;
|
|
@@ -181,17 +206,18 @@ public abstract class StructureNode : Node, IReadOnlyStructureNode, IBackgroundI
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- internal override OneOf<None, IChangeInfo, List<IChangeInfo>> DeserializeAdditionalData(IReadOnlyDocument target, IReadOnlyDictionary<string, object> data)
|
|
|
|
|
|
+ internal override OneOf<None, IChangeInfo, List<IChangeInfo>> DeserializeAdditionalData(IReadOnlyDocument target,
|
|
|
|
+ IReadOnlyDictionary<string, object> data)
|
|
{
|
|
{
|
|
base.DeserializeAdditionalData(target, data);
|
|
base.DeserializeAdditionalData(target, data);
|
|
bool hasMask = data.ContainsKey("embeddedMask");
|
|
bool hasMask = data.ContainsKey("embeddedMask");
|
|
if (hasMask)
|
|
if (hasMask)
|
|
{
|
|
{
|
|
ChunkyImage? mask = (ChunkyImage?)data["embeddedMask"];
|
|
ChunkyImage? mask = (ChunkyImage?)data["embeddedMask"];
|
|
-
|
|
|
|
|
|
+
|
|
EmbeddedMask?.Dispose();
|
|
EmbeddedMask?.Dispose();
|
|
- EmbeddedMask = mask;
|
|
|
|
-
|
|
|
|
|
|
+ EmbeddedMask = mask;
|
|
|
|
+
|
|
return new List<IChangeInfo> { new StructureMemberMask_ChangeInfo(Id, mask != null) };
|
|
return new List<IChangeInfo> { new StructureMemberMask_ChangeInfo(Id, mask != null) };
|
|
}
|
|
}
|
|
|
|
|