Browse Source

Image now IDisposable

This appears to have injected some voom into things. Not benchmarked
though and needs double checking for memory leaks.

touch #360


Former-commit-id: 0707a36781e3a5fa4d273d933d1bd1b04b022092
Former-commit-id: f7ec165c3d10d9e1e35b8eec3545ca75cf302cf9
Former-commit-id: d5bc57ce1cde9f1486a954d3453040514dc4d629
pull/1/head
James South 10 years ago
parent
commit
d12eedb9fe
  1. 6
      src/ImageProcessorCore/Filters/Blend.cs
  2. 2
      src/ImageProcessorCore/Formats/Png/PngEncoder.cs
  3. 2
      src/ImageProcessorCore/Formats/Png/PngEncoderCore.cs
  4. 2
      src/ImageProcessorCore/IImageBase.cs
  5. 26
      src/ImageProcessorCore/Image.cs
  6. 156
      src/ImageProcessorCore/ImageBase.cs
  7. 1
      src/ImageProcessorCore/ImageExtensions.cs
  8. 40
      src/ImageProcessorCore/ParallelImageProcessor.cs
  9. 25
      src/ImageProcessorCore/Samplers/EntropyCrop.cs
  10. 2
      src/ImageProcessorCore/Samplers/Resize.cs
  11. 2
      tests/ImageProcessorCore.Tests/FileTestBase.cs
  12. 23
      tests/ImageProcessorCore.Tests/Formats/BitmapTests.cs
  13. 120
      tests/ImageProcessorCore.Tests/Formats/EncoderDecoderTests.cs
  14. 11
      tests/ImageProcessorCore.Tests/Formats/PngTests.cs
  15. 15
      tests/ImageProcessorCore.Tests/Processors/Filters/FilterTests.cs
  16. 87
      tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs

6
src/ImageProcessorCore/Filters/Blend.cs

@ -73,5 +73,11 @@ namespace ImageProcessorCore.Filters
} }
}); });
} }
/// <inheritdoc/>
protected override void AfterApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle)
{
this.toBlend?.Dispose();
}
} }
} }

2
src/ImageProcessorCore/Formats/Png/PngEncoder.cs

@ -18,7 +18,7 @@ namespace ImageProcessorCore.Formats
/// <summary> /// <summary>
/// Gets or sets the quality of output for images. /// Gets or sets the quality of output for images.
/// </summary> /// </summary>
public int Quality { get; set; } = int.MaxValue; public int Quality { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
public string MimeType => "image/png"; public string MimeType => "image/png";

2
src/ImageProcessorCore/Formats/Png/PngEncoderCore.cs

@ -35,7 +35,7 @@ namespace ImageProcessorCore.Formats
/// <summary> /// <summary>
/// Gets or sets the quality of output for images. /// Gets or sets the quality of output for images.
/// </summary> /// </summary>
public int Quality { get; set; } = int.MaxValue; public int Quality { get; set; }
/// <summary> /// <summary>
/// The compression level 1-9. /// The compression level 1-9.

2
src/ImageProcessorCore/IImageBase.cs

@ -17,7 +17,7 @@ namespace ImageProcessorCore
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// The returned array has a length of Width * Height * 4 bytes /// The returned array has a length of Width * Height * 4 bytes
/// and stores the blue, the green, the red and the alpha value for /// and stores the red, the green, the blue, and the alpha value for
/// each pixel in this order. /// each pixel in this order.
/// </remarks> /// </remarks>
float[] Pixels { get; } float[] Pixels { get; }

26
src/ImageProcessorCore/Image.cs

@ -45,7 +45,7 @@ namespace ImageProcessorCore
new BmpFormat(), new BmpFormat(),
new JpegFormat(), new JpegFormat(),
new PngFormat(), new PngFormat(),
new GifFormat(), new GifFormat()
}); });
/// <summary> /// <summary>
@ -223,6 +223,30 @@ namespace ImageProcessorCore
encoder.Encode(this, stream); encoder.Encode(this, stream);
} }
/// <inheritdoc/>
protected override void Dispose(bool disposing)
{
if (this.IsDisposed)
{
return;
}
if (disposing)
{
// Dispose of any managed resources here.
if (this.Frames.Any())
{
foreach (ImageFrame frame in this.Frames)
{
frame.Dispose();
}
this.Frames.Clear();
}
}
base.Dispose(disposing);
}
/// <summary> /// <summary>
/// Loads the image from the given stream. /// Loads the image from the given stream.
/// </summary> /// </summary>

156
src/ImageProcessorCore/ImageBase.cs

@ -6,13 +6,39 @@
namespace ImageProcessorCore namespace ImageProcessorCore
{ {
using System; using System;
using System.Runtime.InteropServices;
/// <summary> /// <summary>
/// The base class of all images. Encapsulates the basic properties and methods /// The base class of all images. Encapsulates the basic properties and methods
/// required to manipulate images. /// required to manipulate images.
/// </summary> /// </summary>
public abstract class ImageBase : IImageBase public abstract unsafe class ImageBase : IImageBase, IDisposable
{ {
/// <summary>
/// The position of the first pixel in the bitmap.
/// </summary>
private float* pixelsBase;
/// <summary>
/// The array of pixels.
/// </summary>
private float[] pixelsArray;
private GCHandle pixelsHandle;
/// <summary>
/// A value indicating whether this instance of the given entity has been disposed.
/// </summary>
/// <value><see langword="true"/> if this instance has been disposed; otherwise, <see langword="false"/>.</value>
/// <remarks>
/// If the entity is disposed, it must not be disposed a second
/// time. The isDisposed field is set the first time the entity
/// is disposed. If the isDisposed field is true, then the Dispose()
/// method will not dispose again. This help not to prolong the entity's
/// life in the Garbage Collector.
/// </remarks>
protected bool IsDisposed;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ImageBase"/> class. /// Initializes a new instance of the <see cref="ImageBase"/> class.
/// </summary> /// </summary>
@ -23,12 +49,8 @@ namespace ImageProcessorCore
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ImageBase"/> class. /// Initializes a new instance of the <see cref="ImageBase"/> class.
/// </summary> /// </summary>
/// <param name="width"> /// <param name="width">The width of the image in pixels.</param>
/// The width of the image in pixels. /// <param name="height">The height of the image in pixels.</param>
/// </param>
/// <param name="height">
/// The height of the image in pixels.
/// </param>
/// <exception cref="ArgumentOutOfRangeException"> /// <exception cref="ArgumentOutOfRangeException">
/// Thrown if either <paramref name="width"/> or <paramref name="height"/> are less than or equal to 0. /// Thrown if either <paramref name="width"/> or <paramref name="height"/> are less than or equal to 0.
/// </exception> /// </exception>
@ -40,7 +62,10 @@ namespace ImageProcessorCore
this.Width = width; this.Width = width;
this.Height = height; this.Height = height;
this.Pixels = new float[width * height * 4]; // Assign the pointer and pixels.
this.pixelsArray = new float[width * height * 4];
this.pixelsHandle = GCHandle.Alloc(this.pixelsArray, GCHandleType.Pinned);
this.pixelsBase = (float*)this.pixelsHandle.AddrOfPinnedObject().ToPointer();
} }
/// <summary> /// <summary>
@ -56,14 +81,22 @@ namespace ImageProcessorCore
{ {
Guard.NotNull(other, nameof(other), "Other image cannot be null."); Guard.NotNull(other, nameof(other), "Other image cannot be null.");
float[] pixels = other.Pixels;
this.Width = other.Width; this.Width = other.Width;
this.Height = other.Height; this.Height = other.Height;
this.Quality = other.Quality; this.Quality = other.Quality;
this.FrameDelay = other.FrameDelay; this.FrameDelay = other.FrameDelay;
this.Pixels = new float[pixels.Length];
Array.Copy(pixels, this.Pixels, pixels.Length); // Assign the pointer and copy the pixels.
this.pixelsArray = new float[this.Width * this.Height * 4];
this.pixelsHandle = GCHandle.Alloc(this.pixelsArray, GCHandleType.Pinned);
this.pixelsBase = (float*)this.pixelsHandle.AddrOfPinnedObject().ToPointer();
Array.Copy(other.pixelsArray, this.pixelsArray, other.pixelsArray.Length);
}
/// <inheritdoc/>
~ImageBase()
{
this.Dispose(false);
} }
/// <summary> /// <summary>
@ -76,45 +109,25 @@ namespace ImageProcessorCore
/// </summary> /// </summary>
public static int MaxHeight { get; set; } = int.MaxValue; public static int MaxHeight { get; set; } = int.MaxValue;
/// <summary> /// <inheritdoc/>
/// Gets the image pixels as byte array. public float[] Pixels => this.pixelsArray;
/// </summary>
/// <remarks>
/// The returned array has a length of Width * Height * 4 bytes
/// and stores the blue, the green, the red and the alpha value for
/// each pixel in this order.
/// </remarks>
public float[] Pixels { get; private set; }
/// <summary> /// <inheritdoc/>
/// Gets the width in pixels.
/// </summary>
public int Width { get; private set; } public int Width { get; private set; }
/// <summary> /// <inheritdoc/>
/// Gets the height in pixels.
/// </summary>
public int Height { get; private set; } public int Height { get; private set; }
/// <summary> /// <inheritdoc/>
/// Gets the pixel ratio made up of the width and height.
/// </summary>
public double PixelRatio => (double)this.Width / this.Height; public double PixelRatio => (double)this.Width / this.Height;
/// <summary> /// <inheritdoc/>
/// Gets the <see cref="Rectangle"/> representing the bounds of the image.
/// </summary>
public Rectangle Bounds => new Rectangle(0, 0, this.Width, this.Height); public Rectangle Bounds => new Rectangle(0, 0, this.Width, this.Height);
/// <inheritdoc/> /// <inheritdoc/>
public int Quality { get; set; } public int Quality { get; set; }
/// <summary> /// <inheritdoc/>
/// Gets or sets the frame delay for animated images.
/// If not 0, this field specifies the number of hundredths (1/100) of a second to
/// wait before continuing with the processing of the Data Stream.
/// The clock starts ticking immediately after the graphic is rendered.
/// </summary>
public int FrameDelay { get; set; } public int FrameDelay { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
@ -133,9 +146,7 @@ namespace ImageProcessorCore
throw new ArgumentOutOfRangeException(nameof(y), "Value cannot be less than zero or greater than the bitmap height."); throw new ArgumentOutOfRangeException(nameof(y), "Value cannot be less than zero or greater than the bitmap height.");
} }
#endif #endif
return *((Color*)(this.pixelsBase + ((y * this.Width) + x) * 4));
int start = ((y * this.Width) + x) * 4;
return new Color(this.Pixels[start], this.Pixels[start + 1], this.Pixels[start + 2], this.Pixels[start + 3]);
} }
set set
@ -151,12 +162,7 @@ namespace ImageProcessorCore
throw new ArgumentOutOfRangeException(nameof(y), "Value cannot be less than zero or greater than the bitmap height."); throw new ArgumentOutOfRangeException(nameof(y), "Value cannot be less than zero or greater than the bitmap height.");
} }
#endif #endif
int start = ((y * this.Width) + x) * 4; *(Color*)(this.pixelsBase + (((y * this.Width) + x) * 4)) = value;
this.Pixels[start + 0] = value.R;
this.Pixels[start + 1] = value.G;
this.Pixels[start + 2] = value.B;
this.Pixels[start + 3] = value.A;
} }
} }
@ -181,7 +187,10 @@ namespace ImageProcessorCore
#endif #endif
this.Width = width; this.Width = width;
this.Height = height; this.Height = height;
this.Pixels = pixels;
this.pixelsArray = pixels;
this.pixelsHandle = GCHandle.Alloc(this.pixelsArray, GCHandleType.Pinned);
this.pixelsBase = (float*)this.pixelsHandle.AddrOfPinnedObject().ToPointer();
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -205,9 +214,52 @@ namespace ImageProcessorCore
#endif #endif
this.Width = width; this.Width = width;
this.Height = height; this.Height = height;
float[] clonedPixels = new float[pixels.Length];
Array.Copy(pixels, clonedPixels, pixels.Length); // Assign the pointer and copy the pixels.
this.Pixels = clonedPixels; this.pixelsArray = new float[pixels.Length];
this.pixelsHandle = GCHandle.Alloc(this.pixelsArray, GCHandleType.Pinned);
this.pixelsBase = (float*)this.pixelsHandle.AddrOfPinnedObject().ToPointer();
Array.Copy(pixels, this.pixelsArray, pixels.Length);
}
/// <inheritdoc/>
public void Dispose()
{
this.Dispose(true);
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SuppressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
/// <summary>
/// Disposes the object and frees resources for the Garbage Collector.
/// </summary>
/// <param name="disposing">If true, the object gets disposed.</param>
protected virtual void Dispose(bool disposing)
{
if (this.IsDisposed)
{
return;
}
if (disposing)
{
// Dispose of any managed resources here.
this.pixelsArray = null;
}
if (this.pixelsHandle.IsAllocated)
{
this.pixelsHandle.Free();
this.pixelsBase = null;
}
// Note disposing is done.
this.IsDisposed = true;
} }
} }
} }

1
src/ImageProcessorCore/ImageExtensions.cs

@ -164,6 +164,7 @@ namespace ImageProcessorCore
} }
} }
source.Dispose();
return transformedImage; return transformedImage;
} }
} }

40
src/ImageProcessorCore/ParallelImageProcessor.cs

@ -37,10 +37,7 @@ namespace ImageProcessorCore
{ {
try try
{ {
// We don't want to affect the original source pixels so we make clone here. this.OnApply(source, target, target.Bounds, sourceRectangle);
ImageFrame frame = source as ImageFrame;
Image temp = frame != null ? new Image(frame) : new Image((Image)source);
this.OnApply(temp, target, target.Bounds, sourceRectangle);
this.numRowsProcessed = 0; this.numRowsProcessed = 0;
this.totalRows = sourceRectangle.Height; this.totalRows = sourceRectangle.Height;
@ -54,24 +51,27 @@ namespace ImageProcessorCore
for (int p = 0; p < partitionCount; p++) for (int p = 0; p < partitionCount; p++)
{ {
int current = p; int current = p;
tasks[p] = Task.Run(() => tasks[p] = Task.Run(
{ () =>
int batchSize = sourceRectangle.Height / partitionCount; {
int yStart = sourceRectangle.Y + (current * batchSize); int batchSize = sourceRectangle.Height / partitionCount;
int yEnd = current == partitionCount - 1 ? sourceRectangle.Bottom : yStart + batchSize; int yStart = sourceRectangle.Y + (current * batchSize);
int yEnd = current == partitionCount - 1
this.Apply(target, temp, target.Bounds, sourceRectangle, yStart, yEnd); ? sourceRectangle.Bottom
}); : yStart + batchSize;
this.Apply(target, source, target.Bounds, sourceRectangle, yStart, yEnd);
});
} }
Task.WaitAll(tasks); Task.WaitAll(tasks);
} }
else else
{ {
this.Apply(target, temp, target.Bounds, sourceRectangle, sourceRectangle.Y, sourceRectangle.Bottom); this.Apply(target, source, target.Bounds, sourceRectangle, sourceRectangle.Y, sourceRectangle.Bottom);
} }
this.AfterApply(temp, target, target.Bounds, sourceRectangle); this.AfterApply(source, target, target.Bounds, sourceRectangle);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -93,10 +93,7 @@ namespace ImageProcessorCore
sourceRectangle = source.Bounds; sourceRectangle = source.Bounds;
} }
// We don't want to affect the original source pixels so we make clone here. this.OnApply(source, target, target.Bounds, sourceRectangle);
ImageFrame frame = source as ImageFrame;
Image temp = frame != null ? new Image(frame) : new Image((Image)source);
this.OnApply(temp, target, target.Bounds, sourceRectangle);
targetRectangle = target.Bounds; targetRectangle = target.Bounds;
this.numRowsProcessed = 0; this.numRowsProcessed = 0;
@ -117,7 +114,7 @@ namespace ImageProcessorCore
int yStart = current * batchSize; int yStart = current * batchSize;
int yEnd = current == partitionCount - 1 ? targetRectangle.Bottom : yStart + batchSize; int yEnd = current == partitionCount - 1 ? targetRectangle.Bottom : yStart + batchSize;
this.Apply(target, temp, targetRectangle, sourceRectangle, yStart, yEnd); this.Apply(target, source, targetRectangle, sourceRectangle, yStart, yEnd);
}); });
} }
@ -125,14 +122,13 @@ namespace ImageProcessorCore
} }
else else
{ {
this.Apply(target, temp, targetRectangle, sourceRectangle, targetRectangle.Y, targetRectangle.Bottom); this.Apply(target, source, targetRectangle, sourceRectangle, targetRectangle.Y, targetRectangle.Bottom);
} }
this.AfterApply(temp, target, target.Bounds, sourceRectangle); this.AfterApply(source, target, target.Bounds, sourceRectangle);
} }
catch (Exception ex) catch (Exception ex)
{ {
throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex); throw new ImageProcessingException($"An error occured when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex);
} }
} }

25
src/ImageProcessorCore/Samplers/EntropyCrop.cs

@ -42,20 +42,21 @@ namespace ImageProcessorCore.Samplers
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle) protected override void OnApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle)
{ {
ImageBase temp = new Image(source.Width, source.Height); using (ImageBase temp = new Image(source.Width, source.Height))
{
// Detect the edges. // Detect the edges.
new Sobel().Apply(temp, source, sourceRectangle); new Sobel().Apply(temp, source, sourceRectangle);
// Apply threshold binarization filter. // Apply threshold binarization filter.
new Threshold(.5f).Apply(temp, temp, sourceRectangle); new Threshold(.5f).Apply(temp, temp, sourceRectangle);
// Search for the first white pixels // Search for the first white pixels
Rectangle rectangle = ImageMaths.GetFilteredBoundingRectangle(temp, 0); Rectangle rectangle = ImageMaths.GetFilteredBoundingRectangle(temp, 0);
// Reset the target pixel to the correct size. // Reset the target pixel to the correct size.
target.SetPixels(rectangle.Width, rectangle.Height, new float[rectangle.Width * rectangle.Height * 4]); target.SetPixels(rectangle.Width, rectangle.Height, new float[rectangle.Width * rectangle.Height * 4]);
this.cropRectangle = rectangle; this.cropRectangle = rectangle;
}
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -73,7 +74,7 @@ namespace ImageProcessorCore.Samplers
int endX = this.cropRectangle.Width; int endX = this.cropRectangle.Width;
int maxX = this.cropRectangle.Right - 1; int maxX = this.cropRectangle.Right - 1;
int maxY = this.cropRectangle.Bottom - 1; int maxY = this.cropRectangle.Bottom - 1;
Parallel.For( Parallel.For(
startY, startY,
endY, endY,

2
src/ImageProcessorCore/Samplers/Resize.cs

@ -164,6 +164,8 @@ namespace ImageProcessorCore.Samplers
{ {
target.ClonePixels(target.Width, target.Height, source.Pixels); target.ClonePixels(target.Width, target.Height, source.Pixels);
} }
this.firstPass?.Dispose();
} }
} }
} }

2
tests/ImageProcessorCore.Tests/FileTestBase.cs

@ -29,7 +29,7 @@ namespace ImageProcessorCore.Tests
//"TestImages/Formats/Png/indexed.png", // Perf: Enable for local testing only //"TestImages/Formats/Png/indexed.png", // Perf: Enable for local testing only
"TestImages/Formats/Png/splash.png", "TestImages/Formats/Png/splash.png",
"TestImages/Formats/Gif/rings.gif", "TestImages/Formats/Gif/rings.gif",
//"TestImages/Formats/Gif/giphy.gif" // Perf: Enable for local testing only "TestImages/Formats/Gif/giphy.gif" // Perf: Enable for local testing only
}; };
protected void ProgressUpdate(object sender, ProgressEventArgs e) protected void ProgressUpdate(object sender, ProgressEventArgs e)

23
tests/ImageProcessorCore.Tests/Formats/BitmapTests.cs

@ -27,20 +27,21 @@ namespace ImageProcessorCore.Tests
using (FileStream stream = File.OpenRead(file)) using (FileStream stream = File.OpenRead(file))
{ {
Stopwatch watch = Stopwatch.StartNew(); Stopwatch watch = Stopwatch.StartNew();
Image image = new Image(stream); using (Image image = new Image(stream))
string encodeFilename = "TestOutput/Encode/Bitmap/" + "24-" + Path.GetFileNameWithoutExtension(file) + ".bmp";
using (FileStream output = File.OpenWrite(encodeFilename))
{ {
image.Save(output, new BmpEncoder { BitsPerPixel = BmpBitsPerPixel.Pixel24 }); string encodeFilename = "TestOutput/Encode/Bitmap/" + "24-" + Path.GetFileNameWithoutExtension(file) + ".bmp";
}
encodeFilename = "TestOutput/Encode/Bitmap/" + "32-" + Path.GetFileNameWithoutExtension(file) + ".bmp"; using (FileStream output = File.OpenWrite(encodeFilename))
{
image.Save(output, new BmpEncoder { BitsPerPixel = BmpBitsPerPixel.Pixel24 });
}
using (FileStream output = File.OpenWrite(encodeFilename)) encodeFilename = "TestOutput/Encode/Bitmap/" + "32-" + Path.GetFileNameWithoutExtension(file) + ".bmp";
{
image.Save(output, new BmpEncoder { BitsPerPixel = BmpBitsPerPixel.Pixel32 }); using (FileStream output = File.OpenWrite(encodeFilename))
{
image.Save(output, new BmpEncoder { BitsPerPixel = BmpBitsPerPixel.Pixel32 });
}
} }
Trace.WriteLine($"{file} : {watch.ElapsedMilliseconds}ms"); Trace.WriteLine($"{file} : {watch.ElapsedMilliseconds}ms");

120
tests/ImageProcessorCore.Tests/Formats/EncoderDecoderTests.cs

@ -30,13 +30,14 @@ namespace ImageProcessorCore.Tests
using (FileStream stream = File.OpenRead(file)) using (FileStream stream = File.OpenRead(file))
{ {
Stopwatch watch = Stopwatch.StartNew(); Stopwatch watch = Stopwatch.StartNew();
Image image = new Image(stream); using (Image image = new Image(stream))
string encodeFilename = "TestOutput/Encode/" + Path.GetFileName(file);
using (FileStream output = File.OpenWrite(encodeFilename))
{ {
image.Save(output); string encodeFilename = "TestOutput/Encode/" + Path.GetFileName(file);
using (FileStream output = File.OpenWrite(encodeFilename))
{
image.Save(output);
}
} }
Trace.WriteLine($"{file} : {watch.ElapsedMilliseconds}ms"); Trace.WriteLine($"{file} : {watch.ElapsedMilliseconds}ms");
@ -56,30 +57,37 @@ namespace ImageProcessorCore.Tests
{ {
using (FileStream stream = File.OpenRead(file)) using (FileStream stream = File.OpenRead(file))
{ {
Image image = new Image(stream); using (Image image = new Image(stream))
IQuantizer quantizer = new OctreeQuantizer();
QuantizedImage quantizedImage = quantizer.Quantize(image, 256);
using (FileStream output = File.OpenWrite($"TestOutput/Quantize/Octree-{Path.GetFileName(file)}"))
{ {
quantizedImage.ToImage().Save(output, image.CurrentImageFormat); IQuantizer quantizer = new OctreeQuantizer();
} QuantizedImage quantizedImage = quantizer.Quantize(image, 256);
quantizer = new WuQuantizer(); using (FileStream output = File.OpenWrite($"TestOutput/Quantize/Octree-{Path.GetFileName(file)}"))
quantizedImage = quantizer.Quantize(image, 256); {
using (Image qi = quantizedImage.ToImage())
{
qi.Save(output, image.CurrentImageFormat);
}
}
using (FileStream output = File.OpenWrite($"TestOutput/Quantize/Wu-{Path.GetFileName(file)}")) quantizer = new WuQuantizer();
{ quantizedImage = quantizer.Quantize(image, 256);
quantizedImage.ToImage().Save(output, image.CurrentImageFormat);
}
quantizer = new PaletteQuantizer(); using (FileStream output = File.OpenWrite($"TestOutput/Quantize/Wu-{Path.GetFileName(file)}"))
quantizedImage = quantizer.Quantize(image, 256); {
quantizedImage.ToImage().Save(output, image.CurrentImageFormat);
}
using (FileStream output = File.OpenWrite($"TestOutput/Quantize/Palette-{Path.GetFileName(file)}")) quantizer = new PaletteQuantizer();
{ quantizedImage = quantizer.Quantize(image, 256);
quantizedImage.ToImage().Save(output, image.CurrentImageFormat);
using (FileStream output = File.OpenWrite($"TestOutput/Quantize/Palette-{Path.GetFileName(file)}"))
{
using (Image qi = quantizedImage.ToImage())
{
qi.Save(output, image.CurrentImageFormat);
}
}
} }
} }
} }
@ -97,26 +105,27 @@ namespace ImageProcessorCore.Tests
{ {
using (FileStream stream = File.OpenRead(file)) using (FileStream stream = File.OpenRead(file))
{ {
Image image = new Image(stream); using (Image image = new Image(stream))
using (FileStream output = File.OpenWrite($"TestOutput/Format/{Path.GetFileNameWithoutExtension(file)}.gif"))
{ {
image.SaveAsGif(output); using (FileStream output = File.OpenWrite($"TestOutput/Format/{Path.GetFileNameWithoutExtension(file)}.gif"))
} {
image.SaveAsGif(output);
}
using (FileStream output = File.OpenWrite($"TestOutput/Format/{Path.GetFileNameWithoutExtension(file)}.bmp")) using (FileStream output = File.OpenWrite($"TestOutput/Format/{Path.GetFileNameWithoutExtension(file)}.bmp"))
{ {
image.SaveAsBmp(output); image.SaveAsBmp(output);
} }
using (FileStream output = File.OpenWrite($"TestOutput/Format/{Path.GetFileNameWithoutExtension(file)}.jpg")) using (FileStream output = File.OpenWrite($"TestOutput/Format/{Path.GetFileNameWithoutExtension(file)}.jpg"))
{ {
image.SaveAsJpeg(output); image.SaveAsJpeg(output);
} }
using (FileStream output = File.OpenWrite($"TestOutput/Format/{Path.GetFileNameWithoutExtension(file)}.png")) using (FileStream output = File.OpenWrite($"TestOutput/Format/{Path.GetFileNameWithoutExtension(file)}.png"))
{ {
image.SaveAsPng(output); image.SaveAsPng(output);
}
} }
} }
} }
@ -134,22 +143,25 @@ namespace ImageProcessorCore.Tests
{ {
using (FileStream stream = File.OpenRead(file)) using (FileStream stream = File.OpenRead(file))
{ {
Image image = new Image(stream); using (Image image = new Image(stream))
byte[] serialized;
using (MemoryStream memoryStream = new MemoryStream())
{ {
image.Save(memoryStream); byte[] serialized;
memoryStream.Flush(); using (MemoryStream memoryStream = new MemoryStream())
serialized = memoryStream.ToArray(); {
} image.Save(memoryStream);
memoryStream.Flush();
using (MemoryStream memoryStream = new MemoryStream(serialized)) serialized = memoryStream.ToArray();
{ }
Image image2 = new Image(memoryStream);
using (FileStream output = File.OpenWrite($"TestOutput/Serialized/{Path.GetFileName(file)}")) using (MemoryStream memoryStream = new MemoryStream(serialized))
{ {
image2.Save(output); using (Image image2 = new Image(memoryStream))
{
using (FileStream output = File.OpenWrite($"TestOutput/Serialized/{Path.GetFileName(file)}"))
{
image2.Save(output);
}
}
} }
} }
} }

11
tests/ImageProcessorCore.Tests/Formats/PngTests.cs

@ -25,12 +25,13 @@ namespace ImageProcessorCore.Tests
{ {
using (FileStream stream = File.OpenRead(file)) using (FileStream stream = File.OpenRead(file))
{ {
Image image = new Image(stream); using (Image image = new Image(stream))
using (FileStream output = File.OpenWrite($"TestOutput/Encode/Png/{Path.GetFileNameWithoutExtension(file)}.png"))
{ {
image.Quality = 256; using (FileStream output = File.OpenWrite($"TestOutput/Encode/Png/{Path.GetFileNameWithoutExtension(file)}.png"))
image.Save(output, new PngFormat()); {
image.Quality = 256;
image.Save(output, new PngFormat());
}
} }
} }
} }

15
tests/ImageProcessorCore.Tests/Processors/Filters/FilterTests.cs

@ -70,15 +70,16 @@ namespace ImageProcessorCore.Tests
using (FileStream stream = File.OpenRead(file)) using (FileStream stream = File.OpenRead(file))
{ {
Stopwatch watch = Stopwatch.StartNew(); Stopwatch watch = Stopwatch.StartNew();
Image image = new Image(stream); using (Image image = new Image(stream))
string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"TestOutput/Filter/{ Path.GetFileName(filename) }"))
{ {
processor.OnProgress += this.ProgressUpdate; string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
image.Process(processor).Save(output); using (FileStream output = File.OpenWrite($"TestOutput/Filter/{Path.GetFileName(filename)}"))
processor.OnProgress -= this.ProgressUpdate; {
processor.OnProgress += this.ProgressUpdate;
image.Process(processor).Save(output);
processor.OnProgress -= this.ProgressUpdate;
}
} }
Trace.WriteLine($"{ name }: { watch.ElapsedMilliseconds}ms"); Trace.WriteLine($"{ name }: { watch.ElapsedMilliseconds}ms");
} }
} }

87
tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs

@ -60,14 +60,16 @@
{ {
Stopwatch watch = Stopwatch.StartNew(); Stopwatch watch = Stopwatch.StartNew();
Image image = new Image(stream); Image image = new Image(stream);
string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file); string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"TestOutput/Sample/{ Path.GetFileName(filename) }")) using (FileStream output = File.OpenWrite($"TestOutput/Sample/{Path.GetFileName(filename)}"))
{ {
processor.OnProgress += this.ProgressUpdate; processor.OnProgress += this.ProgressUpdate;
image = image.Process(image.Width / 2, image.Height / 2, processor); image = image.Process(image.Width / 2, image.Height / 2, processor);
image.Save(output); image.Save(output);
processor.OnProgress -= this.ProgressUpdate; processor.OnProgress -= this.ProgressUpdate;
} }
image.Dispose();
Trace.WriteLine($"{ name }: { watch.ElapsedMilliseconds}ms"); Trace.WriteLine($"{ name }: { watch.ElapsedMilliseconds}ms");
} }
@ -88,14 +90,15 @@
using (FileStream stream = File.OpenRead(file)) using (FileStream stream = File.OpenRead(file))
{ {
Stopwatch watch = Stopwatch.StartNew(); Stopwatch watch = Stopwatch.StartNew();
Image image = new Image(stream); using (Image image = new Image(stream))
string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"TestOutput/Resize/{filename}"))
{ {
image.Resize(image.Width / 2, image.Height / 2, sampler, false, this.ProgressUpdate) string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
.Save(output); using (FileStream output = File.OpenWrite($"TestOutput/Resize/{filename}"))
{
image.Resize(image.Width / 2, image.Height / 2, sampler, false, this.ProgressUpdate)
.Save(output);
}
} }
Trace.WriteLine($"{name}: {watch.ElapsedMilliseconds}ms"); Trace.WriteLine($"{name}: {watch.ElapsedMilliseconds}ms");
} }
} }
@ -116,12 +119,14 @@
using (FileStream stream = File.OpenRead(file)) using (FileStream stream = File.OpenRead(file))
{ {
Stopwatch watch = Stopwatch.StartNew(); Stopwatch watch = Stopwatch.StartNew();
Image image = new Image(stream); using (Image image = new Image(stream))
string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"TestOutput/Resize/{filename}"))
{ {
image.Resize(image.Width / 3, 0, new TriangleResampler(), false, this.ProgressUpdate) string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
.Save(output); using (FileStream output = File.OpenWrite($"TestOutput/Resize/{filename}"))
{
image.Resize(image.Width / 3, 0, new TriangleResampler(), false, this.ProgressUpdate)
.Save(output);
}
} }
Trace.WriteLine($"{name}: {watch.ElapsedMilliseconds}ms"); Trace.WriteLine($"{name}: {watch.ElapsedMilliseconds}ms");
@ -144,12 +149,14 @@
using (FileStream stream = File.OpenRead(file)) using (FileStream stream = File.OpenRead(file))
{ {
Stopwatch watch = Stopwatch.StartNew(); Stopwatch watch = Stopwatch.StartNew();
Image image = new Image(stream); using (Image image = new Image(stream))
string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"TestOutput/Resize/{filename}"))
{ {
image.Resize(0, image.Height / 3, new TriangleResampler(), false, this.ProgressUpdate) string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
.Save(output); using (FileStream output = File.OpenWrite($"TestOutput/Resize/{filename}"))
{
image.Resize(0, image.Height / 3, new TriangleResampler(), false, this.ProgressUpdate)
.Save(output);
}
} }
Trace.WriteLine($"{name}: {watch.ElapsedMilliseconds}ms"); Trace.WriteLine($"{name}: {watch.ElapsedMilliseconds}ms");
@ -171,12 +178,14 @@
using (FileStream stream = File.OpenRead(file)) using (FileStream stream = File.OpenRead(file))
{ {
Stopwatch watch = Stopwatch.StartNew(); Stopwatch watch = Stopwatch.StartNew();
Image image = new Image(stream); using (Image image = new Image(stream))
string filename = Path.GetFileNameWithoutExtension(file) + "-" + rotateType + flipType + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"TestOutput/RotateFlip/{filename}"))
{ {
image.RotateFlip(rotateType, flipType, this.ProgressUpdate) string filename = Path.GetFileNameWithoutExtension(file) + "-" + rotateType + flipType
.Save(output); + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"TestOutput/RotateFlip/{filename}"))
{
image.RotateFlip(rotateType, flipType, this.ProgressUpdate).Save(output);
}
} }
Trace.WriteLine($"{rotateType + "-" + flipType}: {watch.ElapsedMilliseconds}ms"); Trace.WriteLine($"{rotateType + "-" + flipType}: {watch.ElapsedMilliseconds}ms");
@ -198,13 +207,15 @@
using (FileStream stream = File.OpenRead(file)) using (FileStream stream = File.OpenRead(file))
{ {
Stopwatch watch = Stopwatch.StartNew(); Stopwatch watch = Stopwatch.StartNew();
Image image = new Image(stream); using (Image image = new Image(stream))
string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"TestOutput/Rotate/{filename}"))
{ {
image.Rotate(45, sampler, false, this.ProgressUpdate) string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
//.BackgroundColor(Color.Aqua) using (FileStream output = File.OpenWrite($"TestOutput/Rotate/{filename}"))
.Save(output); {
image.Rotate(45, sampler, false, this.ProgressUpdate)
//.BackgroundColor(Color.Aqua)
.Save(output);
}
} }
Trace.WriteLine($"{name}: {watch.ElapsedMilliseconds}ms"); Trace.WriteLine($"{name}: {watch.ElapsedMilliseconds}ms");
@ -224,11 +235,13 @@
{ {
using (FileStream stream = File.OpenRead(file)) using (FileStream stream = File.OpenRead(file))
{ {
Image image = new Image(stream); using (Image image = new Image(stream))
string filename = Path.GetFileNameWithoutExtension(file) + "-EntropyCrop" + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"TestOutput/EntropyCrop/{filename}"))
{ {
image.EntropyCrop(.5f, this.ProgressUpdate).Save(output); string filename = Path.GetFileNameWithoutExtension(file) + "-EntropyCrop" + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"TestOutput/EntropyCrop/{filename}"))
{
image.EntropyCrop(.5f, this.ProgressUpdate).Save(output);
}
} }
} }
} }
@ -246,11 +259,13 @@
{ {
using (FileStream stream = File.OpenRead(file)) using (FileStream stream = File.OpenRead(file))
{ {
Image image = new Image(stream); using (Image image = new Image(stream))
string filename = Path.GetFileNameWithoutExtension(file) + "-Crop" + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"TestOutput/Crop/{filename}"))
{ {
image.Crop(image.Width / 2, image.Height / 2, this.ProgressUpdate).Save(output); string filename = Path.GetFileNameWithoutExtension(file) + "-Crop" + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"TestOutput/Crop/{filename}"))
{
image.Crop(image.Width / 2, image.Height / 2, this.ProgressUpdate).Save(output);
}
} }
} }
} }

Loading…
Cancel
Save