Browse Source

Added progress callback to IImageProcessor and ParallelImageProcessor

Former-commit-id: 837027deaf8f46ed9dc6b9adf50d7fa3596de04d
Former-commit-id: cc005f037f4e6d9ead5763ab73c49abe09aadbe4
Former-commit-id: 2036768cdc0c07c71f746a5c0eb64633c2893f3a
af/merge-core
voidstar69 10 years ago
parent
commit
09786d2627
  1. 1
      src/ImageProcessor/Filters/Brightness.cs
  2. 17
      src/ImageProcessor/IImageProcessor.cs
  3. 33
      src/ImageProcessor/ParallelImageProcessor.cs
  4. 14
      tests/ImageProcessor.Tests/Processors/Filters/FilterTests.cs

1
src/ImageProcessor/Filters/Brightness.cs

@ -57,6 +57,7 @@ namespace ImageProcessor.Filters
target[x, y] = Color.Compress(new Color(vector3, color.A)); target[x, y] = Color.Compress(new Color(vector3, color.A));
} }
this.OnRowProcessed();
} }
}); });
} }

17
src/ImageProcessor/IImageProcessor.cs

@ -5,11 +5,28 @@
namespace ImageProcessor namespace ImageProcessor
{ {
public struct ProgressedEventArgs
{
public int numRowsProcessed;
public int totalRows;
}
public delegate void ProgressedEventHandler(object sender, ProgressedEventArgs e);
/// <summary> /// <summary>
/// Encapsulates methods to alter the pixels of an image. /// Encapsulates methods to alter the pixels of an image.
/// </summary> /// </summary>
public interface IImageProcessor public interface IImageProcessor
{ {
/// <summary>
/// Event fires when each row of the source image has been processed.
/// </summary>
/// <remarks>
/// This event may be called from threads other than the client thread, and from multiple threads simultaneously.
/// Individual row notifications may arrived out of order.
/// </remarks>
event ProgressedEventHandler OnProgressed;
/// <summary> /// <summary>
/// Applies the process to the specified portion of the specified <see cref="ImageBase"/>. /// Applies the process to the specified portion of the specified <see cref="ImageBase"/>.
/// </summary> /// </summary>

33
src/ImageProcessor/ParallelImageProcessor.cs

@ -6,6 +6,7 @@
namespace ImageProcessor namespace ImageProcessor
{ {
using System; using System;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
/// <summary> /// <summary>
@ -13,11 +14,38 @@ namespace ImageProcessor
/// </summary> /// </summary>
public abstract class ParallelImageProcessor : IImageProcessor public abstract class ParallelImageProcessor : IImageProcessor
{ {
/// <inheritdoc/>
public event ProgressedEventHandler OnProgressed;
/// <summary> /// <summary>
/// Gets or sets the count of workers to run the process in parallel. /// Gets or sets the count of workers to run the process in parallel.
/// </summary> /// </summary>
public virtual int Parallelism { get; set; } = Environment.ProcessorCount * 2; public virtual int Parallelism { get; set; } = Environment.ProcessorCount * 2;
/// <summary>
/// The number of rows processed by a derived class.
/// </summary>
private int numRowsProcessed;
/// <summary>
/// The total number of rows that will be processed by a derived class.
/// </summary>
private int totalRows;
/// <summary>
/// Must be called by derived classes after processing a single row.
/// </summary>
protected void OnRowProcessed()
{
if(this.OnProgressed != null)
{
int currThreadNumRows = Interlocked.Add(ref this.numRowsProcessed, 1);
// Report progress. This may be on the client's thread, or on a Task library thread.
this.OnProgressed(this, new ProgressedEventArgs { numRowsProcessed = currThreadNumRows, totalRows = this.totalRows });
}
}
/// <inheritdoc/> /// <inheritdoc/>
public void Apply(ImageBase target, ImageBase source, Rectangle sourceRectangle) public void Apply(ImageBase target, ImageBase source, Rectangle sourceRectangle)
{ {
@ -26,6 +54,9 @@ namespace ImageProcessor
Image temp = frame != null ? new Image(frame) : new Image((Image)source); Image temp = frame != null ? new Image(frame) : new Image((Image)source);
this.OnApply(temp, target, target.Bounds, sourceRectangle); this.OnApply(temp, target, target.Bounds, sourceRectangle);
this.numRowsProcessed = 0;
this.totalRows = sourceRectangle.Height;
if (this.Parallelism > 1) if (this.Parallelism > 1)
{ {
int partitionCount = this.Parallelism; int partitionCount = this.Parallelism;
@ -72,6 +103,8 @@ namespace ImageProcessor
this.OnApply(temp, target, target.Bounds, sourceRectangle); this.OnApply(temp, target, target.Bounds, sourceRectangle);
targetRectangle = target.Bounds; targetRectangle = target.Bounds;
this.numRowsProcessed = 0;
this.totalRows = targetRectangle.Bottom;
if (this.Parallelism > 1) if (this.Parallelism > 1)
{ {

14
tests/ImageProcessor.Tests/Processors/Filters/FilterTests.cs

@ -1,6 +1,7 @@
 
namespace ImageProcessor.Tests namespace ImageProcessor.Tests
{ {
using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
@ -65,12 +66,25 @@ namespace ImageProcessor.Tests
string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file); string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"TestOutput/Filter/{ Path.GetFileName(filename) }")) using (FileStream output = File.OpenWrite($"TestOutput/Filter/{ Path.GetFileName(filename) }"))
{ {
lastRowProcessed = 0;
processor.OnProgressed += ProgressUpdate;
image.Process(processor).Save(output); image.Process(processor).Save(output);
processor.OnProgressed -= ProgressUpdate;
} }
Trace.WriteLine($"{ name }: { watch.ElapsedMilliseconds}ms"); Trace.WriteLine($"{ name }: { watch.ElapsedMilliseconds}ms");
} }
} }
} }
private static int allowedVariability = Environment.ProcessorCount * 4;
private int lastRowProcessed;
private void ProgressUpdate(object sender, ProgressedEventArgs e)
{
Assert.InRange(e.numRowsProcessed, 1, e.totalRows);
Assert.InRange(e.numRowsProcessed, lastRowProcessed - allowedVariability, lastRowProcessed + allowedVariability);
lastRowProcessed = e.numRowsProcessed;
}
} }
} }

Loading…
Cancel
Save