📷 A modern, cross-platform, 2D Graphics library for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

123 lines
4.5 KiB

// <copyright file="ParallelImageProcessor.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessor
{
using System;
using System.Threading.Tasks;
/// <summary>
/// Allows the application of processors using parallel processing.
/// </summary>
public abstract class ParallelImageProcessor : IImageProcessor
{
/// <summary>
/// Gets or sets the count of workers to run the process in parallel.
/// </summary>
public int Parallelism { get; set; } = Environment.ProcessorCount;
/// <inheritdoc/>
public void Apply(ImageBase target, ImageBase source, Rectangle rectangle)
{
this.OnApply();
if (this.Parallelism > 1)
{
int partitionCount = this.Parallelism;
Task[] tasks = new Task[partitionCount];
for (int p = 0; p < partitionCount; p++)
{
int current = p;
tasks[p] = Task.Run(() =>
{
int batchSize = rectangle.Height / partitionCount;
int yStart = rectangle.Y + (current * batchSize);
int yEnd = current == partitionCount - 1 ? rectangle.Bottom : yStart + batchSize;
this.Apply(target, source, rectangle, yStart, yEnd);
});
}
Task.WaitAll(tasks);
}
else
{
this.Apply(target, source, rectangle, rectangle.Y, rectangle.Bottom);
}
}
/// <inheritdoc/>
public void Apply(ImageBase target, ImageBase source, int width, int height, Rectangle targetRectangle = default(Rectangle), Rectangle sourceRectangle = default(Rectangle))
{
this.OnApply();
byte[] pixels = new byte[width * height * 4];
target.SetPixels(width, height, pixels);
if (targetRectangle == Rectangle.Empty)
{
targetRectangle = target.Bounds;
}
if (sourceRectangle == Rectangle.Empty)
{
sourceRectangle = source.Bounds;
}
if (this.Parallelism > 1)
{
int partitionCount = this.Parallelism;
Task[] tasks = new Task[partitionCount];
for (int p = 0; p < partitionCount; p++)
{
int current = p;
tasks[p] = Task.Run(() =>
{
int batchSize = targetRectangle.Bottom / partitionCount;
int yStart = current * batchSize;
int yEnd = current == partitionCount - 1 ? targetRectangle.Bottom : yStart + batchSize;
this.Apply(target, source, targetRectangle, sourceRectangle, yStart, yEnd);
});
}
Task.WaitAll(tasks);
}
else
{
this.Apply(target, source, targetRectangle, sourceRectangle, targetRectangle.Y, targetRectangle.Bottom);
}
}
/// <summary>
/// This method is called before the process is applied to prepare the processor.
/// </summary>
protected virtual void OnApply()
{
}
protected abstract void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY);
/// <summary>
/// Apply a process to an image to alter the pixels at the area of the specified rectangle.
/// </summary>
/// <param name="target">Target image to apply the process to.</param>
/// <param name="source">The source image. Cannot be null.</param>
/// <param name="rectangle">
/// The rectangle, which defines the area of the image where the process should be applied to.
/// </param>
/// <param name="startY">The index of the row within the image to start processing.</param>
/// <param name="endY">The index of the row within the image to end processing.</param>
/// <remarks>
/// The method keeps the source image unchanged and returns the
/// the result of image processing filter as new image.
/// </remarks>
protected abstract void Apply(ImageBase target, ImageBase source, Rectangle rectangle, int startY, int endY);
}
}