Browse Source

Complete progress messaging.

Former-commit-id: 6a4ad9c1dc9ae7635bb4108e2840bcf88c4894e5
Former-commit-id: 5fe2186a4fbc536b4d050ac4a5b2649b8532fe21
Former-commit-id: 691721d5d4d6e3aa1d18deb7b2da7b521f497bff
pull/17/head
James Jackson-South 10 years ago
parent
commit
06d5ce7e64
  1. 384
      src/ImageProcessor/Filters/ImageFilterExtensions.cs
  2. 12
      src/ImageProcessor/IImageProcessor.cs
  3. 38
      src/ImageProcessor/ParallelImageProcessor.cs
  4. 23
      src/ImageProcessor/ProgressEventArgs.cs
  5. 15
      src/ImageProcessor/Samplers/ImageSampleExtensions.cs
  6. 6
      tests/ImageProcessor.Tests/Processors/Filters/FilterTests.cs
  7. 16
      tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs

384
src/ImageProcessor/Filters/ImageFilterExtensions.cs

@ -15,10 +15,11 @@ namespace ImageProcessor.Filters
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="percent">The new opacity of the image. Must be between 0 and 100.</param> /// <param name="percent">The new opacity of the image. Must be between 0 and 100.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image Alpha(this Image source, int percent) public static Image Alpha(this Image source, int percent, ProgressEventHandler progressHandler = null)
{ {
return Alpha(source, percent, source.Bounds); return Alpha(source, percent, source.Bounds, progressHandler);
} }
/// <summary> /// <summary>
@ -29,10 +30,21 @@ namespace ImageProcessor.Filters
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image Alpha(this Image source, int percent, Rectangle rectangle) public static Image Alpha(this Image source, int percent, Rectangle rectangle, ProgressEventHandler progressHandler = null)
{ {
return source.Process(rectangle, new Alpha(percent)); Alpha processor = new Alpha(percent);
processor.OnProgress += progressHandler;
try
{
return source.Process(rectangle, processor);
}
finally
{
processor.OnProgress -= progressHandler;
}
} }
/// <summary> /// <summary>
@ -40,10 +52,21 @@ namespace ImageProcessor.Filters
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="color">The color to set as the background.</param> /// <param name="color">The color to set as the background.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image BackgroundColor(this Image source, Color color) public static Image BackgroundColor(this Image source, Color color, ProgressEventHandler progressHandler = null)
{ {
return source.Process(source.Bounds, new BackgroundColor(color)); BackgroundColor processor = new BackgroundColor(color);
processor.OnProgress += progressHandler;
try
{
return source.Process(source.Bounds, processor);
}
finally
{
processor.OnProgress -= progressHandler;
}
} }
/// <summary> /// <summary>
@ -52,10 +75,11 @@ namespace ImageProcessor.Filters
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="image">The image to blend with the currently processing image.</param> /// <param name="image">The image to blend with the currently processing image.</param>
/// <param name="percent">The opacity of the image image to blend. Must be between 0 and 100.</param> /// <param name="percent">The opacity of the image image to blend. Must be between 0 and 100.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image Blend(this Image source, ImageBase image, int percent = 50) public static Image Blend(this Image source, ImageBase image, int percent = 50, ProgressEventHandler progressHandler = null)
{ {
return source.Process(source.Bounds, new Blend(image, percent)); return Blend(source, image, percent, source.Bounds, progressHandler);
} }
/// <summary> /// <summary>
@ -67,20 +91,32 @@ namespace ImageProcessor.Filters
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image Blend(this Image source, ImageBase image, int percent, Rectangle rectangle) public static Image Blend(this Image source, ImageBase image, int percent, Rectangle rectangle, ProgressEventHandler progressHandler = null)
{ {
return source.Process(rectangle, new Blend(image, percent)); Blend processor = new Blend(image, percent);
processor.OnProgress += progressHandler;
try
{
return source.Process(rectangle, processor);
}
finally
{
processor.OnProgress -= progressHandler;
}
} }
/// <summary> /// <summary>
/// Applies black and white toning to the image. /// Applies black and white toning to the image.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image BlackWhite(this Image source) public static Image BlackWhite(this Image source, ProgressEventHandler progressHandler = null)
{ {
return BlackWhite(source, source.Bounds); return BlackWhite(source, source.Bounds, progressHandler);
} }
/// <summary> /// <summary>
@ -90,10 +126,21 @@ namespace ImageProcessor.Filters
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image BlackWhite(this Image source, Rectangle rectangle) public static Image BlackWhite(this Image source, Rectangle rectangle, ProgressEventHandler progressHandler = null)
{ {
return source.Process(rectangle, new BlackWhite()); BlackWhite processor = new BlackWhite();
processor.OnProgress += progressHandler;
try
{
return source.Process(rectangle, processor);
}
finally
{
processor.OnProgress -= progressHandler;
}
} }
/// <summary> /// <summary>
@ -101,10 +148,11 @@ namespace ImageProcessor.Filters
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="radius">The 'radius' value representing the size of the area to sample.</param> /// <param name="radius">The 'radius' value representing the size of the area to sample.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image BoxBlur(this Image source, int radius = 7) public static Image BoxBlur(this Image source, int radius = 7, ProgressEventHandler progressHandler = null)
{ {
return BoxBlur(source, radius, source.Bounds); return BoxBlur(source, radius, source.Bounds, progressHandler);
} }
/// <summary> /// <summary>
@ -115,10 +163,21 @@ namespace ImageProcessor.Filters
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image BoxBlur(this Image source, int radius, Rectangle rectangle) public static Image BoxBlur(this Image source, int radius, Rectangle rectangle, ProgressEventHandler progressHandler = null)
{ {
return source.Process(rectangle, new BoxBlur(radius)); BoxBlur processor = new BoxBlur(radius);
processor.OnProgress += progressHandler;
try
{
return source.Process(rectangle, processor);
}
finally
{
processor.OnProgress -= progressHandler;
}
} }
/// <summary> /// <summary>
@ -126,10 +185,11 @@ namespace ImageProcessor.Filters
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="amount">The new brightness of the image. Must be between -100 and 100.</param> /// <param name="amount">The new brightness of the image. Must be between -100 and 100.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image Brightness(this Image source, int amount) public static Image Brightness(this Image source, int amount, ProgressEventHandler progressHandler = null)
{ {
return Brightness(source, amount, source.Bounds); return Brightness(source, amount, source.Bounds, progressHandler);
} }
/// <summary> /// <summary>
@ -140,10 +200,21 @@ namespace ImageProcessor.Filters
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image Brightness(this Image source, int amount, Rectangle rectangle) public static Image Brightness(this Image source, int amount, Rectangle rectangle, ProgressEventHandler progressHandler = null)
{ {
return source.Process(rectangle, new Brightness(amount)); Brightness processor = new Brightness(amount);
processor.OnProgress += progressHandler;
try
{
return source.Process(rectangle, processor);
}
finally
{
processor.OnProgress -= progressHandler;
}
} }
/// <summary> /// <summary>
@ -151,10 +222,11 @@ namespace ImageProcessor.Filters
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="amount">The new contrast of the image. Must be between -100 and 100.</param> /// <param name="amount">The new contrast of the image. Must be between -100 and 100.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image Contrast(this Image source, int amount) public static Image Contrast(this Image source, int amount, ProgressEventHandler progressHandler = null)
{ {
return Contrast(source, amount, source.Bounds); return Contrast(source, amount, source.Bounds, progressHandler);
} }
/// <summary> /// <summary>
@ -165,10 +237,21 @@ namespace ImageProcessor.Filters
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image Contrast(this Image source, int amount, Rectangle rectangle) public static Image Contrast(this Image source, int amount, Rectangle rectangle, ProgressEventHandler progressHandler = null)
{ {
return source.Process(rectangle, new Contrast(amount)); Contrast processor = new Contrast(amount);
processor.OnProgress += progressHandler;
try
{
return source.Process(rectangle, processor);
}
finally
{
processor.OnProgress -= progressHandler;
}
} }
/// <summary> /// <summary>
@ -176,10 +259,11 @@ namespace ImageProcessor.Filters
/// operating in greyscale mode. /// operating in greyscale mode.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image DetectEdges(this Image source) public static Image DetectEdges(this Image source, ProgressEventHandler progressHandler = null)
{ {
return DetectEdges(source, source.Bounds, new Sobel { Greyscale = true }); return DetectEdges(source, source.Bounds, new Sobel { Greyscale = true }, progressHandler);
} }
/// <summary> /// <summary>
@ -187,10 +271,11 @@ namespace ImageProcessor.Filters
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="filter">The filter for detecting edges.</param> /// <param name="filter">The filter for detecting edges.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image DetectEdges(this Image source, IEdgeDetectorFilter filter) public static Image DetectEdges(this Image source, IEdgeDetectorFilter filter, ProgressEventHandler progressHandler = null)
{ {
return DetectEdges(source, source.Bounds, filter); return DetectEdges(source, source.Bounds, filter, progressHandler);
} }
/// <summary> /// <summary>
@ -201,10 +286,20 @@ namespace ImageProcessor.Filters
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="filter">The filter for detecting edges.</param> /// <param name="filter">The filter for detecting edges.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image DetectEdges(this Image source, Rectangle rectangle, IEdgeDetectorFilter filter) public static Image DetectEdges(this Image source, Rectangle rectangle, IEdgeDetectorFilter filter, ProgressEventHandler progressHandler = null)
{ {
return source.Process(rectangle, filter); filter.OnProgress += progressHandler;
try
{
return source.Process(rectangle, filter);
}
finally
{
filter.OnProgress -= progressHandler;
}
} }
/// <summary> /// <summary>
@ -212,10 +307,11 @@ namespace ImageProcessor.Filters
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="mode">The formula to apply to perform the operation.</param> /// <param name="mode">The formula to apply to perform the operation.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image Greyscale(this Image source, GreyscaleMode mode = GreyscaleMode.Bt709) public static Image Greyscale(this Image source, GreyscaleMode mode = GreyscaleMode.Bt709, ProgressEventHandler progressHandler = null)
{ {
return Greyscale(source, source.Bounds, mode); return Greyscale(source, source.Bounds, mode, progressHandler);
} }
/// <summary> /// <summary>
@ -226,12 +322,24 @@ namespace ImageProcessor.Filters
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="mode">The formula to apply to perform the operation.</param> /// <param name="mode">The formula to apply to perform the operation.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image Greyscale(this Image source, Rectangle rectangle, GreyscaleMode mode = GreyscaleMode.Bt709) public static Image Greyscale(this Image source, Rectangle rectangle, GreyscaleMode mode = GreyscaleMode.Bt709, ProgressEventHandler progressHandler = null)
{ {
return mode == GreyscaleMode.Bt709 IImageProcessor processor = mode == GreyscaleMode.Bt709
? source.Process(rectangle, new GreyscaleBt709()) ? (IImageProcessor)new GreyscaleBt709()
: source.Process(rectangle, new GreyscaleBt601()); : new GreyscaleBt601();
processor.OnProgress += progressHandler;
try
{
return source.Process(rectangle, processor);
}
finally
{
processor.OnProgress -= progressHandler;
}
} }
/// <summary> /// <summary>
@ -239,10 +347,11 @@ namespace ImageProcessor.Filters
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="sigma">The 'sigma' value representing the weight of the blur.</param> /// <param name="sigma">The 'sigma' value representing the weight of the blur.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image GuassianBlur(this Image source, float sigma = 3f) public static Image GuassianBlur(this Image source, float sigma = 3f, ProgressEventHandler progressHandler = null)
{ {
return GuassianBlur(source, sigma, source.Bounds); return GuassianBlur(source, sigma, source.Bounds, progressHandler);
} }
/// <summary> /// <summary>
@ -253,10 +362,21 @@ namespace ImageProcessor.Filters
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image GuassianBlur(this Image source, float sigma, Rectangle rectangle) public static Image GuassianBlur(this Image source, float sigma, Rectangle rectangle, ProgressEventHandler progressHandler = null)
{ {
return source.Process(rectangle, new GuassianBlur(sigma)); GuassianBlur processor = new GuassianBlur(sigma);
processor.OnProgress += progressHandler;
try
{
return source.Process(rectangle, processor);
}
finally
{
processor.OnProgress -= progressHandler;
}
} }
/// <summary> /// <summary>
@ -264,10 +384,11 @@ namespace ImageProcessor.Filters
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="sigma">The 'sigma' value representing the weight of the blur.</param> /// <param name="sigma">The 'sigma' value representing the weight of the blur.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image GuassianSharpen(this Image source, float sigma = 3f) public static Image GuassianSharpen(this Image source, float sigma = 3f, ProgressEventHandler progressHandler = null)
{ {
return GuassianSharpen(source, sigma, source.Bounds); return GuassianSharpen(source, sigma, source.Bounds, progressHandler);
} }
/// <summary> /// <summary>
@ -278,10 +399,21 @@ namespace ImageProcessor.Filters
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image GuassianSharpen(this Image source, float sigma, Rectangle rectangle) public static Image GuassianSharpen(this Image source, float sigma, Rectangle rectangle, ProgressEventHandler progressHandler = null)
{ {
return source.Process(rectangle, new GuassianSharpen(sigma)); GuassianSharpen processor = new GuassianSharpen(sigma);
processor.OnProgress += progressHandler;
try
{
return source.Process(rectangle, processor);
}
finally
{
processor.OnProgress -= progressHandler;
}
} }
/// <summary> /// <summary>
@ -289,10 +421,11 @@ namespace ImageProcessor.Filters
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="degrees">The angle in degrees to adjust the image.</param> /// <param name="degrees">The angle in degrees to adjust the image.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image Hue(this Image source, float degrees) public static Image Hue(this Image source, float degrees, ProgressEventHandler progressHandler = null)
{ {
return Hue(source, degrees, source.Bounds); return Hue(source, degrees, source.Bounds, progressHandler);
} }
/// <summary> /// <summary>
@ -303,20 +436,32 @@ namespace ImageProcessor.Filters
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image Hue(this Image source, float degrees, Rectangle rectangle) public static Image Hue(this Image source, float degrees, Rectangle rectangle, ProgressEventHandler progressHandler = null)
{ {
return source.Process(rectangle, new Hue(degrees)); Hue processor = new Hue(degrees);
processor.OnProgress += progressHandler;
try
{
return source.Process(rectangle, processor);
}
finally
{
processor.OnProgress -= progressHandler;
}
} }
/// <summary> /// <summary>
/// Inverts the colors of the image. /// Inverts the colors of the image.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image Invert(this Image source) public static Image Invert(this Image source, ProgressEventHandler progressHandler = null)
{ {
return Invert(source, source.Bounds); return Invert(source, source.Bounds, progressHandler);
} }
/// <summary> /// <summary>
@ -326,20 +471,32 @@ namespace ImageProcessor.Filters
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image Invert(this Image source, Rectangle rectangle) public static Image Invert(this Image source, Rectangle rectangle, ProgressEventHandler progressHandler = null)
{ {
return source.Process(rectangle, new Invert()); Invert processor = new Invert();
processor.OnProgress += progressHandler;
try
{
return source.Process(rectangle, processor);
}
finally
{
processor.OnProgress -= progressHandler;
}
} }
/// <summary> /// <summary>
/// Alters the colors of the image recreating an old Kodachrome camera effect. /// Alters the colors of the image recreating an old Kodachrome camera effect.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image Kodachrome(this Image source) public static Image Kodachrome(this Image source, ProgressEventHandler progressHandler = null)
{ {
return Kodachrome(source, source.Bounds); return Kodachrome(source, source.Bounds, progressHandler);
} }
/// <summary> /// <summary>
@ -349,20 +506,32 @@ namespace ImageProcessor.Filters
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image Kodachrome(this Image source, Rectangle rectangle) public static Image Kodachrome(this Image source, Rectangle rectangle, ProgressEventHandler progressHandler = null)
{ {
return source.Process(rectangle, new Kodachrome()); Kodachrome processor = new Kodachrome();
processor.OnProgress += progressHandler;
try
{
return source.Process(rectangle, processor);
}
finally
{
processor.OnProgress -= progressHandler;
}
} }
/// <summary> /// <summary>
/// Alters the colors of the image recreating an old Lomograph camera effect. /// Alters the colors of the image recreating an old Lomograph camera effect.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image Lomograph(this Image source) public static Image Lomograph(this Image source, ProgressEventHandler progressHandler = null)
{ {
return Lomograph(source, source.Bounds); return Lomograph(source, source.Bounds, progressHandler);
} }
/// <summary> /// <summary>
@ -372,20 +541,32 @@ namespace ImageProcessor.Filters
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image Lomograph(this Image source, Rectangle rectangle) public static Image Lomograph(this Image source, Rectangle rectangle, ProgressEventHandler progressHandler = null)
{ {
return source.Process(rectangle, new Lomograph()); Lomograph processor = new Lomograph();
processor.OnProgress += progressHandler;
try
{
return source.Process(rectangle, processor);
}
finally
{
processor.OnProgress -= progressHandler;
}
} }
/// <summary> /// <summary>
/// Alters the colors of the image recreating an old Polaroid camera effect. /// Alters the colors of the image recreating an old Polaroid camera effect.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image Polaroid(this Image source) public static Image Polaroid(this Image source, ProgressEventHandler progressHandler = null)
{ {
return Polaroid(source, source.Bounds); return Polaroid(source, source.Bounds, progressHandler);
} }
/// <summary> /// <summary>
@ -395,10 +576,21 @@ namespace ImageProcessor.Filters
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image Polaroid(this Image source, Rectangle rectangle) public static Image Polaroid(this Image source, Rectangle rectangle, ProgressEventHandler progressHandler = null)
{ {
return source.Process(rectangle, new Polaroid()); Polaroid processor = new Polaroid();
processor.OnProgress += progressHandler;
try
{
return source.Process(rectangle, processor);
}
finally
{
processor.OnProgress -= progressHandler;
}
} }
/// <summary> /// <summary>
@ -406,10 +598,11 @@ namespace ImageProcessor.Filters
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="size">The size of the pixels.</param> /// <param name="size">The size of the pixels.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image Pixelate(this Image source, int size = 4) public static Image Pixelate(this Image source, int size = 4, ProgressEventHandler progressHandler = null)
{ {
return source.Process(source.Bounds, new Pixelate(size)); return Pixelate(source, size, source.Bounds, progressHandler);
} }
/// <summary> /// <summary>
@ -420,10 +613,21 @@ namespace ImageProcessor.Filters
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image Pixelate(this Image source, int size, Rectangle rectangle) public static Image Pixelate(this Image source, int size, Rectangle rectangle, ProgressEventHandler progressHandler = null)
{ {
return source.Process(rectangle, new Pixelate(size)); Pixelate processor = new Pixelate(size);
processor.OnProgress += progressHandler;
try
{
return source.Process(rectangle, processor);
}
finally
{
processor.OnProgress -= progressHandler;
}
} }
/// <summary> /// <summary>
@ -431,10 +635,11 @@ namespace ImageProcessor.Filters
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="amount">The new saturation of the image. Must be between -100 and 100.</param> /// <param name="amount">The new saturation of the image. Must be between -100 and 100.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image Saturation(this Image source, int amount) public static Image Saturation(this Image source, int amount, ProgressEventHandler progressHandler = null)
{ {
return Saturation(source, amount, source.Bounds); return Saturation(source, amount, source.Bounds, progressHandler);
} }
/// <summary> /// <summary>
@ -445,20 +650,32 @@ namespace ImageProcessor.Filters
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image Saturation(this Image source, int amount, Rectangle rectangle) public static Image Saturation(this Image source, int amount, Rectangle rectangle, ProgressEventHandler progressHandler = null)
{ {
return source.Process(rectangle, new Saturation(amount)); Saturation processor = new Saturation(amount);
processor.OnProgress += progressHandler;
try
{
return source.Process(rectangle, processor);
}
finally
{
processor.OnProgress -= progressHandler;
}
} }
/// <summary> /// <summary>
/// Applies sepia toning to the image. /// Applies sepia toning to the image.
/// </summary> /// </summary>
/// <param name="source">The image this method extends.</param> /// <param name="source">The image this method extends.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image Sepia(this Image source) public static Image Sepia(this Image source, ProgressEventHandler progressHandler = null)
{ {
return Sepia(source, source.Bounds); return Sepia(source, source.Bounds, progressHandler);
} }
/// <summary> /// <summary>
@ -468,10 +685,21 @@ namespace ImageProcessor.Filters
/// <param name="rectangle"> /// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter. /// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param> /// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image"/>.</returns> /// <returns>The <see cref="Image"/>.</returns>
public static Image Sepia(this Image source, Rectangle rectangle) public static Image Sepia(this Image source, Rectangle rectangle, ProgressEventHandler progressHandler = null)
{ {
return source.Process(rectangle, new Sepia()); Sepia processor = new Sepia();
processor.OnProgress += progressHandler;
try
{
return source.Process(rectangle, processor);
}
finally
{
processor.OnProgress -= progressHandler;
}
} }
} }
} }

12
src/ImageProcessor/IImageProcessor.cs

@ -5,17 +5,11 @@
namespace ImageProcessor namespace ImageProcessor
{ {
public class ProgressEventArgs : System.EventArgs
{
public int numRowsProcessed;
public int totalRows;
}
/// <summary> /// <summary>
/// A delegate which is called as progress is made processing the image. /// A delegate which is called as progress is made processing an image.
/// </summary> /// </summary>
/// <param name="sender"></param> /// <param name="sender">The source of the event.</param>
/// <param name="e"></param> /// <param name="e">An object that contains the event data.</param>
public delegate void ProgressEventHandler(object sender, ProgressEventArgs e); public delegate void ProgressEventHandler(object sender, ProgressEventArgs e);
/// <summary> /// <summary>

38
src/ImageProcessor/ParallelImageProcessor.cs

@ -32,24 +32,6 @@ namespace ImageProcessor
/// </summary> /// </summary>
private int totalRows; private int totalRows;
/// <summary>
/// Must be called by derived classes after processing a single row.
/// </summary>
protected void OnRowProcessed()
{
if(this.OnProgress != null)
{
int currThreadNumRows = Interlocked.Add(ref this.numRowsProcessed, 1);
// Multi-pass filters process multiple times more rows than totalRows, so update totalRows on the fly
if (currThreadNumRows > this.totalRows)
this.totalRows = currThreadNumRows;
// Report progress. This may be on the client's thread, or on a Task library thread.
this.OnProgress(this, new ProgressEventArgs { 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)
{ {
@ -191,5 +173,25 @@ namespace ImageProcessor
protected virtual void AfterApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle) protected virtual void AfterApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle)
{ {
} }
/// <summary>
/// Must be called by derived classes after processing a single row.
/// </summary>
protected void OnRowProcessed()
{
if (this.OnProgress != null)
{
int currThreadNumRows = Interlocked.Add(ref this.numRowsProcessed, 1);
// Multi-pass filters process multiple times more rows than totalRows, so update totalRows on the fly
if (currThreadNumRows > this.totalRows)
{
this.totalRows = currThreadNumRows;
}
// Report progress. This may be on the client's thread, or on a Task library thread.
this.OnProgress(this, new ProgressEventArgs { RowsProcessed = currThreadNumRows, TotalRows = this.totalRows });
}
}
} }
} }

23
src/ImageProcessor/ProgressEventArgs.cs

@ -0,0 +1,23 @@
// <copyright file="ProgressEventArgs.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessor
{
/// <summary>
/// Contains event data related to the progress made processing an image.
/// </summary>
public class ProgressEventArgs : System.EventArgs
{
/// <summary>
/// Gets or sets the number of rows processed.
/// </summary>
public int RowsProcessed { get; set; }
/// <summary>
/// Gets or sets the total number of rows.
/// </summary>
public int TotalRows { get; set; }
}
}

15
src/ImageProcessor/Samplers/ImageSampleExtensions.cs

@ -47,8 +47,9 @@ namespace ImageProcessor.Samplers
source = source.Resize(sourceRectangle.Width, sourceRectangle.Height); source = source.Resize(sourceRectangle.Width, sourceRectangle.Height);
} }
var processor = new Crop(); Crop processor = new Crop();
processor.OnProgress += progressHandler; processor.OnProgress += progressHandler;
try try
{ {
return source.Process(width, height, sourceRectangle, new Rectangle(0, 0, width, height), processor); return source.Process(width, height, sourceRectangle, new Rectangle(0, 0, width, height), processor);
@ -68,8 +69,9 @@ namespace ImageProcessor.Samplers
/// <returns>The <see cref="Image"/></returns> /// <returns>The <see cref="Image"/></returns>
public static Image EntropyCrop(this Image source, float threshold = .5f, ProgressEventHandler progressHandler = null) public static Image EntropyCrop(this Image source, float threshold = .5f, ProgressEventHandler progressHandler = null)
{ {
var processor = new EntropyCrop(threshold); EntropyCrop processor = new EntropyCrop(threshold);
processor.OnProgress += progressHandler; processor.OnProgress += progressHandler;
try try
{ {
return source.Process(source.Width, source.Height, source.Bounds, source.Bounds, processor); return source.Process(source.Width, source.Height, source.Bounds, source.Bounds, processor);
@ -135,8 +137,9 @@ namespace ImageProcessor.Samplers
height = source.Height * width / source.Width; height = source.Height * width / source.Width;
} }
var processor = new Resize(sampler); Resize processor = new Resize(sampler);
processor.OnProgress += progressHandler; processor.OnProgress += progressHandler;
try try
{ {
return source.Process(width, height, sourceRectangle, new Rectangle(0, 0, width, height), processor); return source.Process(width, height, sourceRectangle, new Rectangle(0, 0, width, height), processor);
@ -169,8 +172,9 @@ namespace ImageProcessor.Samplers
/// <returns>The <see cref="Image"/></returns> /// <returns>The <see cref="Image"/></returns>
public static Image Rotate(this Image source, float degrees, IResampler sampler, ProgressEventHandler progressHandler = null) public static Image Rotate(this Image source, float degrees, IResampler sampler, ProgressEventHandler progressHandler = null)
{ {
var processor = new Rotate(sampler) { Angle = degrees }; Rotate processor = new Rotate(sampler) { Angle = degrees };
processor.OnProgress += progressHandler; processor.OnProgress += progressHandler;
try try
{ {
return source.Process(source.Width, source.Height, source.Bounds, source.Bounds, processor); return source.Process(source.Width, source.Height, source.Bounds, source.Bounds, processor);
@ -191,8 +195,9 @@ namespace ImageProcessor.Samplers
/// <returns>The <see cref="Image"/></returns> /// <returns>The <see cref="Image"/></returns>
public static Image RotateFlip(this Image source, RotateType rotateType, FlipType flipType, ProgressEventHandler progressHandler = null) public static Image RotateFlip(this Image source, RotateType rotateType, FlipType flipType, ProgressEventHandler progressHandler = null)
{ {
var processor = new RotateFlip(rotateType, flipType); RotateFlip processor = new RotateFlip(rotateType, flipType);
processor.OnProgress += progressHandler; processor.OnProgress += progressHandler;
try try
{ {
return source.Process(source.Width, source.Height, source.Bounds, source.Bounds, processor); return source.Process(source.Width, source.Height, source.Bounds, source.Bounds, processor);

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

@ -65,9 +65,9 @@ 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) }"))
{ {
processor.OnProgress += ProgressUpdate; processor.OnProgress += this.ProgressUpdate;
image.Process(processor).Save(output); image.Process(processor).Save(output);
processor.OnProgress -= ProgressUpdate; processor.OnProgress -= this.ProgressUpdate;
} }
Trace.WriteLine($"{ name }: { watch.ElapsedMilliseconds}ms"); Trace.WriteLine($"{ name }: { watch.ElapsedMilliseconds}ms");
@ -77,7 +77,7 @@ namespace ImageProcessor.Tests
private void ProgressUpdate(object sender, ProgressEventArgs e) private void ProgressUpdate(object sender, ProgressEventArgs e)
{ {
Assert.InRange(e.numRowsProcessed, 1, e.totalRows); Assert.InRange(e.RowsProcessed, 1, e.TotalRows);
} }
} }
} }

16
tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs

@ -57,7 +57,7 @@
string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file); string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"TestOutput/Resize/{filename}")) using (FileStream output = File.OpenWrite($"TestOutput/Resize/{filename}"))
{ {
image.Resize(image.Width / 2, image.Height / 2, sampler, ProgressUpdate) image.Resize(image.Width / 2, image.Height / 2, sampler, this.ProgressUpdate)
.Save(output); .Save(output);
} }
@ -85,7 +85,7 @@
string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file); string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"TestOutput/Resize/{filename}")) using (FileStream output = File.OpenWrite($"TestOutput/Resize/{filename}"))
{ {
image.Resize(image.Width / 3, 0, new TriangleResampler(), ProgressUpdate) image.Resize(image.Width / 3, 0, new TriangleResampler(), this.ProgressUpdate)
.Save(output); .Save(output);
} }
@ -113,7 +113,7 @@
string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file); string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"TestOutput/Resize/{filename}")) using (FileStream output = File.OpenWrite($"TestOutput/Resize/{filename}"))
{ {
image.Resize(0, image.Height / 3, new TriangleResampler(), ProgressUpdate) image.Resize(0, image.Height / 3, new TriangleResampler(), this.ProgressUpdate)
.Save(output); .Save(output);
} }
@ -140,7 +140,7 @@
string filename = Path.GetFileNameWithoutExtension(file) + "-" + rotateType + flipType + Path.GetExtension(file); string filename = Path.GetFileNameWithoutExtension(file) + "-" + rotateType + flipType + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"TestOutput/RotateFlip/{filename}")) using (FileStream output = File.OpenWrite($"TestOutput/RotateFlip/{filename}"))
{ {
image.RotateFlip(rotateType, flipType, ProgressUpdate) image.RotateFlip(rotateType, flipType, this.ProgressUpdate)
.Save(output); .Save(output);
} }
@ -167,7 +167,7 @@
string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file); string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"TestOutput/Rotate/{filename}")) using (FileStream output = File.OpenWrite($"TestOutput/Rotate/{filename}"))
{ {
image.Rotate(45, sampler, ProgressUpdate) image.Rotate(45, sampler, this.ProgressUpdate)
//.BackgroundColor(Color.Aqua) //.BackgroundColor(Color.Aqua)
.Save(output); .Save(output);
} }
@ -193,7 +193,7 @@
string filename = Path.GetFileNameWithoutExtension(file) + "-EntropyCrop" + Path.GetExtension(file); string filename = Path.GetFileNameWithoutExtension(file) + "-EntropyCrop" + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"TestOutput/EntropyCrop/{filename}")) using (FileStream output = File.OpenWrite($"TestOutput/EntropyCrop/{filename}"))
{ {
image.EntropyCrop(.5f, ProgressUpdate).Save(output); image.EntropyCrop(.5f, this.ProgressUpdate).Save(output);
} }
} }
} }
@ -215,7 +215,7 @@
string filename = Path.GetFileNameWithoutExtension(file) + "-Crop" + Path.GetExtension(file); string filename = Path.GetFileNameWithoutExtension(file) + "-Crop" + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"TestOutput/Crop/{filename}")) using (FileStream output = File.OpenWrite($"TestOutput/Crop/{filename}"))
{ {
image.Crop(image.Width / 2, image.Height / 2, ProgressUpdate).Save(output); image.Crop(image.Width / 2, image.Height / 2, this.ProgressUpdate).Save(output);
} }
} }
} }
@ -238,7 +238,7 @@
private void ProgressUpdate(object sender, ProgressEventArgs e) private void ProgressUpdate(object sender, ProgressEventArgs e)
{ {
Assert.InRange(e.numRowsProcessed, 1, e.totalRows); Assert.InRange(e.RowsProcessed, 1, e.TotalRows);
} }
} }
} }

Loading…
Cancel
Save