Pass additional arguments to the PixelAccessor delegate to allow external span access. #1904
-
@anton struggling to port some of the ImageSharp.Drawing brushes to use the new memory API. They used For example public override void Apply(Span<float> scanline, int x, int y)
{
Span<TPixel> destinationRow = this.Target.GetPixelRowSpan(y).Slice(x);
// Constrain the spans to each other
if (destinationRow.Length > scanline.Length)
{
destinationRow = destinationRow.Slice(0, scanline.Length);
}
else
{
scanline = scanline.Slice(0, destinationRow.Length);
}
Configuration configuration = this.Configuration;
if (this.Options.BlendPercentage == 1F)
{
// TODO: refactor the BlendPercentage == 1 logic to a separate, simpler BrushApplicator class.
this.Blender.Blend(configuration, destinationRow, destinationRow, this.colors.Memory.Span, scanline);
}
else
{
Span<float> amounts = this.blenderBuffers.AmountSpan.Slice(0, scanline.Length);
for (int i = 0; i < scanline.Length; i++)
{
amounts[i] = scanline[i] * this.Options.BlendPercentage;
}
this.Blender.Blend(
configuration,
destinationRow,
destinationRow,
this.colors.Memory.Span,
amounts);
}
} We have two options as I see it.
Delegate /// <summary>
/// A delegate to be executed on a <see cref="PixelAccessor{TPixel}"/>.
/// </summary>
/// <typeparam name="TPixel">The pixel type.</typeparam>
/// <typeparam name="TArg">The type of the object that represents the processor arguments.
/// This type parameter is contravariant.That is, you can use either the type you specified or any type that is less derived.
/// </typeparam>
public delegate void PixelAccessorActionWithArgument<TPixel, in TArg>(PixelAccessor<TPixel> pixelAccessor, TArg arg)
where TPixel : unmanaged, IPixel<TPixel>; Method /// <summary>
/// Execute <paramref name="processPixels"/> to process image pixels in a safe and efficient manner.
/// </summary>
/// <param name="arg">The processor arguments.</param>
/// <param name="processPixels">The <see cref="PixelAccessorAction{TPixel}"/> defining the pixel operations.</param>
/// <typeparam name="TArg">The type of the object that represents the processor arguments.
/// This type parameter is contravariant.That is, you can use either the type you specified or any type that is less derived.
/// </typeparam>
public void ProcessPixelRows<TArg>(TArg arg, PixelAccessorActionWithArgument<TPixel, TArg> processPixels)
{
Guard.NotNull(processPixels, nameof(processPixels));
Buffer2D<TPixel> buffer = this.Frames.RootFrame.PixelBuffer;
buffer.FastMemoryGroup.IncreaseRefCounts();
try
{
var accessor = new PixelAccessor<TPixel>(buffer);
processPixels(accessor, arg);
}
finally
{
buffer.FastMemoryGroup.DecreaseRefCounts();
}
} Calling the correct method would be something like this? public override void Apply(Span<float> scanline, int x, int y)
{
unsafe
{
fixed (float* ptr = scanline)
{
this.Target.ProcessPixelRows((Ptr: (IntPtr)ptr, scanline.Length),
(accessor, args) =>
{
var scanline2 = new Span<float>((float*)args.Ptr, args.Length);
Span<TPixel> destinationRow = accessor.GetRowSpan(y).Slice(x);
// Constrain the spans to each other
if (destinationRow.Length > scanline2.Length)
{
destinationRow = destinationRow.Slice(0, scanline2.Length);
}
else
{
scanline2 = scanline2.Slice(0, destinationRow.Length);
}
Configuration configuration = this.Configuration;
if (this.Options.BlendPercentage == 1F)
{
// TODO: refactor the BlendPercentage == 1 logic to a separate, simpler BrushApplicator class.
this.Blender.Blend(configuration, destinationRow, destinationRow, this.colors.Memory.Span, scanline2);
}
else
{
Span<float> amounts = this.blenderBuffers.AmountSpan.Slice(0, scanline2.Length);
for (int i = 0; i < scanline2.Length; i++)
{
amounts[i] = scanline2[i] * this.Options.BlendPercentage;
}
this.Blender.Blend(
configuration,
destinationRow,
destinationRow,
this.colors.Memory.Span,
amounts);
}
});
}
}
} |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
I think we should keep We should better expose so ImageSharp.Drawing can go with
|
Beta Was this translation helpful? Give feedback.
I think we should keep
ProcessPixelRows
concepts simple, it's an API for direct pixel access outside of processor code.We should better expose
ImageFrame<T>.PixelBuffer
:ImageSharp/src/ImageSharp/ImageFrame{TPixel}.cs
Line 151 in 04f64b6
so ImageSharp.Drawing can go with
Buffer2D<T>.DangerousGetRowSpan(y)
, just like most core library processors after my refactor:https://github.com/SixLabors/ImageSharp/search?q=dangerousgetrowspan
image.Frames.RootFrame.PixelBuffer
is hidden enough from beginners, I actually thought it's already public API!