Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

複数のエフェクトターゲット #938

Merged
merged 22 commits into from
Feb 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b7e3ee6
FilterEffectContext.BoundsにNaNを指定できるようにした
yuto-trd Feb 19, 2024
c5bf149
FilterEffectCustomOperationContextでEffectTargetをリストにした
yuto-trd Feb 19, 2024
b437cb4
FilterEffect (Node, Context, Activator) に複数ターゲットの対応
yuto-trd Feb 19, 2024
b2ccd46
画像を分割するエフェクトを追加
yuto-trd Feb 19, 2024
2444638
FilterEffctContext.Customを使っているエフェクトの複数ターゲット対応
yuto-trd Feb 19, 2024
ef2bbaf
一回のカスタムエフェクトの呼び出し複数ターゲットを処理できるようにした
yuto-trd Feb 19, 2024
63b8a83
Bounds.Invalidになった後のFEItemの順番を修正
yuto-trd Feb 20, 2024
6e72421
複数のEffectTargetでNodeCacheに対応
yuto-trd Feb 22, 2024
7dfe208
Update RenderLayer.cs
yuto-trd Feb 22, 2024
ed8705f
BrushやPenのImmutable化
yuto-trd Feb 23, 2024
03cfbeb
`FilterEffectActivator.Apply`の修正
yuto-trd Feb 23, 2024
f508224
エフェクトが追加されたとき、キャッシュが無効化されないのを修正
yuto-trd Feb 23, 2024
0aafae2
EffectTargetが空の時、returnするようにした。
yuto-trd Feb 23, 2024
d34d4bf
コメント
yuto-trd Feb 23, 2024
9b8979e
"パーツ分解"を追加
yuto-trd Feb 23, 2024
ea8a23a
不要なフィールドを削除
yuto-trd Feb 23, 2024
058f4e1
互換性を保つようにした
yuto-trd Feb 24, 2024
eb9d2ae
SplitEffect, PartsSplitEffectのSourceOperatorを追加
yuto-trd Feb 24, 2024
4046af7
パーツ分解エフェクトで「回」の内側の「口」が複数回描画されるのを修正
yuto-trd Feb 24, 2024
7eb3065
ノードキャッシュの有効性の確認を一括で行うようにした
yuto-trd Feb 24, 2024
32e638e
メモリ関係の例外が発生するのを修正
yuto-trd Feb 24, 2024
c468ea1
キャッシュ可能範囲が増えた場合、その部分までキャッシュするようにした
yuto-trd Feb 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Beutl.Engine/Graphics/Drawable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ static Drawable()
BlendModeProperty);
}

// DrawableBrushで使われる
public Rect Bounds { get; protected set; }

[Display(Name = nameof(Strings.ImageFilter), ResourceType = typeof(Strings), GroupName = nameof(Strings.ImageFilter))]
Expand Down
18 changes: 10 additions & 8 deletions src/Beutl.Engine/Graphics/FilterEffects/Border.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,15 +134,15 @@ public override Rect TransformBounds(Rect rect)

public override void ApplyTo(FilterEffectContext context)
{
context.Custom((Offset, Thickness, Color, MaskType, Style), Apply, TransformBounds);
context.CustomEffect((Offset, Thickness, Color, MaskType, Style), Apply, TransformBounds);
}

private static Rect TransformBounds((Point Offset, int Thickness, Color Color, MaskTypes MaskType, BorderStyles Style) data, Rect rect)
{
return rect.Union(rect.Translate(new Vector(data.Offset.X, data.Offset.Y)).Inflate(data.Thickness / 2)).Inflate(8);
}

private static void Apply((Point Offset, int Thickness, Color Color, MaskTypes MaskType, BorderStyles Style) data, FilterEffectCustomOperationContext context)
private static void Apply((Point Offset, int Thickness, Color Color, MaskTypes MaskType, BorderStyles Style) data, CustomFilterEffectContext context)
{
Point offset = data.Offset;
Color color = data.Color;
Expand Down Expand Up @@ -177,16 +177,17 @@ Bitmap<Bgra8888> RenderBorderBitmap(Bitmap<Bgra8888> src)
return border;
}

if (context.Target.Surface != null)
for (int i = 0; i < context.Targets.Count; i++)
{
using SKImage skimage = context.Target.Surface.Value.Snapshot();
EffectTarget target = context.Targets[i];
using SKImage skimage = target.Surface!.Value.Snapshot();
using var src = skimage.ToBitmap();
using var srcRef = Ref<IBitmap>.Create(src);
using var srcBitmapSource = new BitmapSource(srcRef, "Temp");

// 縁取りの画像を生成
using Bitmap<Bgra8888> border = RenderBorderBitmap(src);
var rect = new Rect(0, 0, src.Width, src.Height);
var rect = target.Bounds;
Rect borderBounds = rect.Translate(offset).Inflate(thicknessHalf);
Rect canvasRect = rect.Union(borderBounds).Inflate(8);
var borderRect = new Rect(0, 0, border.Width, border.Height);
Expand All @@ -197,7 +198,7 @@ Bitmap<Bgra8888> RenderBorderBitmap(Bitmap<Bgra8888> src)
maskBrush = new ImmutableImageBrush(srcBitmapSource, stretch: Stretch.None);
}

using EffectTarget newTarget = context.CreateTarget((int)canvasRect.Width, (int)canvasRect.Height);
EffectTarget newTarget = context.CreateTarget(canvasRect);
using (ImmediateCanvas canvas = context.Open(newTarget))
using (canvas.PushTransform(Matrix.CreateTranslation(8, 8)))
{
Expand All @@ -224,9 +225,10 @@ Bitmap<Bgra8888> RenderBorderBitmap(Bitmap<Bgra8888> src)
using (canvas.PushTransform(srcTranslate))
canvas.DrawBitmap(src, Brushes.White, null);
}

context.ReplaceTarget(newTarget);
}

target.Dispose();
context.Targets[i] = newTarget;
}
}
/* Contours to SKPath
Expand Down
10 changes: 6 additions & 4 deletions src/Beutl.Engine/Graphics/FilterEffects/ChromaKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public float SaturationRange

public override void ApplyTo(FilterEffectContext context)
{
context.Custom((Color.ToHsv(), HueRange, SaturationRange), OnApplyTo, (_, r) => r);
context.CustomEffect((Color.ToHsv(), HueRange, SaturationRange), OnApplyTo, (_, r) => r);
}

private static unsafe void CopyFromCPU(MemoryBuffer1D<Bgra8888, Stride1D.Dense> source, SKSurface surface, SKImageInfo imageInfo)
Expand All @@ -86,15 +86,17 @@ private static unsafe void CopyToCPU(MemoryBuffer1D<Bgra8888, Stride1D.Dense> so
source.View.CopyToCPU(ref Unsafe.AsRef<Bgra8888>((void*)bitmap.GetPixels()), source.Length);
}

private unsafe void OnApplyTo((Hsv hsv, float hueRange, float satRange) data, FilterEffectCustomOperationContext context)
private unsafe void OnApplyTo((Hsv hsv, float hueRange, float satRange) data, CustomFilterEffectContext context)
{
if (context.Target.Surface?.Value is { } surface)
for (int i = 0; i < context.Targets.Count; i++)
{
var target = context.Targets[i];
var surface = target.Surface!.Value;
Accelerator accelerator = SharedGPUContext.Accelerator;
var kernel = accelerator.LoadAutoGroupedStreamKernel<
Index1D, ArrayView<Bgra8888>, Hsv, float, float>(EffectKernel);

var size = PixelSize.FromSize(context.Target.Size, 1);
var size = PixelSize.FromSize(target.Bounds.Size, 1);
var imgInfo = new SKImageInfo(size.Width, size.Height, SKColorType.Bgra8888);

using var source = accelerator.Allocate1D<Bgra8888>(size.Width * size.Height);
Expand Down
20 changes: 12 additions & 8 deletions src/Beutl.Engine/Graphics/FilterEffects/Clipping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public Thickness Thickness

public override void ApplyTo(FilterEffectContext context)
{
context.Custom(Thickness, Apply, TransformBounds);
context.CustomEffect(Thickness, Apply, TransformBounds);
}

public override Rect TransformBounds(Rect bounds)
Expand All @@ -42,27 +42,31 @@ private Rect TransformBounds(Thickness thickness, Rect rect)
return rect.Deflate(thickness).Normalize();
}

private void Apply(Thickness thickness, FilterEffectCustomOperationContext context)
private void Apply(Thickness thickness, CustomFilterEffectContext context)
{
if (context.Target.Surface?.Value is SKSurface surface)
for (int i = 0; i < context.Targets.Count; i++)
{
Rect originalRect = new(context.Target.Size);
var target = context.Targets[i];
var surface = target.Surface!.Value;
Rect originalRect = target.Bounds;
Rect clipRect = originalRect.Deflate(thickness).Normalize();

Rect intersect = originalRect.Intersect(clipRect);

if (intersect.IsEmpty || clipRect.Width == 0 || clipRect.Height == 0)
{
context.ReplaceTarget(EffectTarget.Empty);
context.Targets.RemoveAt(i);
i--;
}
else
{
using SKImage skimage = surface.Snapshot(SKRectI.Floor(intersect.ToSKRect()));
using EffectTarget target = context.CreateTarget((int)clipRect.Width, (int)clipRect.Height);
EffectTarget newtarget = context.CreateTarget(clipRect);

target.Surface?.Value?.Canvas?.DrawImage(skimage, intersect.X - clipRect.X, intersect.Y - clipRect.Y);
newtarget.Surface?.Value?.Canvas?.DrawImage(skimage, intersect.X - clipRect.X, intersect.Y - clipRect.Y);

context.ReplaceTarget(target);
target.Dispose();
context.Targets[i] = newtarget;
}
}
}
Expand Down
14 changes: 8 additions & 6 deletions src/Beutl.Engine/Graphics/FilterEffects/ColorKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,14 @@ public float Range

public override void ApplyTo(FilterEffectContext context)
{
var colorNtsc =
var colorNtsc =
(_color.R * 0.11448f) +
(_color.G * 0.58661f) +
(_color.B * 0.29891f);
colorNtsc = Math.Clamp(colorNtsc, 0, 255);
colorNtsc = MathF.Round(colorNtsc);

context.Custom((colorNtsc, Range), OnApplyTo, (_, r) => r);
context.CustomEffect((colorNtsc, Range), OnApplyTo, (_, r) => r);
}

private static unsafe void CopyFromCPU(MemoryBuffer1D<Bgra8888, Stride1D.Dense> source, SKSurface surface, SKImageInfo imageInfo)
Expand All @@ -81,15 +81,17 @@ private static unsafe void CopyToCPU(MemoryBuffer1D<Bgra8888, Stride1D.Dense> so
source.View.CopyToCPU(ref Unsafe.AsRef<Bgra8888>((void*)bitmap.GetPixels()), source.Length);
}

private unsafe void OnApplyTo((float colorNtsc, float range) data, FilterEffectCustomOperationContext context)
private unsafe void OnApplyTo((float colorNtsc, float range) data, CustomFilterEffectContext context)
{
if (context.Target.Surface?.Value is { } surface)
for (int i = 0; i < context.Targets.Count; i++)
{
var target = context.Targets[i];
var surface = target.Surface!.Value;
Accelerator accelerator = SharedGPUContext.Accelerator;
var kernel = accelerator.LoadAutoGroupedStreamKernel<
Index1D, ArrayView<Bgra8888>, float, float>(EffectKernel);

var size = PixelSize.FromSize(context.Target.Size, 1);
var size = PixelSize.FromSize(target.Bounds.Size, 1);
var imgInfo = new SKImageInfo(size.Width, size.Height, SKColorType.Bgra8888);

using var source = accelerator.Allocate1D<Bgra8888>(size.Width * size.Height);
Expand Down Expand Up @@ -119,7 +121,7 @@ private static void EffectKernel(Index1D index, ArrayView<Bgra8888> src, float c

ntsc = IntrinsicMath.Clamp(ntsc, 0, 255);
ntsc = XMath.Round(ntsc);

if (IntrinsicMath.Abs(colorNtsc - ntsc) < range)
{
src[index] = default;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using System.Collections.Immutable;

using Beutl.Media.Source;

using SkiaSharp;

namespace Beutl.Graphics.Effects;

public class CustomFilterEffectContext
{
internal readonly IImmediateCanvasFactory _factory;
internal readonly ImmutableArray<FEItemWrapper> _history;

internal CustomFilterEffectContext(
IImmediateCanvasFactory canvas,
EffectTargets targets,
ImmutableArray<FEItemWrapper> history)
{
Targets = targets;
_factory = canvas;
_history = history;
}

public EffectTargets Targets { get; }

public void ForEach(Action<int, EffectTarget> action)
{
for (int i = 0; i < Targets.Count; i++)
{
EffectTarget target = Targets[i];
action(i, target);
}
}

public void ForEach(Func<int, EffectTarget, EffectTarget> action)
{
for (int i = 0; i < Targets.Count; i++)
{
EffectTarget target = Targets[i];
EffectTarget newTarget = action(i, target);
if (newTarget != target)
{
target.Dispose();
Targets[i] = newTarget;
}
}
}

public void ForEach(Func<int, EffectTarget, EffectTargets> action)
{
for (int i = 0; i < Targets.Count; i++)
{
using EffectTarget target = Targets[i];
EffectTargets newTargets = action(i, target.Clone());

Targets.RemoveAt(i);
Targets.InsertRange(i, newTargets);
i += newTargets.Count - 1;
}
}

public EffectTarget CreateTarget(Rect bounds)
{
SKSurface? surface = _factory.CreateRenderTarget((int)bounds.Width, (int)bounds.Height);
if (surface != null)
{
using var surfaceRef = Ref<SKSurface>.Create(surface);
var obj = new EffectTarget(surfaceRef, bounds);

obj._history.AddRange(_history);
return obj;
}
else
{
var obj = new EffectTarget();

obj._history.AddRange(_history);
return obj;
}
}

public ImmediateCanvas Open(EffectTarget target)
{
if (target.Surface == null)
{
throw new InvalidOperationException("無効なEffectTarget");
}

return _factory.CreateCanvas(target.Surface.Value, true);
}
}
37 changes: 29 additions & 8 deletions src/Beutl.Engine/Graphics/FilterEffects/EffectTarget.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using Beutl.Graphics.Rendering;
using System.ComponentModel;

using Beutl.Collections.Pooled;
using Beutl.Graphics.Rendering;
using Beutl.Media.Source;

using SkiaSharp;
Expand All @@ -7,32 +10,45 @@ namespace Beutl.Graphics.Effects;

public sealed class EffectTarget : IDisposable
{
[Obsolete("Use a constructor with no parameters.")]
public static readonly EffectTarget Empty = new();

private object? _target;

internal readonly PooledList<FEItemWrapper> _history = [];

public EffectTarget(FilterEffectNode node)
{
_target = node;
Size = node.Bounds.Size;
OriginalBounds = node.OriginalBounds;
Bounds = node.OriginalBounds;
}

public EffectTarget(Ref<SKSurface> surface, Size size)
public EffectTarget(Ref<SKSurface> surface, Rect originalBounds)
{
_target = surface.Clone();
Size = size;
OriginalBounds = originalBounds;
Bounds = originalBounds;
}

public EffectTarget()
{
}

public Size Size { get; private set; }
public Rect OriginalBounds { get; set; }

public Rect Bounds { get; set; }

[Obsolete()]
[EditorBrowsable(EditorBrowsableState.Never)]
public Size Size => Bounds.Size;

public FilterEffectNode? Node => _target as FilterEffectNode;

public Ref<SKSurface>? Surface => _target as Ref<SKSurface>;

public bool IsEmpty => _target == null;

public EffectTarget Clone()
{
if (Node != null)
Expand All @@ -41,7 +57,12 @@ public EffectTarget Clone()
}
else if (Surface != null)
{
return new EffectTarget(Surface, Size);
var obj = new EffectTarget(Surface, OriginalBounds)
{
Bounds = Bounds
};
obj._history.AddRange(_history.Select(v => v.Inherit()));
return obj;
}
else
{
Expand All @@ -53,7 +74,8 @@ public void Dispose()
{
Surface?.Dispose();
_target = null;
Size = default;
OriginalBounds = default;
_history.Dispose();
}

public void Draw(ImmediateCanvas canvas)
Expand All @@ -71,4 +93,3 @@ public void Draw(ImmediateCanvas canvas)
}
}
}

Loading