// // Copyright © James South and contributors. // Licensed under the Apache License, Version 2.0. // namespace ImageProcessor { using System; using System.Threading.Tasks; /// /// Allows the application of processors using parallel processing. /// public abstract class ParallelImageProcessor : IImageProcessor { /// /// Gets or sets the count of workers to run the process in parallel. /// public int Parallelism { get; set; } = Environment.ProcessorCount; /// public void Apply(ImageBase target, ImageBase source, Rectangle sourceRectangle) { 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 = sourceRectangle.Height / partitionCount; int yStart = sourceRectangle.Y + (current * batchSize); int yEnd = current == partitionCount - 1 ? sourceRectangle.Bottom : yStart + batchSize; this.Apply(target, source, target.Bounds, sourceRectangle, yStart, yEnd); }); } Task.WaitAll(tasks); } else { this.Apply(target, source, target.Bounds, sourceRectangle, sourceRectangle.Y, sourceRectangle.Bottom); } } /// 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); } } /// /// This method is called before the process is applied to prepare the processor. /// protected virtual void OnApply() { } /// /// Applies the process to the specified portion of the specified at the specified location /// and with the specified size. /// /// Target image to apply the process to. /// The source image. Cannot be null. /// /// The structure that specifies the location and size of the drawn image. /// The image is scaled to fit the rectangle. /// /// /// The structure that specifies the portion of the image object to draw. /// /// The index of the row within the source image to start processing. /// The index of the row within the source image to end processing. /// /// The method keeps the source image unchanged and returns the /// the result of image process as new image. /// protected abstract void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY); } }