Browse Source

Simplify base API.

Former-commit-id: fa3e308c24fe7f25fe55573397c16bb8d98bb9d7
Former-commit-id: 31697ba859fab7faa06642f42bf9032ad84cab6f
Former-commit-id: 2c3cc280c73dd1c4d7976e57f7c3eca9fc17d194
af/merge-core
James Jackson-South 11 years ago
parent
commit
d0d344136f
  1. 4
      src/ImageProcessor/Filters/Contrast.cs
  2. 34
      src/ImageProcessor/Filters/IImageFilter.cs
  3. 64
      src/ImageProcessor/Filters/ImageFilterExtensions.cs
  4. 2
      src/ImageProcessor/IImage.cs
  5. 33
      src/ImageProcessor/IImageProcessor.cs
  6. 14
      src/ImageProcessor/Image.cs
  7. 13
      src/ImageProcessor/ImageBase.cs
  8. 111
      src/ImageProcessor/ImageExtensions.cs
  9. 11
      src/ImageProcessor/ImageFrame.cs
  10. 9
      src/ImageProcessor/ImageProcessor.csproj
  11. 29
      src/ImageProcessor/ParallelImageProcessor.cs
  12. 23
      src/ImageProcessor/Samplers/IImageSampler.cs
  13. 6
      tests/ImageProcessor.Tests/Filters/FilterTests.cs
  14. 21
      tests/ImageProcessor.Tests/Formats/EncoderDecoderTests.cs

4
src/ImageProcessor/Filters/Contrast.cs

@ -8,9 +8,9 @@ namespace ImageProcessor.Filters
using System;
/// <summary>
/// An <see cref="IImageFilter"/> to change the contrast of an <see cref="Image"/>.
/// An <see cref="IImageProcessor"/> to change the contrast of an <see cref="Image"/>.
/// </summary>
public class Contrast : ParallelImageFilter
public class Contrast : ParallelImageProcessor
{
/// <summary>
/// Initializes a new instance of the <see cref="Contrast"/> class.

34
src/ImageProcessor/Filters/IImageFilter.cs

@ -1,34 +0,0 @@
namespace ImageProcessor.Filters
{
/// <summary>
/// Image processing filter interface.
/// </summary>
/// <remarks>
/// The interface defines the set of methods, which should be
/// provided by all image processing filters. Methods of this interface
/// manipulate the original image.
/// </remarks>
public interface IImageFilter
{
/// <summary>
/// Apply filter to an image at the area of the specified rectangle.
/// </summary>
/// <param name="target">Target image to apply filter 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 filter should be applied to.</param>
/// <remarks>The method keeps the source image unchanged and returns the
/// the result of image processing filter as new image.</remarks>
/// <exception cref="System.ArgumentNullException">
/// <paramref name="target"/>
/// is null.
/// - or -
/// <paramref name="source"/>
/// is null.
/// </exception>
/// <exception cref="System.ArgumentException">
/// <paramref name="rectangle"/> doesnt fit the dimension of the image.
/// </exception>
void Apply(ImageBase target, ImageBase source, Rectangle rectangle);
}
}

64
src/ImageProcessor/Filters/ImageFilterExtensions.cs

@ -5,77 +5,17 @@
namespace ImageProcessor.Filters
{
using System;
/// <summary>
/// Exstension methods for performing filtering methods again an image.
/// Exstensions methods for <see cref="Image"/> to apply filters to the image.
/// </summary>
public static class ImageFilterExtensions
{
/// <summary>
/// Applies the collection of filters to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="filters">Any filters to apply to the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Filter(this Image source, params IImageFilter[] filters) => Filter(source, source.Bounds, filters);
/// <summary>
/// Applies the collection of filters to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="rectangle">
/// The rectangle defining the bounds of the pixels the image filter with adjust.</param>
/// <param name="filters">Any filters to apply to the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Filter(this Image source, Rectangle rectangle, params IImageFilter[] filters)
{
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (IImageFilter filter in filters)
{
source = PerformAction(source, true, (sourceImage, targetImage) => filter.Apply(targetImage, sourceImage, rectangle));
}
return source;
}
/// <summary>
/// Alters the contrast component of the image.
/// </summary>
/// <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>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Contrast(this Image source, int amount) => source.Filter(new Contrast(amount));
/// <summary>
/// Performs the given action on the source image.
/// </summary>
/// <param name="source">The image to perform the action against.</param>
/// <param name="clone">Whether to clone the image.</param>
/// <param name="action">The <see cref="Action"/> to perform against the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
private static Image PerformAction(Image source, bool clone, Action<ImageBase, ImageBase> action)
{
Image transformedImage = clone ? new Image(source) : new Image(source.Width, source.Height);
action(source, transformedImage);
for (int i = 0; i < source.Frames.Count; i++)
{
ImageFrame frame = source.Frames[i];
ImageFrame tranformedFrame = new ImageFrame(frame);
action(frame, tranformedFrame);
if (!clone)
{
transformedImage.Frames.Add(tranformedFrame);
}
else
{
transformedImage.Frames[i] = tranformedFrame;
}
}
return transformedImage;
}
public static Image Contrast(this Image source, int amount) => source.Process(new Contrast(amount));
}
}

2
src/ImageProcessor/IImage.cs

@ -85,7 +85,7 @@ namespace ImageProcessor
void Save(Stream stream);
/// <summary>
/// Saves the image to the given stream using the currently loaded image format.
/// Saves the image to the given stream using the given image format.
/// </summary>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="format">The format to save the image as.</param>

33
src/ImageProcessor/IImageProcessor.cs

@ -0,0 +1,33 @@
// <copyright file="IImageProcessor.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessor
{
/// <summary>
/// Encapsulates methods to alter the pixels of an image.
/// </summary>
public interface IImageProcessor
{
/// <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>
/// <remarks>
/// The method keeps the source image unchanged and returns the
/// the result of image processing filter as new image.
/// </remarks>
/// <exception cref="System.ArgumentNullException">
/// <paramref name="target"/> is null or <paramref name="source"/> is null.
/// </exception>
/// <exception cref="System.ArgumentException">
/// <paramref name="rectangle"/> doesnt fit the dimension of the image.
/// </exception>
void Apply(ImageBase target, ImageBase source, Rectangle rectangle);
}
}

14
src/ImageProcessor/Image.cs

@ -1,13 +1,7 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Image.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// <copyright file="Image.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Image class which stores the pixels and provides common functionality
// such as loading images from files and streams or operation like resizing or cropping.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor
{
@ -187,7 +181,7 @@ namespace ImageProcessor
public IList<ImageProperty> Properties { get; } = new List<ImageProperty>();
/// <inheritdoc/>
public IImageFormat CurrentImageFormat { get; private set; }
public IImageFormat CurrentImageFormat { get; internal set; }
/// <inheritdoc/>
public void Save(Stream stream)

13
src/ImageProcessor/ImageBase.cs

@ -1,13 +1,7 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ImageBase.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// <copyright file="ImageBase.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// The base class of all images. Encapsulates all the properties and methods
// required to manipulate images.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor
{
@ -168,7 +162,6 @@ namespace ImageProcessor
throw new ArgumentOutOfRangeException(nameof(y), "Value cannot be less than zero or greater than the bitmap height.");
}
#endif
int start = ((y * this.Width) + x) * 4;
this.Pixels[start + 0] = value.B;

111
src/ImageProcessor/ImageExtensions.cs

@ -0,0 +1,111 @@
// <copyright file="ImageFilterExtensions.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessor
{
using System;
using System.IO;
using Formats;
/// <summary>
/// Exstension methods for the <see cref="Image"/> type.
/// </summary>
public static class ImageExtensions
{
/// <summary>
/// Saves the image to the given stream with the bmp format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <exception cref="ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsBmp(this ImageBase source, Stream stream) => new BmpEncoder().Encode(source, stream);
/// <summary>
/// Saves the image to the given stream with the png format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <exception cref="ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsPng(this ImageBase source, Stream stream) => new PngEncoder().Encode(source, stream);
/// <summary>
/// Saves the image to the given stream with the jpeg format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="quality">The quality to save the image to. Between 1 and 100.</param>
/// <exception cref="ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsJpeg(this ImageBase source, Stream stream, int quality = 80) => new JpegEncoder { Quality = quality }.Encode(source, stream);
/// <summary>
/// Saves the image to the given stream with the gif format.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="stream">The stream to save the image to.</param>
/// <param name="quality">The quality to save the image to representing the number of colors. Between 1 and 100.</param>
/// <exception cref="ArgumentNullException">Thrown if the stream is null.</exception>
public static void SaveAsGif(this ImageBase source, Stream stream, int quality = 256) => new GifEncoder() { Quality = quality }.Encode(source, stream);
/// <summary>
/// Applies the collection of processors to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="processors">Any processors to apply to the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Process(this Image source, params IImageProcessor[] processors) => Process(source, source.Bounds, processors);
/// <summary>
/// Applies the collection of processors to the image.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="rectangle">
/// The rectangle defining the bounds of the pixels the image filter with adjust.</param>
/// <param name="processors">Any processors to apply to the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image Process(this Image source, Rectangle rectangle, params IImageProcessor[] processors)
{
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (IImageProcessor filter in processors)
{
source = PerformAction(source, true, (sourceImage, targetImage) => filter.Apply(targetImage, sourceImage, rectangle));
}
return source;
}
/// <summary>
/// Performs the given action on the source image.
/// </summary>
/// <param name="source">The image to perform the action against.</param>
/// <param name="clone">Whether to clone the image.</param>
/// <param name="action">The <see cref="Action"/> to perform against the image.</param>
/// <returns>The <see cref="Image"/>.</returns>
private static Image PerformAction(Image source, bool clone, Action<ImageBase, ImageBase> action)
{
Image transformedImage = clone ? new Image(source) : new Image();
transformedImage.CurrentImageFormat = source.CurrentImageFormat;
action(source, transformedImage);
for (int i = 0; i < source.Frames.Count; i++)
{
ImageFrame sourceFrame = source.Frames[i];
ImageFrame tranformedFrame = clone ? new ImageFrame(sourceFrame) : new ImageFrame();
action(sourceFrame, tranformedFrame);
if (!clone)
{
transformedImage.Frames.Add(tranformedFrame);
}
else
{
transformedImage.Frames[i] = tranformedFrame;
}
}
return transformedImage;
}
}
}

11
src/ImageProcessor/ImageFrame.cs

@ -1,12 +1,7 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ImageFrame.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// <copyright file="ImageFrame.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Represents a single frame in a animation.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor
{

9
src/ImageProcessor/ImageProcessor.csproj

@ -13,7 +13,7 @@
<DefaultLanguage>en-US</DefaultLanguage>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TargetFrameworkProfile>Profile78</TargetFrameworkProfile>
<TargetFrameworkProfile>Profile259</TargetFrameworkProfile>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
<RestorePackages>true</RestorePackages>
@ -38,12 +38,14 @@
</PropertyGroup>
<ItemGroup>
<!-- A reference to the entire .NET Framework is automatically included -->
<Folder Include="Samplers\" />
</ItemGroup>
<ItemGroup>
<Compile Include="Filters\Contrast.cs" />
<Compile Include="Filters\ImageFilterExtensions.cs" />
<Compile Include="Filters\ParallelImageFilter.cs" />
<Compile Include="Filters\IImageFilter.cs" />
<Compile Include="ImageExtensions.cs" />
<Compile Include="ParallelImageProcessor.cs" />
<Compile Include="IImageProcessor.cs" />
<Compile Include="Colors\Hsv.cs" />
<Compile Include="Colors\YCbCr.cs" />
<Compile Include="Common\Extensions\ByteExtensions.cs" />
@ -177,7 +179,6 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Numerics\Rectangle.cs" />
<Compile Include="Numerics\Size.cs" />
<Compile Include="Samplers\IImageSampler.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="ICSharpCode.SharpZipLib.Portable, Version=0.86.0.51802, Culture=neutral, processorArchitecture=MSIL">

29
src/ImageProcessor/Filters/ParallelImageFilter.cs → src/ImageProcessor/ParallelImageProcessor.cs

@ -1,15 +1,20 @@
namespace ImageProcessor.Filters
// <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 filters using prallel processing.
/// Allows the application of processors using parallel processing.
/// </summary>
public abstract class ParallelImageFilter : IImageFilter
public abstract class ParallelImageProcessor : IImageProcessor
{
/// <summary>
/// Gets or sets the count of workers to run the filter in parallel.
/// Gets or sets the count of workers to run the process in parallel.
/// </summary>
public int Parallelism { get; set; } = Environment.ProcessorCount;
@ -46,12 +51,26 @@
}
/// <summary>
/// This method is called before the filter is applied to prepare the filter.
/// This method is called before the process is applied to prepare the processor.
/// </summary>
protected virtual void OnApply()
{
}
/// <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);
}
}

23
src/ImageProcessor/Samplers/IImageSampler.cs

@ -1,23 +0,0 @@
// <copyright file="IImageSampler.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessor.Samplers
{
/// <summary>
/// Encapsulates the methods required for all image sampling (resizing) algorithms.
/// </summary>
public interface IImageSampler
{
/// <summary>
/// Resizes the specified source image by creating a new image with
/// the specified size which is a resized version of the passed image.
/// </summary>
/// <param name="source">The source image.</param>
/// <param name="target">The target image.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
void Sample(ImageBase source, ImageBase target, int width, int height);
}
}

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

@ -23,7 +23,7 @@ namespace ImageProcessor.Tests.Filters
//{ "../../TestImages/Formats/Gif/giphy.gif" },
};
public static readonly TheoryData<string, IImageFilter> Filters = new TheoryData<string, IImageFilter>
public static readonly TheoryData<string, IImageProcessor> Filters = new TheoryData<string, IImageProcessor>
{
{ "Contrast-50", new Contrast(50) },
{ "Contrast--50", new Contrast(-50) },
@ -31,7 +31,7 @@ namespace ImageProcessor.Tests.Filters
[Theory]
[MemberData("Filters")]
public void FilterImage(string name, IImageFilter filter)
public void FilterImage(string name, IImageProcessor processor)
{
if (!Directory.Exists("Filtered"))
{
@ -47,7 +47,7 @@ namespace ImageProcessor.Tests.Filters
string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"Filtered/{ Path.GetFileName(filename) }"))
{
image.Filter(filter).Save(output);
image.Process(processor).Save(output);
}
Trace.WriteLine($"{ name }: { watch.ElapsedMilliseconds}ms");

21
tests/ImageProcessor.Tests/Formats/EncoderDecoderTests.cs

@ -31,29 +31,10 @@
string encodedFilename = "Encoded/" + Path.GetFileName(filename);
//if (!image.IsAnimated)
//{
using (FileStream output = File.OpenWrite(encodedFilename))
{
image.Save(output);
}
//}
//else
//{
// using (var output = File.OpenWrite(
// string.Format("Encoded/{ Path.GetFileNameWithoutExtension(filename) }.jpg"))
// {
// image.SaveAsJpeg(output, 40);
// }
// for (int i = 0; i < image.Frames.Count; i++)
// {
// using (var output = File.OpenWrite($"Encoded/{ i }_{ Path.GetFileNameWithoutExtension(filename) }.png"))
// {
// image.Frames[i].SaveAsPng(output);
// }
// }
//}
Trace.WriteLine($"{filename} : {watch.ElapsedMilliseconds}ms");
}
@ -75,7 +56,7 @@
using (FileStream output = File.OpenWrite($"Quantized/{ Path.GetFileName(filename) }"))
{
quantizedImage.ToImage().Save(output, image.CurrentImageFormat);
quantizedImage.ToImage().Save(output);
}
}
}

Loading…
Cancel
Save