Skip to content

Commit

Permalink
Merge pull request #1200 from b-editor/feat/render-target
Browse files Browse the repository at this point in the history
Create RenderTarget wrapper and refactor rendering classes
  • Loading branch information
yuto-trd authored Dec 11, 2024
2 parents 492ec36 + c2cae84 commit 53922c4
Show file tree
Hide file tree
Showing 51 changed files with 498 additions and 546 deletions.
28 changes: 13 additions & 15 deletions src/Beutl.Engine/Graphics/BrushConstructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,14 @@

namespace Beutl.Graphics;

public readonly struct BrushConstructor(Rect bounds, IBrush? brush, BlendMode blendMode, IImmediateCanvasFactory factory)
public readonly struct BrushConstructor(Rect bounds, IBrush? brush, BlendMode blendMode)
{
public Rect Bounds { get; } = bounds;

public IBrush? Brush { get; } = brush;

public BlendMode BlendMode { get; } = blendMode;

public IImmediateCanvasFactory Factory { get; } = factory;

public void ConfigurePaint(SKPaint paint)
{
float opacity = (Brush?.Opacity ?? 0) / 100f;
Expand Down Expand Up @@ -195,7 +193,7 @@ private void ConfigureGradientBrush(SKPaint paint, IGradientBrush gradientBrush)

private void ConfigureTileBrush(SKPaint paint, ITileBrush tileBrush)
{
SKSurface? surface = null;
RenderTarget? renderTarget = null;
SKBitmap? skbitmap = null;
PixelSize pixelSize;

Expand All @@ -204,10 +202,10 @@ private void ConfigureTileBrush(SKPaint paint, ITileBrush tileBrush)
RenderScene? scene = sceneBrush.Scene;
if (scene != null)
{
surface = Factory.CreateRenderTarget(scene.Size.Width, scene.Size.Height);
if (surface != null)
renderTarget = RenderTarget.Create(scene.Size.Width, scene.Size.Height);
if (renderTarget != null)
{
using (ImmediateCanvas icanvas = Factory.CreateCanvas(surface, true))
using (var icanvas = new ImmediateCanvas(renderTarget))
using (icanvas.PushTransform(Matrix.CreateTranslation(-sceneBrush.Bounds.X, -sceneBrush.Bounds.Y)))
{
scene.Render(icanvas);
Expand Down Expand Up @@ -239,20 +237,20 @@ private void ConfigureTileBrush(SKPaint paint, ITileBrush tileBrush)
throw new InvalidOperationException($"'{tileBrush.GetType().Name}' not supported.");
}

if (surface == null && skbitmap == null)
if (renderTarget == null && skbitmap == null)
return;

SKSurface? intermediate = null;
RenderTarget? intermediate = null;
try
{
var calc = new TileBrushCalculator(tileBrush, pixelSize.ToSize(1), Bounds.Size);
SKSizeI intermediateSize = calc.IntermediateSize.ToSKSize().ToSizeI();

intermediate = Factory.CreateRenderTarget(intermediateSize.Width, intermediateSize.Height);
intermediate = RenderTarget.Create(intermediateSize.Width, intermediateSize.Height);
if (intermediate == null)
return;

SKCanvas canvas = intermediate.Canvas;
SKCanvas canvas = intermediate.Value.Canvas;
using var ipaint = new SKPaint();
{
ipaint.FilterQuality = tileBrush.BitmapInterpolationMode.ToSKFilterQuality();
Expand All @@ -262,8 +260,8 @@ private void ConfigureTileBrush(SKPaint paint, ITileBrush tileBrush)
canvas.ClipRect(calc.IntermediateClip.ToSKRect());
canvas.SetMatrix(calc.IntermediateTransform.ToSKMatrix());

if (surface != null)
canvas.DrawSurface(surface, default, ipaint);
if (renderTarget != null)
canvas.DrawSurface(renderTarget.Value, default, ipaint);
else if (skbitmap != null)
canvas.DrawBitmap(skbitmap, (SKPoint)default, ipaint);

Expand Down Expand Up @@ -296,15 +294,15 @@ private void ConfigureTileBrush(SKPaint paint, ITileBrush tileBrush)
tileTransform = tileTransform.PreConcat(transform.ToSKMatrix());
}

using (SKImage skimage = intermediate.Snapshot())
using (SKImage skimage = intermediate.Value.Snapshot())
using (SKShader shader = skimage.ToShader(tileX, tileY, tileTransform))
{
paint.Shader = shader;
}
}
finally
{
surface?.Dispose();
renderTarget?.Dispose();
skbitmap?.Dispose();
intermediate?.Dispose();
}
Expand Down
4 changes: 2 additions & 2 deletions src/Beutl.Engine/Graphics/FilterEffects/Border.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

namespace Beutl.Graphics.Effects;

[Obsolete("Use StrokeEffect instead.")]
public class Border : FilterEffect
{
public static readonly CoreProperty<Point> OffsetProperty;
Expand Down Expand Up @@ -180,8 +181,7 @@ Bitmap<Bgra8888> RenderBorderBitmap(Bitmap<Bgra8888> src)
for (int i = 0; i < context.Targets.Count; i++)
{
EffectTarget target = context.Targets[i];
using SKImage skimage = target.Surface!.Value.Snapshot();
using var src = skimage.ToBitmap();
using var src = target.RenderTarget!.Snapshot();
using var srcRef = Ref<IBitmap>.Create(src);
using var srcBitmapSource = new BitmapSource(srcRef, "Temp");

Expand Down
2 changes: 1 addition & 1 deletion src/Beutl.Engine/Graphics/FilterEffects/ChromaKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ private unsafe void OnApplyTo((Hsv hsv, float hueRange, float satRange) data, Cu
for (int i = 0; i < context.Targets.Count; i++)
{
var target = context.Targets[i];
var surface = target.Surface!.Value;
var surface = target.RenderTarget!.Value;
Accelerator accelerator = SharedGPUContext.Accelerator;
var kernel = accelerator.LoadAutoGroupedStreamKernel<
Index1D, ArrayView<Bgra8888>, Hsv, float, float>(EffectKernel);
Expand Down
4 changes: 2 additions & 2 deletions src/Beutl.Engine/Graphics/FilterEffects/Clipping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ private static void Apply((Thickness thickness, bool autoCenter, bool autoClip)
{
Thickness thickness = originalThickness;
var target = context.Targets[i];
var surface = target.Surface!.Value;
var surface = target.RenderTarget!.Value;
if (data.autoClip)
{
thickness += FindRectAndReturnThickness(surface);
Expand Down Expand Up @@ -246,7 +246,7 @@ private static void Apply((Thickness thickness, bool autoCenter, bool autoClip)
newTarget = context.CreateTarget(newBounds);
}

newTarget.Surface?.Value?.Canvas?.DrawImage(
newTarget.RenderTarget?.Value?.Canvas?.DrawImage(
skImage,
intersect.X - clipRect.X + pointX,
intersect.Y - clipRect.Y + pointY);
Expand Down
2 changes: 1 addition & 1 deletion src/Beutl.Engine/Graphics/FilterEffects/ColorKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ private unsafe void OnApplyTo((float colorNtsc, float range) data, CustomFilterE
for (int i = 0; i < context.Targets.Count; i++)
{
var target = context.Targets[i];
var surface = target.Surface!.Value;
var surface = target.RenderTarget!.Value;
Accelerator accelerator = SharedGPUContext.Accelerator;
var kernel = accelerator.LoadAutoGroupedStreamKernel<
Index1D, ArrayView<Bgra8888>, float, float>(EffectKernel);
Expand Down
8 changes: 4 additions & 4 deletions src/Beutl.Engine/Graphics/FilterEffects/ColorShift.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ private static void OnApply(
for (int i = 0; i < context.Targets.Count; i++)
{
var target = context.Targets[i];
var surface = target.Surface!;
var renderTarget = target.RenderTarget!;

var bounds = TransformBoundsCore(data, target.Bounds);
var pixelRect = PixelRect.FromRect(bounds);
Expand All @@ -95,7 +95,7 @@ private static void OnApply(
int minOffsetY = Math.Min(data.RedOffset.Y,
Math.Min(data.GreenOffset.Y, Math.Min(data.BlueOffset.Y, data.AlphaOffset.Y)));

var size = surface.Value.Canvas.DeviceClipBounds.Size;
var size = new PixelSize(renderTarget.Width, renderTarget.Height);
Accelerator accelerator = SharedGPUContext.Accelerator;
var kernel = accelerator.LoadAutoGroupedStreamKernel<
Index2D, ArrayView2D<Vec4b, Stride2D.DenseX>, ArrayView2D<Vec4b, Stride2D.DenseX>,
Expand All @@ -104,7 +104,7 @@ private static void OnApply(
using var source = accelerator.Allocate2DDenseX<Vec4b>(new(size.Width, size.Height));
using var dest = accelerator.Allocate2DDenseX<Vec4b>(new(pixelRect.Width, pixelRect.Height));

SharedGPUContext.CopyFromCPU(source, surface.Value,
SharedGPUContext.CopyFromCPU(source, renderTarget.Value,
new SKImageInfo(size.Width, size.Height, SKColorType.Bgra8888));

kernel(
Expand All @@ -119,7 +119,7 @@ private static void OnApply(
SharedGPUContext.CopyToCPU(dest, skBmp);

EffectTarget newTarget = context.CreateTarget(bounds);
newTarget.Surface!.Value.Canvas.DrawBitmap(skBmp, 0, 0);
newTarget.RenderTarget!.Value.Canvas.DrawBitmap(skBmp, 0, 0);

target.Dispose();
context.Targets[i] = newTarget;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
using Beutl.Media.Source;
using Beutl.Graphics.Rendering;
using Beutl.Media.Source;
using SkiaSharp;

namespace Beutl.Graphics.Effects;

public class CustomFilterEffectContext
{
internal readonly IImmediateCanvasFactory _factory;

internal CustomFilterEffectContext(IImmediateCanvasFactory canvas, EffectTargets targets)
internal CustomFilterEffectContext(EffectTargets targets)
{
Targets = targets;
_factory = canvas;
}

public EffectTargets Targets { get; }
Expand Down Expand Up @@ -53,11 +51,10 @@ public void ForEach(Func<int, EffectTarget, EffectTargets> action)

public EffectTarget CreateTarget(Rect bounds)
{
SKSurface? surface = _factory.CreateRenderTarget((int)bounds.Width, (int)bounds.Height);
if (surface != null)
using var renderTarget = RenderTarget.Create((int)bounds.Width, (int)bounds.Height);
if (renderTarget != null)
{
using var surfaceRef = Ref<SKSurface>.Create(surface);
return new EffectTarget(surfaceRef, bounds);
return new EffectTarget(renderTarget, bounds);
}
else
{
Expand All @@ -67,11 +64,11 @@ public EffectTarget CreateTarget(Rect bounds)

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

return _factory.CreateCanvas(target.Surface.Value, true);
return new ImmediateCanvas(target.RenderTarget);
}
}
22 changes: 11 additions & 11 deletions src/Beutl.Engine/Graphics/FilterEffects/EffectTarget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ public EffectTarget(RenderNodeOperation node)
Bounds = node.Bounds;
}

public EffectTarget(Ref<SKSurface> surface, Rect originalBounds)
public EffectTarget(RenderTarget renderTarget, Rect originalBounds)
{
_target = surface.Clone();
_target = renderTarget.ShallowCopy();
OriginalBounds = originalBounds;
Bounds = originalBounds;
}
Expand All @@ -40,16 +40,16 @@ public EffectTarget()
public Size Size => Bounds.Size;

public RenderNodeOperation? NodeOperation => _target as RenderNodeOperation;

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

public bool IsEmpty => _target == null;

public EffectTarget Clone()
{
if (Surface != null)
if (RenderTarget != null)
{
return new EffectTarget(Surface, OriginalBounds) { Bounds = Bounds };
return new EffectTarget(RenderTarget, OriginalBounds) { Bounds = Bounds };
}
else
{
Expand All @@ -59,21 +59,21 @@ public EffectTarget Clone()

public void Dispose()
{
Surface?.Dispose();
RenderTarget?.Dispose();
NodeOperation?.Dispose();
_target = null;
OriginalBounds = default;
}

public void Draw(ImmediateCanvas canvas)
{
if (Surface != null)
if (RenderTarget != null)
{
canvas.DrawSurface(Surface.Value, default);
canvas.DrawRenderTarget(RenderTarget, default);
}
else if (NodeOperation != null)
else
{
NodeOperation.Render(canvas);
NodeOperation?.Render(canvas);
}
}
}
2 changes: 1 addition & 1 deletion src/Beutl.Engine/Graphics/FilterEffects/FEImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public void Accepts(CustomFilterEffectContext context)
{
using (target)
{
var innerContext = new FilterEffectCustomOperationContext(context._factory, target);
var innerContext = new FilterEffectCustomOperationContext(target);
Action.Invoke(Data, innerContext);

innerContext.Target.Bounds = TransformBounds!(Data, innerContext.Target.Bounds);
Expand Down
20 changes: 9 additions & 11 deletions src/Beutl.Engine/Graphics/FilterEffects/FilterEffectActivator.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using Beutl.Media.Source;
using Beutl.Graphics.Rendering;

using SkiaSharp;

namespace Beutl.Graphics.Effects;

public sealed class FilterEffectActivator(EffectTargets targets, SKImageFilterBuilder builder, IImmediateCanvasFactory factory) : IDisposable
public sealed class FilterEffectActivator(EffectTargets targets, SKImageFilterBuilder builder) : IDisposable
{
public SKImageFilterBuilder Builder { get; } = builder;

Expand All @@ -26,20 +26,18 @@ public void Flush(bool force = true)
for (int i = 0; i < CurrentTargets.Count; i++)
{
EffectTarget target = CurrentTargets[i];
SKSurface? surface = factory.CreateRenderTarget((int)target.OriginalBounds.Width, (int)target.OriginalBounds.Height);
using RenderTarget? surface = RenderTarget.Create((int)target.OriginalBounds.Width, (int)target.OriginalBounds.Height);

if (surface != null)
{
using ImmediateCanvas canvas = factory.CreateCanvas(surface, true);

using (var canvas = new ImmediateCanvas(surface))
using (canvas.PushTransform(Matrix.CreateTranslation(-target.OriginalBounds.X, -target.OriginalBounds.Y)))
using (canvas.PushPaint(paint))
{
target.Draw(canvas);
}

using var surfaceRef = Ref<SKSurface>.Create(surface);
var newTarget = new EffectTarget(surfaceRef, target.Bounds)
var newTarget = new EffectTarget(surface, target.Bounds)
{
OriginalBounds = target.OriginalBounds
};
Expand Down Expand Up @@ -85,7 +83,7 @@ public void Apply(FilterEffectContext context)
Flush();
if (CurrentTargets.Count == 0) return;

var customContext = new CustomFilterEffectContext(factory, CurrentTargets);
var customContext = new CustomFilterEffectContext(CurrentTargets);
custom.Accepts(customContext);

foreach (EffectTarget t in CurrentTargets)
Expand Down Expand Up @@ -127,7 +125,7 @@ public void Apply(FilterEffectContext context)

using EffectTargets cloned = CurrentTargets.Clone();
using var builder = new SKImageFilterBuilder();
using var activator = new FilterEffectActivator(cloned, builder, factory);
using var activator = new FilterEffectActivator(cloned, builder);

activator.Apply(context);
activator.Flush(false);
Expand All @@ -137,9 +135,9 @@ public void Apply(FilterEffectContext context)

foreach (EffectTarget t in activator.CurrentTargets)
{
if (t.Surface == null) continue;
if (t.RenderTarget == null) continue;

SKSurface innerSurface = t.Surface.Value;
SKSurface innerSurface = t.RenderTarget.Value;
using SKImage skImage = innerSurface.Snapshot();

if (filter == null)
Expand Down
Loading

0 comments on commit 53922c4

Please sign in to comment.