Browse Source

Add row action overload

af/octree-no-pixelmap
James Jackson-South 6 years ago
parent
commit
c338cdcfec
  1. 37
      src/ImageSharp/Advanced/ParallelUtils/IRowAction.cs
  2. 76
      src/ImageSharp/Advanced/ParallelUtils/ParallelHelper.cs
  3. 55
      src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs

37
src/ImageSharp/Advanced/ParallelUtils/IRowAction.cs

@ -0,0 +1,37 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Advanced.ParallelUtils
{
/// <summary>
/// Defines the contract for.
/// </summary>
public interface IRowAction
{
/// <summary>
/// Invokes the method passing the row interval.
/// </summary>
/// <param name="rows">The row interval.</param>
void Invoke(in RowInterval rows);
}
internal readonly struct WrappingRowAction<T> : IRowAction
where T : struct, IRowAction
{
private readonly T action;
public WrappingRowAction(ref T action)
{
this.action = action;
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
{
this.action.Invoke(in rows);
}
}
}

76
src/ImageSharp/Advanced/ParallelUtils/ParallelHelper.cs

@ -28,19 +28,86 @@ namespace SixLabors.ImageSharp.Advanced.ParallelUtils
{
var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration);
IterateRows(rectangle, parallelSettings, body);
IterateRows(rectangle, in parallelSettings, body);
}
/// <summary>
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s.
/// </summary>
/// <typeparam name="T">The type of row action to perform.</typeparam>
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="parallelSettings">The <see cref="ParallelExecutionSettings"/>.</param>
/// <param name="configuration">The <see cref="Configuration"/> to get the parallel settings from.</param>
/// <param name="body">The method body defining the iteration logic on a single <see cref="RowInterval"/>.</param>
public static void IterateRows(
public static void IterateRowsFast<T>(Rectangle rectangle, Configuration configuration, ref T body)
where T : struct, IRowAction
{
var parallelSettings = ParallelExecutionSettings.FromConfiguration(configuration);
IterateRowsFast(rectangle, in parallelSettings, ref body);
}
internal static void IterateRowsFast<T>(
Rectangle rectangle,
in ParallelExecutionSettings parallelSettings,
Action<RowInterval> body)
ref T body)
where T : struct, IRowAction
{
ValidateRectangle(rectangle);
int maxSteps = DivideCeil(
rectangle.Width * rectangle.Height,
parallelSettings.MinimumPixelsProcessedPerTask);
int numOfSteps = Math.Min(parallelSettings.MaxDegreeOfParallelism, maxSteps);
// Avoid TPL overhead in this trivial case:
if (numOfSteps == 1)
{
var rows = new RowInterval(rectangle.Top, rectangle.Bottom);
body.Invoke(in rows);
return;
}
int verticalStep = DivideCeil(rectangle.Height, numOfSteps);
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = numOfSteps };
int top = rectangle.Top;
int bottom = rectangle.Bottom;
var rowAction = new WrappingRowAction<T>(ref body);
Parallel.For(
0,
numOfSteps,
parallelOptions,
i =>
{
int yMin = top + (i * verticalStep);
if (yMin >= bottom)
{
return;
}
int yMax = Math.Min(yMin + verticalStep, bottom);
var rows = new RowInterval(yMin, yMax);
rowAction.Invoke(in rows);
});
}
/// <summary>
/// Iterate through the rows of a rectangle in optimized batches defined by <see cref="RowInterval"/>-s.
/// </summary>
/// <param name="rectangle">The <see cref="Rectangle"/>.</param>
/// <param name="parallelSettings">The <see cref="ParallelExecutionSettings"/>.</param>
/// <param name="body">The method body defining the iteration logic on a single <see cref="RowInterval"/>.</param>
public static void IterateRows(
Rectangle rectangle,
in ParallelExecutionSettings parallelSettings,
Action<RowInterval> body)
{
ValidateRectangle(rectangle);
@ -72,6 +139,7 @@ namespace SixLabors.ImageSharp.Advanced.ParallelUtils
int yMax = Math.Min(yMin + verticalStep, rectangle.Bottom);
var rows = new RowInterval(yMin, yMax);
body(rows);
});
}

55
src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs

@ -2,8 +2,10 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Advanced.ParallelUtils;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
@ -50,18 +52,49 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
ParallelExecutionSettings parallelSettings = ParallelExecutionSettings.FromConfiguration(this.Configuration)
.MultiplyMinimumPixelsPerTask(4);
ParallelHelper.IterateRows(
// ParallelHelper.IterateRows(
// bounds,
// parallelSettings,
// rows =>
// {
// for (int y = rows.Min; y < rows.Max; y++)
// {
// Span<TPixel> sourceRow = source.GetPixelRowSpan(y).Slice(bounds.Left);
// Span<TPixel> targetRow = destination.GetPixelRowSpan(y - bounds.Top);
// sourceRow.Slice(0, bounds.Width).CopyTo(targetRow);
// }
// });
var rowAction = new RowAction(ref bounds, source, destination);
ParallelHelper.IterateRowsFast(
bounds,
parallelSettings,
rows =>
{
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> sourceRow = source.GetPixelRowSpan(y).Slice(bounds.Left);
Span<TPixel> targetRow = destination.GetPixelRowSpan(y - bounds.Top);
sourceRow.Slice(0, bounds.Width).CopyTo(targetRow);
}
});
in parallelSettings,
ref rowAction);
}
private readonly struct RowAction : IRowAction
{
private readonly Rectangle bounds;
private readonly ImageFrame<TPixel> source;
private readonly ImageFrame<TPixel> destination;
public RowAction(ref Rectangle bounds, ImageFrame<TPixel> source, ImageFrame<TPixel> destination)
{
this.bounds = bounds;
this.source = source;
this.destination = destination;
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
{
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> sourceRow = this.source.GetPixelRowSpan(y).Slice(this.bounds.Left);
Span<TPixel> targetRow = this.destination.GetPixelRowSpan(y - this.bounds.Top);
sourceRow.Slice(0, this.bounds.Width).CopyTo(targetRow);
}
}
}
}
}

Loading…
Cancel
Save