Skip to content

Commit 371aa37

Browse files
Add slowpath transform
1 parent b47a539 commit 371aa37

3 files changed

Lines changed: 61 additions & 18 deletions

File tree

src/ImageSharp.Drawing/Processing/Backends/CompositionSceneCommandBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public PathCompositionSceneCommand(in CompositionCommand command)
5656
/// <summary>
5757
/// Gets the wrapped composition command.
5858
/// </summary>
59-
public CompositionCommand Command { get; }
59+
public CompositionCommand Command { get; internal set; }
6060

6161
/// <inheritdoc />
6262
public override void Accept(ICompositionSceneCommandVisitor visitor) => visitor.Visit(this);

src/ImageSharp.Drawing/Processing/Backends/FlushScene.cs

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -695,7 +695,8 @@ private static bool TryPrepareFillPath(
695695
MemoryAllocator allocator,
696696
out PreparedFillItem prepared)
697697
{
698-
IPath path = ResolveCommandPath(command);
698+
IPath path = command.SourcePath;
699+
699700
if (!TryResolveRasterization(
700701
command.Brush,
701702
path.Bounds,
@@ -716,6 +717,7 @@ private static bool TryPrepareFillPath(
716717
command.DestinationOffset.Y,
717718
rasterizerOptions,
718719
allocator);
720+
719721
if (rasterizable is null)
720722
{
721723
prepared = default;
@@ -732,7 +734,7 @@ private static bool TryPrepareStrokePath(
732734
MemoryAllocator allocator,
733735
out PreparedStrokeItem prepared)
734736
{
735-
IPath path = ResolveCommandPath(command);
737+
IPath path = command.SourcePath;
736738

737739
if (!TryResolveRasterization(
738740
command.Brush,
@@ -793,6 +795,7 @@ private static bool TryPrepareLineSegmentStroke(
793795
command.DestinationOffset.X,
794796
command.DestinationOffset.Y,
795797
rasterizerOptions);
798+
796799
if (rasterizable is null)
797800
{
798801
prepared = default;
@@ -841,18 +844,6 @@ private static bool TryPreparePolylineStroke(
841844
return true;
842845
}
843846

844-
private static IPath ResolveCommandPath(in CompositionCommand command)
845-
{
846-
IPath path = command.SourcePath;
847-
848-
if (command.ClipPaths is { Count: > 0 })
849-
{
850-
path = path.Clip(command.ShapeOptions, command.ClipPaths);
851-
}
852-
853-
return path;
854-
}
855-
856847
private static bool TryResolveRasterization(
857848
Brush brush,
858849
RectangleF bounds,
@@ -896,6 +887,7 @@ private static bool TryResolveRasterization(
896887
options.RasterizationMode,
897888
options.SamplingOrigin,
898889
options.AntialiasThreshold);
890+
899891
brushBounds = absoluteInterest;
900892
return true;
901893
}

src/ImageSharp.Drawing/Processing/DrawingCanvasBatcher{TPixel}.cs

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Six Labors.
22
// Licensed under the Six Labors Split License.
33

4+
using System.Numerics;
45
using SixLabors.ImageSharp.Drawing.Processing.Backends;
56

67
namespace SixLabors.ImageSharp.Drawing.Processing;
@@ -22,6 +23,7 @@ internal sealed class DrawingCanvasBatcher<TPixel>
2223
private CompositionSceneCommand[] commands;
2324
private int commandCount;
2425
private bool hasLayers;
26+
private bool hasClips;
2527

2628
internal DrawingCanvasBatcher(
2729
Configuration configuration,
@@ -48,6 +50,7 @@ public void AddComposition(in CompositionCommand composition)
4850
this.EnsureCommandCapacity(this.commandCount + 1);
4951
this.commands[this.commandCount++] = new PathCompositionSceneCommand(composition);
5052
this.hasLayers |= composition.Kind is not CompositionCommandKind.FillLayer;
53+
this.hasClips |= composition.ClipPaths is not null;
5154
}
5255

5356
/// <summary>
@@ -86,9 +89,10 @@ public void FlushCompositions()
8689

8790
try
8891
{
89-
CompositionScene scene = new(
90-
new ArraySegment<CompositionSceneCommand>(this.commands, 0, this.commandCount),
91-
this.hasLayers);
92+
this.ApplyClipping();
93+
94+
CompositionScene scene = new(this.commands, this.hasLayers);
95+
9296
this.backend.FlushCompositions(this.configuration, this.TargetFrame, scene);
9397
}
9498
finally
@@ -118,4 +122,51 @@ private void EnsureCommandCapacity(int requiredCapacity)
118122

119123
Array.Resize(ref this.commands, nextCapacity);
120124
}
125+
126+
private void ApplyClipping()
127+
{
128+
if (!this.hasClips)
129+
{
130+
return;
131+
}
132+
133+
_ = Parallel.For(0, this.commandCount, i =>
134+
{
135+
CompositionSceneCommand command = this.commands[i];
136+
if (command is PathCompositionSceneCommand pathCommand)
137+
{
138+
CompositionCommand composition = pathCommand.Command;
139+
140+
// If clipping is present we need to apply that now before handing the command
141+
// to the backend. This avoids complicating the backend with clipping logic
142+
// and allows us to reuse the same optimized backend code for clipped and unclipped paths.
143+
if (composition.ClipPaths is { Count: > 0 })
144+
{
145+
IPath path = composition.SourcePath;
146+
147+
path = path.Transform(composition.Transform);
148+
149+
if (composition.Pen is not null)
150+
{
151+
path = path.GenerateOutline(composition.Pen.StrokeWidth);
152+
}
153+
154+
path = path.Clip(composition.ShapeOptions, composition.ClipPaths);
155+
156+
RasterizerOptions rasterizerOptions = composition.RasterizerOptions;
157+
158+
// Update the command with the clipped path.
159+
pathCommand.Command = CompositionCommand.Create(
160+
path,
161+
composition.Brush.Transform(composition.Transform),
162+
composition.GraphicsOptions,
163+
in rasterizerOptions,
164+
composition.ShapeOptions,
165+
Matrix4x4.Identity,
166+
composition.TargetBounds,
167+
composition.DestinationOffset);
168+
}
169+
}
170+
});
171+
}
121172
}

0 commit comments

Comments
 (0)